Enocean Empfänger an ESP32 mit MQTT Übertragung

Ziel war es, mit überschaubarem Aufwand einen relativ frei positionierbaren Enocean Empfänger zu erhalten, der die empfangenen Enocean Telegramme ohne großes Tamtam zum MQTT Broker überträgt. Dort kümmert sich dann z.B. Node Red um die Verarbeitung. Damit lassen sich recht schlank die netten batterielosen Taster und Fenstergriffe in die Homeautomation einbinden. Aber insbesondere auch die Sensoren, die manchmal aufgrund von Aufbau- und Einbausituation nur wenige Meter Reichweite erreichen. Da reicht es bei mir nicht immer bis zur Zentrale mit dem USB Enocean Empfänger.
Zum Einsatz kommen ein ESP32 C6 Module (vielleicht nutze ich ja irgendwann auch noch die Zigbee und Thread Fähigkeiten diese Moduls) und ein Enocean TCM 320 Modul (ca. 20 €).
Der Aufbau ist recht trivial, das Enocean Modul wird vom ESP Board mit 3,3V versorgt und da ich nur empfangen will, gibt es lediglich eine Verbindung zwischen Enocean TX zu ESP RX UART. 9600 Baud, 8N1. Das war es schon. Das Enocean Modul braucht eine Brücke zwischen GND und dem ersten benachbarten IO Pin, um in den einfachen Empfänger-Modus zu gehen, bei dem alle empfangenen Enocean Telegramme ungefiltert über den UART TX ausgegeben werden.

Der sicherlich noch verbesserungsfähige Arduino Sketch ist angefügt. Läuft schon und erfüllt für meine Zwecke seinen Dienst.

Ich hoffe, den ein oder anderen lädt das zum Nachmachen und Verfeinern ein.

Die Telegramminhalte müssen noch gerätespezifisch ausgewertet werden. Im Falle der Taster und Fenstergriffe ist das recht schlicht. Man sieht ja, wie sich die verschiedenen Tasten oder Fenstergriff-Zustandswechsel im (bereits von Sender ID isoliertem) Telegramminhalt darstellen. Ich habe gegenwärtig leider keinen Zugriff auf weitere Enocean Sensoren, wie z.B. Wetterstation oder Temperaturregelung. Ich gehe aber davon aus, dass diese Telegramminhalte sich auch leicht decodieren lassen.

Die etwas aufwendige Konstruktion rührt daher, dass dieses Enocean Modul einen 2,0 mm statt üblichen 2,54 mm Pitch hat und ich erst annahm, ich müsste mehr Verbindungen herstellen. Daher habe ich eine 2,0 <-> 2,54 Adapterplatine besorgt. Das ist jedoch für die nur drei nötigen Verbindungen eher overkill.

#include <WiFi.h>
#include "wifi_secrets.h"
#include <PubSubClient.h>
#include <esp_wifi.h>

//const char* ssid = "***From wifi_secrets.h ***";
//const char* password = "***From wifi_secrets.h ***";

//The MQTT broker address
const char* mqtt_server = "192.168.30.40";

uint8_t baseMac[6];
char mac_str[13];

void readMacAddress(){
  esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
  if (ret == ESP_OK) {
    Serial.printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
                  baseMac[0], baseMac[1], baseMac[2],
                  baseMac[3], baseMac[4], baseMac[5]);
  } else {
    Serial.println("Failed to read MAC address");
  }
  snprintf (mac_str, 13, "%02X%02X%02X%02X%02X%02X\n",
                  baseMac[0], baseMac[1], baseMac[2],
                  baseMac[3], baseMac[4], baseMac[5]);
}

WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE	(50)
char msg[MSG_BUFFER_SIZE];
int value = 0;

void setup_wifi() {

  delay(10);
  
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}


void callback(char* topic, byte* payload, unsigned int length)
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, HIGH);
  } else {
    digitalWrite(BUILTIN_LED, LOW);
  }

}

String clientId = "ESP32C6Client-";

void reconnect() {
  
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.print("Lost WiFi, trying to reconnect ...");
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
      delay(500);
      Serial.print(".");
    }
  }
  
  while (!client.connected()) {
    Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
    Serial.print("Attempting MQTT connection...");
    
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      //client.publish("eocean/hardbeat", "thump thump");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  clientId += String(random(0xffff), HEX);
  pinMode(BUILTIN_LED, OUTPUT);
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  readMacAddress();

  //Start UART for the Enocean module TCM320
  Serial1.begin(9600, SERIAL_8N1, 12, 13);
  if(Serial1)
  {
    Serial.println("Serial1 successfully set up");
  }
}

char serial_buf[100];
int serial_buf_end = 0;
int serial_buf_ts = 0;
int sync_byte = 0;
unsigned long now = 0;
int target_length = 99;
int enocean_telegram_ready = 0;

void check_seriellen_empfang() 
{  
   while (Serial1.available()) 
   {
      now = millis();
      char aktuellesZeichen = (char)Serial1.read();
      
      if ((now - serial_buf_ts) > 15) //Timeout for receiving complete telegram
      {
        serial_buf_end = 0;
        sync_byte = 0;
        serial_buf_ts = now;
        target_length = 99;
        enocean_telegram_ready = 0;
      }

      if ((sync_byte == 0) && (aktuellesZeichen == 0xa5))
      {
        sync_byte = 1;
        serial_buf_ts = now;
      }

      if ((sync_byte == 2) && (serial_buf_end < (target_length + 3)) && (serial_buf_end < 99))
      {
        if (serial_buf_end == 2)
        {
          target_length = (aktuellesZeichen & 31);
          enocean_telegram_ready = 0;
        }

        serial_buf[serial_buf_end] = aktuellesZeichen;

        serial_buf_end++;
        serial_buf_ts = now;
      }

      if ((sync_byte == 1) && (aktuellesZeichen == 0x5a))
      {
        sync_byte = 2;
        serial_buf[0] = 0xa5;
        serial_buf[1] = 0x5a;
        serial_buf_end = 2;
        serial_buf_ts = now;
        target_length = 99;
        enocean_telegram_ready = 0;
        serial_buf_ts = now;
      }

      if (serial_buf_end == (target_length + 3))
      {
        enocean_telegram_ready = serial_buf_end;
        
        Serial.print("Enocean telegram: ");
        for(int i = 0; i < serial_buf_end; i++)
        {
          if(serial_buf[i] < 0xa)
          {
            Serial.print("0");
            Serial.print(serial_buf[i],HEX);
          }
          else
          {
            Serial.print(serial_buf[i],HEX);
          }
          Serial.print(" ");
        }
        Serial.println("");
      }
   }
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  check_seriellen_empfang();

  now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    char temp_str[100];
    snprintf (msg, MSG_BUFFER_SIZE, "thump thump #%ld", value);
    Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
    Serial.print("Publish message: ");
    Serial.println(msg);
    strcpy(temp_str,"enocean/heartbeat/");
    strcat(temp_str, mac_str);
    client.publish(temp_str, msg);
  }

  if(enocean_telegram_ready > 0) //enocean_telegram_ready contains the telegram length
  {
    // ********** publish the whole raw telegram received by the Enocean module  
    char temp_str[100];
    strcpy(temp_str,"enocean/raw/");
    strcat(temp_str, mac_str);
    Serial.println(temp_str);
    client.publish(temp_str, (const uint8_t*) serial_buf, enocean_telegram_ready);


    // ********** publish only the telegram data with the Enocean ID to identify the source | to distiguish different receivers (if you use more than one) the MAC adress of the ESP is added | the topic looks like this:
    // "enocean/ID_FED2E7E4/404CCA47E49C" | you can pick it up without the MAC adress: "enocean/ID_FED2E7E4/#" 
    snprintf (temp_str, 20, "enocean/ID_%02X%02X%02X%02X",
                  serial_buf[enocean_telegram_ready-6], serial_buf[enocean_telegram_ready-5], serial_buf[enocean_telegram_ready-4],
                  serial_buf[enocean_telegram_ready-3]);

    strcat(temp_str, "/");
    strcat(temp_str, mac_str);

    client.publish(temp_str, (const uint8_t*) serial_buf+4, enocean_telegram_ready-10);

    enocean_telegram_ready = 0;
  }
}

Die Enocean MQTT bridge stellt die Enocean Information in folgenden MQTT Topics zur Verfügung:

  1. enocean/heartbeat/#
    Alle 2 Sekunden ein Lebenszeichen der bridge.

  2. enocean/raw/404CCA47E49C (Beispiel)
    Wobei 404CCA47E49C die MAC Adresse des ESP boards ist.
    Dieses Topic reicht die vom Enocean TCM 320 Modul zur Verfügung gestellten Telegramminformationen einfach durch. Dieses topic ist unter anderem hilfreich, um neue Enocean Quellen zu identifizieren. Die MAC Adresse sorgt dafür, dass auch mehrere dieser Enocean MQTT bridges koexistieren können. Wenn nur eines betrieben wird oder nicht zwischen bridges unterschieden werden soll, sind die Telegramm Rohdaten einfach zu bekommen unter:
    enocean/raw/#

  3. enocean/ID_FED2E7E4/404CCA47E49C (Beispiel)
    Jede Enocean Quelle ereugt so ihr eigenes MQTT topic, dass den eigentlichen Telegramminhalt (bei TCM 320 immer 4 byte) bereinigt von Quelladresse, header usw. bereitstellt. ID_XXXXXXXX identifiziert die Telegrammquelle (also das Enocean Gerät) und 404CCA47E49C erneut die MAC Adresse der Enocean MQTT bridge, die meist wieder ignoriert werden kann durch:
    enocean/ID_FED2E7E4/#

Hier noch drei Node Red function nodes, die die Telegrammdaten von einem (Jung) Enocean 2fach, 4fach Taster und einem (Eltaco) Enocean Fenstergriff auswerten (ich glaube, eigentlich steckt hier in allen Fällen Eltaco dahinter). Die Tasterauswertung wirkt etwas "groß" weil ich für jede der Tasten einmal den reinen gedrückt-Status, Erkennung kurzer Tastendruck, Status langer Tastendruck auswerte und über je einen Ausgang ausgebe.

[
    {
        "id": "e26dc31320f72492",
        "type": "function",
        "z": "cf80121d93fd6aa6",
        "name": "Jung Enocean 4g button eval",
        "func": "let wait_timer_return = context.get(\"wait_timer_return\") || 0;\nlet button_states = context.get(\"button_states\") || [0, 0, 0, 0]; //[bottom left, top left, br, tr]\nlet long_button_states = context.get(\"long_button_states\") || [0, 0, 0, 0]; //[bottom left, top left, br, tr]\n\nlet msg01 = {};\nlet msg02 = {};\nlet msg03 = {};\nlet msg04 = {};\nlet msg05 = {};\nlet msg06 = {};\nlet msg07 = {};\nlet msg08 = {};\nlet msg09 = {};\nlet msg10 = {};\nlet msg11 = {};\nlet msg12 = {};\nlet msg13 = {};\n\nmsg01.topic = \"b1_state\";\nmsg02.topic = \"b1_short\";\nmsg03.topic = \"b1_long\";\nmsg04.topic = \"b2_state\";\nmsg05.topic = \"b2_short\";\nmsg06.topic = \"b2_long\";\nmsg07.topic = \"b3_state\";\nmsg08.topic = \"b3_short\";\nmsg09.topic = \"b3_long\";\nmsg10.topic = \"b4_state\";\nmsg11.topic = \"b4_short\";\nmsg12.topic = \"b4_long\";\n\n/*\nmsg01.payload = x; // Switch status 1=pressed, 0=released;\nmsg02.payload = x; // 1=short press recognised\nmsg03.payload = x; // 1=long press recognised, 0=long press released\n*/\n\nif (msg.topic == \"timer_long_press_elapsed\") //Timer for long press detection elapsed\n{\n    wait_timer_return = 0;\n    context.set(\"wait_timer_return\", 0);\n\n    //detect, if we wait for Longpress detection --> send long pressed event\n\n    /// ****************** Start of long_button_state press section **********************\n    {\n        if (button_states[0] == 1) {\n            msg01 = null;\n            msg02 = null;\n            msg03.payload = 1;\n            long_button_states[0] = 1;\n        }\n        else\n        {\n            long_button_states[0] = 0;\n            msg01 = null;\n            msg02 = null;\n            msg03 = null;\n        }\n\n        if (button_states[1] == 1) {\n            msg04 = null;\n            msg05 = null;\n            msg06.payload = 1;\n            long_button_states[1] = 1;\n        }\n        else\n        {\n            long_button_states[1] = 0;\n            msg04 = null;\n            msg05 = null;\n            msg06 = null;\n        }\n\n        if (button_states[2] == 1) {\n            msg07 = null;\n            msg08 = null;\n            msg09.payload = 1;\n            long_button_states[2] = 1;\n        }\n        else {\n            long_button_states[2] = 0;\n            msg07 = null;\n            msg08 = null;\n            msg09 = null;\n        }\n\n        if (button_states[3] == 1) {\n            msg10 = null;\n            msg11 = null;\n            msg12.payload = 1;\n            long_button_states[3] = 1;\n        }\n        else {\n            long_button_states[3] = 0;\n            msg10 = null;\n            msg11 = null;\n            msg12 = null;\n        }\n\n        context.set(\"long_button_states\", long_button_states);\n\n        msg13 = null;\n    }\n    /// ****************** End of of long_button_state press section **********************\n}\nelse\n{\n    if (msg.payload[0] != 0) //some button pressed\n    {\n        wait_timer_return = 1;\n        context.set(\"wait_timer_return\", 1);\n\n        msg13.topic = \"timer_long_press_elapsed\";\n        msg13.id = msg._msgid;\n\n        //Which bottom is pressed:\n        let button1 = msg.payload[0] & 0xF0;\n        button1 = button1 >> 4;\n\n        let button2 = msg.payload[0] & 0x0F;\n\n        button_states[0] = 0;\n        button_states[1] = 0;\n        button_states[2] = 0;\n        button_states[3] = 0;\n        \n        switch (button1)\n        {\n            case 1:\n                button_states[0] = 1;\n                msg01.payload = 1;\n                msg02 = null;\n                msg03 = null;\n                break;\n            case 3:\n                button_states[1] = 1;\n                msg04.payload = 1;\n                msg05 = null;\n                msg06 = null;\n                break;\n            case 5:\n                button_states[2] = 1;\n                msg07.payload = 1;\n                msg08 = null;\n                msg09 = null;\n                break;\n            case 7:\n                button_states[3] = 1;\n                msg10.payload = 1;\n                msg11 = null;\n                msg12 = null;\n                break;\n        }\n        switch (button2) {\n            case 1:\n                button_states[0] = 1;\n                msg01.payload = 1;\n                msg02 = null;\n                msg03 = null;\n                break;\n            case 3:\n                button_states[1] = 1;\n                msg04.payload = 1;\n                msg05 = null;\n                msg06 = null;\n                break;\n            case 5:\n                button_states[2] = 1;\n                msg07.payload = 1;\n                msg08 = null;\n                msg09 = null;\n                break;\n            case 7:\n                button_states[3] = 1;\n                msg10.payload = 1;\n                msg11 = null;\n                msg12 = null;\n                break;\n        }\n\n        context.set(\"button_states\", button_states);\n        \n        if (button_states[0] == 0)\n        {\n            msg01 = null;\n            msg02 = null;\n            msg03 = null;\n        }\n        if (button_states[1] == 0)\n        {\n            msg04 = null;\n            msg05 = null;\n            msg06 = null;\n        }\n        if (button_states[2] == 0)\n        {\n            msg07 = null;\n            msg08 = null;\n            msg09 = null;\n        }\n        if (button_states[3] == 0)\n        {\n            msg10 = null;\n            msg11 = null;\n            msg12 = null;\n        }   \n    }\n    else //button released\n    {\n        msg13 = null;\n\n        /// ****************** Start of button state section **********************\n        {\n            if (button_states[0] == 1)\n            {\n                msg01.payload = 0;\n            }\n            else\n            {\n                msg01 = null;\n            }\n\n            if (button_states[1] == 1) {\n                msg04.payload = 0;\n            }\n            else {\n                msg04 = null;\n            }\n\n            if (button_states[2] == 1) {\n                msg07.payload = 0;\n            }\n            else {\n                msg07 = null;\n            }\n\n            if (button_states[3] == 1) {\n                msg10.payload = 0;\n            }\n            else {\n                msg10 = null;\n            }\n\n            context.set(\"button_states\", [0, 0, 0, 0]);\n        }\n        /// ****************** End of button state section **********************\n\n        /// ****************** Start of long_button_state release section **********************\n        {\n            if (long_button_states[0] == 1)\n            {\n                msg02 = null;\n                msg03.payload = 0;\n                msg05 = null;\n                msg08 = null;\n                msg11 = null;\n            }\n            else\n            {\n                msg03 = null;\n            }\n\n            if (long_button_states[1] == 1) {\n                msg05 = null;\n                msg06.payload = 0;\n                msg02 = null;\n                msg08 = null;\n                msg11 = null;\n            }\n            else {\n                msg06 = null;\n            }\n\n            if (long_button_states[2] == 1) {\n                msg08 = null;\n                msg09.payload = 0;\n                msg02 = null;\n                msg05 = null;\n                msg11 = null;\n            }\n            else {\n                msg09 = null;\n            }\n\n            if (long_button_states[3] == 1) {\n                msg11 = null;\n                msg12.payload = 0;\n                msg02 = null;\n                msg05 = null;\n                msg08 = null;\n            }\n            else {\n                msg12 = null;\n            }\n\n            context.set(\"long_button_states\", [0, 0, 0, 0]);\n        }\n        /// ****************** End of of long_button_state release section **********************\n\n        /// ****************** Start of button short section **********************\n        {\n            if (wait_timer_return == 1)\n            {\n                if (button_states[0] == 1)\n                {\n                    msg02.payload = 1;\n                    msg03 = null;\n                }\n                else\n                {\n                    msg02 = null;\n                }\n\n                if (button_states[1] == 1) {\n                    msg05.payload = 1;\n                    msg06 = null;\n                }\n                else\n                {\n                    msg05 = null;\n                }\n\n                if (button_states[2] == 1) {\n                    msg08.payload = 1;\n                    msg09 = null;\n                }\n                else\n                {\n                    msg08 = null;\n                }\n\n                if (button_states[3] == 1) {\n                    msg11.payload = 1;\n                    msg12 = null;\n                }\n                else\n                {\n                    msg11 = null;\n                }\n            }\n        }\n        /// ****************** Emd of button short section **********************\n\n        wait_timer_return = 0;\n        context.set(\"wait_timer_return\", 0);\n    }\n}\n\n\nreturn [msg01, msg02, msg03, msg04, msg05, msg06, msg07, msg08, msg09, msg10, msg11, msg12, msg13];",
        "outputs": 13,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 360,
        "wires": [
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [
                "64afd72459788ab7"
            ]
        ],
        "outputLabels": [
            "Button 1 (bl) status (pressed 1 / released 0)",
            "Button 1 (bl) short press (1)",
            "Button 1 (bl) long press (start 1 / end 0)",
            "Button 2 (tl) status (pressed 1 / released 0)",
            "Button 2 (tl) short press (1)",
            "Button 2 (tl) long press (start 1 / end 0)",
            "Button 3 (br) status (pressed 1 / released 0)",
            "Button 3 (br) short press (1)",
            "Button 3 (br) long press (start 1 / end 0)",
            "Button 4 (tr) status (pressed 1 / released 0)",
            "Button 4 (tr) short press (1)",
            "Button 4 (tr) long press (start 1 / end 0)",
            "Start timer for long button press recognition"
        ],
        "icon": "font-awesome/fa-toggle-on"
    },
    {
        "id": "ab85a5b440b8f359",
        "type": "mqtt in",
        "z": "cf80121d93fd6aa6",
        "name": "Jung_rocker4gang_01",
        "topic": "enocean/ID_XXXXXXXX/#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "9c9e7e65436a5109",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 120,
        "y": 360,
        "wires": [
            [
                "e26dc31320f72492"
            ]
        ]
    },
    {
        "id": "64afd72459788ab7",
        "type": "delay",
        "z": "cf80121d93fd6aa6",
        "name": "Longpress time",
        "pauseType": "delay",
        "timeout": "300",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 443,
        "y": 514,
        "wires": [
            [
                "e26dc31320f72492"
            ]
        ]
    },
    {
        "id": "73ae7d391b235aa7",
        "type": "mqtt in",
        "z": "cf80121d93fd6aa6",
        "name": "Eltaco_window_handle_01",
        "topic": "enocean/ID_XXXXXXXX/#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "9c9e7e65436a5109",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 130,
        "y": 600,
        "wires": [
            [
                "0de14ba6ece1060b"
            ]
        ]
    },
    {
        "id": "0de14ba6ece1060b",
        "type": "function",
        "z": "cf80121d93fd6aa6",
        "name": "Enocean window handle",
        "func": "\nswitch (msg.payload[0])\n{\n    case 0xc0:\n        msg.payload = 1;\n        break;\n    case 0xe0:\n        msg.payload = 1;\n        break;\n    case 0xf0:\n        msg.payload = 2;\n        break;\n    case 0xd0:\n        msg.payload = 0;\n        break;\n    default:\n        msg.payload = 99;\n        break;\n}\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 600,
        "wires": [
            []
        ],
        "outputLabels": [
            "0 closed, 1 open, 2 tilted"
        ],
        "icon": "font-awesome/fa-toggle-on"
    },
    {
        "id": "58d6a49e79bd69e3",
        "type": "mqtt in",
        "z": "cf80121d93fd6aa6",
        "name": "Jung_rocker2gang_02",
        "topic": "enocean/ID_XXXXXXXX/#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "9c9e7e65436a5109",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 130,
        "y": 100,
        "wires": [
            [
                "93ac0df52d8e9698"
            ]
        ]
    },
    {
        "id": "93ac0df52d8e9698",
        "type": "function",
        "z": "cf80121d93fd6aa6",
        "name": "Jung Enocean 2g button eval",
        "func": "let wait_timer_return = context.get(\"wait_timer_return\") || 0;\nlet button_states = context.get(\"button_states\") || [0, 0, 0, 0]; //[bottom left, top left, br, tr]\nlet long_button_states = context.get(\"long_button_states\") || [0, 0, 0, 0]; //[bottom left, top left, br, tr]\n\nlet msg07 = {};\nlet msg08 = {};\nlet msg09 = {};\nlet msg10 = {};\nlet msg11 = {};\nlet msg12 = {};\nlet msg13 = {};\n\nmsg07.topic = \"b1_state\";\nmsg08.topic = \"b1_short\";\nmsg09.topic = \"b1_long\";\nmsg10.topic = \"b2_state\";\nmsg11.topic = \"b2_short\";\nmsg12.topic = \"b2_long\";\n\n/*\nmsg01.payload = x; // Switch status 1=pressed, 0=released;\nmsg02.payload = x; // 1=short press recognised\nmsg03.payload = x; // 1=long press recognised, 0=long press released\n*/\n\nif (msg.topic == \"timer_long_press_elapsed\") //Timer for long press detection elapsed\n{\n    wait_timer_return = 0;\n    context.set(\"wait_timer_return\", 0);\n\n    //detect, if we wait for Longpress detection --> send long pressed event\n\n    /// ****************** Start of long_button_state press section **********************\n    {\n        if (button_states[2] == 1) {\n            msg07 = null;\n            msg08 = null;\n            msg09.payload = 1;\n            long_button_states[2] = 1;\n        }\n        else {\n            long_button_states[2] = 0;\n            msg07 = null;\n            msg08 = null;\n            msg09 = null;\n        }\n\n        if (button_states[3] == 1) {\n            msg10 = null;\n            msg11 = null;\n            msg12.payload = 1;\n            long_button_states[3] = 1;\n        }\n        else {\n            long_button_states[3] = 0;\n            msg10 = null;\n            msg11 = null;\n            msg12 = null;\n        }\n\n        context.set(\"long_button_states\", long_button_states);\n\n        msg13 = null;\n    }\n    /// ****************** End of of long_button_state press section **********************\n}\nelse\n{\n    if (msg.payload[0] != 0) //some button pressed\n    {\n        wait_timer_return = 1;\n        context.set(\"wait_timer_return\", 1);\n\n        msg13.topic = \"timer_long_press_elapsed\";\n        msg13.id = msg._msgid;\n\n        //Which bottom is pressed:\n        let button1 = msg.payload[0] & 0xF0;\n        button1 = button1 >> 4;\n\n        let button2 = msg.payload[0] & 0x0F;\n\n        button_states[0] = 0;\n        button_states[1] = 0;\n        button_states[2] = 0;\n        button_states[3] = 0;\n        \n        switch (button1)\n        {\n            case 5:\n                button_states[2] = 1;\n                msg07.payload = 1;\n                msg08 = null;\n                msg09 = null;\n                break;\n            case 7:\n                button_states[3] = 1;\n                msg10.payload = 1;\n                msg11 = null;\n                msg12 = null;\n                break;\n        }\n        switch (button2) {\n            case 5:\n                button_states[2] = 1;\n                msg07.payload = 1;\n                msg08 = null;\n                msg09 = null;\n                break;\n            case 7:\n                button_states[3] = 1;\n                msg10.payload = 1;\n                msg11 = null;\n                msg12 = null;\n                break;\n        }\n\n        context.set(\"button_states\", button_states);\n        \n        if (button_states[2] == 0)\n        {\n            msg07 = null;\n            msg08 = null;\n            msg09 = null;\n        }\n        if (button_states[3] == 0)\n        {\n            msg10 = null;\n            msg11 = null;\n            msg12 = null;\n        }   \n    }\n    else //button released\n    {\n        msg13 = null;\n\n        /// ****************** Start of button state section **********************\n        {\n\n            if (button_states[2] == 1) {\n                msg07.payload = 0;\n            }\n            else {\n                msg07 = null;\n            }\n\n            if (button_states[3] == 1) {\n                msg10.payload = 0;\n            }\n            else {\n                msg10 = null;\n            }\n\n            context.set(\"button_states\", [0, 0, 0, 0]);\n        }\n        /// ****************** End of button state section **********************\n\n        /// ****************** Start of long_button_state release section **********************\n        {\n\n            if (long_button_states[2] == 1) {\n                msg08 = null;\n                msg09.payload = 0;\n                msg11 = null;\n            }\n            else {\n                msg09 = null;\n            }\n\n            if (long_button_states[3] == 1) {\n                msg11 = null;\n                msg12.payload = 0;\n                msg08 = null;\n            }\n            else {\n                msg12 = null;\n            }\n\n            context.set(\"long_button_states\", [0, 0, 0, 0]);\n        }\n        /// ****************** End of of long_button_state release section **********************\n\n        /// ****************** Start of button short section **********************\n        {\n            if (wait_timer_return == 1)\n            {\n\n                if (button_states[2] == 1) {\n                    msg08.payload = 1;\n                    msg09 = null;\n                }\n                else\n                {\n                    msg08 = null;\n                }\n\n                if (button_states[3] == 1) {\n                    msg11.payload = 1;\n                    msg12 = null;\n                }\n                else\n                {\n                    msg11 = null;\n                }\n            }\n        }\n        /// ****************** Emd of button short section **********************\n\n        wait_timer_return = 0;\n        context.set(\"wait_timer_return\", 0);\n    }\n}\n\n\nreturn [msg07, msg08, msg09, msg10, msg11, msg12, msg13];",
        "outputs": 7,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 100,
        "wires": [
            [],
            [],
            [],
            [],
            [],
            [],
            [
                "54830c406fa7e126"
            ]
        ],
        "outputLabels": [
            "Button 1 (bottom) status (pressed 1 / released 0)",
            "Button 1 (bottom) short press (1)",
            "Button 1 (bottom) long press (start 1 / end 0)",
            "Button 2 (top) status (pressed 1 / released 0)",
            "Button 2 (top) short press (1)",
            "Button 2 (top) long press (start 1 / end 0)",
            "Start timer for long button press recognition"
        ],
        "icon": "font-awesome/fa-toggle-on"
    },
    {
        "id": "54830c406fa7e126",
        "type": "delay",
        "z": "cf80121d93fd6aa6",
        "name": "Longpress time",
        "pauseType": "delay",
        "timeout": "300",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 440,
        "y": 200,
        "wires": [
            [
                "93ac0df52d8e9698"
            ]
        ]
    },
    {
        "id": "9c9e7e65436a5109",
        "type": "mqtt-broker",
        "name": "local MQTT broker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]
1 Like

Danke, daß Du uns Dein Projekt zeigst.
Du könntest es aber in den Tread

veröffentlichen, vieleicht mit einem Foto des Aufbaus.
Grüße Uwe

Moin Uwe!
Kann man machen. Ich denke bei meinem beschränkten Forum Kenntnissen jedoch, dass dieses kleine Projekt in dieser Art leichter zu finden ist. Für jemanden, der so etwas sucht. Oder?

Weiß nicht wo man etwas besser finden kann.
Was sagen die anderen?
Grüße Uwe

Der "GeileProjekte" - Sammelthread ist inzwischen ziemlich groß, da hat fiso42 schon recht.
Der Sammelthread selbst ist zwar leicht zu finden, da oben festgenagelt, dort langfristig ein bestimmtes Projekt zu finden aber schwierig.

Mein Tip: Das Internet lebt von Links, warum nicht dort einen Link (evtl. mit einem kurzen Stichwort-Text) hierher setzen.

Dein Vorschlag gefällt mir.
fiso42 machst Du das?
Grüße Uwe

Danke für die Anregung!
Ich habe den Link in "Zeigt her ..." angelegt.

1 Like

Ich habe im ursprünglichen Post noch die neue Enocean 2fach Taster Auswertung unter Node Red ergänzt.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.