Video Tutorial · MQTT · ~9 minutes

Two-way control — ESP32

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.

Create free account Jump to the code

What you'll build

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.

Wire up the board

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):

ComponentESP32 PinNotes
Push buttonGPIO 4INPUT_PULLUP
Green LEDGPIO 13OUTPUT (+ resistor)
Red LEDGPIO 12OUTPUT (+ resistor)

Create your account & sign in

Register for a free account and log in to the console.

Create a device & three fields

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.

Devices  ·  Fields

Flash your ESP32

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.

Broker: cloud.virtuino.com : 1883  ·  Publish (device → cloud): <MQTT_USERNAME>/in/device/esp32/button  ·  Subscribe (cloud → device): <MQTT_USERNAME>/out/device/esp32/green_led & .../red_led
#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);
  }
}
Prefer a plain publish-only example, or need TLS / ESP8266 code? See the full MQTT reference docs.

Check the Serial Monitor

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.

Build the dashboard

Create a new dashboard, unlock it, and add three widgets:

1. An LED widget bound to the button field — this mirrors the physical button state.
2. A switch widget bound to green_led (inline alignment works well).
3. A switch widget bound to red_led.

Lock the dashboard when you're done positioning the widgets.

Test it

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.

Start now — it's free