Broadcasting over MQTT with ESP8266
Mar 30, 2021Using an Raspberry Pi for MQTT broadcasting sometimes is somewhat overpowered, so we’ll look at the option of using a ESP8266 instead, or to be more specific, a NodeMCU V3 (Lolin) development board. The goal was to set up a very minimal configuration sending GPIO data over MQTT to Home Assistant. I wanted to keep it as lightweight as possible so chose to program it in C++ rather than Lua.
Ready? Grab a ☕, we’re going in hot!
Requirements
- NodeMCU V3 (Lolin) development board
- (Micro) USB cable
- 5V LED
- Photoresistor
- Jump wires
- optional: Breadboard
- optional: 330 Ohm resistor
Preparing the development board
-
Download and install the board’s macOS / Windows / Linux drivers for your system. I’ll not go into detail of how to configure this for Windows/Linux. For more details check the instructions of your board’s vendor. If you’re using the same board (by AZ-Delivery) as I do, checkout their e-book for exact configuration details.
-
Download and install the Arduin IDE. We will program, compile and upload or C++ scripts to our ESP8266 with this IDE.
- After installation, start the IDE and go to
Arduino / Preferences / Additional Boards Manager URLs
and add the following url. This will add the ESP8266 boards to the boards.http://arduino.esp8266.com/stable/package_esp8266com_index.json
-
Install the PubSubClient library by Nick O’Leary (currently at v2.8.0) via
Tools / Manage Libraries...
. If you cannot find it, search for something like “PubSubClient MQTT lightweight”. -
Go to
Tools / Board: "Arduino Yún" / Boards Manager
and search for “ESP8266”. You’ll see the boards package called esp8266 by ESP8266 Community, currently at version 2.7.4. Install it. - Connect your developer board via USB and select
Tools / Board: "Arduino Yún" / NodeMCU 1.0 (ESP-12E Module)
via the menu.
Preparing the wiring ⚡
Setup the wiring according to the schematic below. If you’d like to adjust the scheme feel free to download the
source files.
The schematic is build with Fritzing. Notice that the LED Digital GPIO is named D5 but is in fact GPIO 14 according to the GPIO pinout. This means you can either use D5
or 14
as name in your blink script.
Your done, let’s get coding! 🤓
Programming the messenger
Members and credentials
First we’ll be setting up the members:
#include "ESP8266WiFi.h"
#include "PubSubClient.h"
#include <string.h>
// D5 or 14 (GPIO)
#define LED 14
// Wifi credentials
const char* ssid = "Your Wifi name";
const char* password = "YourWifiPassword";
// MQTT credentials
const int mqttPort = 1883;
const char* mqttServer = "192.168.xxx.xxx"; // MQTT Broker IP address
const char* mqttUser = "mqtt_username";
const char* mqttPassword = "MqTtPaSsWoRd";
const char* mqttName = "ESP8266-Licht-Sensor";
// Some channels
const char* topic_logs = "home/livingroom/esp/logs";
const char* topic_light = "home/livingroom/esp/light";
WiFiClient espClient;
PubSubClient client(espClient);
Connecting to Wifi and the MQTT Broker
Below the members, let’s add the setup method. The code below will set both GPIO 14 and the onboard LED to output mode.
The connectToWifi
method will contain logic for connecting to the Wifi network with the provided credentials.
The connectToBroker
method will contain logic for connecting to the MQTT browser with the provided credentials.
I am using Home Assistant as my broker.
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
connectToWifi();
connectToBroker();
}
void connectToWifi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
}
void connectToBroker() {
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
while (!client.connected()) {
Serial.println("Connecting to MQTT...");
if (client.connect(mqttName, mqttUser, mqttPassword )) {
Serial.println("connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(1000);
}
}
client.subscribe(topic_logs);
client.subscribe(topic_light);
client.publish(topic_logs, "Connected");
}
Loop
The main logic will happen in the loop
method for now. When code gets more complex you might want to split things up a bit. For now, we’re just reading the AO (Analog Output)
data, which is in fact the photoresistor sensor data, a value between 0
and 1
.
We’ll publish these values to channel home/livingroom/esp/light
on the MQTT broker. After that we’ll wait for 5 seconds and blink our LEDs to visually indicate the loop is running.
This blink method submits “Blink” to the home/livingroom/esp/logs
channel.
void loop()
{
client.loop();
double result = (double) analogRead(A0) / 1024;
constexpr size_t BUFFER_SIZE = 7; //1 char for the sign, 1 char for the decimal dot, 4 chars for the value & 1 char for null termination
char buffer[BUFFER_SIZE];
dtostrf(result, BUFFER_SIZE - 1 /*width, including the decimal dot and minus sign*/, 2 /*precision*/, buffer);
client.publish(topic_light, buffer, BUFFER_SIZE );
waitForBlink(5000);
}
void waitForBlink(int amount)
{
client.publish(topic_logs, "Blink");
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LED, LOW);
delay(amount);
}
Handling messages
As you might’ve noticed we’re adding a callback
with client.setCallback(callback);
in the connectToBroker
method. We can register a callback method
to handle data send by the MQTT broker. We can for example log messages to the serial monitor:
void callback(char* topic, byte* payload, unsigned int length){
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();
Serial.println("-----------------------");
}
Conclusion
That’s it for now. Hit the upload button. It should compile your script, upload it to the ESP8266 and start blinking the LEDs. I hope this will show how relatively easy it is to setup a ESP8266 and broadcast some GPIO sensor data to your MQTT broker.
Trouble Shooting Big Sur Issue
If you’re on macOS Big Sur you might get the following upload error:
pyserial or esptool directories not found next to this upload.py tool.
An error occurred while uploading the sketch
The solution is to adjust a configuration file:
sudo nano ~/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/tools/pyserial/serial/tools/list_ports_osx.py
// replace:
iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit'))
cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
// with:
iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
Also, always debug your connection with a tool like MQTT Explorer to check whether your messages are received and broadcasted over the network or not.