Off-The-Shelf Hacker: Microcontroller Wireless Messaging with MQTT

My original plan for building a backyard sensor was to use an ESP8266 model 01 wireless transceiver and matching relay board along with a passive infrared (PIR) device to detect warm bodies as they slink by. I even ordered a single-channel relay board that mounts the tiny ESP8226 and a USB programmer that could be used for flashing firmware.
Alas, I haven’t been able to get the USB programming board to work, so putting firmware on the 01 microcontroller is a pain. I also forgot that the 8266-01 only has two semi-usable general-purpose I/O (GPIO) pins. One goes to the relay and the other is used for resetting the processor or some such thing.
The slightly larger NodeMCU board turned out to be a better choice for an outdoor sensor device. The board has plenty of pins and is easy to program using just a USB cable. They go for less than $5 each.
We covered hooking up the PIR sensor to the NodeMCU wireless module last week. This week we’ll look at the code, which now incorporates MQTT messaging and a simple workaround to use the single-channel relay board (designed for the ESP8266-01) with the NodeMCU.
Coding Particulars
The code starts with the usual run-of-the-mill variables, library setup and initializations. We only needed the libraries for the 8266-specific WiFi and MQTT client (PubSubClient) to make this code work.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
#include <ESP8266WiFi.h> #include <PubSubClient.h> const char* ssid = "mynet"; const char* password = "123eieio"; const char* mqtt_server = "192.168.5.20"; WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; long lastInput = 0; char msg[50]; int value = 0; int inputPin = 10; int val = 1; int calibrationTime = 5; void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { if ((char)payload[0] == '2') { digitalWrite(16, LOW); // Turn the LED on (Note that LOW is the voltage level digitalWrite(5, HIGH); Serial.println("LED on"); // but actually the LED is on; this is because // it is acive low on the ESP-01) } else { digitalWrite(16, HIGH); // Turn the LED off by making the voltage HIGH digitalWrite(5, LOW); } } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("mqtt", "starting"); // ... and resubscribe client.subscribe("inTopic"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void setup() { pinMode(16, OUTPUT); // Initialize the BUILTIN_LED - GPIO16 pin as an output pinMode(2, OUTPUT); pinMode(5, OUTPUT); pinMode(inputPin, INPUT_PULLUP); // declare sensor as input digitalWrite(16, LOW); delay(1000); digitalWrite(16, HIGH); Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); // Calibrate sensor Serial.print("calibrating sensor "); for(int i = 0; i 500){ Serial.println("Motion detected!"); client.publish("mqtt", "M1"); } lastInput = now; } else { // Serial.println(val); digitalWrite(2, 1); } } |
Next comes connecting to my wireless access point. Of course, you’ll use your own SSID, username and password. WiFi.begin() function starts the wireless network access.
After this comes a callback. It toggles the D1 (GPIO 16) pin on and off according to the messages received by the MQTT client code, that’s initialized in the setup part of the program. Whenever a message comes into the MQTT client, the program breaks out of the main loop and executes the code in the callback. Upon completion, it then jumps back to wherever it was and continues. Switching the relay is done by publishing either a “0” or a “1” to the “inTopic” topic on the MQTT broker.
Naturally, an MQTT broker needs to be running on the network, as well as the MQTT client on the NodeMCU for the whole thing to work. I used the Mosquitto server (broker) on my Linux notebook for testing. At some point soon, I’ll install a permanent MQTT broker on a spare Raspberry Pi clone to route messages for the sprinkler controller and the yard sensors.
Handling the PIR sensor input and sending the MQTT message out was the most challenging part of the project.
The PIR sensor works by actually comparing two internal heat sensors against each other. As an object moves in front of the device, the first sensor will pick up the heat from the human or animal. A split second later, the second sensor will also detect the heat and the electronics will register the difference between the two sensors. There is a plastic lens on the front of the device to widen the field of view. As a result, you’ll get a quick succession of 5 or 10 “hits” as something moves in front of the sensor. The 8266 chip is certainly fast enough to pick up ALL those hits.
We definitely don’t need to detect and publish 10 or 20 MQTT messages each time the PIR sensor trips. We just need one message every couple of seconds.
My solution is to publish the initial “somebody is here” MQTT message, then measure the time between successive sensor “hits.” If we receive another input within a certain time, in this case, 500 milliseconds, just ignore it and keep looping until no new inputs are detected. Then wait for the next “hit” and go through the timing loop, again over and over.
During this whole process, we might also receive a message to actuate the relay. I arbitrarily chose a “2” for ON and a “0” for OFF. The relay will power a couple of 120-volt floodlights. The floodlights will turn on upon receiving a “2” from the MQTT broker.
With the code working, let’s look at the relay board.
Hacking the Relay Board
I puzzled and puzzled about how I could use the 8266-01 modeled relay board with the NodeMCU. Then it hit me. Simply wire one of the NodeMCU pins in place of the 01 device. Hello… that’s why we call this the off-the-shelf hacker column.
The relay board is shown below.

Single-channel relay board for ESP8266-01
We’d normally plug an ESP8266-01 board into the yellow connector, after updating the firmware. Instead, I just ran a jumper from pin D1 (GPIO 16) on the NodeMCU board to pin 0 on the yellow connector. This corresponds to GPIO0 on the ESP8266-01 board. The relay logic is hooked up to the GPIO0 pin. Looking at the relay board with the terminals at the top (relay at the bottom) the GPIO0 pin is on the bottom row, second from the left on the yellow connector. I’ll wire in a pin that goes into the socket when the device gets buttoned up in a weatherproof case.
Of course, we also have to connect +5 volts and ground to the relay board for power. Since the project is still at the prototype stage I just connected those into the existing power rail on the NodeMCU breadboard. The NodeMCU D1 pin is then toggled on/off according to the code, to actuate the relay.
Works like a charm.
Next Steps
I’m steadily moving toward deploying an array of MQTT-connected yard sensors around my house. The next step is to stuff the electronics in a waterproof case and hook it up to the house current and floodlights. I’m thinking that everything might fit neatly into a standard round plastic electrical outlet box. The PIR sensor will have to be waterproofed and mounted on the box, as well.
Look for a roundup on the yard alarm sensor, in the near future.