ESP32 + Adafruit IO

Building a Cloud-Connected Temperature, Humidity & Relay Dashboard using MQTT

1. Recap: MQTT Protocol Fundamentals

Before connecting the ESP32 to any cloud service, it's essential to remember the core building blocks of the MQTT protocol, since almost every IoT cloud platform (ThingSpeak, Adafruit IO, AWS IoT, etc.) is built around the same publish/subscribe model.

ESP32 (Publisher) MQTT Broker (Adafruit IO) Topic: user/feeds/temperature Dashboard (Subscriber) PUBLISH SUBSCRIBE

Figure 1 — Basic MQTT publish/subscribe flow between a device and a broker.

To use any MQTT-based service, you always need the following pieces of information:

Key concept: A single ESP32 device can act as a publisher, a subscriber, or both at the same time — for example, publishing sensor readings while subscribing to a relay-control topic.

2. Introducing Adafruit IO

While ThingSpeak is a common choice for logging sensor data, this guide introduces an alternative cloud platform called Adafruit IO — a managed IoT service operated by Adafruit Industries (based in New York) that makes it extremely easy to build visual dashboards for IoT projects.

Free Plan Feature Limit
Number of accounts / dashboards Free personal use allowed
Data rate 1 data point every 2 seconds
Data storage 30 days of history
Feeds Up to 10 feeds on free tier
Automation Basic "Actions" supported

The platform can be accessed at io.adafruit.com and is suitable for both personal experimentation and light professional use.

3. Creating Your Adafruit IO Account

Follow these steps in order to create a free account:

  1. Open your browser and go to io.adafruit.com
  2. Click "Get Started for Free"
  3. Enter your First Name
  4. Enter your Last Name
  5. Choose a Username
  6. Choose a Password
  7. Submit the form to create your account
  8. Sign in with your new credentials
Tip: When you log in for the first time, your account will be completely empty — no feeds, no dashboards. This is expected and is the starting point for the entire project.

4. Creating Feeds

In Adafruit IO terminology, a Feed is equivalent to a "field" in ThingSpeak — it represents a single stream of data (for example, one sensor reading or one control value). For this project, three feeds are required:

🌡️ temperature — sensor reading feed
💧 humidity — sensor reading feed
💡 relay — control feed (used to switch a lamp/appliance ON or OFF)

Steps to create each feed

  1. Go to the "Feeds" section
  2. Click "New Feed"
  3. Type the feed name: temperature
  4. (Optional) Add a description
  5. Click "Create"
  6. Repeat the same steps for the feed: humidity
  7. Repeat the same steps for the feed: relay
The free Adafruit IO account currently supports up to 10 feeds. This project only uses 3, leaving room for future expansion.

5. Creating the Dashboard

A Dashboard in Adafruit IO is the visual interface where feed data is displayed and controlled in real time.

  1. Go to the "Dashboards" section
  2. Click "New Dashboard"
  3. Name the dashboard: ESP32 IoT Project
  4. (Optional) Add a description
  5. Click "Create"
  6. Open the newly created dashboard

At this point the dashboard is a blank canvas, ready for visualization blocks to be added.

6. Adding Visualization Blocks

Adafruit IO provides several block types: Gauge, Toggle, Slider, Line Chart, Stream, Text, Number, Color Picker, Icon, and more. This project uses four specific blocks.

6.1 Gauge Block — Temperature

  1. Click "Create New Block"
  2. Select block type: Gauge
  3. Connect it to feed: temperature
  4. Set title: Room Temperature
  5. Set minimum value: 0
  6. Set maximum value: 60
  7. Set low range: 0 to 20 (shown in blue)
  8. Set normal range: 21 to 34 (shown in green)
  9. Set high range: 35 and above (shown in red)
  10. Select a thermometer icon
  11. Click "Create Block"
27°C Room Temperature

Figure 2 — Gauge block with low (blue), normal (green), and high (red) color zones.

6.2 Gauge Block — Humidity

  1. Click "Create New Block"
  2. Select block type: Gauge
  3. Connect it to feed: humidity
  4. Set title: Humidity
  5. Set unit label: %
  6. Click "Create Block"

6.3 Toggle Block — Relay / Lamp Control

  1. Click "Create New Block"
  2. Select block type: Toggle
  3. Connect it to feed: relay
  4. Set title: Lamp
  5. Click "Create Block"

6.4 Line Chart Block — Temperature History

  1. Click "Create New Block"
  2. Select block type: Line Chart
  3. Connect it to feed: temperature
  4. Set title: Temperature Trend
  5. Click "Create Block"

Once all four blocks are created, use "Edit Layout" to resize and rearrange them so the dashboard looks clean and readable — this step is purely cosmetic and fully customizable.

7. Example ESP32 Code (Adafruit IO via MQTT)

The following sketch demonstrates the generic MQTT connection pattern described earlier: the ESP32 publishes temperature and humidity readings, while subscribing to the relay feed to control a lamp. This structure is not limited to Adafruit IO — the same pattern applies to virtually any MQTT broker.

#include <WiFi.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#include <DHT.h>

#define WLAN_SSID       "YOUR_WIFI_SSID"
#define WLAN_PASS       "YOUR_WIFI_PASSWORD"

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "YOUR_ADAFRUIT_IO_USERNAME"
#define AIO_KEY         "YOUR_ADAFRUIT_IO_KEY"

#define DHTPIN   4
#define DHTTYPE  DHT22
#define RELAYPIN 5

DHT dht(DHTPIN, DHTTYPE);
WiFiClient client;

Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

Adafruit_MQTT_Publish temperatureFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/temperature");
Adafruit_MQTT_Publish humidityFeed    = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/humidity");
Adafruit_MQTT_Subscribe relayFeed     = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/relay");

void setup() {
  Serial.begin(115200);
  dht.begin();
  pinMode(RELAYPIN, OUTPUT);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");

  mqtt.subscribe(&relayFeed);
}

void MQTT_connect() {
  if (mqtt.connected()) return;
  Serial.println("Connecting to MQTT...");
  while (mqtt.connect() != 0) {
    delay(2000);
  }
  Serial.println("MQTT connected");
}

void loop() {
  MQTT_connect();

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {
    if (subscription == &relayFeed) {
      int relayState = atoi((char *)relayFeed.lastread);
      digitalWrite(RELAYPIN, relayState);
      Serial.print("Relay set to: ");
      Serial.println(relayState);
    }
  }

  float temperature = dht.readTemperature();
  float humidity     = dht.readHumidity();

  temperatureFeed.publish(temperature);
  humidityFeed.publish(humidity);

  Serial.print("Temperature: ");
  Serial.println(temperature);
  Serial.print("Humidity: ");
  Serial.println(humidity);

  delay(5000);
}
    
Remember: the free Adafruit IO plan enforces a minimum publish interval — sending data faster than every 2 seconds may throttle or drop messages.

8. What's Next?

At this stage, the Adafruit IO account, feeds, and dashboard are fully configured, and the ESP32 sketch is ready to connect. The next step — covered separately — is wiring the DHT sensor and relay module to the ESP32 and verifying that live data flows correctly from the device to the cloud dashboard and back.

DHT22 Sensor Temp / Humidity ESP32 Wi-Fi + MQTT Client Relay Control Relay Module Lamp / Appliance ↑ Adafruit IO Cloud ↓

Figure 3 — Complete system: sensor → ESP32 → cloud dashboard → relay actuation.