Poor man's solution to address 433-switches with MQTT/Arduino

This examples is most useful for newbies like me who have experience with Arduino sketches and want to integrate their Arduino automation with 433 switches into OpenHAB.

Let’s face it this will not the only/best road to Rome, other solutions like 1technophile/OpenMQTTGateway will cover perhaps this better. You can also use the RPi as 433-device over an Arduino (or Wemos). In a way easier because you do not require a MQTT-broker (the MQTT-broker setup is not part of this example). The majority of the lines in these sketches are borrowed from others in other words LEGO (C)-style programming (cut, paste, trial and redo).

The most important part are the following routines:

  • getStringPartByNr: to split the topic into smaller pieces to determine the model/brand/type and ID-nummer of the switch (and use in the callback rourine).

  • callback: which includes the different actions for each model/switch. As you can see the sketch is using two different models each model can require their own library, the library for the action switch: flamingo.h is included but can possible already replaced with an official library. If you have other switches you should already know which library you should use.

For your conviniency How to setup these 433/MQTT items in OpenHAB?

Switch Switch7 “Red Lamp” (gLight)
{ mqtt=“<[broker:433/action/7/:state:default], >[broker:433/action/7/:command:*:default]” }

Switch Switch9 “Garden Lamp” (gLight)
{ mqtt=“<[broker:433/flamingo/2/:state:default], >[broker:433/flamingo/2/:command:*:default]” }

Missing is the icon “light” and most important here is the last “/” in the topics, otherwise routine getStringPartByNr will not work correctly!!

The Arduino Sketch (requires Arduino UNO + W5100 + 433-transmitter): (I have also adapted this sketch for the Wemos D1 R2 not published, but better to use earlier mention total solution with OTA and config file).

    /*
     Starting Skectch from: PubSubClient
     Splitting topic based upoin: https://arduino.stackexchange.com/questions/1013/how-do-i-split-an-incoming-string
     BTW make sure your topic ends with "/"
     The following topics can be used in this sketch:
     * 433/action/x/    //x value must be in the range 0 to 7 
     * 433/flamingo/x/  //x value can be ...? please experiment 1 and 2 works with me
     433 can be replaced by any onther string, action and flamingo are mandatory
    */

    #include <SPI.h>
    #include <Ethernet.h>
    #include <PubSubClient.h>
    #include <Dns.h>
    #include <Dhcp.h>

    /************************* Ethernet Client Setup *****************************/
    // Update these with values suitable for your network.
    byte mac[] = {0xDC, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
    //Uncomment the following, and set to a valid ip if you don't have dhcp available.
    // IPAddress iotIP (192, 168, y, yyy);
    //Uncomment the following, and set to your preference if you don't have automatic dns.
    //IPAddress dnsIP (8, 8, 8, 8);
    //If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)"

    /*************************** MQTT Setup **************************************/
    #define MQTT_SERVER      "192.168.x.xxx"
    #define MQTT_SERVER_UID  "openhabian"
    #define MQTT_SERVER_PWD  "MyotherSecretPassword"
    #define MQTT_SERVERPORT  1883

    /*************************** Library Setup **************************************/
    // library, variables for Action/Flaminco Switches)
    // #define rfPin D7       // uses digital 7 on wemos (the D is important)
    #define rfPin 7           // uses digital 7 on Arduino Uno
    // const int rfPin = 13;  // Pin Digital D7 on Wemos D1 R2                 
    #include "flamingo.h"     // library in seperate tab for Action switch (self-learning)

    // for Flamingo SF-501PWD (among others)
    #include <NewRemoteTransmitter.h> //library can be found on https://bitbucket.org/fuzzillogic/433mhzforarduino/wiki/Home
    NewRemoteTransmitter transmitter(14727168, rfPin, 260, 3); //values are transmittercode, pin, miliseconds, repeat

    /*************************** Routines **************************************/
    void callback(char* topic, byte* payload, unsigned int length) {

      int deviceSwitchNr;
      String model = getStringPartByNr(topic, '/', 1);   // the first part is model
      String device = getStringPartByNr(topic, '/', 2);  // the second part is device
      int deviceNr = device.toInt();                     // make from the second part an integer
      payload[length] = '\0';                            // set a proper EOF to payload
      String deviceSwitch = String((char*)payload);      // set payload to a string

      Serial.print("Message arrived [");
      Serial.print(topic);
      Serial.print("] ");
      Serial.println(String((char*)payload));
      
      if ( deviceSwitch == "OFF") {
          deviceSwitchNr = 1;
        } else {
          deviceSwitchNr = 0;
        }

      if ( model == "action" ) {
          Flamingo_Send(deviceNr, deviceSwitchNr);
      } else if ( model == "flamingo" ) {
          transmitter.sendUnit(deviceNr, !deviceSwitchNr); // in this on/off is reversed!
      } else {
        Serial.println("there is something wrong"); 
      }
    }

    EthernetClient ethClient;
    PubSubClient client(MQTT_SERVER, MQTT_SERVERPORT, callback, ethClient);

    void setup()
    {
      // Ethernet.begin(mac, iotIP); // with IP inside aketch
      Ethernet.begin(mac);           // with IP based upon DSN-server  
      
      // Note - the default maximum packet size is 128 bytes. If the
      // combined length of clientId, username and password exceed this,
      // you will need to increase the value of MQTT_MAX_PACKET_SIZE in
      // PubSubClient.h
      if (client.connect(MQTT_SERVER, MQTT_SERVER_UID, MQTT_SERVER_PWD)) {
        //client.publish("Topic","hello world");
        client.subscribe("433/#");
      }
      Serial.begin(115200);
      while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
      }
      Serial.println("Starting.....");
    }

    void loop()
    {
      client.loop();
    }
    String getStringPartByNr(String data, char separator, int index)
    {
        // spliting a string and return the part nr index
        // split by separator
        
        int stringData = 0;        //variable to count data part nr 
        String dataPart = "";      //variable to hole the return text
        
        for(int i = 0; i<data.length()-1; i++) {    //Walk through the text one letter at a time
          
          if(data[i]==separator) {
            //Count the number of times separator character appears in the text
            stringData++;
            
          }else if(stringData==index) {
            //get the text when separator is the rignt one
            dataPart.concat(data[i]);
            
          }else if(stringData>index) {
            //return text and stop if the next separator appears - to save CPU-time
            return dataPart;
            break;
            
          }

        }
        //return text if this is the last part
        return dataPart;
    }

The flamingo.h library:

/*********************************************************************************************************
 * Based upon collective works by an unknown author, adapted for Cayenne (or Blynck) by Wouter-Jan       *
 * Info Flamingo: http://forum.arduino.cc/index.php?topic=201771.0                                       *
 * Info 433: a self-learning 433 libary in wording: https://forum.fhem.de/index.php/topic,36399.60.html  *
 * *******************************************************************************************************

******************************************************************************** 
** Flamingo_send                                                              **
**                                                                            **
**       Flamingo device commands (on/off)hard coded (not deciphered yet)     **
**       code is 28 byte long. To transfer it in 32 bit code 4 *0             **
**       is added to the left. For programming reason hex code representation **
**       of device code is used. E.g. Remote 0,C,On:                          **
**         code 28 bits: 0001100110111111110100100110                         **
**         code 32 bits: 00011001101111111101001001100000                     **
**         Hex:          19BFD260                                             **
**                                                                            **
**        "Sync" 1 pulse High, 15 pulse low                                   **
**        "1"    1 pulse High, 3 pulse low                                    **
**        "0"    3 pulse High, 1 pulse low                                    **
**        "end"  N/A                                                          **
**                                                                            **
**       For internal ref: Unit 0 = white remote , unit 1 = black remote      **
**                                                                            **
*******************************************************************************/

// Compiler only coding
#define FUNITS  12                                 /* Amount of units supported */
#define FCMDN   2                                  /* Amount of CMD 0 = On, 1 = Off */

// int rfPin = D7;                                 /* with ethernet shield D4 = SS (for SD card) (now in main sketch)*/

// define device codes:

uint32_t fdev[FUNITS][FCMDN] = {0xD9762A10, 0xDAA47850, /* Remote 0 */ 
                                0xDBDA22E0, 0xDBA27220, /* Remote 1 */
                                0x19BFD260, 0x195EEAA0, /* Remote 2 */
                                0x984CC650, 0x9A8C1050, /* Remote 3 */
                                0xDBFFFE90, 0xD91CEF10, /* Remote 4 */
                                0xDBC52FA0, 0xD9E35160, /* Remote 5 */
                                0x19B0FE60, 0x19682B20, /* Remote 6 */
                                0x9924E7D0, 0x9BA928D0, /* Remote 7 */                                        
                                0x25B25B60, 0x24DC2060, /* Remote 8  (not tested) */
                                0x2717B510, 0x275BADD0, /* Remote 9  (not tested) */
                                0xE56BF910, 0xE4D49F50, /* Remote 10 (not tested) */
                                0x65F1C2A0, 0x65B67B60  /* Remote 11 (not tested) */ 
           };                                         


// Define Flamingo_send variables / constants

int fpulse = 300;                              /* Pulse witdh in microseconds */
int fretrans = 5;                              /* Code retransmission         */
uint32_t fsendbuff;
uint32_t fdatabit;
uint32_t fdatamask = 0x80000000;

void Flamingo_Send(int funitc, int fcmd)
{

 // Test if used codes are valid

 if ((funitc < 0) || (funitc >  11)) {                  // check if unit code between 0-11 (Remote 0 to 11)
   Serial.print("Unit error: ");
   Serial.println(funitc);
   return;
 }

 if ((fcmd < 0) || (fcmd > 1)) {                       // check if command = 0 (off) or 1 (on)
   Serial.print("Command error: ");
   Serial.println(fcmd);
   return;
 }
 
 //End test used codes
 Serial.println("");
 Serial.println("Send Flamingo command: ");
 Serial.print("Flamingo Unit = :");
 Serial.println(funitc);
 Serial.print("command = :");
 Serial.println(fcmd);
 // Send Command

 for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) {

   fsendbuff = fdev[funitc][fcmd];

   // send SYNC 1P High, 15P low
   Serial.println("Send sync");

   digitalWrite(rfPin, HIGH);
   delayMicroseconds(fpulse * 1);
   digitalWrite(rfPin, LOW);
   delayMicroseconds(fpulse * 15);

   // end send SYNC

   // Send command

   for (int i = 0; i < 28; i++)                                 // Flamingo command is only 28 bits */
   {
     // read data bit
     fdatabit = fsendbuff & fdatamask;                         // Get most left bit
     fsendbuff = (fsendbuff << 1);                             // Shift left

     if (fdatabit != fdatamask)
     { // Write 0
       digitalWrite(rfPin, HIGH);
       delayMicroseconds(fpulse * 3);
       digitalWrite(rfPin, LOW);
       delayMicroseconds(fpulse * 1);
     }
     else
     { // Write 1
       digitalWrite(rfPin, HIGH);
       delayMicroseconds(fpulse * 1);
       digitalWrite(rfPin, LOW);
       delayMicroseconds(fpulse * 3);
     }
   }
 }
   //Serial.println("send code = :");
   //Serial.println(fsendbuff);
   Serial.println(rfPin);
}
2 Likes

Please refer to the actual device (d1, lolin, etc). Wemos, is a brand

Also include the code comments (top block) inside code fences.

You’re missing out on a very important piece: OTA

Which brings me to the next suggestion. Avoid hardcoding values in your code. This makes it hard to maintain. Since you mention esp8266 already, write a frontend using a webserver to configure your 433mhz switches, You can even control when the webserver starts, e.g., only when the ESP is on AP mode and a specific IP is connected. This way, you can upload exactly the same sketch to multiple devices, and you can still configure them individually.

Hi @luckymallari,

Fully agree with most of your comments and I learned from you to use “code fences” over “preformatted text”, not sure why I missed this better option till now; most likely a matter of RTFM. Updated text were appropiate.

Your next suggestion (OTA, config file) is a bridge too far for me; at this moment and as indicated there are better solutions already out there. The objective for me was to understand, myself for starters, how I could use in the openHAB environment my 433-switches, how MQTT ticks, etc. And let’s face it if you are using an Arduino UNO it is easy to upload a sketch. Really no need to have OTA yet when experimenting for the first time with OpenHAB/MQTT using an UNO. Also the end result is also better readable for newbies (like me) than a complete solution.

well done. I would not really call this a “poor man’s solution” as the other solutions you mention (raspi, technophile) aren’ t really that different.
In fact, technophile’s program is on an ESP8266 which might be tadd cheaper than a UNO+W5100, but ofcourse if you already have those two, no use letting them go to waste. I still use two UNO’ s with resp a W5100 and an ENC28J60 plus a pro-mini with ENC28J60 rather than an ESP8266 for no other reason that otherwise they’d be collecting dust :slight_smile:
Dare I guess you live in the Netherlands as you seem to use the Action and Flamingo, devices? :wink:
I have to look a bit deeper into your code as I found the self learning flamingo devices quite a hassle to ‘automate’ as they seem to have some sort of ‘rolling code’. Would be nice if I got them to work reliable