Simple WiFi relay board: a 4 channel DIY WiFi relay board (2)

Driving a 4 channel relay board with ESP8266-01 through MQTT


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.  You could use an LDO like the AMS1117 3.3 to drop the 5Volt to 3.3 however,do not connect the two Vcc’s directly. Wait, let me emphasize that: DO NOT CONNECT THE Vcc OF THE ESP TO THE VCC OF THE RELAY BORD, JUST DONT!!!!

The full board
The ESP8266-01 adapter board

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]" (GF_Corridor) { mqtt="[mosquitto:home/sb/node01/dev17:command:*:default]" }
Switch rel2 "WiFi relay 2 [%s]" (GF_Corridor) { mqtt="[mosquitto:home/sb/node01/dev18:command:*:default]" }
Switch rel3 "WiFi relay 3 [%s]" (GF_Corridor) { mqtt="[mosquitto:home/sb/node01/dev19:command:*:default]" }
Switch rel4 "WiFi relay 4 [%s]" (GF_Corridor) { mqtt="[mosquitto:home/sb/node01/dev16:command:*:default]" }

this will render the following menu:

The program can be downloaded here. FYI, I trimmed down an existing, bigger program of mine. 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

If you are using a different relay board that does not have it’s inputs pulled high in rest, then you need to add 10k pull-ups on GPIO 0 and GPIO2

Advertisements

Simple WiFi relay board: an overview (1)

In his video nr 107 youtuber Ralph Bacon describes his ‘frustration’ with an ESP8266-01 based wireless relay he got from AliExpress.

Wifi relay with ESP8266-01 and STC15F104 microprocessor

His frustration is understandable as that particular module is needlessly complicated. It seems the ESP8266-01 is mainly there to make the WiFi connection, while the relay is triggered by yet another microprocessor, the STC15F104. Communication between the two is via AT commands, as if the designers thought, how can we make this in the dumbest way possible.

The ‘simple’ relay board

In his follow up video # 110  Ralph describes another, simpler relay board (pictured), that also frustrated him as the manufacturer apparently had not included the necessary pull-up resistors on the Chip Enable and on GPIO0 and GPIO2. (Edit: this turned out not to be entirely true as the board comes with an ESP8266-01S that has the necessary pullups on board)

Both videos came in my focus again, when i discussed the ‘simpler’ board with a diy mate and frequent commenter. It is very cheap to buy and once you add the resistors (to make it start up correctly) factually you have a Sonoff SV.
Ofcourse the Sonoff SV is less than 5 Euro (plus shipping), as opposed to the ‘brandless’ relay board only costing some 2.60 euro, so you might as well get the real thing, but it opens some interesting perspectives, especially as I had most of the stuff laying around namely an ESP8266-01 a relay module and a 3.3Volt power module, all fairly cheap. Just a couple of DuPont cables to connect the three, and it should be fine. I know it is all nickles & dimes stuff but lets do a quick calculation.

Total 2.08 euro as opposed to 2.62 euro (in a nicer package), so not really cost effective to ‘DIY’  but if you have the stuff laying around, better to use it than for it gather dust. It also allows you to choose another pin than GPIO2 to drive the relay.
Ralph also offers a program to replace the existing firmware in the ESP8266, as well as a phone app (all found in the description of his video). Ofcourse it is also possible to replace the firmware with MQTT responsive firmware. For that you could e.g. use my Sonoff Touch program, albeit that in line 17, you have to change “TouchLED=12;”  to “TouchLED=2;

But why stop there? the ESP8266-01 has 4 I/O pins, if we ad a small 220->5V power module and a 4 channel relay board, we could make a sonoff 4ch. These cost about 22 Euro. So that would be more rewarding to build.
That however will be for part 2.

Flashing Sonoff Touch – No soldering

Although it is not really a big thing to solder an angled header in a SonOff Touch in order to reprogram it, one also has to solder a wire to the GPIO-0 pin of the ESP8285 chip as the SonOff Touch has no button that can be used to get it into flash mode.
I therefore wondered if it wouldn’t be possible to flash the Sonoff Touch without any soldering. Spoiler: yes it is possible, but you need some dexterity.
First open the Sonoff Touch. This is easiest done with cautiously prying a screwdriver on between the casing and the frontplate at the side.
once that is done you can see a small board that you can just pull-out.
On that board there are 4 in line holes that we need to connect an FTDI programmer to. On the bottom side of the board is also a 2×2 male header. That is the one that we just pulled out of its header in the housing. We are going to need that header a bit later as well.

indicated  the function of the 1×4 header in the image. You are going to need an FTDI to USB module  THAT YOU CAN SET TO 3.3 VOLT. You also will need a 1×4  straight pinheader and 4 female to female DuPont wires.
Now make the following connection between the 1×4 male header and the FTDI to USB connector:

header FTDI module
Ground Ground
Tx Rx
Rx Tx
Vcc Vcc

To make clear, if it already wasn’t, this is a connection to a loose, 4 pin male header.
The next thing you need is a female to male DuPont cable. Connect the female end to the ground connection on the 2×2 header.

Also, you need to identify GPIO0 on the ESP8285 chip:

 

For the final flashing you need a bit of dexterity but this is what you do:

  • Have your Arduino IDE available with the  required program loaded.
  • Under tools-boards choose the generic 8285 module
  • Flash size 1 Mbyte 16k SPIFFS
  • Make sure the FTDI module is not connected to the USB port of your computer.
  • Now press the 4 pin male header that is connected to your FTDI module in the proper holes. Make sure it is the right way around, so Ground connects to ground, Vcc to Vcc, Rx to Tx and Tx to Rx. If there is a bit of slack between the header and the holes. push against the pins with  the mouse of yr thumb or with your little finger, so it makes  poper connection.
  • Now take the male pin of the DuPont cable that you connected to the ground on the 2×2 pin header and push it against GPIO 0.
    Keep it in place with your thumb.
  • You still should have one hand free. Use that to push the FTDI module in the USB connection of your computer.
  • Once that is done, you can let go of the male pin pressed against GPIO0
  • In the IDE chose the right port and press ‘upload’.

That’s it.

So, what program should you upload to make the Sonof Touch work?
Of course there is Tasmota and many people are very happy with it. Truthfully, I found it cumbersome and couldn’t even compile it. Considering all we need to do is switch 1 pin, it shouldn’t be so hard to write something simple.
Given the fact that the Sonoff Touch will most likely disappear in the wall and you don’t want to have to take it out and flash it again, two things come in very handy in the program:

  • OTA (Over The Air) flashing
  • WiFiManager

The code also gives MQTT feedback about the

  • MAC
  • IP
  • Filename
  • SignalStrength

It can be downloaded here

Reading the DHT11 or DHT22 on the Raspberry via an overlay and send it to the openHab REST API

In an earlier tutorial I showed how to read the popular DHTxx sensor on a Raspberry Pi and then to MQTT that into Openhab.
However, there are more ways to skin a cat, so what I like to do this time is:

1) Read the sensor with a device tree overlay on a Raspi and then
2) Send the data to OpenHab with the REST-API

I am not saying one method is better than the other, but suppose you dont want to install an MQTT server because all your other channels don’t need MQTT, then the REST API comes in handy.

1) Reading the DHT sensor
First we need to load the dht11 overlay at boot-up
Do
sudo nano /boot/config.txt
and add:

dtoverlay=dht11,gpiopin=4

obviously one can pick another pin here, or declare a different sensor such as the DHT22 or DHT21 (AM2301)

then all you have to do is to read the following 2 files:

/sys/devices/platform/dht11@0/iio:device0/in_temp_input
/sys/devices/platform/dht11@0/iio:device0/in_humidityrelative_input

Once you have loaded the overlay and connected your sensor, a simple:
cat /sys/devices/platform/dht11@0/iio:device0/in_temp_input,
would show you the temperature.
whereas:
cat /sys/devices/platform/dht11@0/iio:device0/in_humidityrelative_input, would show you the humidity

Upon doing that in a python program, I noticed there can be quite a number of I/O errors. This seems to be a frequent problem with this overlay, so in order not to interrupt the program, I had to do a bit of Error catching.

2) Sending values to OpenHab
The REST API is fairly simple to use. We only need 1 line for every Item we want to update. Such a line looks like this:
requests.put('http://192.168.xxx.yyy:800/rest/items/<YOUR ITEM>/state',value).
The ‘value’-variable needs to be a string.

3) Wrapping it up
A simple Python program would look like so:

import time
import requests
tfile='/sys/devices/platform/dht11@0/iio:device0/in_temp_input'
hfile='/sys/devices/platform/dht11@0/iio:device0/in_humidityrelative_input'

def read_temp_raw():
   f=open(tfile,'r')
   lines=float(f.read())/1000.0
   f.close()
   return lines

def read_humidity_raw():
   b=open(hfile,'r')
   hlines=float(b.read())/1000
   b.close()
   return hlines

while True:
   try:
     T=str(read_temp_raw())
     print(T)
     H=str(read_humidity_raw())
     print(H)
     time.sleep(2)
     #Put your own IP and Itemnames here,
     requests.put('http://192.168.1.103:8080/rest/items/<ITEMNAME>/state',T)
     requests.put('http://192.168.1.103:8080/rest/items/<ITEMNAME>/state',H)

   except IOError:
      print("I/O error")

Beware that Python programs rely on proper indenting. As this sometimes can be corrupted by publication on a website, I will leave an image of the properly indented program below:

As I am not the world’ s best python coder, I am sure improvements can be made, but at least this code will get you up and running fast.
Since the items are updated through the REST API, they do not need a channel and could look as simple as this:

Number HH "REST Humidity [%.0f %%]" <humidity> (Weather)
Number HT "REST temperature [%.1f °C]" <temperature> (Weather)

JSON, MQTT, PubSub and OpenHAB

Usually I communicate between my various nodes (arduino, esp8266) with MQTT and OpenHAB being the interface. Simple, just one string command per message. Now there comes a moment that it seems better or more convenient, to send more commands in one MQTT message, and that is where JSON comes in. JSON messages are basically an organized string and MQTT is good in sending strings.
Author of the Pubsub library Nick O’Leary’s advice on sending JSON’s is as follows:

However, you need a few tricks before you can send a string variable.

I started off by using the ArduinoJson library to make my JSON’s, but I was not quite happy with that. There are various ways to do it and you end up with some sort of data/char array.

Supposedly you could send that with "client.publish(topic, (char*) payload.c_str())". But for various reasons that didnt suit me so I used another method.

As I was a bit unhappy about adding the ArduinoJson library just to make a JSON string, I thought it would be  best to simply compile the string myself and send that. I am not saying there is no better way… and if there is, please do tell, but this worked for me.

Anyway, let’s start with something simple, I have a LiPo battery feeding an ESP8266 and in openHAB I like to keep track of the voltage of the battery, but I also like to display the Percentage of the battery charged. Ofcourse I can calculate the latter in OpenHAB, but i prefer to have the ESP8266 do that.
Note: the below calculation is for a Wemos D1, with a 100k  resistor  in series with A0. Together with the  residing voltage divider it makes a 100k/(100k+220k+100k) = 1/4.2 voltage divider,  reducing a 4.2Volt voltage to 1 Volt. Therefore a reading of 1023 (=1V) on the A0 port correlates with 4.2 Volt on the LiPo battery)

voltage=analogRead(A0);// (mind you, this is 1 V max)
voltage=voltage/1023.0;// so 1V max renders "1" here
voltage=voltage*4.2;// We want a max of 4.2, so multiply
percent=100*voltage/4.2;// and calculate percentage

The JSON string I need to make, will look as follows

{"Voltage": voltage_value,"Percent": percent_value}

to insert a quote in a string you need to use the backslash as an escape character, so, we make the JSON string as follows:

String payload="{\"Voltage\":"+String(voltage)+ ",\"Percent\":"+String(percent)+"} ";

So now we have the Stringvariable “payload” that we have to send via PubSub.

There may be various ways to do that, I did it as follows:

payload.toCharArray(data, (payload.length() + 1));
client.publish("home/battery",data);

You could probably also use: client.publish("home/battery", payload.c_str());, but I didn’t*).
So, once in openHAB it is quite easy to process the the JSON, once the proper transformation Add-on is installed.

In the items file it then looks like this:

Number battery_garden "Battery Garden [%.1f V]"  (Back_Garden, batteries) {mqtt="<[mosquitto:home/battery:state:JSONPATH($.Voltage)]"}
Number battery_garden_pct "Battery Garden [%.0f %%]"  (Back_Garden, batteries) {mqtt="<[mosquitto:home/battery:state:JSONPATH($.Percent)]"}

And on screen like this:

OK, let’s try that with the reading results of a DHT11. Sure, there might be many reasons why you would want to send the humidity reading and temperature reasing as two different MQTT strings, but for arguments sake let’ s try it as a JSON.

We will first read the DHT11 sensor,then we will create a JSON that looks like {“Temperature”: 20.0, “Humidity”: 68}  with 20.0 and 68 of course just snapshot  examples. Subsequently we will create the MQTT message and display it in openHAB

float humid_coop = dht.readHumidity();
float temp_coop = dht.readTemperature();
String payload="{\"Humidity\":"+String(humid_coop)+ ",\"Temperature\":"+String(temp_coop)+"} ";
payload.toCharArray(data, (payload.length() + 1));
client.publish("home/coop", data);

and then in the itemsfile:

Number Humidity_Coop "Humidity Coop [%.0f %%]" (coop) {mqtt="<[mosquitto:home/coop:state:JSONPATH($.Humidity)]"}
and
Number Temperature_coop "Temperatuur [%.2f °C]" (coop) {mqtt="<[mosquitto:home/coop:state:JSONPATH($.Temperature)]"}

Let’s now have a look at sending more than just two data  values. Suppose you have an I/O chip (or more) that lets you read 12 channels at the same time, you could ofcourse make a JSON like this: {“channel1” : value1, “channel2″:value2,  ……..”channel12”: value12}, but that seems to send a lot of unnecessary data, so we will create the following JSON:

{"data":[value1,.............value12]}
We do that as follows

String payload="{\"data\":["+String(ana0)+","+String(ana1)+","+String(ana2)+","+String(ana3)+","+String(ana4)+","+String(ana5)+","+String(ana6)+","+String(ana7)+","+String(ana8)+","+String(ana9)+","+String(ana10)+","+String(ana11)+ "]}";
payload.toCharArray(data, (payload.length() + 1));
client.publish("home/json", data);

in the MQTT channel in the items file we will now use a slightly different JSON format. The data will now be read by index number like:

JSONPATH($.data[0])

the full itemsfile then will be

Number Moisture1 "Moisture 1 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[0])]"}
Number Moisture2 "Moisture 2 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[1])]"}
Number Moisture3 "Moisture 3 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[2])]"}
Number Moisture4 "Moisture 4 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[3])]"}
Number Moisture5 "Moisture 5 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[4])]"}
Number Moisture6 "Moisture 6 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[5])]"}
Number Moisture7 "Moisture 7 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[6])]"}
Number Moisture8 "Moisture 8 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[7])]"}
Number Moisture9 "Moisture 9 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[8])]"}
Number Moisture10 "Moisture 10 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[9])]"}
Number Moisture11 "Moisture 11 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[10])]"}
Number Moisture12 "Moisture 12 [%.0f]" <plantbed> (Garden_moist) {mqtt="<[mosquitto:home/json:state:JSONPATH($.data[11])]"}

and look like:

There is nothing stopping you from including data from more than 1 sensor in a JSON and send that all in one MQTT message. This way you could for instance put all the sensor data from say one room, or the entire garden in one MQTT message and send it.
Beware though that the default packet size supported by the PubSub client is 128 bytes. You can increase this limit by editing the value of MQTT_MAX_PACKET_SIZE in PubSubClient.h.
___________________
*)There is a fork of Nick O’Leary’s PubSub library, by Imroy, that will happily send:
client.publish(topic, String (value));