Hi,
getestet mit ESP32-C6 Super Mini, Arduino Framework, Zigbee2MQTT.
#include <Arduino.h>
#ifndef ZIGBEE_MODE_ZCZR
#error "Select Zigbee ZCZR mode in Tools -> Zigbee mode"
#endif
#include "Zigbee.h"
#define BUTTON_PIN BOOT_PIN
#define ACTION_SINGLE 1
#define ACTION_DOUBLE 2
#define ACTION_TRIPLE 3
#define ACTION_HOLD 4
ZigbeeMultistate buttonDevice(1);
bool lastState = HIGH;
uint32_t pressStart = 0;
uint32_t lastRelease = 0;
uint8_t clickCount = 0;
bool holdSent = false;
void sendAction(uint8_t action)
{
buttonDevice.setMultistateInput(action);
buttonDevice.reportMultistateInput();
Serial.printf("Action sent: %u\n", action);
}
void setup()
{
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Factory Reset: BOOT-Button beim Start gedrückt halten
delay(200);
if (digitalRead(BUTTON_PIN) == LOW) {
Serial.println("Factory reset triggered!");
Zigbee.factoryReset();
}
buttonDevice.setManufacturerAndModel("Custom", "ESP32-Zigbee-Button");
buttonDevice.addMultistateInput();
buttonDevice.setMultistateInputStates(4);
Zigbee.addEndpoint(&buttonDevice);
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER))
{
Serial.println("Zigbee start failed!");
ESP.restart();
}
// Timeout nach 30s – läuft auch ohne Verbindung weiter
uint32_t timeout = millis();
while (!Zigbee.connected() && millis() - timeout < 30000)
{
Serial.print(".");
delay(100);
}
if (Zigbee.connected()) {
Serial.println("\nConnected");
} else {
Serial.println("\nNot connected, running anyway...");
}
}
void loop()
{
bool state = digitalRead(BUTTON_PIN);
if (state == LOW && lastState == HIGH)
{
pressStart = millis();
holdSent = false;
}
if (state == LOW && !holdSent && millis() - pressStart > 1000)
{
sendAction(ACTION_HOLD);
holdSent = true;
clickCount = 0;
}
if (state == HIGH && lastState == LOW)
{
if (!holdSent)
{
clickCount++;
lastRelease = millis();
}
}
if (clickCount > 0 && millis() - lastRelease > 500)
{
if (clickCount == 1) sendAction(ACTION_SINGLE);
else if (clickCount == 2) sendAction(ACTION_DOUBLE);
else sendAction(ACTION_TRIPLE);
clickCount = 0;
}
lastState = state;
delay(10);
}Arduino IDE Einstellungen:- Board ESP32C6 Dev Module
- USB CDC On Boot Enabled
- CPU Frequency 160MHz (WiFi)
- Core Debug Level None
- Erase All Flash Before Sketch Upload Enabled
- Flash Frequency 80MHz
- Flash Mode QIO
- Flash Size 4MB (32Mb)
- JTAG Adapter Disabled
- Partition SchemeZigbee ZCZR 4MB with spiffs
- Upload Speed 921600
- Zigbee ModeZigbee ZCZR (coordinator/router)
Z2M External Converter:Datei
/opt/zigbee2mqtt/data/external_converters/custom_devices.js:const e = require('zigbee-herdsman-converters/lib/exposes');
const lastSeen = {};
const definition = {
zigbeeModel: ['ESP32-Zigbee-Button'],
model: 'ESP32-Zigbee-Button',
vendor: 'Custom',
description: 'ESP32-C6 Zigbee Button',
fromZigbee: [
{
cluster: 'genMultistateInput',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
const actionMap = {1: 'single', 2: 'double', 3: 'triple', 4: 'hold'};
const value = msg.data['presentValue'];
if (!actionMap[value]) return;
// Duplikat-Filter für Mesh-Routing
const key = `${meta.device.ieeeAddr}_${value}`;
const now = Date.now();
if (lastSeen[key] && now - lastSeen[key] < 1000) return;
lastSeen[key] = now;
return {action: actionMap[value]};
},
},
],
toZigbee: [],
exposes: [e.enum('action', e.access.STATE, ['single', 'double', 'triple', 'hold'])
.withDescription('Button action')],
configure: async (device, coordinatorEndpoint) => {
const endpoint = device.getEndpoint(1);
await endpoint.bind('genMultistateInput', coordinatorEndpoint);
await endpoint.configureReporting('genMultistateInput', [{
attribute: 'presentValue',
minimumReportInterval: 0,
maximumReportInterval: 0,
reportableChange: 1,
}]);
},
};
module.exports = definition;z2m Log:
[17.6.2026, 16:33:38] z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0x543204fffe3d996c', payload '{"action":"single","linkquality":183}'
[17.6.2026, 16:33:40] z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0x543204fffe3d996c', payload '{"action":"double","linkquality":183}'
[17.6.2026, 16:33:42] z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0x543204fffe3d996c', payload '{"action":"triple","linkquality":122}'
[17.6.2026, 16:33:44] z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0x543204fffe3d996c', payload '{"action":"hold","linkquality":191}'Kein Eintrag in configuration.yaml nötig. Z2M lädt alle Dateien aus dem external_converters-Ordner automatisch.
Hinweise:- Als Router gedacht (dauerhaft am Strom), erweitert das Zigbee-Mesh
- Factory Reset: BOOT-Button beim Einschalten gedrückt halten
- Beim ersten Pairing: in Z2M permit_join aktivieren, dann ESP starten
- Getestet mit Zigbee2MQTT v2.9.1 / zigbee-herdsman-converters v26.12.0
Hat ne Weile gedauert bis alles lief, aber der Weg war das Ziel. Spaß hats auf jeden Fall gemacht!
Verbesserungsvorschläge, Ideen oder Erweiterungen sind herzlich willkommen.
Gruss Thomas
hi,
hab mal
Getestet mit Zigbee2MQTT v2.12.0 / zigbee-herdsman-converters v26.63.0
Ergebnis siehe Bild
gruss
Sieht für mich auf den ersten Blick so aus das die Converter Datei nicht existiert oder nicht erkannt wurde.
Was sagt:
ls -l /opt/zigbee2mqtt/data/external_converters
hi,
insgesamt 4
-rw-r--r-- 1 root root 1570 17. Jun 19:26 custom_devices.js
gruss
Rechteproblem? z2m wird ja nicht mit root laufen?
Wie startest Du z2m?
ls -l /opt/zigbee2mqtt/data/?
hi,
danke, hab's auch grade gesehen und geändert. ::)
gruss