ESP8266 and MQTT

So I have big plans for extending my flat :slight_smile:
I got:

  • 10 ws2811 strips all cables wired up to a central location(star configuration)
  • 2 stepper motors for blinds, (power +4wire wired up to a central location(star configuration))
  • 3 sonoff dual controllors(some 230v wall lights)
  • 10 motion sensor, ethernet cable to entral location(star configuration)
  • 5 nano arduinos(irrigation, bar, wardrobe motor,valves, hvac) with 4 wire cables wired up to a central location(star configuration)

So the question is how would people out there integrate all this into OpenHab. My idea is to use MQTT since I am already using that for my plant sensor. The question is:

  • What library to use(tasmoto?(can this run on normal arduinos?), mk smarthome, mysensors?)
  • How to name topis?
  • What hardware to use in the central location(my fusebox, where rpi with OH, switch, power++ is located)

Any tips, examples would be highly appreciated…

I use the homie library. Is designed for mqtt use and promotes a consistent topic path.

I’ve blogged here the use of the library (and integration to openHAB). Some of it might be useful to you https://www.geekzone.co.nz/davidcole/8960

I also use Homie on my python based sensors (raspberry pi) with homie-python

I’ve only advice on the topic names.

Come up with a scheme that makes sense to you. You will want to be able to readily identify the device based on the topic name. If you can be specific (e.g. sonoff/bedroomlight/cmd) instead of device focused (e.g. sonoff/basic/1/channel2/cmd) it will be easier for you to tell what everything is in the long run.

Add labels to all of your devices with their configurations printed on them. Their name and the topics they publish/subscribe.

Separate your command and status channels into separate topics. This will help you avoid infinite loops.

Good luck with the rest! I just received a set of four Sonoff Basics myself. I’m hoping to get one flashed to Tasmoto this evening, assuming I can get the SonOTA script to work. Otherwise I have to wait for the programmer and probe pins to arrive.

Hi,

If you need hardware connection (using MQTT), have a look at https://www.letscontrolit.com/index.php#ESPEasy

-ben

That certainly makes sense and it is how I started out as well, but I went to a more structured approach making all topics the same length with me only having to check the last two digits to know the topic.
Not sure yet what is more advantageous.

What i did do is to add a direction. So for all messages from any device to my broker/Openhab, I add 'nb´ for ‘northbound’ and for all messages from the broker/OpenHab to a device, I add ‘sb’ for ‘southbound’. That takes care of most of the separation of commands and statusses.

I also have all my nodes/sonoffs send their IP, MAC and software version so it is easy to keep track of where is what, in addition to having them labelled

Come to think of it, I need to order a few more Sonoff’s (basic) myself

I’ve used the pubsubclient for Arduino as the MQTT client and it works really well and is non-blocking:

Which just means you can have code run in parallel to the MQTT client and there’s a callback function that gets executed only when a message comes in. But the idea is that the PubSub Client doesn’t block the main loop from executing while it polls the MQTT broker. I’d at least give it a look, it was super easy to get it up and running.

Take a look at this link for the program I wrote to run the LED strip under my cabinets for lighting in the kitchen:

https://drive.google.com/open?id=0B02x81-uvEZLekgxUHdHRWFhMVE

Much of this sort of thing is personal taste and the following is my personal taste. It isn’t right nor is any other approach is wrong. It is all about what gives you, the human developer, an easier job of identifying and understanding what an Item, Topic, Rule, etc. corresponds to what physical device. I emphasize the physical because knowing the IP address or serial number for a device doesn’t tell you that the device is actually controlling the living room lamp near the window.

So, for me, I find it much more advantageous to use a topic that self-describes what physical device it is controlling over a numbering or naming scheme that I have to then mentally (or look at a table) map to what it is actually controlling.

This goes along with my mantra that the human’s time is far more valuable than the computer’s time. So anything you can do to lessen the load on the human, be it requiring less typing (I love auto-completion) or requiring less effort to mentally model their system the better.

You can have structured and human-friendly, but probably not with same length topic names.

This is an important best practice. The standard I’ve seen is CMD and STATUS or some variation of that instead of nb and sb, but the result is the same. The commands and status messages go to different topics. Call them whatever makes things easier for you.

I think Tasmota does that by default on the TELEM topic, right? I use static DHCP tables in my firewall for all my devices so I can get to everything by name instead of IP (another instance of my mantra) so I can look at the label on the device or the topic name in my .items file and know that I can go to http://blah.koshak.net and the blah device’s web admin page will come up. By using human-friendly names, I usually don’t have to look at the label because the hostname is self-evident and intuitive.

Oh I fully agree. I didnt want to suggest mine was better, just ‘adding my two cents’ My thinking was that once it all runs I am not gonna see the messages anyway :slight_smile:

I fully agree with your mantra.

regarding IP, MAC,Software version.
Though I know Tasmota very well (who doesnt), I have not used it but opted for software closer to my specific needs, but I think TELEM topic does that.
My router is a bit fidgy in passing the DHCP name, so I get the IP, but I also have the node MQTT it’s name to me.

Anyway, just all suggestions, the main issue I fully agree is to make it easy, understandable and above all… efficiient in a way that works for the one working with it.
And who knows after a while I may think "Nope, going back to descriptive names as ‘home\living\ceilinglamp’ "

1 Like

So I did some more research concerning software, and these are the options I found:


https://pubsubclient.knolleary.net/
https://github.com/i-n-g-o/esp-mqtt-arduino (only ESP??)
https://github.com/arendst/Sonoff-Tasmota/wiki (Only ESP??)

Since I will have a mix of ESP and arduinos on the network, it would be nice with something that support Nano,mega and esp at least.

Maybe too barebone for your set-up, but I have been running https://github.com/pfalcon/esp-open-sdk along with https://github.com/SuperHouse/esp-open-rtos - this does not need the Arduino.

Adafruit: Slim MQTT software. Have used it but mainly I use PubSubclient, either from Knolleary or from Imroy
The knolleary pubsubclient is indeed the backbone of most MQTT systems
I know of the ingo but never used it.
The tasmota software is widely used as firmware on ESP8266’s but it will do ESP8255 as well I think.

As far as I know the Adfafruit library works on Arduino’s as well, The knolleary pubsubclient does for sure
The Tasmota/Sonoff is completely different, it is node software for ESP only.

Connecting Arduino’s with pubsubclient kinda goes like this:

#include "Ethernet.h"
#include "PubSubClient.h"
#include "DHT.h"
//kennelijk  geeft update van DHT sensor library boven 1.2.1 een fout
#define CLIENT_ID       "Hal"
#define PUBLISH_DELAY   3000
#define DHTPIN          3
#define DHTTYPE         DHT11
#define ledPin 13
#define relayPin 8
String ip = "";
bool statusKD = HIGH;
bool statusBD = HIGH;
bool statusGD = HIGH;
bool relaystate = LOW;
bool pir = LOW;
bool startsend = HIGH;
int lichtstatus;
uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x06};

EthernetClient ethClient;
PubSubClient mqttClient;
DHT dht(DHTPIN, DHTTYPE);

long previousMillis;

void setup() {
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(relayPin, OUTPUT);

  // setup serial communication

  Serial.begin(9600);
  while (!Serial) {};
  Serial.println(F("MQTT Arduino Demo"));
  Serial.println();

  // setup ethernet communication using DHCP
  if (Ethernet.begin(mac) == 0) {
    //Serial.println(F("Unable to configure Ethernet using DHCP"));
    for (;;);
  }

  Serial.println(F("Ethernet configured via DHCP"));
  Serial.print("IP address: ");
  Serial.println(Ethernet.localIP());
  Serial.println();

  ip = String (Ethernet.localIP()[0]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[1]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[2]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[3]);
  //Serial.println(ip);

  // setup mqtt client
  mqttClient.setClient(ethClient);
  
  mqttClient.setServer( "192.168.1.102", 1883);
  //Serial.println(F("MQTT client configured"));
  mqttClient.setCallback(callback);
  // setup DHT sensor
  dht.begin();
  Serial.println(F("DHT sensor initialized"));

  Serial.println();
  Serial.println(F("Ready to send data"));
  previousMillis = millis();
  mqttClient.publish("home/br/nb/ip", ip.c_str());
}

void loop() {
  statusBD = digitalRead(4);// FrontdoorSwitch
  statusGD = digitalRead(5);// Garagedoor Switch
  statusKD = (digitalRead(6));//LivingRoom Switch

  lichtstatus = analogRead(A0);//Reads an LDR
  pir = digitalRead(7);//Reads a PIR sensor
  relaystate = digitalRead(relayPin);// Reads the state of a relay

  // it's time to send new data?
  if (millis() - previousMillis > PUBLISH_DELAY) {
    sendData();
    previousMillis = millis();

  }

  mqttClient.loop();
}

void sendData() {

  char msgBuffer[20];
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.println("oC");
  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.println("%");
  Serial.print("Relay is: ");
  Serial.println((relaystate == LOW) ? "OPEN" : "CLOSED");
  if (mqttClient.connect(CLIENT_ID)) {
    mqttClient.publish("home/br/nb/temp", dtostrf(t, 6, 2, msgBuffer));
    mqttClient.publish("home/br/nb/humid", dtostrf(h, 6, 2, msgBuffer));
    mqttClient.publish("home/br/nb/deur", (statusBD == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/garage", (statusGD == HIGH) ? "OPEN" : "DICHT");
    mqttClient.publish("home/br/nb/bel", (statusKD == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/l", dtostrf(lichtstatus, 4, 0, msgBuffer));
    mqttClient.publish("home/br/nb/p", (pir == HIGH) ? "OPEN" : "CLOSED");
    mqttClient.publish("home/br/nb/relay", (relaystate == LOW) ? "OPEN" : "CLOSED");
    mqttClient.subscribe("home/br/sb");
    if (startsend) {
     // mqttClient.publish("home/br/nb/relay", (relaystate == LOW) ? "OPEN" : "CLOSED");
      mqttClient.publish("home/br/nb/ip", ip.c_str());
      startsend = LOW;
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  char msgBuffer[20];
  // I am only using one ascii character as command, so do not need to take an entire word as payload
  // However, if you want to send full word commands, uncomment the next line and use for string comparison
   //payload[length] = '\0';            // terminate string with '0'
  //String strPayload = String((char*)payload);  // convert to string
  // Serial.println(strPayload); //can use this if using longer southbound topics
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");//MQTT_BROKER
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  Serial.println(payload[0]);

  // Examine only the first character of the message
  if (payload[0] == 49)             // Message "1" in ASCII (turn output ON)
  {
    digitalWrite(LED_BUILTIN, HIGH);    //
    digitalWrite(relayPin, HIGH);
  } else if (payload[0] == 48)      // Message "0" in ASCII (turn output OFF)
  {
    digitalWrite(relayPin, LOW);     //
    digitalWrite(LED_BUILTIN, LOW);
  } else if (payload[0] == 50)
  {
    mqttClient.publish("home/br/nb/ip", ip.c_str());// publish IP nr
  } else {
    Serial.println("Unknown value");
    mqttClient.publish("home/br/nb", "Syntax Error");
  }

}

Ofcourse only very simple program, just as an example.

As far as hardware suggestions go: the Arduino ofcourse has many I/O pins, but realise that you need to add something like a W5100/W5500 ethernet module and that there isnt much memory left after setting up internet connection. Plus you may run out of hardware ports on your router
Wemos ofcourse is very cheap, handy because WiFi and if you need more I/O, consider adding a cheap I2C I/O extender
8 channel
https://www.aliexpress.com/item/For-Arduino-PCF8574-PCF8574T-I-O-for-I2C-Port-Interface-Support-Cascading-Extended-Module/32799349209.html

16 channels
https://www.aliexpress.com/item/Bidirectional-16-Bit-I-O-Expander-with-I2C-IIC-Serial-Interface-Module-MCP23017/32779407626.html

So for my bluetooth miflora I use sensor1 … sensor12, here I used that naming sheet due to the fact even with sensors I managed to kill my plants, so I just name them correctly in the sitemap as i keep swapping them around. With lights moving around is unlikely so maybe your naming pattern is better :slight_smile:

So currently I am in St petersburg on holiday, but today I had some time to think about this topic and would like to share my thoughts with you regarding ledstrips.

Intiially I was thinking of naming the ledstrips ledstrip01 … ledstrip24 instead of livingroom/ledstripShelf1 and so on… I am still not sure what is best of those two, second requires that you are creative with naming :slight_smile:

The ledstrip that I am using are neopixel(adreesable, so that you can control every single led on the strip) so my idea, when using the first method above was:

ledstrip01/rgb/0,0,255,255,255
ledstrip01/rgb/5,3,155,0,0
ledstrip01/sweep/10,1,155,155,155
ledstrip01/random/1,1,0,134,0
ledstrip01/random/-1,-1,155,134,126
ledstrip01/random/-10,1,155,134,126

So rgb command takes the following input: on time(0 is always on) off time,red value(0 is off),green value, blue value
So first will constantly light it white, while second will blink slightly dimmed red where it blinks every 8 s(its on for 5s and off for 3s)
sweep command takes following input: time on for each led(ms),direction(0 for max led number to 0),red color,green color,blue color,

The random command i am a bit unsure about, but the idea is: ontime,off time, random red up to value,random green up to value, random blue up to value,
so ledstrip01/random/1,1,0,134,0 will blink with 1s intervall with different(random) strength of greeen color
ledstrip01/random/-1,-1,155,134,126 will blink with 1s intervall and each led with random colors up to the given value,

then maybe a random sweep would be nice? ledstrip01/random/-10,1,155,134,126 sweep from low to high with 10ms on each led…

This was just an idea? Any feedback to this approach as a generic approach?

This is good point and a special case. You can’t be very specific when you have a mobile device that can and does change jobs frequently. My Sonoff Basics are similarly named with generic names (I just used the defaults with a slight modification).

I have no experience with these devices so all I can say is it looks like a good approach to me.