Skip to content

Basic Sketch Architecture

Basic Sketch Architecture

This section presents the complete “Basic Sketch” architecture that combines Wi-Fi connection, MQTT communication, and non-blocking timing. By the end of this section, you will be able to:

  • Understand the multi-file project structure of a PlatformIO ESP32 sketch
  • Implement the canonical Wi-Fi + MQTT basic sketch that serves as the foundation for all course projects
  • Organize ESP32 code into functional modules (Wi-Fi, MQTT, sensors, main)
  • Wi-Fi connection implementation (01-09)
  • MQTT client setup (01-10)
  • PlatformIO IDE setup (01-05)

All IoT projects in this course follow the same architectural pattern:

┌──────────────────────────────────────────────────┐
│ main.cpp │
│ setup() → init serial → connect WiFi → │
│ init MQTT → init sensors │
│ loop() → maintain MQTT → read sensors → │
│ publish data → non-blocking delays │
└──────────────────────────────────────────────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌──────────┐ ┌──────────────┐
│ wifi_mqtt.h │ │credentials.h│ │ sensor.h │
│ - WiFi connect │ │ - SSID │ │ - read data │
│ - MQTT callback │ │ - Password│ │ - process │
│ - reconnect │ │ - Broker │ │ - format │
└─────────────────┘ └──────────┘ └──────────────┘

This separation of concerns means:

  • Wi-Fi/MQTT code is reusable across projects
  • Credentials are isolated in a single file (excluded from version control)
  • Sensor logic is modular and swappable
  • main.cpp is the orchestrator, not the implementation
basic-sketch/
├── platformio.ini # Board, framework, libraries
└── src/
├── main.cpp # setup() and loop() orchestration
├── wifi_mqtt.h # Wi-Fi and MQTT connection functions
└── credentials.h # SSID, password, broker address
  1. Create a new PlatformIO project: esp32-basic-sketch
  2. Board: Espressif ESP32 Dev Module
  3. Framework: Arduino
  4. Add to platformio.ini:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps =
knolleary/PubSubClient @ ^2.8
bblanchon/ArduinoJson @ ^7.0
#ifndef CREDENTIALS_H
#define CREDENTIALS_H
// Wi-Fi
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
// MQTT Broker
const char* mqttServer = "192.168.1.100";
const int mqttPort = 1883;
const char* mqttUser = ""; // Leave empty if no auth
const char* mqttPassword = "";
#endif
#ifndef WIFI_MQTT_H
#define WIFI_MQTT_H
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
// Forward declarations
void connectToWiFi();
void connectToMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);
void sendMQTTValues();
// Wi-Fi client and MQTT client objects
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// Client identifier
const char* clientId = "esp32-basic";
// MQTT topics
const char* topicPublish = "esp32/data";
const char* topicSubscribe = "esp32/commands";
void connectToWiFi() {
Serial.print("Connecting to Wi-Fi");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 40) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.println("Wi-Fi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println();
Serial.println("Wi-Fi connection failed!");
Serial.println("Restarting in 2 seconds...");
delay(2000);
ESP.restart();
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("MQTT message arrived [");
Serial.print(topic);
Serial.print("]: ");
String message = "";
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// Topic-based routing
if (String(topic) == "esp32/commands") {
if (message == "ON") {
Serial.println("Command: ON");
// Add your ON logic here
} else if (message == "OFF") {
Serial.println("Command: OFF");
// Add your OFF logic here
}
}
}
void connectToMQTT() {
mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setCallback(mqttCallback);
while (!mqttClient.connected()) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect(clientId, mqttUser, mqttPassword)) {
Serial.println("connected!");
// Subscribe to topics
mqttClient.subscribe(topicSubscribe);
// Publish online status
mqttClient.publish("esp32/status", "online");
} else {
Serial.print("failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
#endif
#include <Arduino.h>
#include "credentials.h"
#include "wifi_mqtt.h"
// Constants
const unsigned long PUBLISH_INTERVAL = 5000; // Publish every 5 seconds
const int LED_PIN = 2; // Built-in LED on most DevKits
// Global variables
unsigned long previousMillis = 0;
int ledState = LOW;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("ESP32 Basic Sketch");
Serial.println("=================");
// Initialize hardware
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Connect to Wi-Fi
connectToWiFi();
// Connect to MQTT
connectToMQTT();
}
void loop() {
unsigned long currentMillis = millis();
// 1. Maintain MQTT connection (must be called frequently)
if (!mqttClient.connected()) {
// Check Wi-Fi first
if (WiFi.status() != WL_CONNECTED) {
connectToWiFi();
}
connectToMQTT();
}
mqttClient.loop();
// 2. Non-blocking LED blink (status indicator)
digitalWrite(LED_PIN, ledState);
// 3. Periodic data publishing
if (currentMillis - previousMillis >= PUBLISH_INTERVAL) {
previousMillis = currentMillis;
// Toggle LED on each publish
ledState = !ledState;
// Publish sensor data
sendMQTTValues();
}
}
void sendMQTTValues() {
// Simulate sensor data (replace with real sensor readings)
int temperature = random(20, 35);
int humidity = random(40, 80);
// Create JSON payload
String payload = "{\"temperature\":";
payload += temperature;
payload += ",\"humidity\":";
payload += humidity;
payload += "}";
// Publish to MQTT topic
if (mqttClient.publish(topicPublish, payload.c_str())) {
Serial.print("Published: ");
Serial.println(payload);
} else {
Serial.println("Publish failed!");
}
}
  1. Power On: ESP32 starts and executes setup()
  2. Serial Init: Serial port opens at 115200 baud
  3. LED Init: Built-in LED pin is configured as output
  4. Wi-Fi Connect: Connects to the Wi-Fi network (retries up to 40 times = ~20 seconds)
  5. MQTT Connect: Connects to the MQTT broker, subscribes to commands topic
  6. Loop Start: Enters the main loop
  7. MQTT Maintain: Calls client.loop() to process incoming messages and keep the connection alive
  8. Timer Check: Every 5 seconds, publishes simulated sensor data
  9. LED Toggle: The built-in LED toggles with each publish (visual heartbeat indicator)
  1. Upload the sketch to ESP32
  2. Open Serial Monitor (115200 baud)
  3. Observe the connection sequence
  4. Verify “Published” messages appear every 5 seconds
  5. From a command line, subscribe to receive data:
    Terminal window
    mosquitto_sub -h 192.168.1.100 -t "esp32/data"
  6. Send a command:
    Terminal window
    mosquitto_pub -h 192.168.1.100 -t "esp32/commands" -m "ON"
  • Serial Monitor shows the complete connection sequence (Wi-Fi → MQTT)
  • Data is published every 5 seconds to the correct MQTT topic
  • Built-in LED toggles with each publish (visual confirmation)
  • MQTT subscriber receives JSON payloads correctly
  • Sending a command via mosquitto_pub is received in the callback
  • Disconnecting and reconnecting the broker recovers automatically

Sketch does not start, only “Connecting to Wi-Fi” appears

Section titled “Sketch does not start, only “Connecting to Wi-Fi” appears”

Causes: Wi-Fi credentials wrong, or router not reachable.

Solutions:

  1. Verify SSID and password in credentials.h
  2. Check that the router is powered on and broadcasting
  3. Verify that the ESP32 is within range
  4. Check for 2.4 GHz vs 5 GHz: ESP32 only supports 2.4 GHz

Causes:

  • Publish interval timer not resetting
  • MQTT client disconnected without detection

Solutions:

  1. Verify PUBLISH_INTERVAL is set to a reasonable value (5000 ms)
  2. Check mqttClient.connected() returns true
  3. Ensure mqttClient.loop() is called every iteration
  4. Use mqttClient.publish() return value to detect failure

ESP32 disconnects from Wi-Fi after some hours

Section titled “ESP32 disconnects from Wi-Fi after some hours”

Causes:

  • Router DHCP lease timeout
  • Wi-Fi radio interference
  • Power supply issues

Solutions:

  1. Enable auto-reconnect: WiFi.setAutoReconnect(true) in setup()
  2. Add a periodic Wi-Fi status check in loop()
  3. Implement a watchdog timer (ESP32 built-in) that reboots if connection lost for 5+ minutes
  • Start every new project from this Basic Sketch: It provides proven Wi-Fi, MQTT, and timing infrastructure
  • Use the multi-file structure: Separate credentials, Wi-Fi/MQTT, and sensor logic into different files
  • Implement the non-blocking pattern: Never use delay() for timing in production code
  • Add a status LED: A heartbeat LED (toggling on each data publish) provides immediate visual feedback
  • Log all connection state changes: This helps diagnose field issues remotely
  • Use random() or dummy data initially: Get the communication layer working before adding real sensors
  1. The Basic Sketch architecture separates concerns into main.cpp, wifi_mqtt.h, and credentials.h
  2. The loop() function maintains MQTT, checks timers, publishes data, and handles callbacks
  3. Wi-Fi and MQTT reconnection logic ensures robustness against network interruptions
  4. Non-blocking timing with millis() enables multiple concurrent tasks
  5. This architecture is the foundation for all subsequent course projects