So, I’m out of ideas and options here. I tried to google it up but the few posts and reddit subs I found were’nt really helpful. I currently try to connect the Sensirion Sen55 to my Home Assistant using an ESP32 flashed with ESPHome. To connect the Sen55 to the ESP32 I used the Adafruit Breaker Board in the middle. The Breaker Board and the Sen55 are connected with the JST GH 6 Pin Cable and the breaker board and the ESP32 are connected through a JST SH 4 Pin Cable. The ESP32 and the adafruit LEDs are working. Cables are 150mm and 100mm long. Nothing else on the breaker board or ESP32.
ESP32 board: ESP32 NodeMCU Development Board
Adafruit adapter board: Adafruit SEN54 or SEN55 Adapter Breakout - STEMMA QT / Qwiic : ID 5964 : Adafruit Industries, Unique & fun DIY electronics and kits
Dupont Cable: SparkFun Qwiic - flexibles Adapterkabel, 4 Pin Dupont Female, 150mm - kaufen bei BerryBase
I connected:
GND → GND
VIN → 3V3
SDA → GP16 (I also tried 21)
SCL → GP17 (I also tried 22)
The EPS32 is powered through a 5V power supply.
To clarify, the ESP32 can connect to ESPHome and HA and will show up as a device but without devices and entities. And as the logs below show, the SEN5X can’t connect and therefore there is no data.
I already tried:
- changing GPIOs
- re-installing ESPHome
- removing the address: 0x69 (as currently shown in YAML)
- lowering the frequency to 10 kHz
- changing the different GNDs on the ESP32
Here is my YAML:
substitutions:
# --- Grenzwerte für Ampelanzeige ---
pm1_green_max: "10"
pm1_yellow_max: "25"
pm25_green_max: "15"
pm25_yellow_max: "35"
pm10_green_max: "45"
pm10_yellow_max: "100"
voc_green_max: "100"
voc_yellow_max: "200"
nox_green_max: "100"
nox_yellow_max: "200"
esphome:
name: luftsensor-stromraum
friendly_name: Luftsensor Stromraum
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: redacted
ota:
- platform: esphome
password: redacted
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Luftsensor-Stromraum"
password: redacted
captive_portal:
# ---------- Hardware-Bus zum SEN55 ----------
i2c:
sda: 16
scl: 17
scan: true
# ---------- interne Speicherwerte für Ampel-Logik ----------
globals:
- id: pm1_val
type: float
restore_value: no
initial_value: '0.0'
- id: pm25_val
type: float
restore_value: no
initial_value: '0.0'
- id: pm10_val
type: float
restore_value: no
initial_value: '0.0'
- id: voc_val
type: float
restore_value: no
initial_value: '0.0'
- id: nox_val
type: float
restore_value: no
initial_value: '0.0'
# ---------- eigentliche Messwerte vom SEN55 ----------
sensor:
- platform: sen5x
# address: 0x69
pm_1_0:
name: "Feinstaub PM1.0"
id: pm1
unit_of_measurement: "µg/m³"
icon: mdi:blur
filters:
- median:
window_size: 5
send_every: 1
pm_2_5:
name: "Feinstaub PM2.5"
id: pm25
unit_of_measurement: "µg/m³"
icon: mdi:blur
filters:
- median:
window_size: 5
send_every: 1
pm_10_0:
name: "Feinstaub PM10"
id: pm10
unit_of_measurement: "µg/m³"
icon: mdi:blur
filters:
- median:
window_size: 5
send_every: 1
humidity:
name: "Luftfeuchtigkeit"
unit_of_measurement: "%"
icon: mdi:water-percent
temperature:
name: "Temperatur"
unit_of_measurement: "°C"
icon: mdi:thermometer
voc:
name: "VOC Index"
id: voc_index
icon: mdi:chemical-weapon
filters:
- median:
window_size: 5
send_every: 1
nox:
name: "NOx Index"
id: nox_index
icon: mdi:smog
filters:
- median:
window_size: 5
send_every: 1
# Spiegel die aktuellen Messwerte in globale Variablen,
# damit wir sie in der Ampel-Logik sauber vergleichen können.
- platform: template
id: pm1_shadow
internal: true
update_interval: 5s
lambda: |-
id(pm1_val) = id(pm1).state;
id(pm25_val) = id(pm25).state;
id(pm10_val) = id(pm10).state;
id(voc_val) = id(voc_index).state;
id(nox_val) = id(nox_index).state;
return 0;
# ---------- Text-Sensoren für die Farbstufen (gruen / gelb / rot) ----------
text_sensor:
- platform: template
name: "PM1.0 Ampel"
icon: mdi:traffic-light
update_interval: 5s
lambda: |-
const float g = atof("${pm1_green_max}");
const float y = atof("${pm1_yellow_max}");
float v = id(pm1_val);
if (v < g) return {"gruen"};
else if (v <= y) return {"gelb"};
else return {"rot"};
- platform: template
name: "PM2.5 Ampel"
icon: mdi:traffic-light
update_interval: 5s
lambda: |-
const float g = atof("${pm25_green_max}");
const float y = atof("${pm25_yellow_max}");
float v = id(pm25_val);
if (v < g) return {"gruen"};
else if (v <= y) return {"gelb"};
else return {"rot"};
- platform: template
name: "PM10 Ampel"
icon: mdi:traffic-light
update_interval: 5s
lambda: |-
const float g = atof("${pm10_green_max}");
const float y = atof("${pm10_yellow_max}");
float v = id(pm10_val);
if (v < g) return {"gruen"};
else if (v <= y) return {"gelb"};
else return {"rot"};
- platform: template
name: "VOC Ampel"
icon: mdi:traffic-light
update_interval: 5s
lambda: |-
const float g = atof("${voc_green_max}");
const float y = atof("${voc_yellow_max}");
float v = id(voc_val);
if (v < g) return {"gruen"};
else if (v <= y) return {"gelb"};
else return {"rot"};
- platform: template
name: "NOx Ampel"
icon: mdi:traffic-light
update_interval: 5s
lambda: |-
const float g = atof("${nox_green_max}");
const float y = atof("${nox_yellow_max}");
float v = id(nox_val);
if (v < g) return {"gruen"};
else if (v <= y) return {"gelb"};
else return {"rot"};
- platform: template
name: "Luftstatus Gesamt"
icon: mdi:air-filter
update_interval: 5s
lambda: |-
auto worst = std::string("gruen");
auto worse = [&](std::string a, std::string b)->std::string{
auto rank = [&](std::string s)->int{
if (s == "gruen") return 0;
if (s == "gelb") return 1;
return 2;
};
return (rank(a) >= rank(b)) ? a : b;
};
auto eval = [&](float v, float g, float y)->std::string{
if (v < g) return "gruen";
else if (v <= y) return "gelb";
else return "rot";
};
worst = worse(worst, eval(id(pm1_val), atof("${pm1_green_max}"), atof("${pm1_yellow_max}")));
worst = worse(worst, eval(id(pm25_val), atof("${pm25_green_max}"), atof("${pm25_yellow_max}")));
worst = worse(worst, eval(id(pm10_val), atof("${pm10_green_max}"), atof("${pm10_yellow_max}")));
worst = worse(worst, eval(id(voc_val), atof("${voc_green_max}"), atof("${voc_yellow_max}")));
worst = worse(worst, eval(id(nox_val), atof("${nox_green_max}"), atof("${nox_yellow_max}")));
return {worst.c_str()};
# ---------- Binäre Komfort-/Sicherheits-Flags für Home Assistant ----------
binary_sensor:
- platform: template
name: "Luft OK (ohne Maske)"
device_class: safety
lambda: |-
return id(pm1_val) < atof("${pm1_green_max}")
&& id(pm25_val) < atof("${pm25_green_max}")
&& id(pm10_val) < atof("${pm10_green_max}")
&& id(voc_val) < atof("${voc_green_max}")
&& id(nox_val) < atof("${nox_green_max}");
filters:
- delayed_on: 10s
- delayed_off: 10s
- platform: template
name: "Luft belastet (Maske empfohlen)"
device_class: safety
lambda: |-
bool any_yellow =
((id(pm1_val) >= atof("${pm1_green_max}") && id(pm1_val) <= atof("${pm1_yellow_max}")) ||
(id(pm25_val) >= atof("${pm25_green_max}") && id(pm25_val) <= atof("${pm25_yellow_max}")) ||
(id(pm10_val) >= atof("${pm10_green_max}") && id(pm10_val) <= atof("${pm10_yellow_max}")) ||
(id(voc_val) >= atof("${voc_green_max}") && id(voc_val) <= atof("${voc_yellow_max}")) ||
(id(nox_val) >= atof("${nox_green_max}") && id(nox_val) <= atof("${nox_yellow_max}")));
bool all_green =
id(pm1_val) < atof("${pm1_green_max}")
&& id(pm25_val) < atof("${pm25_green_max}")
&& id(pm10_val) < atof("${pm10_green_max}")
&& id(voc_val) < atof("${voc_green_max}")
&& id(nox_val) < atof("${nox_green_max}");
return any_yellow && !all_green;
filters:
- delayed_on: 10s
- delayed_off: 10s
- platform: template
name: "Luft stark belastet (Tür zu, Maske Pflicht)"
device_class: safety
lambda: |-
return id(pm1_val) > atof("${pm1_yellow_max}")
|| id(pm25_val) > atof("${pm25_yellow_max}")
|| id(pm10_val) > atof("${pm10_yellow_max}")
|| id(voc_val) > atof("${voc_yellow_max}")
|| id(nox_val) > atof("${nox_yellow_max}");
filters:
- delayed_on: 5s
- delayed_off: 10s
Here is my log:
INFO ESPHome 2025.10.3
INFO Reading configuration /config/esphome/luftsensor-stromraum.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing luftsensor-stromraum (board: esp32dev; framework: espidf; platform: https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-2/platform-espressif32.zip)
--------------------------------------------------------------------------------
INFO Package configuration completed successfully
INFO Package configuration completed successfully
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
- framework-espidf @ 3.50402.0 (5.4.2)
- tool-cmake @ 3.30.2
- tool-esp-rom-elfs @ 2024.10.11
- tool-esptoolpy @ 5.0.2
- tool-mklittlefs @ 3.2.0
- tool-ninja @ 1.13.1
- tool-scons @ 4.40801.0 (4.8.1)
- toolchain-xtensa-esp-elf @ 14.2.0+20241119
Reading CMake configuration...
Dependency Graph
|-- noise-c @ 0.1.10
RAM: [= ] 10.5% (used 34484 bytes from 327680 bytes)
Flash: [===== ] 51.6% (used 947246 bytes from 1835008 bytes)
========================= [SUCCESS] Took 20.88 seconds =========================
INFO Successfully compiled program.
INFO Connecting to 192.168.68.84 port 3232...
INFO Connected to 192.168.68.84
INFO Uploading /data/build/luftsensor-stromraum/.pioenvs/luftsensor-stromraum/firmware.bin (947648 bytes)
Uploading: [============================================================] 100% Done...
INFO Upload took 17.33 seconds, waiting for result...
INFO OTA successful
INFO Successfully uploaded program.
INFO Starting log output from 192.168.68.84 using esphome API
INFO Successfully resolved luftsensor-stromraum @ 192.168.68.84 in 0.000s
INFO Successfully connected to luftsensor-stromraum @ 192.168.68.84 in 4.168s
INFO Successful handshake with luftsensor-stromraum @ 192.168.68.84 in 0.435s
[19:08:49.791][I][app:185]: ESPHome version 2025.10.3 compiled on Oct 30 2025, 19:02:15
[19:08:49.791][C][wifi:679]: WiFi:
[19:08:49.791][C][wifi:458]: Local MAC: redacted
[19:08:49.792][C][wifi:465]: IP Address: 192.168.68.84
[19:08:49.797][C][wifi:469]: SSID: [redacted]
[19:08:49.797][C][wifi:469]: BSSID: [redacted]
[19:08:49.797][C][wifi:469]: Hostname: 'luftsensor-stromraum'
[19:08:49.797][C][wifi:469]: Signal strength: -58 dB ▂▄▆█
[19:08:49.797][C][wifi:469]: Channel: 5
[19:08:49.797][C][wifi:469]: Subnet: redacted
[19:08:49.797][C][wifi:469]: Gateway: redacted
[19:08:49.797][C][wifi:469]: DNS1: redacted
[19:08:49.797][C][wifi:469]: DNS2: redacted
[19:08:49.886][C][logger:261]: Logger:
[19:08:49.886][C][logger:261]: Max Level: DEBUG
[19:08:49.886][C][logger:261]: Initial Level: DEBUG
[19:08:49.886][C][logger:267]: Log Baud Rate: 115200
[19:08:49.886][C][logger:267]: Hardware UART: UART0
[19:08:49.887][C][logger:274]: Task Log Buffer Size: 768
[19:08:49.887][C][i2c.idf:081]: I2C Bus:
[19:08:49.887][C][i2c.idf:082]: SDA Pin: GPIO16
[19:08:49.887][C][i2c.idf:082]: SCL Pin: GPIO17
[19:08:49.887][C][i2c.idf:082]: Frequency: 50000 Hz
[19:08:49.887][C][i2c.idf:092]: Recovery: bus successfully recovered
[19:08:49.888][C][i2c.idf:102]: Results from bus scan:
[19:08:49.888][C][i2c.idf:104]: Found no devices
[19:08:49.888][C][template.sensor:015]: Template Sensor 'pm1_shadow'
[19:08:49.888][C][template.sensor:015]: State Class: ''
[19:08:49.888][C][template.sensor:015]: Unit of Measurement: ''
[19:08:49.888][C][template.sensor:015]: Accuracy Decimals: 1
[19:08:49.889][C][template.sensor:362]: Update Interval: 5.0s
[19:08:49.889][C][template.text_sensor:014]: Template Sensor 'PM1.0 Ampel'
[19:08:49.889][C][template.text_sensor:021]: Icon: 'mdi:traffic-light'
[19:08:49.970][C][template.text_sensor:014]: Template Sensor 'PM2.5 Ampel'
[19:08:49.970][C][template.text_sensor:021]: Icon: 'mdi:traffic-light'
[19:08:49.970][C][template.text_sensor:014]: Template Sensor 'PM10 Ampel'
[19:08:49.971][C][template.text_sensor:021]: Icon: 'mdi:traffic-light'
[19:08:49.971][C][template.text_sensor:014]: Template Sensor 'VOC Ampel'
[19:08:49.971][C][template.text_sensor:021]: Icon: 'mdi:traffic-light'
[19:08:49.971][C][template.text_sensor:014]: Template Sensor 'NOx Ampel'
[19:08:49.971][C][template.text_sensor:021]: Icon: 'mdi:traffic-light'
[19:08:49.971][C][template.text_sensor:014]: Template Sensor 'Luftstatus Gesamt'
[19:08:49.972][C][template.text_sensor:021]: Icon: 'mdi:air-filter'
[19:08:49.972][C][template.binary_sensor:016]: Template Binary Sensor 'Luft OK (ohne Maske)'
[19:08:49.973][C][template.binary_sensor:019]: Device Class: 'safety'
[19:08:49.990][C][template.binary_sensor:016]: Template Binary Sensor 'Luft belastet (Maske empfohlen)'
[19:08:49.991][C][template.binary_sensor:019]: Device Class: 'safety'
[19:08:50.090][C][template.binary_sensor:016]: Template Binary Sensor 'Luft stark belastet (Tür zu, Maske Pflicht)'
[19:08:50.090][C][template.binary_sensor:019]: Device Class: 'safety'
[19:08:50.090][C][sen5x:253]: SEN5X:
[19:08:50.091][C][sen5x:254]: Address: 0x69
[19:08:50.091][W][sen5x:258]: Communication failed
[19:08:50.091][C][sen5x:277]: Product name:
[19:08:50.091][C][sen5x:277]: Firmware version: 0
[19:08:50.091][C][sen5x:277]: Serial number 00.00.00
[19:08:50.091][C][sen5x:362]: Update Interval: 60.0s
[19:08:50.092][C][sen5x:015]: PM 1.0 'Feinstaub PM1.0'
[19:08:50.092][C][sen5x:015]: State Class: 'measurement'
[19:08:50.092][C][sen5x:015]: Unit of Measurement: 'µg/m³'
[19:08:50.092][C][sen5x:015]: Accuracy Decimals: 2
[19:08:50.092][C][sen5x:025]: Device Class: 'pm1'
[19:08:50.097][C][sen5x:029]: Icon: 'mdi:blur'
[19:08:50.097][C][sen5x:015]: PM 2.5 'Feinstaub PM2.5'
[19:08:50.097][C][sen5x:015]: State Class: 'measurement'
[19:08:50.097][C][sen5x:015]: Unit of Measurement: 'µg/m³'
[19:08:50.097][C][sen5x:015]: Accuracy Decimals: 2
[19:08:50.098][C][sen5x:025]: Device Class: 'pm25'
[19:08:50.098][C][sen5x:029]: Icon: 'mdi:blur'
[19:08:50.099][C][sen5x:015]: PM 10.0 'Feinstaub PM10'
[19:08:50.099][C][sen5x:015]: State Class: 'measurement'
[19:08:50.099][C][sen5x:015]: Unit of Measurement: 'µg/m³'
[19:08:50.099][C][sen5x:015]: Accuracy Decimals: 2
[19:08:50.099][C][sen5x:025]: Device Class: 'pm10'
[19:08:50.172][C][sen5x:029]: Icon: 'mdi:blur'
[19:08:50.172][C][sen5x:015]: Temperature 'Temperatur'
[19:08:50.172][C][sen5x:015]: State Class: 'measurement'
[19:08:50.172][C][sen5x:015]: Unit of Measurement: '°C'
[19:08:50.172][C][sen5x:015]: Accuracy Decimals: 2
[19:08:50.173][C][sen5x:025]: Device Class: 'temperature'
[19:08:50.173][C][sen5x:029]: Icon: 'mdi:thermometer'
[19:08:50.173][C][sen5x:015]: Humidity 'Luftfeuchtigkeit'
[19:08:50.173][C][sen5x:015]: State Class: 'measurement'
[19:08:50.173][C][sen5x:015]: Unit of Measurement: '%'
[19:08:50.173][C][sen5x:015]: Accuracy Decimals: 2
[19:08:50.174][C][sen5x:025]: Device Class: 'humidity'
[19:08:50.174][C][sen5x:029]: Icon: 'mdi:water-percent'
[19:08:50.174][C][sen5x:015]: VOC 'VOC Index'
[19:08:50.174][C][sen5x:015]: State Class: 'measurement'
[19:08:50.174][C][sen5x:015]: Unit of Measurement: ''
[19:08:50.174][C][sen5x:015]: Accuracy Decimals: 0
[19:08:50.175][C][sen5x:025]: Device Class: 'aqi'
[19:08:50.176][C][sen5x:029]: Icon: 'mdi:chemical-weapon'
[19:08:50.176][C][sen5x:015]: NOx 'NOx Index'
[19:08:50.176][C][sen5x:015]: State Class: 'measurement'
[19:08:50.176][C][sen5x:015]: Unit of Measurement: ''
[19:08:50.176][C][sen5x:015]: Accuracy Decimals: 0
[19:08:50.186][C][sen5x:025]: Device Class: 'aqi'
[19:08:50.187][C][sen5x:029]: Icon: 'mdi:smog'
[19:08:50.193][E][component:154]: sen5x.sensor is marked FAILED: unspecified
[19:08:50.297][C][captive_portal:116]: Captive Portal:
[19:08:50.298][D][text_sensor:085]: 'PM10 Ampel': Sending state 'rot'
[19:08:50.298][C][esphome.ota:093]: Over-The-Air updates:
[19:08:50.298][C][esphome.ota:093]: Address: luftsensor-stromraum.local:3232
[19:08:50.298][C][esphome.ota:093]: Version: 2
[19:08:50.298][C][esphome.ota:100]: Password configured
[19:08:50.299][C][safe_mode:018]: Safe Mode:
[19:08:50.299][C][safe_mode:018]: Successful after: 60s
[19:08:50.299][C][safe_mode:018]: Invoke after: 10 attempts
[19:08:50.299][C][safe_mode:018]: Duration: 300s
[19:08:50.299][C][web_server.ota:241]: Web Server OTA
[19:08:50.299][C][api:222]: Server:
[19:08:50.299][C][api:222]: Address: luftsensor-stromraum.local:6053
[19:08:50.299][C][api:222]: Listen backlog: 4
[19:08:50.299][C][api:222]: Max connections: 8
[19:08:50.300][C][api:229]: Noise encryption: YES
[19:08:50.309][C][mdns:179]: mDNS:
[19:08:50.309][C][mdns:179]: Hostname: luftsensor-stromraum








