MQTT stands for Message Queuing Telemetry Transport and is a publish-subscribe network protocol for transporting messages between different devices. It was first developed in 1999 and is very popular for IoT (Internet of Things) applications.
The core of an MQTT network is a server also called MQTT broker. Other devices, called clients, can connect through WiFi to the broker and publish or subscribe to certain topics. The broker receives the messages that are published by the clients and sorts them by their topics. Clients can also subscribe to topics; if a new message is published under a certain topic, the broker redirects this message to all clients that are subscribed to that topic.
An example for how to apply this would be to read certain sensor data with a microcontroller and publish it for example under the topics:
Another client could subscribe to these topics and would receive any messages published in those topics. Clients like node-RED allow to subscribe to topics and visualize the data on a website or transfer the results to a database for permanent storage.
It would also be possible to subscribe to a topic with the microcontroller and control for example the lighting in the living room with a smartphone.
For testing MQTT and developing a working program structure, it was not necessary to connect any of the sensors. Instead, 4 example String objects were used which would be of the same format as the results from the sensor measurements. Furthermore, in the final application, the values are to be sent to the eolab server were they are printed and stored using a combination of node-RED, which is an MQTT client, the influxdb database for data storage, and grafana for printing the data. However, for the program code on the ESP32 it is only a minor difference to which server the data is sent. Therefore, for testing, the example data were only published to a local mosquitto server (Eclipse Mosquitto), acting as MQTT broker, and printed using MQTT.fx (MQTT.fx) as MQTT client. Both programs are free to use and are easy to set up and therefore perfectly suited for testing.
The goal was to wake the ESP32 up from deep sleep by using the DS3231 interrupt signal (once a minute for testing). Then reset the alarm, start the WiFi connection and connect to the mqtt broker and, if successful, publish the example data to four different topics and go back into deep sleep. Therefore, the ESP32 needs to be connected to the DS3231 as shown in the ESP32 Deep Sleep Mode-page.
It could be possible due to issues either with the local WiFi, the MQTT broker (e.g. the eolab or the mosquitto server), or the ESP32 itself, that connection to WiFi or MQTT broker might not be possible. To prevent the ESP32 from getting stuck in an endless loop trying to connect to either of the two, a timeout is included in the code. The ESP32 will try to connect to both for 30 seconds maximum each. If no connection is possible, it will increment a counter, write it to its nonvolatile flash memory, restart, and try again to connect. If after three tries, still no connection is possible, it resets the counter and goes back into deep sleep to try again when the next interrupt from the DS3231 occurs. This way, the ESP32 only measures sensor data if a connection could be established and if not, it tries a few times to reconnect and then goes back to deep sleep such that the batteries are not drained due to the ESP32 being stuck in an endless loop.
The ESP32 can be restarted manually by pressing the EN (Enable) button on the board. But there is also an EN pin which can be used for the same thing. The EN pin is connected to the voltage regulator on the board and is normally pulled to high voltage through an internal pullup resistor. If the voltage on the EN pin is low, the ESP32 is powered down and restarts as soon as the voltage level is high again. The button on the board shorts the EN input pin to GND and therefore restarts the board. To be able to restart the ESP32 automatically through software, a transistor is needed that shorts the EN pin to GND when a high voltage signal is applied to the base of the transistor.
Transistors
A transistor is a semiconductor device which basically consists of two diodes connected back to back such that one is always forward-biased and one is always reverse-biased blocking the current from collector to emitter side of the transistor. In an NPN transistor, the base is connected to the positively doped section in the middle. The emitter is connected to GND, while the collector is connected to a voltage source. When a voltage is applied to the base of the transistor, a small current flows from the base to the emitter; that means electrons are going from the emitter side to the base. However, as the p layer is very thin, only a small portion of the electrons goes into the base pin, the rest moves forward through the pn junction between base and emitter layers which makes it conducting. A small base current thus allows for a large collector current. The transistor can thus be used as a current amplifier or as a switch.
Figure 1 PN2222 transistor (Source: PN2222 Datasheet p. 2) |
Here, a PN2222 NPN transistor is used to work as a switch to short the EN-pin to GND when the base voltage is high. The emitter (pin 1) is connected to GND, the base (pin 2) to GPIO 27, and the collector (pin 3) is connected to the EN pin. The necessary connection can be seen in figure 2.
The following sketch includes all the features discussed before. The serial print commands are just for understanding of the sketch and to be able to see what is happening in the serial monitor. In the final sketch, the serial print commands can be left out as they would only consume time and energy during operation.
//ESP32 MQTT + Deep Sleep + Power Reset //WiFi and MQTT #include "WiFi.h" //1 #include "PubSubClient.h" const char* password = "****************"; //2 const char* ssid = "*************"; #define mqtt_broker "192.168.2.103" //3 WiFiClient espClient; //4 PubSubClient mqttClient(espClient); //Example Values: //5 String val1= "Hello"; String val2= "World"; String val3= "Hey"; String val4= "ESP32"; char Buffer[5]; //Deep Sleep #include <Wire.h> //6 #define DS3231RTC_I2C_ADDRESS 0x68 #define I2C_SDA 21 #define I2C_SCL 22 //EEPROM #include <EEPROM.h> //7 #define EEPROM_BYTES 2 #define WIFIRESETADDRESS 0 #define MQTTRESETADDRESS 1 //Restart #define RestartPin 27 //8 void setup(){ Wire.begin(I2C_SDA, I2C_SCL); //9 clearAlarm1(); EEPROM.begin(EEPROM_BYTES); //10 Serial.begin(115200); esp_sleep_enable_ext0_wakeup(GPIO_NUM_13,0); WiFiConnect(); //11 MQTTConnect(); Serial.println("Transmitting data."); val1.toCharArray(Buffer, 6); //12 mqttClient.publish("ESP32/Test1", Buffer); //13 delay(500); //14 val2.toCharArray(Buffer, 6); mqttClient.publish("ESP32/Test2", Buffer); delay(500); val3.toCharArray(Buffer, 4); mqttClient.publish("ESP32/Test3", Buffer); delay(500); val4.toCharArray(Buffer, 6); mqttClient.publish("ESP32/Test4", Buffer); Serial.println("Going into sleep mode"); esp_deep_sleep_start(); //15 } void loop(){ //Main loop is not going to be called. } void clearAlarm1(){ Wire.beginTransmission(DS3231RTC_I2C_ADDRESS); Wire.write(0x0F); Wire.write(B00000000); Wire.endTransmission(); } void WiFiConnect(){ //16 byte WiFiResets = EEPROM.read(WIFIRESETADDRESS); //17 unsigned long Timer = millis()+1000; //18 Serial.print("Connecting with WiFi"); WiFi.begin(ssid, password); //19 unsigned long Timeout = millis()+30000; //20 while(WiFi.status() !=WL_CONNECTED){ //21 if(millis()>Timer){ //22 Timer= millis()+1000; Serial.print("."); } if(millis()>Timeout){ //23 if(WiFiResets ==2){ //24 EEPROM.write(WIFIRESETADDRESS, 0); EEPROM.commit(); Serial.println(); Serial.println("Could not connect to MQTT Broker 3 times. Going into deep sleep."); esp_deep_sleep_start(); } else{ //25 WiFiResets++; EEPROM.write(WIFIRESETADDRESS, WiFiResets); EEPROM.commit(); Serial.println(); Serial.println("Could not connect to MQTT Broker. Restarting MCU."); digitalWrite(RestartPin, HIGH); } } } EEPROM.write(WIFIRESETADDRESS, 0); //26 EEPROM.commit(); Serial.println(); Serial.println("Successfully connected to WiFi"); } void MQTTConnect(){ //27 byte MQTTResets = EEPROM.read(MQTTRESETADDRESS); unsigned long Timer = millis()+1000; Serial.print("Connecting with MQTT Broker"); mqttClient.setServer(mqtt_broker, 1883); //28 mqttClient.connect("ESP32Client"); //29 unsigned long Timeout = millis()+30000; while(!mqttClient.connected()){ //30 if(millis()>Timer){ Timer= millis()+1000; Serial.print("."); mqttClient.connect("ESP32Client"); } if(millis()>Timeout){ if(MQTTResets ==2){ EEPROM.write(MQTTRESETADDRESS, 0); EEPROM.commit(); Serial.println(); Serial.println("Could not connect to MQTT Broker 3 times. Going into deep sleep."); esp_deep_sleep_start(); } else{ MQTTResets++; EEPROM.write(MQTTRESETADDRESS, MQTTResets); EEPROM.commit(); Serial.println(); Serial.println("Could not connect to MQTT Broker. Restarting MCU."); digitalWrite(RestartPin, HIGH); } } } EEPROM.write(MQTTRESETADDRESS, 0); EEPROM.commit(); Serial.println(); Serial.println("Successfully connected to MQTT Broker"); }
After starting the MQTT.fx client, connecting it to the mosquitto server and uploading the sketch to the ESP32, the results are printed to the serial monitor. When all topics are subscribed with the MQTT.fx client by subscribing to “#”, all of the 4 messages are printed to the monitor (figure 3).
The serial monitor of the Arduino IDE shows what is going on while the program sketch is executed. The WiFi and MQTT connections could be established without problems (no restarts of the ESP32 etc.).
The 4 messages were received in 1 minute intervals in the MQTT.fx client which is in accordance with the times displayed in the serial monitor. The 1 minute intervals are due to the ESP32 going into deep sleep and being waken up once a minute by the DS3231 interrupt.
After the successful testing with the placeholder variables, the sketches for measurement and for MQTT transmission were combined to see if the transmission of sensor data would work as expected. The results were observed in the serial monitor and MQTT.fx (figure 4).
All sensors delivered plausible data, the temperature measurement values were all within 0.6°C of each other while measuring the room temperature. All measurements were transmitted successfully with MQTT once a minute due to the DS3231 interrupt.