Skip to main content

Command Palette

Search for a command to run...

From Blinking LED to a Working ESP32 MQTT Client

Published
6 min read
From Blinking LED to a Working ESP32 MQTT Client

Hello, I'm Ganesh. I'm working on FreeDevTools online, currently building a single platform for all development tools, cheat codes, and TL; DRs — a free, open-source hub where developers can quickly find and use tools without the hassle of searching the internet.

Connecting an ESP32 to an MQTT broker is a crucial step for many IoT projects. It sounds simple, but as I recently discovered, the path from a blinking LED to a working client can have a few frustrating hurdles.

In this post, I'll walk you through the structured process I followed to get my ESP32 online and communicating with an MQTT broker, including the two key problems I had to solve. My goal was to subscribe to a topic and see messages appear in my serial monitor.

Here's the logical path, from the ground up.

ESP32 Blinking Light

Before diving into networking and protocols, I always start with the simplest "Hello, World!" for hardware: the humble blink sketch. This confirms that my board is working, my USB cable is good, and my PlatformIO (or Arduino) environment is set up correctly.

This was my simple test code for my src/main.cpp:

#include <Arduino.h>

// Built-in LED pin on most ESP32 boards
const int ledPin = 2; 

void setup() {
  // Set the LED pin as an output
  pinMode(ledPin, OUTPUT); 
}

void loop() {
  // Turn the LED on
  digitalWrite(ledPin, HIGH); 
  delay(1000); // Wait for 1 second

  // Turn the LED off
  digitalWrite(ledPin, LOW);  
  delay(1000); // Wait for 1 second
}

The LED started blinking. Step one, complete.

Getting ESP32 Online

Next, I needed to get the board connected to my local WiFi. I put the MQTT logic aside and focused on just one thing: getting a "WiFi connected!" message and an IP address.

Here is the simple, hard-coded WiFi-only sketch I used.

platformio.ini

[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200

src/main.cpp

#include <Arduino.h>
#include <WiFi.h>

// --- 1. WiFi Credentials ---
const char* ssid = "Purvika"; 
const char* password = "YOUR_PASSWORD_HERE"; 

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println();
  Serial.print("Connecting to: ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  // Wait for the connection to complete
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println(""); 
  Serial.println("WiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  Serial.println("Loop running, WiFi is connected.");
  delay(5000); // Wait 5 seconds
}

When I ran this, I got my success message:

Loop running, WiFi is connected.

A quick note on debugging WiFi: In my earlier attempts, my board was stuck in the . loop forever. I was 100% sure my password was correct. I was wrong. I had a typo.

If you get stuck here, the best tool to use is WiFiManager. It's a library that creates a temporary hotspot, lets you connect with your phone, and enter your WiFi credentials through a web page. It saves the correct password to the ESP32's memory and eliminates typos. This step saved me hours of frustration.

Adding the MQTT

Now that I had a reliable WiFi connection, it was time for the main event. I needed to add an MQTT client. I chose the standard PubSubClient library, which is simple and works well.

Step 1: Update platformio.ini

I added the library to my platformio.ini file:

[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200
lib_deps = 
    knolleary/PubSubClient

Step 2: Update src/main.cpp

I then merged the MQTT logic with my working WiFi code. This was the final, complete script:

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h> // <-- Added MQTT library

// --- 1. WiFi Credentials ---
const char* ssid = "Purvika"; 
const char* password = "YOUR_PASSWORD_HERE"; 

// --- 2. MQTT Credentials ---
const char* mqtt_server = "hhtws-2405-201-d02d-809a-39bb-7637-426-f24.a.free.pinggy.link";
const int   mqtt_port = 46521;
const char* mqtt_user = "my-user";
const char* mqtt_pass = "my-password";

// This is the topic we will listen to
const char* mqtt_topic_to_subscribe = "nodemcu/messages";

// --- 3. MQTT Client Objects ---
WiFiClient espClient;
PubSubClient client(espClient);

// --- 4. Function to handle incoming MQTT messages ---
// This function is called every time a message arrives
void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");

    // Print the message payload
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println();
}

// --- 5. Function to connect to MQTT ---
void reconnect() {
    // Loop until we're reconnected
    while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        if (client.connect("esp32-client-123", mqtt_user, mqtt_pass)) {
            Serial.println("connected");
            // Subscribe to the topic
            client.subscribe(mqtt_topic_to_subscribe);
            Serial.print("Subscribed to topic: ");
            Serial.println(mqtt_topic_to_subscribe);
        } else {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            // Wait 5 seconds before retrying
            delay(5000);
        }
    }
}

// --- 6. setup() ---
void setup() {
    Serial.begin(115200);
    delay(10);

    Serial.print("Connecting to: ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println(""); 
    Serial.println("WiFi connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    // --- Add MQTT setup ---
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback); // Set the function to run when a message arrives
}

// --- 7. loop() ---
void loop() {
    // Check if connected to MQTT, if not, reconnect
    if (!client.connected()) {
        reconnect();
    }
    // This function processes MQTT messages
    client.loop(); 

    // Small delay to keep things stable
    delay(10); 
}

The code connects to WiFi, then in the reconnect() function, it connects to the MQTT broker and subscribes to our topic. The client.loop() in the main loop is essential—it keeps the connection alive and checks for new messages. Any incoming messages are sent to our callback() function, which prints them to the monitor.

Why Can't I See My Messages?

I uploaded the code and... it worked! Mostly.

Attempting MQTT connection...connected

Subscribed to topic: $SYS/broker/system

I saw it connect, but when I published "hello" from my computer, nothing appeared. This was the second major problem.

The Problem: I was subscribing to $SYS/broker/system.

$SYS Topics are special. They are read-only topics that the MQTT broker itself uses to publish its own internal statistics (like uptime, version, number of clients, etc.). You can subscribe to them to monitor the broker's health, but you cannot publish your own messages to them.

The Fix: I needed to use my own, custom topic.

I changed one line in my code:

// Before (Wrong)
// const char* mqtt_topic_to_subscribe = "$SYS/broker/system";

// After (Correct)
const char* mqtt_topic_to_subscribe = "nodemcu/messages";

After uploading this change, my serial monitor showed:

Subscribed to topic: nodemcu/messages

I then used my MQTT client (MQTT Explorer) to publish "hello" to the nodemcu/messages topic, and instantly, the message appeared on my ESP32's monitor:

Message arrived [nodemcu/messages] hello

Conclusion

Success! By tackling the project in logical stages—first hardware, then WiFi, then MQTT—I was able to debug each problem one by one. The two key lessons were:

  1. Don't trust your typing: If WiFi fails, use a tool like WiFiManager to be 100% sure your credentials are correct.

  2. Respect the topics: Don't publish to $SYS topics. Create your own custom topics (like device/livingroom/temp or nodemcu/messages) for your own data.

I hope this structured breakdown helps you get your own ESP32 project online.

FreeDevTools

I’ve been building for FreeDevTools.

A collection of UI/UX-focused tools crafted to simplify workflows, save time, and reduce friction in searching tools/materials.

Any feedback or contributions are welcome!

It’s online, open-source, and ready for anyone to use.

👉 Check it out: FreeDevTools
⭐ Star it on GitHub: freedevtools

More from this blog

Ganesh Kumar

23 posts