Control an ESP32's onboard button and LEDs from a Virtuino Cloud dashboard, and watch the physical button state update live on screen — all over MQTT. Full video walkthrough plus the wiring, code and dashboard steps below.
A push button on the ESP32 controls an LED widget on the dashboard — press it, and the widget changes state instantly. Meanwhile two switch widgets on the dashboard control a green and a red LED wired to the board. Three fields, two directions, one dashboard.
Connect a push button and two LEDs to your ESP32 as follows (button uses the internal pull-up, no external resistor needed on that pin):
| Component | ESP32 Pin | Notes |
|---|---|---|
| Push button | GPIO 4 | INPUT_PULLUP |
| Green LED | GPIO 13 | OUTPUT (+ resistor) |
| Red LED | GPIO 12 | OUTPUT (+ resistor) |
Register for a free account and log in to the console.
Add a device (e.g. esp32), then create three fields under it: button
(the physical button's state, reported by the device), green_led and red_led
(the two LEDs, controlled from the dashboard) — all number type.
Install the PubSubClient library (by Nick O'Leary) in the Arduino IDE. Get your
MQTT username (account MQTT_USERNAME) and password from the console, fill them into the
sketch below along with your WiFi credentials, then upload.
#include <WiFi.h>
#include <PubSubClient.h> // install "PubSubClient" by Nick O'Leary
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "cloud.virtuino.com";
const int MQTT_PORT = 1883;
const char* MQTT_USERNAME = "YOUR_MQTT_USERNAME"; // MQTT username (Console)
const char* MQTT_PASS = "YOUR_MQTT_PASSWORD"; // MQTT password (Console)
const char* DEVICE = "esp32"; // must match the device in the console
const int BUTTON_PIN = 4; // pull-up
const int GREEN_LED_PIN = 13;
const int RED_LED_PIN = 12;
String topicButton = String(MQTT_USERNAME) + "/in/device/" + DEVICE + "/button"; // device publishes -> cloud
String topicGreenLed = String(MQTT_USERNAME) + "/out/device/" + DEVICE + "/green_led"; // device subscribes <- cloud
String topicRedLed = String(MQTT_USERNAME) + "/out/device/" + DEVICE + "/red_led"; // device subscribes <- cloud
WiFiClient net;
PubSubClient mqtt(net);
void onMessage(char* topic, byte* payload, unsigned int len) {
String msg; for (unsigned int i = 0; i < len; i++) msg += (char)payload[i];
String t = String(topic);
if (t == topicGreenLed) digitalWrite(GREEN_LED_PIN, msg == "1" ? HIGH : LOW);
if (t == topicRedLed) digitalWrite(RED_LED_PIN, msg == "1" ? HIGH : LOW);
}
void connectMqtt() {
while (!mqtt.connected()) {
String cid = "esp32-" + String(random(0xffff), HEX);
if (mqtt.connect(cid.c_str(), MQTT_USERNAME, MQTT_PASS)) {
Serial.println("MQTT connected");
mqtt.subscribe(topicGreenLed.c_str());
mqtt.subscribe(topicRedLed.c_str());
} else {
Serial.printf("MQTT failed rc=%d, retrying...\n", mqtt.state());
delay(2000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWiFi connected");
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
static int lastState = -1;
int state = digitalRead(BUTTON_PIN) == LOW ? 1 : 0; // pressed = LOW (pull-up)
if (state != lastState) {
lastState = state;
mqtt.publish(topicButton.c_str(), String(state).c_str(), true); // retained
Serial.println("published " + String(state) + " -> " + topicButton);
}
}
Open the Serial Monitor (115200 baud) and wait for MQTT connected. Press the physical
button — you'll see the published value logged, and the console's Data Monitor will show it
arriving live.
Create a new dashboard, unlock it, and add three widgets:
button field — this mirrors the physical button state.green_led (inline alignment works well).red_led.Lock the dashboard when you're done positioning the widgets.
Press the button on the board — the LED widget on the dashboard changes state immediately. Tap the switches on the dashboard — the green and red LEDs on the board turn on and off in response. Everything is bidirectional and live.