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.
A field's name is the MQTT topic itself, so name them the way the board will publish:
esp32/button (the physical button's state, reported by the board),
esp32/green_led and esp32/red_led (the two LEDs, controlled from
the dashboard). The / only keeps related fields together — you can name your
fields however you like, as long as the sketch uses the same names.
Install the PubSubClient library (by Nick O'Leary) in the Arduino IDE. Get your MQTT username (your Sub-account Key) 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"; // your Sub-account Key (Console)
const char* MQTT_PASS = "YOUR_MQTT_PASSWORD"; // MQTT password (Console)
const int BUTTON_PIN = 4; // pull-up
const int GREEN_LED_PIN = 13;
const int RED_LED_PIN = 12;
// The field names, exactly as in Console → Fields. No account key, no in/out —
// the broker knows your account from the login. PUBLISH = to the cloud, SUBSCRIBE = from the cloud.
const char* topicButton = "esp32/button"; // the board publishes -> cloud
const char* topicGreenLed = "esp32/green_led"; // the board subscribes <- cloud
const char* topicRedLed = "esp32/red_led"; // the board subscribes <- cloud
WiFiClient net;
PubSubClient mqtt(net);
String clientId; // built once in setup()
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()) {
if (mqtt.connect(clientId.c_str(), MQTT_USERNAME, MQTT_PASS)) {
Serial.println("MQTT connected");
mqtt.subscribe(topicGreenLed);
mqtt.subscribe(topicRedLed);
} 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);
// Client ID: any name, the same after every restart and unique among your boards —
// the chip ID does both. It is the name this board shows in My Virtuino World.
clientId = "esp32-" + String((uint32_t)ESP.getEfuseMac(), HEX);
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);
mqtt.setKeepAlive(20); // a power cut shows up in My Virtuino World within ~30 s
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
static int lastState = -1;
static unsigned long lastChange = 0;
int state = digitalRead(BUTTON_PIN) == LOW ? 1 : 0; // pressed = LOW (pull-up)
if (state != lastState && millis() - lastChange > 50) { // 50 ms debounce
lastState = state;
lastChange = millis();
mqtt.publish(topicButton, 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:
esp32/button field — this mirrors the physical button state.esp32/green_led (inline alignment works well).esp32/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.
Open My Virtuino World in the console to watch it from above: the board appears
under its client ID (esp32-…), and every press and every switch travels along its
line to the cloud and back.