Skip to content

ESP32 Relay Control

Control a relay (light, pump, fan, door lock) from your SiliconWit.IO dashboard while also reading sensor data — a complete bidirectional IoT example.

  • ESP32 reads a DHT22 temperature/humidity sensor and sends data to the cloud
  • Dashboard shows live readings and an on/off toggle for a relay
  • Clicking the toggle sends a command over MQTT; the ESP32 switches the relay
┌─────────────┐ MQTT (TLS) ┌──────────────────┐
│ ESP32 │ ◄──────────────────────── │ SiliconWit.IO │
│ + DHT22 │ commands topic │ Dashboard │
│ + Relay │ ────────────────────────► │ (toggle relay) │
│ │ telemetry topic │ │
└─────────────┘ └──────────────────┘
  • ESP32 development board
  • 1-channel relay module (5V or 3.3V, with optocoupler)
  • DHT22 temperature/humidity sensor
  • Jumper wires, breadboard
  • USB cable for programming
  • Arduino IDE with ESP32 board support (setup guide)
  • Libraries: PubSubClient, ArduinoJson, DHT sensor library
  • A registered actuator device with direction set to bidirectional
  • One toggle command configured (e.g. name: relay, label: Light Relay)
  • One data field configured (e.g. name: temperature)
ESP32 PinComponent
GPIO 4DHT22 data pin (with 10K pull-up to 3.3V)
GPIO 26Relay IN (signal)
3.3VDHT22 VCC
5V (VIN)Relay VCC
GNDDHT22 GND, Relay GND
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
// ─── Configuration ───────────────────────────────
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_BROKER = "mqtt.siliconwit.io";
const int MQTT_PORT = 8883;
const char* DEVICE_ID = "YOUR_DEVICE_ID"; // from dashboard
const char* ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"; // from dashboard
// ─── Pins ────────────────────────────────────────
#define DHT_PIN 4
#define RELAY_PIN 26
#define DHT_TYPE DHT22
// ─── Globals ─────────────────────────────────────
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClientSecure espClient;
PubSubClient mqtt(espClient);
String telemetryTopic;
String commandTopic;
unsigned long lastSend = 0;
const unsigned long SEND_INTERVAL = 15000; // 15 seconds
// ─── MQTT command handler ────────────────────────
void onMessage(char* topic, byte* payload, unsigned int length) {
// Parse JSON command
JsonDocument doc;
DeserializationError err = deserializeJson(doc, payload, length);
if (err) {
Serial.print("JSON parse error: ");
Serial.println(err.c_str());
return;
}
const char* command = doc["command"];
if (!command) return;
Serial.print("Command received: ");
Serial.println(command);
// Handle relay command
if (strcmp(command, "relay") == 0) {
bool state = doc["value"] | false;
digitalWrite(RELAY_PIN, state ? LOW : HIGH); // active LOW relay
Serial.print("Relay → ");
Serial.println(state ? "ON" : "OFF");
}
}
// ─── WiFi ────────────────────────────────────────
void setupWifi() {
Serial.print("Connecting to WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("\nConnected — IP: ");
Serial.println(WiFi.localIP());
}
// ─── MQTT connect + subscribe ────────────────────
void connectMqtt() {
while (!mqtt.connected()) {
Serial.print("Connecting to MQTT...");
if (mqtt.connect(DEVICE_ID, DEVICE_ID, ACCESS_TOKEN)) {
Serial.println("connected");
// Subscribe to commands topic
mqtt.subscribe(commandTopic.c_str(), 1);
Serial.print("Subscribed to: ");
Serial.println(commandTopic);
} else {
Serial.print("failed (rc=");
Serial.print(mqtt.state());
Serial.println(") retrying in 5s");
delay(5000);
}
}
}
// ─── Setup ───────────────────────────────────────
void setup() {
Serial.begin(115200);
// Build topic strings
telemetryTopic = "d/" + String(DEVICE_ID) + "/t";
commandTopic = "d/" + String(DEVICE_ID) + "/c";
// Relay pin — start OFF (HIGH for active-low)
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
dht.begin();
setupWifi();
espClient.setInsecure(); // Use setCACert() in production
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(onMessage);
}
// ─── Loop ────────────────────────────────────────
void loop() {
if (!mqtt.connected()) {
connectMqtt();
}
mqtt.loop();
// Send sensor data periodically
if (millis() - lastSend >= SEND_INTERVAL) {
lastSend = millis();
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (!isnan(temp) && !isnan(hum)) {
JsonDocument doc;
doc["temperature"] = round(temp * 10) / 10.0;
doc["humidity"] = round(hum * 10) / 10.0;
char buf[128];
serializeJson(doc, buf);
mqtt.publish(telemetryTopic.c_str(), buf);
Serial.print("Sent: ");
Serial.println(buf);
}
}
}
  1. Go to Dashboard > Devices > Add Device
  2. Set Name to something like “Living Room Controller”
  3. Set Type to Actuator
  4. Set Direction to Bidirectional
  5. Add a Data Field: name temperature, label Temperature, unit °C
  6. Add a Command: name relay, label Light Relay, type Toggle
  7. Click Create Device and copy the Device ID and Access Token
  1. Replace YOUR_WIFI_SSID, YOUR_WIFI_PASSWORD, YOUR_DEVICE_ID, and YOUR_ACCESS_TOKEN in the code
  2. Upload to your ESP32
  3. Open Serial Monitor at 115200 baud — you should see WiFi connect, then MQTT connect
  1. Open your device page — you’ll see temperature readings arriving
  2. In the Control Panel sidebar, click ON next to “Light Relay”
  3. The relay should click and the connected load should turn on
  4. Click OFF to turn it back off
  5. Check the Command History section to see the sent commands
  1. The ESP32 subscribes to d/{id}/c on connect
  2. When you press a toggle in the dashboard, the server publishes {"command":"relay","value":true} to that topic via the MQTT broker
  3. The onMessage callback fires on the ESP32, parses the JSON, and sets the relay GPIO
  4. Meanwhile, every 15 seconds the ESP32 publishes sensor data to d/{id}/t

You can add multiple commands to one device. For example, add a second relay or a PWM-controlled fan:

Add another command in device settings: name relay2, label Fan Relay, type Toggle.

Then extend the callback:

if (strcmp(command, "relay2") == 0) {
bool state = doc["value"] | false;
digitalWrite(RELAY2_PIN, state ? LOW : HIGH);
}

Add a command: name fan_speed, label Fan Speed, type Slider, min 0, max 255.

if (strcmp(command, "fan_speed") == 0) {
int speed = doc["value"] | 0;
analogWrite(FAN_PIN, constrain(speed, 0, 255));
}

For advanced use cases, add a command with type JSON (Custom). This lets you send any JSON payload from the dashboard — useful for multi-field configuration, schedules, or batch operations.

Add a command in device settings: name config, label Device Config, type JSON.

From the dashboard, you can type any valid JSON in the text area, for example:

{"mode": "auto", "threshold": 30, "interval": 10}

Then parse the full value object on the ESP32:

if (strcmp(command, "config") == 0) {
// The "value" field contains whatever JSON you sent
JsonObject cfg = doc["value"];
const char* mode = cfg["mode"] | "manual";
int threshold = cfg["threshold"] | 25;
int interval = cfg["interval"] | 15;
Serial.print("Mode: "); Serial.println(mode);
Serial.print("Threshold: "); Serial.println(threshold);
Serial.print("Interval: "); Serial.println(interval);
// Apply settings to your device logic...
}
ProblemSolution
Relay doesn’t clickCheck wiring, verify relay VCC gets 5V, try inverting HIGH/LOW
Commands not receivedEnsure device direction is Bidirectional, check Serial Monitor for subscribe confirmation
Sensor reads NaNCheck DHT22 wiring, add 10K pull-up resistor on data pin
MQTT disconnectsIncrease mqtt.setKeepAlive(60), check WiFi stability
Relay clicks but load doesn’t switchVerify load wiring to relay’s NO/COM terminals