-
-
Save JavanXD/696d026ef202a7d6455ed4745df63e39 to your computer and use it in GitHub Desktop.
| substitutions: | |
| name: esphome-ledaluc2 | |
| friendly_name: ESPHome LEDA LUC2 | |
| esphome: | |
| name: ${name} | |
| friendly_name: ${friendly_name} | |
| min_version: 2023.6.0 # Use a stable ESPHome version for compatibility | |
| name_add_mac_suffix: false # Prevent adding MAC suffix to the device name | |
| project: | |
| name: esphome.web | |
| version: dev # Version of the project | |
| esp32: | |
| board: esp32dev # Specify the ESP32 development board | |
| framework: | |
| type: arduino # Use the Arduino framework | |
| # Enable logging for debugging purposes | |
| logger: | |
| # Enable Home Assistant API with encryption key from secrets.yaml | |
| api: | |
| encryption: | |
| key: !secret api_key | |
| # Enable over-the-air updates for firmware | |
| ota: | |
| platform: esphome | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Fallback hotspot in case Wi-Fi connection fails | |
| ap: | |
| ssid: "ESPHome-LEDALUC2" | |
| password: !secret ap_password | |
| # Allow Wi-Fi provisioning via serial connection | |
| improv_serial: | |
| # Enable captive portal for Wi-Fi provisioning via the fallback hotspot | |
| captive_portal: | |
| # Import specific components from an example configuration without overwriting local settings | |
| dashboard_import: | |
| package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main | |
| import_full_config: false | |
| # Host a simple web server (e.g., for Improv Wi-Fi) | |
| web_server: | |
| # Configure the SPI interface for the MCP2515 CAN bus module | |
| spi: | |
| clk_pin: GPIO22 # Clock pin | |
| miso_pin: GPIO17 # Master In Slave Out pin | |
| mosi_pin: GPIO21 # Master Out Slave In pin | |
| # Configure the CAN bus using the MCP2515 module | |
| canbus: | |
| - platform: mcp2515 | |
| cs_pin: GPIO16 | |
| can_id: 0x28A | |
| bit_rate: 125KBPS | |
| on_frame: | |
| - can_id: 0x28A | |
| then: | |
| - lambda: |- | |
| // Log all received CAN frames for debugging | |
| if (x.size() > 0 && x.size() < 8) { | |
| // Log the received frame data safely | |
| std::string frame_data; | |
| for (size_t i = 0; i < x.size(); i++) { | |
| char byte_str[5]; | |
| snprintf(byte_str, sizeof(byte_str), "0x%02X ", x[i]); | |
| frame_data += byte_str; | |
| } | |
| // Log the frame data in a single line | |
| ESP_LOGD("CAN", "Received CAN Frame (Size: %d bytes): %s", x.size(), frame_data.c_str()); | |
| // Check for specific sizes and log states | |
| if (x.size() == 2) { | |
| ESP_LOGI("CAN", "Interpreted State: Ventilation turned OFF via Display"); | |
| } else if (x.size() == 1) { | |
| ESP_LOGI("CAN", "Interpreted State: Ventilation turned ON via Display"); | |
| } else { | |
| ESP_LOGW("CAN", "Interpreted State: Unknown or Additional Data"); | |
| } | |
| } | |
| else if (x.size() == 8) { | |
| uint8_t frame_type = x[0]; // First byte determines the frame type | |
| // Frame type 0x00: Pressure difference and exhaust temperature | |
| if (frame_type == 0x00) { | |
| // Extract pressure difference from Byte 2 | |
| float pressure_difference = x[1] * 0.1f; // Convert to Pascals | |
| // Check for adjustment flag in Byte 3 | |
| if (x[2] == 0x81) { | |
| const float PRESSURE_ADJUSTMENT_VALUE = 25.5f; | |
| pressure_difference += PRESSURE_ADJUSTMENT_VALUE; | |
| ESP_LOGI("CAN", "Adjustment Applied: +%.1f Pa", PRESSURE_ADJUSTMENT_VALUE); | |
| } | |
| // Extract exhaust temperature from Byte 4 | |
| uint16_t raw_temp = x[3] | (x[4] << 8); // Little Endian | |
| float exhaust_temperature = static_cast<float>(raw_temp); // °C | |
| // Log decoded values | |
| ESP_LOGI("CAN", "Pressure: %.1f Pa, Temperature: %.1f °C", pressure_difference, exhaust_temperature); | |
| // Publish to sensors | |
| id(pressure_difference_sensor).publish_state(pressure_difference); | |
| id(exhaust_temperature_sensor).publish_state(exhaust_temperature); | |
| } | |
| // Frame type 0x01: Ventilation status | |
| else if (frame_type == 0x01) { | |
| // Extract ventilation status from Byte 6 | |
| bool ventilation_active = (x[5] == 0x01); | |
| // Log ventilation status | |
| ESP_LOGI("CAN", "Ventilation Active: %s", ventilation_active ? "Yes" : "No"); | |
| ESP_LOGD("CAN", "Ventilation Bytes: Data=%02X %02X %02X %02X %02X %02X %02X %02X", | |
| x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); | |
| // Publish to binary sensor | |
| id(ventilation_status_sensor).publish_state(ventilation_active); | |
| } | |
| // Frame type 0x09: Heartbeat signal | |
| else if (frame_type == 0x09) { | |
| // Log loop and counter for reference | |
| ESP_LOGD("CAN", "Heartbeat (0x09): Loop=%02X, Counter=%02X", x[2], x[1]); | |
| } | |
| // Frame type 0x55: Heartbeat signal | |
| else if (frame_type == 0x55) { | |
| // Log loop and counter for reference | |
| ESP_LOGD("CAN", "Heartbeat (0x55): Loop=%02X, Counter=%02X", x[2], x[1]); | |
| } | |
| else if (frame_type == 0x80) { | |
| ESP_LOGW("CAN", "Error Frame (0x80): Data=%02X %02X %02X %02X %02X %02X %02X %02X", | |
| x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); | |
| } | |
| else if (frame_type == 0x81) { | |
| ESP_LOGW("CAN", "Error Frame (0x81): Data=%02X %02X %02X %02X %02X %02X %02X %02X", | |
| x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); | |
| } | |
| // Handle unknown frame types | |
| else { | |
| ESP_LOGW("CAN", "Unknown Frame Type. Data=%02X %02X %02X %02X %02X %02X %02X %02X", | |
| x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); | |
| } | |
| } else { | |
| ESP_LOGW("CAN", "Unexpected frame size: %d bytes", x.size()); | |
| } | |
| # Define template sensors to hold the values published from the CAN bus data | |
| sensor: | |
| - platform: template | |
| name: "Pressure Difference" | |
| id: pressure_difference_sensor | |
| unit_of_measurement: "Pa" | |
| accuracy_decimals: 1 | |
| device_class: pressure # Device class for pressure sensors | |
| icon: "mdi:air-filter" # Optional custom icon for visual clarity | |
| filters: | |
| - throttle_average: 3s | |
| - platform: template | |
| name: "Exhaust Temperature" | |
| id: exhaust_temperature_sensor | |
| unit_of_measurement: "°C" | |
| accuracy_decimals: 1 | |
| device_class: temperature # Device class for temperature sensors | |
| icon: "mdi:thermometer" | |
| filters: | |
| - throttle_average: 3s | |
| binary_sensor: | |
| - platform: template | |
| name: "Ventilation Status" | |
| id: ventilation_status_sensor | |
| device_class: running # Device class for indicating system activity | |
| icon: "mdi:fan" # Optional custom icon for ventilation |
@Hooorny vielen Dank! ...wollte zuerst meine Spannungsquelle genauer einstellen aber mit deinem Filter läuft es auch stabil.
Bei mir bleibt der MCP immer noch ab und zu nach einigen Stunden hängen ... deswegen habe ich einen Watchdog eingebaut, der den Restart-Button auslöst, wenn 1 Minute keine CAN-Daten "empfangen" wurden. Funktioniert für mich jetzt ausreichend gut ;)
# --- Watchdog: merkt sich, wann zuletzt ein Frame kam
globals:
- id: can_last_seen_ms
type: uint32_t
restore_value: no
initial_value: '0'
interval:
- interval: 10s
then:
- lambda: |-
const uint32_t now = millis();
if (id(can_last_seen_ms) != 0 && (now - id(can_last_seen_ms)) > 60000) { // > 1 min ohne Frames
ESP_LOGW("CAN", "No CAN frames for 1 minute -> Restarting node");
id(restart_btn).press();
}
# Configure the CAN bus using the MCP2515 module
canbus:
- platform: mcp2515
cs_pin: GPIO16
id: ledaluc2_canbus
can_id: 0x28A
clock: 8MHZ
bit_rate: 125KBPS
on_frame:
can_id: 0x28A
then:
- lambda: |-
// Zeitpunkt merken (für Watchdog)
id(can_last_seen_ms) = millis();
// Kompakter, allokationsarmer Dump statt eigener String-Bastelei
ESP_LOGD("CAN", "RX: %s", format_hex_pretty(x).c_str());
// Kleiner Bounds-Helper
auto b = [&](size_t i)->uint8_t { return i < x.size() ? x[i] : 0; };
if (x.size() > 0 && x.size() < 8) {
if (x.size() == 2) {
ESP_LOGI("CAN", "Interpreted State: Ventilation turned OFF via Display");
} else if (x.size() == 1) {
ESP_LOGI("CAN", "Interpreted State: Ventilation turned ON via Display");
} else {
ESP_LOGW("CAN", "Interpreted State: Unknown or Additional Data");
}
}
else if (x.size() == 8) {
uint8_t frame_type = b(0);
if (frame_type == 0x00) {
// Byte1: Druck *0.1 Pa
float pressure_difference = b(1) * 0.1f;
if (b(2) == 0x81) {
const float PRESSURE_ADJUSTMENT_VALUE = 25.5f;
pressure_difference += PRESSURE_ADJUSTMENT_VALUE;
ESP_LOGI("CAN", "Adjustment Applied: +%.1f Pa", PRESSURE_ADJUSTMENT_VALUE);
}
uint16_t raw_temp = b(3) | (uint16_t(b(4)) << 8);
float exhaust_temperature = static_cast<float>(raw_temp);
ESP_LOGI("CAN", "Pressure: %.1f Pa, Temperature: %.1f °C",
pressure_difference, exhaust_temperature);
id(pressure_difference_sensor).publish_state(pressure_difference);
id(exhaust_temperature_sensor).publish_state(exhaust_temperature);
}
else if (frame_type == 0x01) {
bool ventilation_active = (b(5) == 0x01);
ESP_LOGI("CAN", "Ventilation Active: %s", ventilation_active ? "Yes" : "No");
ESP_LOGD("CAN", "Ventilation Bytes: %s", format_hex_pretty(x).c_str());
id(ventilation_status_sensor).publish_state(ventilation_active);
}
else if (frame_type == 0x09 || frame_type == 0x55) {
ESP_LOGD("CAN", "Heartbeat (0x%02X): Loop=%02X, Counter=%02X", frame_type, b(2), b(1));
}
else if (frame_type == 0x80 || frame_type == 0x81) {
ESP_LOGW("CAN", "Error Frame (0x%02X): %s", frame_type, format_hex_pretty(x).c_str());
}
else {
ESP_LOGW("CAN", "Unknown Frame Type: %s", format_hex_pretty(x).c_str());
}
} else {
ESP_LOGW("CAN", "Unexpected frame size: %d bytes", x.size());
}
# Define template sensors to hold the values published from the CAN bus data
sensor:
- platform: template
name: "Pressure Difference"
id: pressure_difference_sensor
unit_of_measurement: "Pa"
accuracy_decimals: 1
device_class: pressure # Device class for pressure sensors
state_class: measurement
icon: "mdi:air-filter" # Optional custom icon for visual clarity
update_interval: never
filters:
- timeout: 20s # sent value will be NaN
- median:
window_size: 5
send_every: 3
send_first_at: 1
- platform: template
name: "Exhaust Temperature"
id: exhaust_temperature_sensor
unit_of_measurement: "°C"
accuracy_decimals: 1
device_class: temperature # Device class for temperature sensors
state_class: measurement
icon: "mdi:thermometer"
update_interval: never
filters:
- timeout: 20s # sent value will be NaN
- median:
window_size: 5
send_every: 3
send_first_at: 1
binary_sensor:
- platform: template
name: "Ventilation Status"
id: ventilation_status_sensor
device_class: running # Device class for indicating system activity
icon: "mdi:fan" # Optional custom icon for ventilation
filters:
- delayed_on_off: 1s
button:
- platform: restart
id: restart_btn
name: "CAN-Sniffer Restart"
Auch ich kann endlich Erfolg vermelden. Bei mir lag es auch an der Spannungsversorgung, es dauerte etwas länger da auch das getestete USB Netzteil nicht geholfen hat. Ich habe noch 1yF Tantalelko auf die Platine gelötet und mittel Labornetzteil alles zum laufen bekommen. Die Schaltung kommt jetzt in den Keller und wird an ein Hutschienen Netzteil angeschlossen. Wenn auch das läuft poste ich gern noch mal den Netzteiltyp.
Vielen Dank an alle die hier gepostet haben, hat mir sehr geholfen!
HG Michael
Ist nun in den Keller umgezogen und läuft mit einem MEAN WELL Netzteil 12W 5V 2,4A ; MeanWell HDR-15-5 ; DIN-Rail Trafo, auf anhieb.
Könnt ihr nochmal Fotos machen wie was angelötet/verbunden ist? Ich bekomme es nicht hin Daten zu empfangen 😢
Hier Bilder wie es aktuell aussieht: https://immich.weigert.cc/s/ledalux2
Log:
Time | Level | Tag | Message
-- | -- | -- | --
18:10:17 | [I] | [safe_mode:042] | Boot seems successful; resetting boot loop counter
18:10:17 | [D] | [esp32.preferences:149] | Writing 1 items: 0 cached, 1 written, 0 failed
18:13:42 | [D] | [api:160] | Accept 192.168.1.108
18:13:42 | [D] | [api.connection:1383] | ESPHome Logs 2025.10.4 (192.168.1.108) connected
18:13:43 | [I] | [app:185] | ESPHome version 2025.10.4 compiled on Nov 8 2025, 17:05:29
18:13:43 | [I] | [app:187] | Project esphome.web version dev
18:13:43 | [C] | [wifi:679] | WiFi:
18:13:43 | [C] | [wifi:458] | Local MAC: 8C:4F:00:28:33:58
18:13:43 | [C] | [wifi:465] | IP Address: c0a8:647e:61c:403f:98f3:d80:80ec:fb3f
18:13:43 | [C] | [wifi:469] | SSID: �[5m'W-IoT'
18:13:43 | [C] | [logger:261] | Log
18:13:43 | [C] | [logger:267] | Log Baud Rate: 11
18:13:43 | [C] | [logger:274] | Task Log Buffer Size: 768
18:13:43 | [C] | [spi:067] | SPI bus:
18:13:43 | [C] | [spi:068] | CLK Pin: GPIO22
18:13:43 | [C] | [spi:069] | SDI Pin: GPIO17
18:13:43 | [C] | [spi:070] | SDO Pin: GPIO21
18:13:43 | [C] | [spi:075] | Using HW SPI: SPI
18:13:43 | [C] | [canbus:020] | config standard id=0x28a
18:13:43 | [E] | [component:154] | canbus is marked FAILED: unspecified
18:13:43 | [C] | [captive_portal:116] | Captive Portal:
18:13:43 | [C] | [web_server:317] | Web Ser
18:13:43 | [C] | [esphome.ota:093] | Over-The-Air upda
18:13:43 | [C] | [esphome.ota:100] | Password configured
18:13:43 | [C] | [safe_mode:018] | Safe M
18:13:43 | [C] | [web_server.ota:241] | Web Server OTA
18:13:43 | [C] | [api:222] | Ser
18:13:43 | [C] | [api:229] | Noise encryption: YES
18:13:43 | [C] | [improv_serial:031] | Improv Serial:
18:13:43 | [C] | [mdns:179] | m
Hey, könnte mir einer von euch so ein Teio zusammenbauen und zuschicken? Bin selbst zu dumm dazu. Bezahlung natürlich nach eurem Preis ;)
Ich habe diese Ausreißer mittels Filters geglättet, funktioniert sehr gut.