PROJECT 11 · CLOUD / MQTT

MQTT + Ubidots: Cloud Dashboard and Control

Send DHT11 readings to a live cloud dashboard, and switch an LED or relay back from anywhere.

⬇ Download the sketch Save Project_11_ESP32_MQTT_Ubidots.ino, then open it in the Arduino IDE. Needs the Ubidots ESP32 MQTT, DHT sensor library, and Adafruit Unified Sensor libraries, plus your Wi-Fi and Ubidots token.

🎯 Objective

By the end of this project, you will:

1 · Why MQTT

In Projects 5 to 9 the ESP32 was a web server: your phone had to be on the same Wi-Fi and had to keep asking for data. MQTT flips that around. Devices publish messages to a broker, and anyone interested subscribes. It is lightweight and works from anywhere with internet.

DHT11 --> ESP32 --publish--> [ Ubidots broker ] --> Dashboard (you watch) LED <-- ESP32 <--subscribe-- [ Ubidots broker ] <-- Switch widget (you control)
TermMeaningHere
BrokerServer that routes all messagesHosted by Ubidots
PublishSend a value to a topicESP32 sends temperature, humidity
SubscribeAsk to receive a topicESP32 listens for led

2 · Get your Ubidots token

  1. Sign up for a free Ubidots STEM account at stem.ubidots.com.
  2. Open your profile, then API Credentials, and copy the Default token (looks like BBUS-xxxx).
  3. No need to create the device by hand: it appears the first time the board publishes.

3 · Libraries and wiring

Install from the Library Manager: Ubidots ESP32 MQTT (pulls in PubSubClient), DHT sensor library, Adafruit Unified Sensor.

QtyPartNotes
1ESP32 · breadboard · jumper wiresas always
1DHT11 moduleS to GPIO 4, + to 3.3 V, - to GND (same as Project 9)
1LED + 220 Ohm, or the relay from Project 7to GPIO 26 (the thing the cloud switches)
This is Project 9's sensor plus Project 7's actuator on one breadboard. Nothing new to wire, only new code.

4 · Edit, upload, watch

Edit the marked block at the top of the sketch: WIFI_SSID, WIFI_PASS, UBIDOTS_TOKEN, and a unique DEVICE_LABEL. Upload, then open Serial Monitor at 115200:

==============================================
 Project 11: ESP32 + MQTT + Ubidots
==============================================
DHT11 data -> GPIO 4
Output      -> GPIO 26
[PUB] temperature: 24.6 C, humidity: 41.0 %
[CLOUD] led -> ON (raw "1")

5 · Code Walkthrough: Understanding the Sketch

The first sketch that talks both ways with something outside the room. Here is how it works.

Step 1: One block holds everything platform-specific

const char *WIFI_SSID = "REPLACE_WITH_YOUR_SSID";
const char *WIFI_PASS = "REPLACE_WITH_YOUR_PASSWORD";
const char *UBIDOTS_TOKEN = "REPLACE_WITH_YOUR_UBIDOTS_TOKEN";
const char *DEVICE_LABEL = "esp32-workshop";   // a name for THIS board

const char *VAR_TEMPERATURE = "temperature";
const char *VAR_HUMIDITY    = "humidity";
const char *VAR_LED         = "led";   // the control variable we subscribe to

const int   PUBLISH_EVERY_MS = 5000;   // send readings every 5 seconds

Everything that would change if you moved to another MQTT platform sits in one place. The rest of the sketch reads the same whether the broker is Ubidots, Adafruit IO, or one you run yourself.

The token is a password. Anyone holding it can write data as your device, so keep it out of screenshots and out of version control. DEVICE_LABEL is the name your board files data under, so every student needs a different one.

Step 2: Two objects, two responsibilities

DHT dht(DHTPIN, DHTTYPE);
Ubidots ubidots(UBIDOTS_TOKEN);

unsigned long lastPublish = 0;

dht is Project 9's sensor object, unchanged. ubidots wraps a Wi-Fi client, an MQTT client (PubSubClient underneath), and Ubidots' topic naming. Handing it the token at construction is what ties this board to your account.

Step 3: callback(), the cloud talking back

void callback(char *topic, byte *payload, unsigned int length) {
  char value[8] = {0};
  unsigned int n = (length < sizeof(value) - 1) ? length : sizeof(value) - 1;
  memcpy(value, payload, n);
  float v = atof(value);

  bool on = (v >= 0.5);          // treat anything from ~1 as "on"
  digitalWrite(LED_PIN, on ? HIGH : LOW);
  Serial.printf("[CLOUD] led -> %s (raw \"%s\")\n", on ? "ON" : "OFF", value);
}

This function is never called by your code. You hand it to the library, and the library calls it whenever a subscribed value arrives. That inversion has a name, callback, and it is how almost all event-driven networking works.

The payload arrives as raw bytes with a length, not a C string, so it has no terminating zero. Copying at most 7 bytes into a zeroed 8-byte buffer guarantees one, which is what makes atof() safe. Without the n guard, a long payload would write past the end of value.

v >= 0.5 rather than v == 1 is defensive: a switch may send 1, 1.0, or 1.000000, and comparing floats for exact equality is a bad habit anyway.

Step 4: setup() connects, then subscribes

ubidots.setDebug(true);                        // prints connection details
ubidots.connectToWifi(WIFI_SSID, WIFI_PASS);
ubidots.setCallback(callback);
ubidots.setup();
ubidots.reconnect();
ubidots.subscribeLastValue(DEVICE_LABEL, VAR_LED);

The order matters. Wi-Fi first, because MQTT needs a network. Then register the callback, so no message can arrive before there is somewhere to deliver it. Then connect to the broker, and only then subscribe.

subscribeLastValue() also asks the broker for the variable's current value, so the board picks up a switch you left on before it booted.

Step 5: loop() keeps the link alive and publishes on a timer

if (!ubidots.connected()) {
  ubidots.reconnect();
  ubidots.subscribeLastValue(DEVICE_LABEL, VAR_LED);
}

if (millis() - lastPublish > PUBLISH_EVERY_MS) {
  float t = dht.readTemperature();
  float h = dht.readHumidity();

  if (isnan(t) || isnan(h)) {
    Serial.println("[DHT] Failed to read, skipping this publish");
  } else {
    ubidots.add(VAR_TEMPERATURE, t);
    ubidots.add(VAR_HUMIDITY, h);
    ubidots.publish(DEVICE_LABEL);
  }
  lastPublish = millis();
}

ubidots.loop();   // lets the library process incoming cloud messages

Three jobs, none of them blocking:

  1. Heal the connection. Wi-Fi drops and brokers restart. Re-subscribing after a reconnect is easy to forget, and the symptom (data still uploads, the switch stops working) is confusing.
  2. Publish on a schedule, using Project 4's millis() timer. Two add() calls then one publish() sends both readings in a single message.
  3. Give the library time to work. ubidots.loop() is where incoming messages are read off the socket and your callback() gets invoked. Miss it and nothing arrives.

Note the isnan() check again: a failed reading skips the publish entirely. Sending garbage to a chart is worse than sending nothing, because the gap is honest and the spike is not.

This is why delay() had to go. A blocking wait here would stall ubidots.loop(), and cloud commands would arrive late or not at all.

Key concepts: what each line really does

CodeWhat it does
Ubidots ubidots(TOKEN)Creates the client: Wi-Fi, MQTT, topic naming, and your credentials in one object.
ubidots.connectToWifi(...)Joins the network, the same job WiFi.begin() did in Project 5.
ubidots.setCallback(callback)Registers the function the library calls when a subscribed value arrives.
ubidots.subscribeLastValue(DEVICE_LABEL, VAR_LED)Subscribes to led and pulls its current value immediately.
ubidots.add(VAR, value)Queues one reading. Several add() calls travel in one message.
ubidots.publish(DEVICE_LABEL)Sends the queued readings to the broker, filed under this device.
ubidots.loop()Services the MQTT connection. Incoming messages are delivered from here.
ubidots.connected()Reports whether the broker link is still up, so loop() can heal it.
millis() - lastPublish > PUBLISH_EVERY_MSProject 4's non-blocking timer, now pacing network traffic.
isnan(t) || isnan(h)Skips the publish on a bad reading, leaving a gap rather than a lie.
memcpy into a zeroed bufferTurns a raw MQTT payload into a proper C string before atof().

6 · Build the dashboard

  1. In Devices, confirm your device appeared with temperature and humidity.
  2. Add a control variable: open the device, Add Variable, type Raw, name it exactly led.
  3. In Data, Dashboards, add a Gauge on temperature, a Line chart on temperature and humidity, and a Switch on led.
  4. Flip the switch. The board's LED or relay follows within a few seconds.

7 · Wrapping Up: What You've Learned

This is the project where "connected device" stops meaning "same Wi-Fi as my phone". The key takeaways:

8 · Troubleshooting

SymptomLikely causeFix
Serial stops after Wi-FiWrong tokenRe-copy the Default token
No device in UbidotsNever publishedCheck DHT reads numbers, not --
Switch does nothingVariable not named ledName it exactly led
Reconnect loop5 GHz Wi-FiESP32 is 2.4 GHz only

9 · Going further

← Project 10 · OLED Display Project 12 · Telegram Bot →