Skip to content

MQTT on Arduino

Now the task is to get the Arduino talking to the MQTT broker on the Pi. There are two stages to this - the first is to instruct the ESP8266 to connect to the Pi's WiFi network, and the second is to exchange MQTT messages.

Connecting to the Pi

For this part we just need to use the ESP's built-in AT commands. Later, we will embed the commands in a sketch, but as a first test we can just use the serial monitor. Figure 4 illustrates the use of the commands to list WiFi access points, and to connect to the one with the SSID RPi1. For more information, please refer to the ESP8266 AT instruction set guide which was linked on the ESP setup page.

ESP8266 WiFi connection

Talking MQTT

We will be making use of two different libraries to help simplify the job of exchanging MQTT messages with the broker. Both of them can be installed using the Include Library → Manage Libraries from the menu.

The first one is WiFiEsp by bportaluri, and the second is PubSubClient by Nick O'Leary:

WiFiESP

PubSubCient

Start a new sketch, install the libraries, and then load the example code provided below. You can alter the variable values at the beginning of the sketch to match the details of your Pi.

The code is quite long compared to other examples we have seen. This is mainly to accommodate some of the uncertainty around network connections - for example, there are several sections concerned with retrying and reconnecting. The comments in the code and the notes below the listing provide some further information.

  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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
* Adapted from the following examples:
* WiFiEsp: https://github.com/bportaluri/WiFiEsp/tree/master/examples/WebClient
* PubSubClient: https://github.com/knolleary/pubsubclient/tree/master/examples/mqtt_basic
*/

#include <PubSubClient.h>
#include "WiFiEsp.h"
#include <SoftwareSerial.h>

SoftwareSerial espSerial(10, 11); // RX, TX
long int baudRate = 9600;

char ssid[] = "RPi1";             // your network SSID (name)
char pass[] = "Raspberry";        // your network password
int status = WL_IDLE_STATUS;      // the Wifi radio's status
char server[] = "192.168.1.1";    // IP address of the MQTT server
char topic[] = "Test";            // Default topic string
char clientId[] = "client1";      // Cliwent id: Must be unique on the broker

WiFiEspClient wifi;               // Initialize the Ethernet client object
PubSubClient mqttClient(wifi);    // Initialize the MQTT client

void setup() {
  Serial.begin(9600);

  // Set baud rate of ESP8266 to 9600 regardless of original setting
  set_esp8266_baud_rate(baudRate);
  espSerial.begin(baudRate);
  espSerial.print("AT+UART_CUR=");
  espSerial.print(baudRate);
  espSerial.print(",8,1,0,0\r\n");
  WiFi.init(&espSerial);


  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }

  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }

  // you're connected now, so print out the data
  Serial.println("You're connected to the network");

  printWifiStatus();

  mqttClient.setServer(server, 1883);
  mqttClient.setCallback(onReceive);
}

void loop() {
  char message[256];
  int i;

  for (i=0; i<256; i++) {
    message[i] = 0;
  }

  if (Serial.available()) {
    Serial.readBytesUntil("\r",message, 255);
    mqttClient.publish(topic,message);
  }

  if (!mqttClient.connected()) {
    reconnect();
  }

  mqttClient.loop();

}

void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void onReceive(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (mqttClient.connect(clientId)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      mqttClient.publish(topic,"hello world");
      // ... and resubscribe
      mqttClient.subscribe(topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void set_esp8266_baud_rate(long int baud_rate){
  long int baud_rate_array[] = {1200,2400,4800,9600,19200,38400,57600,74880,115200,230400};
  int i, j, pause=10;
  String response;

  Serial.println("Setting ESP8266 baud rate...");
  for (j=0; j<5; j++){
    for (int i=0; i<10; i++){
      espSerial.begin(baud_rate_array[i]);
      espSerial.print("AT\r\n");
      delay(pause);
      if (espSerial.available()) {
        response=espSerial.readString();
        response.trim();
        if (response.endsWith("OK")) {
          espSerial.print("AT+UART_CUR=");
          espSerial.print(baud_rate);
          espSerial.println(",8,1,0,0");
          delay(pause);
          if (espSerial.available()) {
            response=espSerial.readString();
          }
          espSerial.begin(baudRate);
          delay(pause);
          espSerial.println("AT");
          delay(pause);
          if (espSerial.available()) {
            response=espSerial.readString();
            response.trim();
            if (response.endsWith("OK"))
              break;
            else {
              Serial.println("Trying again...");
            }
          }
          else {
            Serial.println("Trying again...");
          }
        }
      }
    }
    if (response.endsWith("OK"))
      break;
  }
  espSerial.begin(baudRate);
  delay(pause);
  espSerial.println("AT");
  delay(pause);
  if (espSerial.available()) {
    response=espSerial.readString();
    response.trim();
    if (response.endsWith("OK")) {
      Serial.print("\r\nBaud rate is now ");
      Serial.println(baudRate);
    }
    else {
      Serial.println("Sorry - could not set baud rate");
      Serial.println("Try powering off and on again");
      Serial.println("Don't try to use 115200");
    }
  }
}
Line Comment
26-32 One of the difficulties with the ESP8266 is knowing what speed to use before a connection has been made. The functionset_esp8266_baud_rate(baudRate) tries all the common speeds until it finds the one that works.
35-53 These are inherited directly from the WiFiEsp example
55-56 These configure the MQTT client with the values set at the top of the sketch. onReceive is a callback function which is invoked when a message arrives.
59-78 In the main loop, any text entered into the serial monitor is read and published as an MQTT message. The default topic is defined at line 18.
80-96 Inherited from the WiFiEsp example
98-106 Adapted from the PubSubClient example. The function echoes any received messages to the serial monitor
108-127 Inherited from the PubSubClient example
134-190 Function to set the serial communications with the ESP8266 to a known value (9600 baud) as seen on the previous page (see below).

The central loop which starts at line 136 tries each of the speeds in the list. When a response of OK is received, we have identified the current baud rate. The outer loop which starts at line 135 tries up to five times before giving up.

The communications rate of the ESP defaults to 115200 baud on power-up. This speed tends to be unreliable and needs to be reduced. Another interesting thing to note about the sketch is that there are several calls to delay(pause) where the value of pause is 50ms. This is to allow the ESP hardware to react to the command it has just been given. Without the delay, the next operation could start before the first completes leading to unpredictable results.

WiFiEsp reference WiFiEsp reference

PubSubClient reference PubSubClient reference