Simple WiFi relay board or DIY Sonoff SV /Sonoff 4 channel (2)

Driving a 4 channel relay board with ESP8266-01


In a previous post I discussed making a WiFi relay inspired by the trouble  Ralph Bacon had with a single relayboard from Aliexpress. Given the fact that the Sonoff SV is relatively cheap (don’t forget the shipping cost though), and the WiFi relay at AliExpress even cheaper, it is a nickle and dime question whether it is wise to DIY such a project yourself. However, if you already have an unused ESP8266-01 and a relay lying around, basically all you need are some DuPont cables to connect the two (as shown in my previous post).

It becomes more interesting to use all 4 pins of an idle lying ESP8266-01 and add a 4 channel relay board.
The ESP8266-01 however is a bit particular with the pins it has,as two of the 4 I/O pins are the pins that need to be pulled HIGH on startup whereas the remaining two pins are the UART. Those UART pins can be used as GPIO, but the problem is that they do not know that, they need to be told that they are no longer UART but in fact GPIO. We do that with a special pinMode command (more about that later).

The relayboard I had in mind is a bit peculiar. I discussed the full workings of this board in an earlier post.

For now however suffice to say the inputs need an active LOW to  close the relays, while in rest, the inputs are HIGH.
That is in fact quite handy when using an ESP8266-01, as two of the 4 pins, GPIO0 and GPIO2, need to be pulled high on start-up, something this circuit actually does. It is necessary though to feed the board, including the optocoupler with 5Volt. The connections between the ESP8266-01 and the relay board are made as follows:

A reminder: the relayboard needs 5Volt and the ESP-01 needs 3.3Volt. Do not connect the two. Wait, let me emphasize that: DO NOT CONNECT THE Vcc OF THE ESP TO THE VCC OF THE RELAY BORD, JUST DONT!!!!

The program

For the program I followed the structure that is used by computourist with regard to the use of MQTT messages.
The idea behind that is that commands going from MQTT broker to the node are called ‘southbound’  (‘sb’), while the ones going to the broker (so usually the ‘ state’) are called ‘northbound’ (‘nb’). The specific functions in the node are addressed as numbered devices.
This has advantages and disadvantages. The disadvantage being that you get codes like: “home/sb/node01/dev17” rather than something like: “home/cmd/wifirelay/relay1“.
Also the handling of the incoming MQTT is bound to a specified length, so altering it needs to be done with some consideration.
The advantages are that the handling of the code is easier and in fact extending the code with more functions is fairly easy.
What happens in fact is that once a subscribed MQTT code comes in, it is stripped to a its last two digits. These digits define the function it fulfills.
The Payload can be “ON”, “OFF”, “READ”, or a number, depending on the function chosen. The “READ”  payload reads and returns the state of the specific function (‘device’)  that is being read. Some ‘devices’ -as for instance the IP number or the MAC address, can only be read and not ‘set’.

The full list of devices is as follows:
00 uptime: read uptime in minutes
01 interval: read/set transmission interval for push messages
02 RSSI: read WiFi signal strength
03 version: read software version
05 ACK: read/set acknowledge message after a ‘set’ request

10 IP: Read IP address
11 SSID: Read SSID
12 PW: Read Password

16 read/set relay1 output
17 read set relay2 output
18 read set relay3 output
19 read set relay4 output

92 error: tx only: device not supported
91 error: tx only: syntax error
99 wakeup: tx only: first message sent on node startup

As I mentioned earlier, the UART pins need to be told that they should behave like GPIO pins. That can be done with the statement:
pinMode(1,FUNCTION_3);
pinMode(3,FUNCTION_3);

However, this will not work when there are any hardware serial statements left (such as Serial.print, Serial.begin)
So in order to control the relays from e.g. OpenHAB, this is what you add to your itemsfile:

Switch rel1 "WiFi relay 1 [%s]" <relay> (GF_Corridor) { mqtt="<[mosquitto:home/nb/node01/dev17:state:default],>[mosquitto:home/sb/node01/dev17:command:*:default]" }
Switch rel2 "WiFi relay 2 [%s]" <relay> (GF_Corridor) { mqtt="<[mosquitto:home/nb/node01/dev18:state:default],>[mosquitto:home/sb/node01/dev18:command:*:default]" }
Switch rel3 "WiFi relay 3 [%s]" <relay> (GF_Corridor) { mqtt="<[mosquitto:home/nb/node01/dev19:state:default],>[mosquitto:home/sb/node01/dev19:command:*:default]" }
Switch rel4 "WiFi relay 4 [%s]" <relay> (GF_Corridor) { mqtt="<[mosquitto:home/nb/node01/dev16:state:default],>[mosquitto:home/sb/node01/dev16:command:*:default]" }

The program can be downloaded here. FYI, I used a program that was rather extensive and cut that down to size. I left a bit of code here and there that might not be of immediate use in this project, but may come in handy if you want to use the code on a Wemos.
Cost:

  • relayboard 1.80 euro
  • ESP8266-01 1.45 euro
  • 5 and 3.3Volt module 0.40ct
Advertisements

Controlling a GPIO pin on a remote raspberry

In an earlier post I showed how one could address the raspberry GPIO pins from within OpenHab (or NodeRed for that matter). The only limitation here was that it was on a raspberry that also hosted OpenHAB.

What if you have a RaspberryPi whose pins you want to control from within OpenHab, while openHab is in fact operating from another machine?

In that case  MQTT seems the best option. I wrote a Python program that will do just that.
If you installed Jessie or Stretch, it comes already with Python 2.7 which is quite suitable for this goal.
You will need two libraries:

The lightweight MQTT client Eclipse Paho Mqtt: Here I describe how to install it, but you could also just do:
pip install paho-mqtt

The second one you need to install is the Rpi.GPIO library.
LadyAda gives a good explanation  how to do that, but it basically comes down to issuing the command:
sudo apt-get install python-dev python-rpi.gpio

The Python program itself is fairly simple, but there are a few things to point out
The Rpi.GPIO library has two modes to set up the pins. it has ‘BCM mode’ and ‘BOARD mode’
These two modes refer to the numbering of the pin.

The GPIO.BOARD option specifies that you are referring to the pins by the number of the pin in the plug – i.e the numbers printed on the board.

The GPIO.BCM option means that you are referring to the pins by the “Broadcom SOC channel” number, these are the numbers after “GPIO” that can be seen in the various pinout diagrams

If you are still a bit puzzled, you can try and issue “the gpio readall” command on your terminal and depending on the type of Raspberry you have, you may get outputs like this for the Rpi3:

or this one for the B+
You will also need to set the IP for your particular MQTT broker and need to change the topic according to your wishes.

The program currently switches one pin ON or OFF. it is not particularly hard to expand that for more than one pin. The working can be controlled by the gpio readall command (check in the column ‘V’ what the state of the pin is)

I saved the program in my home/pi directory. It can be started by
python mqttpublish2.py (or any other name you want to give it). It is best to have it start at boot-up
Output will look like this:
A slight word of warning: Python sees indents as part of the command structure. If after downloading my file you go change it, be careful not to mess up the indents.
Edit: In the mean time I expanded the program a bit, it now sends the state of the pin as a seperate topic. It also sends the IP nr, the program name and an ‘alive’ message on startup. It uses pin 18.

 

A battery fed MQTT weatherstation

(Note: if you consider building this… I am working on a similar station with a bare ESP12 and a BME280 for even better energy efficiency)

Weatherstations are a popular build for DIY-ers and with the ESP8266 WiFi capabilities that has become very easy.
Here I will present a simple weatherstation (DHT sensor BMP sensor for temperature, humidity and atmospheric pressure) that can be battery fed or mains fed.
It will send the info by MQTT and in order to save batteries, go to sleep most of the time.
Components needed are:

  • A wemos mini D1
  • A DHT11 or DHT22
  • A BMP280
  • A 100k resistor for the battery
  • A small switch or jumper
  • A LithiumIon charger module

Just a word for the wise… the Wemos D1 is not the best option to battery feed, as it has some peripheral components (such as the CH340 chip) that draw current. A better choice would be the bare ESP8266, but it is not for everybody to solder that.

The application has OTA. Using OTA together with deep sleep is always a bit of a tricky situation as you cannot do OTA when the ESP8266 is in deep sleep.
basically there are 3 solutions for that:

  1. Include a pause in which you can do OTA. Problem with that is that you would need some indication of when that pause starts (e.g. once the MQTT messages have come in). Another problem is that that pause in which the processor is not in deep sleep, it wil consume more energy.
  2. Make the deepsleep dependent on the state of a pin. With a jumper or Switch you could make one pin high or low and thus allow deepsleep or not.
  3. By having the software check on a seperate website whether or not there is an update for the software and then download that software. Obviously that requires some more organization and if the Wemos will do that check whenever it awakes, it will do that say every 15 minutes for something that may never happen.

Considering the above, Option 2 seemed the best, so I added a small switch to tell the software whether or not to go into deepsleep.

Software

The program once loaded (get it here) has several tabs in the IDE.
The main program “esp8266_weatherstation” is quite straightforward, it sets up all the usual parameters, variables and the sensors.
It reads the sensors and sends these via MQTT to a broker.
Once that is done, it goes into deepsleep, depending on two parameters:

  • the “FORCE_DEEPSLEEP” variable: if present there will be a 15 min deepsleep. If not set, the deepsleep will be determined by the battery voltage.
  • the state of the ‘SLEEPPIN (D6):  if LOW there will be deepsleep, if HIGH, there will be no deepsleep

If there is no deepsleep, a pause of 30 secs is added to avoid the MQTT messages being send in a frenzy.

You may be puzzled by the following code section:

float h = dht.readHumidity();
float t = dht.readTemperature();
delay(2000);
float h = dht.readHumidity();
float t = dht.readTemperature();

The reason for this is the response time of the DHT22. If you  start a reading, the results you get are from a reading that was started 2 secs before. Usually that is not an issue, but if you put the processor to sleep for 15 minutes, that first reading will give you 15 min old values. If that is not a problem, by all means remove the delay and second reading. It will not make a difference on your battery life as your  DHT22 is not put to sleep.
technically ofcourse it is possible to feed the DHT22 from one of the I/O pins and switch it off when not needed, but the DHT22 also needs a warm-up time of about 2 secs, so you would need to take that into account. No doubt that warm up time could be catered for with the delay(2000) command mentioned above.

The “OTA” section sets up the OTA function. As explained before, OTA and DEEPSLEEP is a bit of an uneasy combination. In order to use OTA, tou would need to put the switch on D6 in the right position (HIGH) and either reset  the WEMOS or wait till the next wake up. Mind you that when DEEPSLEEPing the OTA port  can disappear, but it should come back once the processor is out of DEEPSLEEP.

the “config.h” file defines some variables

the “webconnect” file connects to WiFi. If for some reason that connection cannot be made after 10 times trying, the processor is going back to sleep for 30 more seconds and then tries again.

Libraries

The libaries used are quite standard, with the exception of the PubSubClient library. This is not the well known PubSubclient library from Nick O’Leary, but a fork made by Ian Tester. The reason for using that one is that it is easier to send variables in the MQTT payload.
The DHT library is the DHT sensor Library from Adafruit. If you prefer another DHT library (there are many) you will probably only need to make minor changes.
The BMP280 library is also from Adafruit.

The sensors

DHT22

The DHT22 is the more accurate sibling of the DHT11, but both have a tendency of failure. generally the DHT22 needs a pull up resistor, but one can also use the internal pullup of the I/O pin for that.

There are two specification where the DHT11 is better than the DHT22. That’s the sampling rate which for the DHT11 is 1Hz or one reading every second, while the DHT22 sampling rate is 0,5Hz or one reading every two seconds and also the DHT11 has smaller body size.

If you are using this sensor outside, you can protect it against dirt and  creepy crawlies by covering it with a single layer of teflon tape. Supposedly this is fully permeable for humidity.

The DHT sensors do not have eternal life. they give up working alltogether (and usually permanently) when the humidity is 100%

Properties DHT11 DHT22
Voltage 3-5 Volt 3-5 Volt
Operating current 2.5mA 2.5mA
Temperature range 0 t/m 50 ºC -40 t/m 80 ºC
Temperature accuracy ±2 ºC ±0,5 ºC
Humidity range 20-80% RH 20-95% RH
Humidity accuracy ±5% RH ±2-5% RH
Sampling rate 1Hz 0.5Hz
BMP280

The BMP280 is the successor of the BMP085, BMP 180 and BMP183

Ofcourse it is possible to use one of the above predecessors, but these older sensors are generally more expensive than the BMP280.
The BMP280 can measure barometric pressure with ±1 hPa absolute accuraccy, and temperature with ±1.0°C accuracy. It can be controlled both by SPI and I2C.

Do not mix it up with the BME280. That sensor can also read humidity, but it is slightly more expensive.
The BME280 ofcourse would be a perfect chip to go to if you want to avoid the DHT22 sensor. It can read temperature, humidity and barometric pressure. I just didnt have it lying around, thus couldnt try.

If you are no fan of the DHT11/22 consider the I2C HTU21D, or theSi7021 (replaced by the HTU21D).
If you are only interested in say temperature and not in humidity, the BPM280 is the best choice and you can omit the DHT22. If you only want temperature, the DS18B20 is probably the best choice.

The battery

Lipo Charge module

It is hard to give recommendations for what battery to use. I am using the battery shield to which I connect a LiPo battery. That makes it all very simple but it is not the most efficient way of using the energy from a battery as the battery shield first boosts it up to 5 Volt after which the Wemos makes 3V3 Volt from it.
Another easy way is to use a Lipo battery with a lowdrop 3V3 regulator and a cheap charging module (pictured top). You could combine the charging module with a small solar cell (with a 5-6 Volt max output). The 100k resistor in the  circuit sketch should go directly to the V+batt, to monitor the battery voltage. The choice for the 100k resistor is explained here.  If for power efficiency you decide to use a bare ESP8266 instead, one has the opportunity to increase the resistors of the voltage divider to come to a smaller current through these resistors

Dimming an AC lamp via MQTT

Years ago I published a TRIAC dimmer that could be controlled by a simple microcontroller such as an arduino. Times have changed and right now it is of more importance to be able to control that lamp from a home automation system such as OpenHab, Homematic or NodeRed.

So, let’s have a look at the circuit first:
This is a rather classic circuit. It detects the zerocrossing on the grid and then a microcontroller ignites the TRIAC with a time delay that determines the brightness of the attached Lamp.  The full cycle is 10mS (at 50Hz)
A circuit like this is easy to make for very low cost (1-2 Euro).

However, if you shy away from soldering  such a circuit, there are ready made modules available as well:
You should not need to pay more than 3-4 USD for such a module. Anything above that is robbery

Software to control this dimmer would look something like this:


#include <Ethernet.h>
#include <PubSubClient.h>
#include  <TimerOne.h>

#define CLIENT_ID       "Dimmer"
#define PUBLISH_DELAY   3000

String ip = "";
bool startsend = HIGH;
uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x07};

volatile int i=0;               // Variable to use as a counter volatile as it is in an interrupt
volatile boolean zero_cross=0;  // Boolean to store a "switch" to tell us if we have crossed zero
int AC_pin1 = 4;// Output to Opto Triac
int dim1 = 0;// Dimming level (0-100)  0 = on, 100 = 0ff

int inc=1;          // counting up or down, 1=up, -1=down
int freqStep = 100; // make this 83 for 60Hz gridfrequency

EthernetClient ethClient;
PubSubClient mqttClient;
long previousMillis;

void zero_cross_detect() {    
  zero_cross = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
  i=0;
  digitalWrite(AC_pin1, LOW);       // turn off TRIAC (and AC)
}                                 

// Turn on the TRIAC at the appropriate time
void dim_check() {                   
  if(zero_cross == true) {              
    if(i>=dim1) {                     
      digitalWrite(AC_pin1, HIGH); // turn on light       
      i=0;  // reset time step counter                         
      zero_cross = false; //reset zero cross detection
    } 
    else {
      i++; // increment time step counter                     
    }                                
  }                                  
} 

void setup() {
  
  attachInterrupt(0, zero_cross_detect, RISING);    // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
  Timer1.attachInterrupt(dim_check, freqStep);
  pinMode(4, OUTPUT);

  // setup serial communication

  Serial.begin(9600);
  while (!Serial) {};
  Serial.println(F("dimmer"));
  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.103", 1883); // <= put here the address of YOUR MQTT server //Serial.println(F("MQTT client configured")); mqttClient.setCallback(callback); Serial.println(); Serial.println(F("Ready to send data")); previousMillis = millis(); mqttClient.publish("home/br/nb/ip", ip.c_str()); } void loop() { // it's time to send new data? if (millis() - previousMillis > PUBLISH_DELAY) {
  sendData();
  previousMillis = millis();

  }

  mqttClient.loop();
  Serial.print("dim1 in loop = ");
  Serial.println(dim1);
}

void sendData() {
  char msgBuffer[20];
  if (mqttClient.connect(CLIENT_ID)) {
    mqttClient.subscribe("home/br/sb");
    if (startsend) {
     
      mqttClient.publish("home/br/nb/ip", ip.c_str());
      startsend = LOW;
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  char msgBuffer[20];
  
   payload[length] = '\0';            // terminate string with '0'
  String strPayload = String((char*)payload);  // convert to string
  Serial.print("strPayload =  ");
  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]);

dim1=strPayload.toInt();

}

The value freqStep is usually set at 75 or 78 with this type of circuits, which allows for 128 steps of dimming at 50Hz gridfrequency. I have set the value here purposely to 100 allowing for only 100 steps, which is convenient for use in OpenHAB as  the slider goes from 0-100%.
The calculation is as follows:

50Hz
As we have two zerocrossings per sine wave, the frequency of zerocrossings is 100Hz. Therefore the period between two zerocrossings is 10mSec is 10000uS.
So if you want to have 100 levels of dimming the steps you need to take are 10000/100=100uS

60Hz
The frequency of zerocrossings is 120Hz, therefore the period between two zerocrossings is 8.3mS is 8300uS. So if you want to have 100 levels of dimming, the steps you need to take are 8300/100=83uS -> freqStep=83

 

Controlling Neopixel or RGB LED with Openhab

Controlling Neopixels or RGB LEDs from an ESP8266, controlled by OpenHab is fairly simple. I will present here a working system with a Colorpicker, some predefined buttons and sliders. It will be updateable by OTA and provide some  feedback to openhab on the node parameters

Using a Colorpicker
The sitemap and items file are fairly easy, just using a Colorpicker. I will describe some additions, but the basic files are quite simple

itemsfile

Group All
Color RGBLed "NeoPixel Color" (All)
String RGBLedColor (All) {mqtt=">[mosquitto:OpenHab/RGB:command:*:default]"}

There are 2 items here. One item contains the HSB value, coming from the colorpicker, The other item, that will not be visible on the screen, will contain the calculated RGB code that will be sent out via MQTT.

sitemap file is as follows:

sitemap NeoPixel label="NeoPixel"
{
	Frame label="NeoPixel" {
		Colorpicker item=RGBLed icon="slider"
	}
}

The colorpicker sends an HSB (Hue, Saturation, Brightness) code, but that isnt anything a regular Neopixel LED or regular RGB LED can use, so I set up a rule that triggers on any change of the RGB item to change the HSB code into a regular RGB code. That RGBcode is joined in 1 string called ’ color’. The rule then assigns that ‘color’ string to a new item called “RGBLedColor”

rules file

import org.eclipse.smarthome.core.library.types.DecimalType
import org.eclipse.smarthome.core.library.types.HSBType

rule "Set HSB value of item RGBLed to RGB color value"
when
	Item RGBLed changed
then
	val hsbValue = RGBLed.state as HSBType

	val brightness = hsbValue.brightness.intValue 
	val redValue = ((((hsbValue.red.intValue * 255) / 100) *brightness) /100).toString
	val greenValue = ((((hsbValue.green.intValue * 255) / 100) *brightness) /100).toString
	val blueValue = ((((hsbValue.blue.intValue * 255) / 100) *brightness) /100).toString

	val color = redValue + "," + greenValue + "," + blueValue

	sendCommand( RGBLedColor, color)
end

//part of Neopixel conf files

On the receiving end, there is a Wemos D1 mini that controls a NeoPix strip. The ‘color’ string is split in its separate RGB values. As such it could also be used for an RGB LED or seperate Red, Green, Blue Led’s.
The code is reacting to one MQTT topic, basically switching the entire strip at one go in the desired color and brightness, but I added a structure catching some other MQTT topics that now go to empty routines but that could be used for patterns like a breathing light or a moving pattern. As illustration I added a simple red-blue pattern that responds to MQTT “OpenHab/RGB/scene” with payload “2”  I am using the broker name ‘mosquitto’, if your broker has a different name you needto modify that.

I have to say though that this is not the most efficient way of adding topics, but for beginners it is easiest to follow.
A much more efficient way is to keep the topics of one specified length with only say the last 2 or 3 characters defining the desired action and then only checking for those last characters. A good example of this is for instance in the gateway or sonoff code on https://github.com/computourist/ESP8266-MQTT-client4. For readability sake however, in this example I have chosen to use a more traditional approch and as long as you just want to control some LEDs it is easier.

The ESP8266 code does send some information back: it returns the software version, the IP number and the RSSI.
With regard to the software version and IP number… if you have several ESP8266’s and or Arduino’s in your system, it is easy to lose track which one does what and with what software.

The ESP8266 code is OTA updateable
I have used a fork of the O’Leary PubSub library that makes it easier to send variables as the MQTT payload without having to resort to setting up buffer space and using c_string. Library is here4: https://github.com/Imroy/pubsubclient4.
If you already have the O’Leary PubSub library installed, you will need to put the library files in your sketch folder and change #include <PubSubClient.h> into #include “PubSubClient.h”

Sending predefined colors
If next to the Colorpicker you also want to be able to send predefined colors via a button, then you only need to add some lines in the items and sitemap file:

add to itemsfile:

    Switch NEO_RED	"Red"	<red> {mqtt=">[mosquitto:OpenHab/RGB:command:ON:255,0,0]"}
    Switch NEO_YELLOW	"Yellow"	<yellow>	{mqtt=">[mosquitto:OpenHab/RGB:command:ON:100,96,0]"}
    Switch NEO_BLUE	"Blue"	<darkblue>{mqtt=">[mosquitto:OpenHab/RGB:command:ON:0,0,255]"}

add to the sitemap:

Switch item=NEO_RED mappings=[ON="ON"]
Switch item=NEO_YELLOW mappings=[ON="ON"]
Switch item=NEO_BLUE mappings=[ON="ON"]

As it can be a bit of a bother to find and type all the codes for the colors you may want, I added for download some 160 predefined color settings to pick and add to your sitemap and itemsfile, including some simple icons. As these items already send an R,G,B, code they do not need the rulesfile, only the colorpicker does.

How about Sliders
Perhaps you do not want to use a colorpicker but just individual sliders for Red, Green and Blue.
That is quite easy too.
add the following to your itemsfile:

Dimmer NEO_SLIDERRED "Neopixel Red [%d %%]"  <red> {mqtt=">[mosquitto:OpenHab/RGB/RED:command:*:default]"}
Dimmer NEO_SLIDERGREEN "Neopixel Green [%d %%]" <green> {mqtt=">[mosquitto:OpenHab/RGB/GREEN:command:*:default]"}
Dimmer NEO_SLIDERBLUE "Neopixel Blue [%d %%]" <blue> {mqtt=">[mosquitto:OpenHab/RGB/BLUE:command:*:default]"}

and this to your sitemap

Slider item=NEO_SLIDERRED 
Slider item=NEO_SLIDERGREEN
Slider item=NEO_SLIDERBLUE

Normally that would be enough already, but our ESP8266 program is expecting one input rather than seperate RGB inputs. That is because the colorpicker we used before sends one string of info.
We could add topics to the ESp8266 program to respond to individual RG and B values (and that is what the MQTT topics OpenHab/RGB/RED, OpenHab/RGB/GREEN, and OpenHab/RGB/Blue would be for), but it might be easier to combine those and send as one. For that we need a new rule.

add this to your rules file:

rule "Send  RGB items from slider"
when
Item NEO_SLIDERRED changed or
Item NEO_SLIDERGREEN changed or
Item NEO_SLIDERBLUE changed
then
val redValue=  Math::round(2.55* (NEO_SLIDERRED.state as DecimalType).intValue)
val greenValue=  Math::round(2.55* (NEO_SLIDERGREEN.state as DecimalType).intValue) //
val blueValue=  Math::round(2.55* (NEO_SLIDERBLUE.state as Number).intValue)
val color = redValue + "," + greenValue + "," + blueValue
	sendCommand( RGBLedColor, color)
end

While we are at it
If you want the Filename, IP number and RSSI to show up, add the following to your items file:

Number W_RSSI  "RSSI [%d dbm]" <signal>  {mqtt="<[mosquitto:home/nb/weer/RSSI:state:default]"}
String W_IP "IP [%s]" <network>   {mqtt="<[mosquitto:home/nb/weer/IP:state:default]"}
String W_version "Software versie [%s]" <version>  {mqtt="<[mosquitto:home/nb/weer/version:state:default]"}
String W_MAC "MAC [%s]" <mac> (Wweather) {mqtt="<[mosquitto:home/nb/weer/mac:state:default]"}
Number W_IP_Uptime "Uptime [%d min]" <clock> (Wweather) {mqtt="<[mosquitto:home/nb/weer/uptime:state:default]"}

and the following lines to your sitemap:

   Text item=W_RSSI
   Text item=W_IP
   Text item=W_version
   Text item=W_MAC
   Text item=W_IP_Uptime

download11
https://drive.google.com/open?id=0B9IqgvBSA4aUVzgzRE9MUUY3U2M11

Although I tried on a Wemos D1 mini, I presume this will also work perfectly on the ESP8266-01. Perhaps a great way to use those if you still have on your scrapheap as this is typically a minimal pin project.

The ESP8266 program
The Esp8266 program is rather self explanatory and going into it too deep might fall outside the openhab scope a bit, but in brief, you will find several ino files and a userconfig.h file. These all should be in the same directory and it is sufficient to open the NeopixelOpenhab_vx.ino file in your IDE. The other files will then open up too.

You need to make a few modifications in the userdata.h file.

The essential changes are your password, your SSID and the address of your MQTT server. The other options are indeed ‘optional’, can leave them as is.
the Filename is sent back to openhab as an MQTT message, so in the future you will still know what file it was you put in your ESP8266. obviously you would need to fill out the correct filename. Maybe there is a possibility to automatically pull the filename from the system but if it is, then I haven’t found out about it yet

MQTT for beginners

Although there are ample ready made programs for the ESP8266 to connect to
MQTT, I still get questions of people on how to do it. So I will explain it step by step here. If you already know exactly on how to do this, you may just as well skip this post, although there might be some things of a more general interest towards the end.

For those who really have no idea what MQTT actually is, it is a lightweight protocol for machine to machine communication. It consists of a topic and a message (also called payload) that is used often in iot and homeautomation projects. In short, and pulling it a bit in absurdum, with MQTT your refrigerator can talk to your oven and your coffee-maker, provided these are all ‘smart devices. However, they do not talk directly to eachother, they do that via a ‘broker’. A broker is a machine that keeps track of all the MQTT traffic and knows what messages need to go to what device. That means that when your fridge
wants to talk to the coffee maker, there is no need for the oven to listen to that message.

So first of all you need an MQTT broker. That could be a public broker or your own broker that you run locally (for instance on a Raspberry Pi). Mosquitto is a popular broker to run yourself (check here how). If you do not want to run your own broker you have options like test.mosquitto.org, CloudMQTT, HiveMQ, MQTT Dashboard (though that uses Hive), RabbitMQ and io.adafruit . To get up and running fast, test.mosquitto.org is the easiest. However, it is a completely open test broker. There is no security on it whatsoever so it is adviseable to change to a private or password protected broker as soon as you have your mqtt up and running.

For the ESP8266 program that we will be making, there will be 4 important credentials that we need. Those are the server, the user, the password and the port. Mind you that “test.mosquitto.org” does not require a user log-in and also when you are running your own broker you do not need (but can set) a log-in, but I will introduce these two variables in the program for when you decide you need/want a log in.

The ESP8266 using the MQTT protocol is considered a ‘client’ of the ‘broker’. To let the ESP8266 talk to the broker there is very popular library called PubSubClient. It is called ‘PubSub’ because it allows the ESP to ‘publish’ MQTT messages to the broker and to ‘Subscribe’ to MQTT messages of the broker. Do you remember I said the oven des not need to listen to the fridge talking to the coffeemaker? Well that is what the ‘subscription’ is for: The specific ESP8266 is only getting messages that it ‘subscribed’ to. The library can be installed via Arduino IDE library manager.


The code

Like in all programs, we begin by including the libraries that are needed for all the functionality we want/need. We need the ESP8266WiFi library, for the ESP8266 to connect to a WiFi network, and the PubSubClient library, for the ESP8266 to connect to a MQTT broker and publish/subscribe messages in topics.

#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h

Next, we will declare the global variables for our connections. We need the WiFi credentials, to connect to the WiFi network.

constchar* ssid = "YourWiFiSSID";  constchar* password = "YourWiFiPassword";

Next we need the information of the MQTT server. As explained earlier, you will need the server address, the port, the username and the password, but for our test we can leave the user and password empty. In most MQTT brokers, the port is 1883

const char* mqttServer = "test.mosquitto.com"; 
const int mqttPort = 1883; 
const char* mqttUser = "YourMQTTUsername"; 
const char* mqttPassword = "YourMQTTPassword";

Next we will declare two ‘objects’ (like with all libraries): the WiFiClient object establishes a connection to a specific IP and port. The PubSubClient receives input of the WiFiClient that we just created

We will keep the PubSubclient object simple with just one argument, but it can receive more arguments. For that you may want to check the the API documentation.

WiFiClient espClient;
PubSubClient client(espClient);

In the setup function we connect to the WiFi network and for convenience we print the progress to the serial monitor

Serial.begin(115200);
WiFi.begin(ssid, password);
while(WiFi.status()
!= WL_CONNECTED) {

delay(500);
Serial.print("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");

Then we set the address and the port of the MQTT server. We do this with the setServer method of the PubSubClient object.

client.setServer(mqttServer, mqttPort);

We are almost there, but we still need to tell the program what to do with incoming MQTT messages. For that we use the setCallback method of the PubSub object to set a function that is executed when a MQTT message is received. Traditionally this function is called ‘callback’, but you can give it any other name

client.setCallback(callback);

Now, we need the Setup function to connect to the MQTT server. Preferably we do this in a loop till we get succes.

while
(!client.connected()) {
Serial.println("Connecting to MQTT...");
if (client.connect("ESP8266Client", mqttUser, mqttPassword )) {
Serial.println("connected");
}else{
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}

The setup function is a good place to publish our first MQTT message to tell the world we are there. Kind of an “Hello World” to let the broker know we are on line. We do this with the publish method, that takes a topic and a message as arguments. As a topic we choose “test/esp8266”, and for a message “Hello ESP world”.

client.publish("test/esp8266","Hello ESP world");

Leaves us to subscribe to a topic to let the broker know what messages from other publishers we like top receive. We do that with the subscribe method, with the topic name as argument. We can take any topic we desire, but for now lets subscribe to the same topic as we publish.

client.subscribe("test/esp8266");

The full setup function now looks like this:

voidsetup()
{
  Serial.begin(115200);
  WiFi.begin(ssid,password);
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while(!client.connected()) {
    Serial.println("Connecting to MQTT...");
    if(client.connect("ESP8266Client", mqttUser, mqttPassword )) {
      Serial.println("connected");
    }else{
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
  client.publish("test/esp8266", "Hello ESP world");
  client.subscribe("test/esp8266");
}

The Topic

This would be a good moment to say some words about the format of the topic. In its smallest form, the topic only needs to be one character long.
Traditionally though, topics have a hierarchy that is defined by backslashes. A topic for instance could be “home\garden\lawn\sprinkler”. This hierarchie allows
subscription on several levels. You could for instance subscribe to the topic “home\#” to get all the messages that deal with ‘home’. You could subscribe to the topic “home\garden\#” to get all the messages that have to do with your garden etc. etc. Often you will see a topic starting with a backslash, like
“\home\garden\lawn\sprinkler” There is absolutely no gain in starting a topic with a backslash.

The callback function

The callback function receives the MQTT messages (topic + payload) that our ESP PubSub client has subscribed*) to. The arguments of the callback function are the name of the topic, the payload and the length of that payload.

As illustration, we will setup the function to print
the topic to the serial port, and then print the received payload.
Since we also have the length of the payload as argument of the
function, this can be easily done in a loop.

voidcallback(char* topic, byte* payload, unsigned intlength)
{

  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  for(inti = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

*) To be precise: the subscription is to a topic, the client then receives the topic + the payload coming with that topic. The payload is sometimes called ‘the message’ but confusingly the topic+payload is sometimes also called “the message”

In the above example we have only subscribed to one specific topic:
test/esp8266.

The topic “test/esp8266/node1” for instance, will not arrive. For that we would need to specify a wildcard “#”

The main loop

In the main loop function, we use the loop method of the PubSubClient. This function is called to allow the client to process incoming messages and maintain its connection to the server.

voidloop()
{
  client.loop();
}

The full code

The full code now is as follows:

#include <WiFiClient.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
constchar* ssid = "YourWiFiSSID ";
constchar* password =  "YourWiFiPassword";
constchar* mqttServer = "test.mosquitto.com";
constintmqttPort =1883;
constchar* mqttUser = "YourMQTTUsername";
constchar* mqttPassword = "YourMQTTPassword";
WiFiClient espClient;
PubSubClient client(espClient);
voidsetup()
{
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
    if(client.connect("ESP8266Client", mqttUser, mqttPassword )) 
{
     Serial.println("connected");
    }else{
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
  client.publish("test/esp8266","Hello ESP World");
  client.subscribe("test/esp8266");// here is where you later add a wildcard
}
voidcallback(char* topic, byte* payload, unsigned intlength)
{
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  for(int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}
voidloop()
{
  client.loop();
}


Testing

To test the code, upload and run it on your ESP8266. Open the Arduino IDE serial monitor to print the output.

The ESP8266 will send the “Hello ESP World” message, however, that will not be printed on the serial monitor, but as the program also subscribed to that same topic, it will receive that same messages, coming back from the broker and then print it on the serial monitor.

If any other client would send message to the “test/esp8266” topic, then that also will be printed in the serial monitor

Now if it does not work and you are sure the code is OK, is very wise to have MQTT-Spy installed on your computer. MQTT-spy monitors the MQTT traffic on your network and it can publish and subscribe to any desired MQTT topic.

OK, so now what? How do I send a variable?

After your initial joy that everything is working you immediately would want to send something useful for instance that reading of a DHT22 or a DS18B20 sensor and suddenly you need to make the step of the string “Hello World” to a temperature variable. Ofcourse you will try and just insert the variable in place of the “Hello World” string and hope for the best, but that will fail, as the PubSub library (and the MQTT protocol) will only accept strings.
You will see it will not even accept a string variable, so what to do? Well there are several ways to put a variable in the MQTT payload:

1- take another library.
There is a fork of the PubSubClient library by Ian Tester. It will accept variables if defined like this:
client.publish("topic", String (variable));
However, it is often not a drop in replacement, you will need to make minor adjustments in setting up the various calls.

2-use some C++language
Suppose you want to publish some interger variable  called ‘temperature’

sprintf(buff_msg, "%d", temperature);
client.publish("topic", buff_msg);

ofcourse you will need to set up the buff_msg buffer in your global variable declarations like this:

buff_msg[32]; // mqtt message

A very frequently used  payload to send will be OFF or ON. Although often it  might be just as easy to define an IF statement for your payload like this:

if (status==1){client.publish("topic","ON");}
if (status==0){client.publish("topic","OFF");}

it is also possible to do that with  the sprintf command:

sprintf(buff_msg, "%d",  status);
client.publish("topic",buff_msg);

So, can I send a JSON with MQTT?

Sure you can. I am pretty sure it works with the above method, after all a JSON is just some type of string. However, I prefer another method for a JSON.

Suppose I want to send the following JSON:
"{\"garden\":temperature\",\"data\":["+String(temperature_outside)+","+String(temperature_greenhouse)+","+String(temperature_compost)+","+String(temperature_coldframe)+","+String(temperature_soil)+"]}"
do that as follows:

String payload="{\"garden\":temperature\",\"data\":["+String(temperature_outside)+","+String(temperature_greenhouse)+","+String(temperature_compost)+","+String(temperature_coldframe)+","+String(temperature_soil)+"]}";

payload.toCharArray(data, (payload.length() + 1));
client.publish("topic",data);

I like to send my IP number as MQTT, is that possible?

Ofcourse. There are several methods and in the end all roads lead to Rome. Using c_str is sometimes advised but I prefer the following.

in your general variable declaration declare:
String IP;

Then after  you connect to WiFi  state:
IP = WiFi.localIP().toString();

Then where you handle your publishing, do the following:

for (i = 0; i < 16; i++) {
buff_msg[i] = IP[i];
}
buff_msg[i] = '\0';
client.publish("IPnumber", buff_msg);
}

Can I send my MAC

Yes you can. Again there are several methods. I prefer  this one:
In your general variable declarations, declare:

String MAC;
Then after you connect to WiFi state:
MAC = WiFi.macAddress();  (unlike the IP nr, this does not need the 'toString()' function.

Then where you handle your publishing, do the following:

for (i = 0; i < 16; i++) {
buff_msg[i] = IP[i];
}
buff_msg[i] = '\0';
client.publish("MAC", buff_msg);
}

An easier way though is to use a PubSubClient that in fact an send variables, like the PubSubClient from Ian Tester that I mentioned earlier.
This allows you to do:

IP = WiFi.localIP().toString(); MAC = WiFi.macAddress(); client.publish("home/ip", String(IP)); client.publish("home/mac", String(MAC));

Can I send variables in the topic?

Yes you can. This is in fact not so different from sending variables in the payload:
In your variable declaraion  declare a buffer:
buff_topic[30]; // mqtt topic

Now suppose you want to send the ID number or the name of your  ESP8266 as part of the topic and an error as the payload:

in your  global declarations you have defined the number or name of your ESP:
#define nodeId 13 // node ID

and then where you send your  messages do:

sprintf(buff_topic, "home/nb/node%02d/error", nodeId);
sprintf(buff_msg, "syntax error %d", error);
client.publish(buff_topic, buff_msg);

What about receiving MQTT messages?

Well, I spoke about the callback function that handles the incoming messages, but I admitt that just waiting for one standard solitary “Hello World” message isn’t very useful.
To subscribe to more than one message we need to use a wildcard, so
client.subscribe(“test/esp8266”);
becomes
client.subscribe(“test/esp8266/#”);
this will catch  messages such as:
“test/esp8266/Light1”
“test/esp8266/Light2”
The most obvious way to use these would be to check the incoming topics is it Light1 or Light2, then check the incoming payload (is it “ON” or “OFF”) and then act upon the content of the payload.

Especially if you have MQTT  topics with varying lengths this requires quite some checking of possibly long topics.
Say you have:
test/esp8266/kitchenlight
test/esp8266/Livingroomlight
test/esp8266/washingmachine
test/esp/8266/firstfloor/airco

Although it is very well possible to check for all these messages and then determine what the payload is, it is memory consuming.
A more elegant way is to keep the MQTT messages all the same length and only say vary the last 2 characters of the topic to indicate what it is about. That gives room for 100 different topics, while uou only have to check the last 2 characters, rather than a long string.
Lets give an example:
Suppose your MQTT messages all have the format:  “home/nodeID/devID” or -in practice:
“home/node13/dev00”
…….
“home/node13/dev99”

Your subscription then would be to: “home/node13/#”
and you would only have to check on the last two digits.

An example of  your callback then would be

void callBack(char* topic, byte* payload, unsigned int length) { // receive and handle MQTT messages
int i;
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
if (strlen(topic) == 17) { // correct topic length ?
DID = (topic[15] - '0') * 10 + topic[16] - '0'; // extract device ID from MQTT topic
payload[length] = '\0'; // terminate string with '0'
String strPayload = String((char*)payload); // convert to string
if (length == 0) {
error = 2; // no payload sent
}else {
if (DID == 0) {
device0 = true;
}
if (DID == 1) {
device1 = true;
}
if (DID == 2) {
device2 = true;
}
if (DID == 99) {
device99 = true;
}
}

What the code does here is to just look at the last 2 digits (DID = Device ID) and then set a flag (device0 -device 99) that can be checked in the main loop upon which action can be taken, depending on the content of  the ‘strPayload’  string.

For an extensive example of this check the gateway projects of Computourist on github.

MQTT with the ENC28J60 Ethershield

My old ENC28J60 based shield surely has gathered some dust
My old ENC28J60 based shield surely has gathered some dust

While playing with MQTT on various ESP8266’s, I started to wonder if maybe I could do something with an old ENC28j60 shield and module I still had laying around and actually hardly ever used.

For the youngsters: The ENC28J60 Ethernetshield was the first shield to connect the Arduino with the internet. The major drawback of the chip was that it was lacking a stack, that subsequently had to be constructed in software. As a result it was kinda memory hungry. The initial version was without an SD card slot, the later version had an SD card slot. There were two libraries available: The Ethercard and the Ethershield library. The UIPEthernet library came later.
As far as I could figure out, the PubSubClient library that is needed for MQTT doesnt work with the original Ethershield/EtherCard libraries. It does work with the UIPEthernet library though (extended fork here). The UIPEthernet library is a smart piece of coding that made programs written for the W5100 Ethernetshield suitable to be used with the ENC28J60 Shield, simply by changing the included library. Ofcourse there is a price to pay for this, namely more memory consumption. There is also an MQTT client for the ENC28J60/Atmega328 based Nanode. The Ethercard seems to work with the EthercardMQTT library.

Anyway, there still is enough memory to read a DHT11 sensor an analog port and some switches. In my case those were 3 door contacts. The PubSubClient is the original from Knolleary, though normally I am more a fan of the fork by Imroy. For this example I have used the Mosquitto public broker but ofcourse any broker can be used. I have installed the Mosquitto broker on a local raspberry and I found that a call to (“raspberrypi.local”,1883) does not work, using the  local 192.168.1.xxx ip number does work though

MQTT-Output
MQTT-Output ‘Dicht’ means ‘Closed’

The program is rather ‘spartan’, to save space. Print statements have been removed or commented out after initial testing.

#include <UIPEthernet.h>
#include "PubSubClient.h"
#include "DHT.h"

#define CLIENT_ID       "UnoMQTT"
#define INTERVAL        3000 // 3 sec delay between publishing
#define DHTPIN          3
#define DHTTYPE         DHT11
bool statusKD=HIGH;//living room door
bool statusBD=HIGH;//front door
bool statusGD=HIGH;//garage door
int lichtstatus;
uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};

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

long previousMillis;

void setup() {
pinMode(4,INPUT_PULLUP);
pinMode(5,INPUT_PULLUP);
pinMode(6,INPUT_PULLUP);
  // setup serial communication
  //Serial.begin(9600);
  // setup ethernet communication using DHCP
  if(Ethernet.begin(mac) == 0) {
    //Serial.println(F("Ethernet configuration using DHCP failed"));
    for(;;);
  }
  // setup mqtt client
  mqttClient.setClient(ethClient);
  mqttClient.setServer("test.mosquitto.org",1883);
  //mqttClient.setServer("192.168.1.xxx",1883); //for using local broker
  //mqttClient.setServer("broker.hivemq.com",1883);
  //Serial.println(F("MQTT client configured"));

  // setup DHT sensor
  dht.begin();
  previousMillis = millis();
}

void loop() {
  statusBD=digitalRead(4);
  statusGD=digitalRead(5);
  statusKD=digitalRead(6);
  lichtstatus = analogRead(A0);
  // check interval
  if(millis() - previousMillis > INTERVAL) {
    sendData();
    previousMillis = millis();
  }
  mqttClient.loop();
}

void sendData() {
  char msgBuffer[20];
  float h=dht.readHumidity();
  float t = dht.readTemperature();
  if(mqttClient.connect(CLIENT_ID)) {
   mqttClient.publish("hal/temp", dtostrf(t, 6, 2, msgBuffer));
   mqttClient.publish("hal/humid", dtostrf(h, 6, 2, msgBuffer));
   mqttClient.publish("hal/door", (statusBD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/garage",(statusGD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/kamer",(statusKD == HIGH) ? "OPEN" : "DICHT");
   mqttClient.publish("hal/licht", dtostrf(lichtstatus, 4, 0, msgBuffer));
 //hal=hallway, DICHT=Closed, kamer=room, licht=light
 }
}
The old ENC28J60 shield at work
The old ENC28J60 shield at work

Just for completeness sake, this sketch takes about 77% of memory. This same sketch for the WIZ5100 based Ethernet shield, with the  Ethernet.h library takes about 52% of memory. If you decide to adapt the sketch, be careful with altering the character strings. For instance, using the string “OPEN” 3 times is likely less memory consuming than having 3 different strings of the same or even shorter length. Obviously the ‘topic’ strings take a lot of space as well and if you were to shorten them to less meaningful names, you could add more sensors such as for instance a PIR sensor.

Should you copy the program from this  blog page, you may copy ‘stray characters’  that you have to delete. You can also download it here.
An updated version is found here.

Freeing up memory

Should you really be pressed for memory, there is a way to free up about 5K of Flash: Go to your /../sketchfolder/libraries/UIPEthernet-master/utility/uipethernet-conf.h  and open the uipethernet-conf.h file.
in that file you will see the following section:
udp

If you set UIP_CONF_UDP to ‘0’ you will save 5kB flash, by disabling UDP. However, if you use DHCP to connect to your router, you cannot disable UDP as the DHCP connection requires UDP. In that case you still can gain a bit of memory by reducing the UIP_UDP_CONNS.
An example of using a fixed address is this:

#include <UIPEthernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
//the IP address for the shield:
byte ip[] = { 192, 168, 1, 120 };

void setup()
{
 Ethernet.begin(mac, ip);
}
void loop() {}

A final warning… after I updated my libraries, including the Adafruit DHT library, I received an error on compiling this sketch. That disappeared when I returned it to version 1.2.1. I thought I was not using the Adafruit library but the Tillaert library, but DHT libraries come a dime a dozen so maybe my compiler linked in another than I thought.
(The main sketch is my adaptation of work I found on internet, but I think the original source is from Luca Dentella)