Basic Sketch Architecture
Basic Sketch Architecture
Overview
Section titled “Overview”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)
Prerequisites
Section titled “Prerequisites”- Wi-Fi connection implementation (01-09)
- MQTT client setup (01-10)
- PlatformIO IDE setup (01-05)
Key Concepts
Section titled “Key Concepts”The Basic Sketch Architecture
Section titled “The Basic Sketch Architecture”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
File Structure (PlatformIO)
Section titled “File Structure (PlatformIO)”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 addressImplementation Steps
Section titled “Implementation Steps”Step 1: Create the Project
Section titled “Step 1: Create the Project”- Create a new PlatformIO project:
esp32-basic-sketch - Board:
Espressif ESP32 Dev Module - Framework:
Arduino - Add to
platformio.ini:
[env:esp32dev]platform = espressif32board = esp32devframework = arduinomonitor_speed = 115200upload_speed = 921600
lib_deps = knolleary/PubSubClient @ ^2.8 bblanchon/ArduinoJson @ ^7.0Step 2: Create credentials.h
Section titled “Step 2: Create credentials.h”#ifndef CREDENTIALS_H#define CREDENTIALS_H
// Wi-Ficonst char* ssid = "YourWiFiSSID";const char* password = "YourWiFiPassword";
// MQTT Brokerconst char* mqttServer = "192.168.1.100";const int mqttPort = 1883;const char* mqttUser = ""; // Leave empty if no authconst char* mqttPassword = "";
#endifStep 3: Create wifi_mqtt.h
Section titled “Step 3: Create wifi_mqtt.h”#ifndef WIFI_MQTT_H#define WIFI_MQTT_H
#include <Arduino.h>#include <WiFi.h>#include <PubSubClient.h>
// Forward declarationsvoid connectToWiFi();void connectToMQTT();void mqttCallback(char* topic, byte* payload, unsigned int length);void sendMQTTValues();
// Wi-Fi client and MQTT client objectsWiFiClient wifiClient;PubSubClient mqttClient(wifiClient);
// Client identifierconst char* clientId = "esp32-basic";
// MQTT topicsconst 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); } }}
#endifStep 4: Create main.cpp
Section titled “Step 4: Create main.cpp”#include <Arduino.h>#include "credentials.h"#include "wifi_mqtt.h"
// Constantsconst unsigned long PUBLISH_INTERVAL = 5000; // Publish every 5 secondsconst int LED_PIN = 2; // Built-in LED on most DevKits
// Global variablesunsigned 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!"); }}Step 5: Complete Flow Description
Section titled “Step 5: Complete Flow Description”- Power On: ESP32 starts and executes
setup() - Serial Init: Serial port opens at 115200 baud
- LED Init: Built-in LED pin is configured as output
- Wi-Fi Connect: Connects to the Wi-Fi network (retries up to 40 times = ~20 seconds)
- MQTT Connect: Connects to the MQTT broker, subscribes to commands topic
- Loop Start: Enters the main loop
- MQTT Maintain: Calls
client.loop()to process incoming messages and keep the connection alive - Timer Check: Every 5 seconds, publishes simulated sensor data
- LED Toggle: The built-in LED toggles with each publish (visual heartbeat indicator)
Step 6: Testing the Complete Sketch
Section titled “Step 6: Testing the Complete Sketch”- Upload the sketch to ESP32
- Open Serial Monitor (115200 baud)
- Observe the connection sequence
- Verify “Published” messages appear every 5 seconds
- From a command line, subscribe to receive data:
Terminal window mosquitto_sub -h 192.168.1.100 -t "esp32/data" - Send a command:
Terminal window mosquitto_pub -h 192.168.1.100 -t "esp32/commands" -m "ON"
Verification
Section titled “Verification”- 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_pubis received in the callback - Disconnecting and reconnecting the broker recovers automatically
Troubleshooting
Section titled “Troubleshooting”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:
- Verify SSID and password in
credentials.h - Check that the router is powered on and broadcasting
- Verify that the ESP32 is within range
- Check for 2.4 GHz vs 5 GHz: ESP32 only supports 2.4 GHz
MQTT connected but no messages published
Section titled “MQTT connected but no messages published”Causes:
- Publish interval timer not resetting
- MQTT client disconnected without detection
Solutions:
- Verify
PUBLISH_INTERVALis set to a reasonable value (5000 ms) - Check
mqttClient.connected()returns true - Ensure
mqttClient.loop()is called every iteration - 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:
- Enable auto-reconnect:
WiFi.setAutoReconnect(true)insetup() - Add a periodic Wi-Fi status check in
loop() - Implement a watchdog timer (ESP32 built-in) that reboots if connection lost for 5+ minutes
Best Practices
Section titled “Best Practices”- 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
Summary
Section titled “Summary”- The Basic Sketch architecture separates concerns into
main.cpp,wifi_mqtt.h, andcredentials.h - The
loop()function maintains MQTT, checks timers, publishes data, and handles callbacks - Wi-Fi and MQTT reconnection logic ensures robustness against network interruptions
- Non-blocking timing with
millis()enables multiple concurrent tasks - This architecture is the foundation for all subsequent course projects