·13 min read·BASHCAT 技術團隊·iot

ESP32 IoT Development in Practice: Complete MQTT + LoRa Application

A deep dive into ESP32 IoT development, from WiFi connectivity and MQTT communication to LoRa long-range transmission, building a complete IoT solution. Includes advanced techniques such as low-power design and OTA updates.

#ESP32#IoT#MQTT#LoRa#WiFi#Internet of Things#Embedded Development

ESP32 IoT Development in Practice: Complete MQTT + LoRa Application

ESP32 is currently the most popular IoT development board, integrating WiFi, Bluetooth, and a dual-core processor at an affordable price with powerful capabilities. This article shares comprehensive hands-on experience with ESP32 IoT development.

ESP32 Core Advantages

Hardware Specifications

CPU: Xtensa LX6 Dual-Core @ 240MHz
RAM: 520KB SRAM
Flash: 4MB (expandable)
WiFi: 802.11 b/g/n (2.4GHz)
Bluetooth: BLE 4.2 + Classic
GPIO: 34 programmable I/O pins
ADC: 18-channel 12-bit
DAC: 2-channel 8-bit
Touch Sensing: 10 touch pins

Development Environment Setup

# Arduino IDE method
# 1. Install Arduino IDE
# 2. Add ESP32 Board Manager URL
# https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

# PlatformIO method (recommended)
pip install platformio

# Create project
pio project init --board esp32dev

# platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

# Compile and upload
pio run --target upload
pio device monitor

MQTT IoT Communication

Basic MQTT Implementation

// mqtt_basic.ino
#include <WiFi.h>
#include <PubSubClient.h>

// WiFi configuration
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// MQTT configuration
const char* mqtt_server = "mqtt.example.com";
const int mqtt_port = 1883;
const char* mqtt_user = "your_username";
const char* mqtt_password = "your_password";

WiFiClient espClient;
PubSubClient mqtt(espClient);

// Topic definitions
const char* TOPIC_STATUS = "sensor/esp32/status";
const char* TOPIC_TEMPERATURE = "sensor/esp32/temperature";
const char* TOPIC_COMMAND = "command/esp32";

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

    // Connect to WiFi
    setupWiFi();

    // Configure MQTT
    mqtt.setServer(mqtt_server, mqtt_port);
    mqtt.setCallback(mqttCallback);

    // Connect to MQTT
    connectMQTT();
}

void setupWiFi() {
    Serial.print("Connecting to WiFi...");
    WiFi.begin(ssid, password);

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

    Serial.println("\nWiFi connected");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
}

void connectMQTT() {
    while (!mqtt.connected()) {
        Serial.print("Connecting to MQTT Broker...");

        String clientId = "ESP32-" + String(WiFi.macAddress());

        if (mqtt.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
            Serial.println("MQTT connected");

            // Subscribe to topic
            mqtt.subscribe(TOPIC_COMMAND);

            // Send online notification
            mqtt.publish(TOPIC_STATUS, "online");
        } else {
            Serial.print("Connection failed, rc=");
            Serial.println(mqtt.state());
            delay(5000);
        }
    }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message received [");
    Serial.print(topic);
    Serial.print("]: ");

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

    // Handle commands
    if (String(topic) == TOPIC_COMMAND) {
        handleCommand(message);
    }
}

void handleCommand(String command) {
    if (command == "LED_ON") {
        digitalWrite(LED_BUILTIN, HIGH);
        mqtt.publish(TOPIC_STATUS, "LED: ON");
    }
    else if (command == "LED_OFF") {
        digitalWrite(LED_BUILTIN, LOW);
        mqtt.publish(TOPIC_STATUS, "LED: OFF");
    }
    else if (command == "RESTART") {
        ESP.restart();
    }
}

void loop() {
    // Ensure MQTT connection
    if (!mqtt.connected()) {
        connectMQTT();
    }
    mqtt.loop();

    // Send temperature data every 30 seconds
    static unsigned long lastSend = 0;
    if (millis() - lastSend > 30000) {
        float temperature = readTemperature();

        char tempString[8];
        dtostrf(temperature, 6, 2, tempString);

        mqtt.publish(TOPIC_TEMPERATURE, tempString);
        lastSend = millis();
    }
}

float readTemperature() {
    // Use DHT22 or another temperature sensor here
    return 25.5;  // Example value
}

LoRa Long-Range Communication

LoRa Hardware Wiring

ESP32      LoRa SX1278
─────────────────────
3.3V   →   VCC
GND    →   GND
GPIO5  →   NSS (CS)
GPIO18 →   SCK
GPIO19 →   MISO
GPIO23 →   MOSI
GPIO2  →   RST
GPIO26 →   DIO0

LoRa Communication Implementation

// lora_communication.ino
#include <SPI.h>
#include <LoRa.h>

#define SS 5
#define RST 2
#define DIO0 26

// LoRa frequency band configuration
#define BAND 915E6  // 915MHz for AS923 (Taiwan)

String deviceId = "NODE_001";

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

    // Initialize LoRa
    SPI.begin();
    LoRa.setPins(SS, RST, DIO0);

    if (!LoRa.begin(BAND)) {
        Serial.println("LoRa initialization failed!");
        while (1);
    }

    // LoRa parameter configuration
    LoRa.setSpreadingFactor(7);     // Spreading factor 7-12
    LoRa.setSignalBandwidth(125E3); // Bandwidth 125KHz
    LoRa.setCodingRate4(5);         // Coding rate 4/5
    LoRa.setTxPower(20);            // Transmit power 20dBm

    Serial.println("LoRa initialized");
}

void loop() {
    // Send data
    sendLoRaData();

    // Receive data
    receiveLoRaData();

    delay(5000);
}

void sendLoRaData() {
    // Read sensors
    float temperature = 25.5;
    float humidity = 60.0;
    int battery = 85;

    // Build JSON packet
    String packet = "{";
    packet += "\"id\":\"" + deviceId + "\",";
    packet += "\"temp\":" + String(temperature, 2) + ",";
    packet += "\"hum\":" + String(humidity, 2) + ",";
    packet += "\"bat\":" + String(battery);
    packet += "}";

    // Send packet
    LoRa.beginPacket();
    LoRa.print(packet);
    LoRa.endPacket();

    Serial.println("Sent: " + packet);
}

void receiveLoRaData() {
    int packetSize = LoRa.parsePacket();

    if (packetSize) {
        String received = "";

        // Read packet
        while (LoRa.available()) {
            received += (char)LoRa.read();
        }

        // Get RSSI
        int rssi = LoRa.packetRssi();
        float snr = LoRa.packetSnr();

        Serial.println("Received: " + received);
        Serial.printf("   RSSI: %d dBm, SNR: %.2f dB\n", rssi, snr);

        // Parse and process data
        parseLoRaData(received);
    }
}

void parseLoRaData(String data) {
    // Parse using ArduinoJson
    // or simple string processing
}

Low-Power Design

// deep_sleep_example.ino
#include "esp_sleep.h"

#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP  60  // 60 seconds

RTC_DATA_ATTR int bootCount = 0;

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

    // Increment boot count
    ++bootCount;
    Serial.println("Boot count: " + String(bootCount));

    // Read sensor and send data
    float data = readSensorAndSend();

    // Enter deep sleep
    goToDeepSleep();
}

void loop() {
    // loop() will not execute in deep sleep mode
}

void goToDeepSleep() {
    Serial.println("Entering deep sleep for " + String(TIME_TO_SLEEP) + " seconds");
    Serial.flush();

    // Set wake-up timer
    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

    // Or use external wake-up
    // esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1);

    // Enter deep sleep
    esp_deep_sleep_start();
}

float readSensorAndSend() {
    // Read sensor
    float value = 25.5;

    // Quickly connect to WiFi and send
    quickConnectAndSend(value);

    return value;
}

void quickConnectAndSend(float data) {
    // Use static IP for faster connection
    IPAddress local_IP(192, 168, 1, 100);
    IPAddress gateway(192, 168, 1, 1);
    IPAddress subnet(255, 255, 255, 0);

    WiFi.config(local_IP, gateway, subnet);
    WiFi.begin(ssid, password);

    // Wait for connection (max 10 seconds)
    int timeout = 0;
    while (WiFi.status() != WL_CONNECTED && timeout < 20) {
        delay(500);
        timeout++;
    }

    if (WiFi.status() == WL_CONNECTED) {
        // Send data to server
        sendDataToServer(data);
    }

    WiFi.disconnect(true);
}

OTA Remote Updates

// ota_update.ino
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

void setup() {
    Serial.begin(115200);
    WiFi.begin(ssid, password);

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

    // Configure OTA
    ArduinoOTA.setHostname("ESP32-OTA");
    ArduinoOTA.setPassword("admin123");

    ArduinoOTA.onStart([]() {
        String type = (ArduinoOTA.getCommand() == U_FLASH) ? "sketch" : "filesystem";
        Serial.println("Starting OTA update: " + type);
    });

    ArduinoOTA.onEnd([]() {
        Serial.println("\nOTA update complete");
    });

    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    });

    ArduinoOTA.onError([](ota_error_t error) {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR) Serial.println("Authentication failed");
        else if (error == OTA_BEGIN_ERROR) Serial.println("Begin failed");
        else if (error == OTA_CONNECT_ERROR) Serial.println("Connection failed");
        else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive failed");
        else if (error == OTA_END_ERROR) Serial.println("End failed");
    });

    ArduinoOTA.begin();
    Serial.println("OTA ready");
}

void loop() {
    ArduinoOTA.handle();

    // Your application logic...
}

Complete Project: Environmental Monitoring Station

// environment_monitor.ino
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>

#define DHTPIN 4
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp;
WiFiClient espClient;
PubSubClient mqtt(espClient);

struct SensorData {
    float temperature;
    float humidity;
    float pressure;
    float altitude;
    int airQuality;
    unsigned long timestamp;
};

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

    // Initialize sensors
    dht.begin();
    if (!bmp.begin(0x76)) {
        Serial.println("BMP280 initialization failed");
    }

    // WiFi and MQTT
    setupWiFi();
    mqtt.setServer("mqtt.example.com", 1883);
    connectMQTT();
}

void loop() {
    if (!mqtt.connected()) {
        connectMQTT();
    }
    mqtt.loop();

    // Read once per minute
    static unsigned long lastRead = 0;
    if (millis() - lastRead > 60000) {
        SensorData data = readAllSensors();
        publishSensorData(data);
        lastRead = millis();
    }
}

SensorData readAllSensors() {
    SensorData data;

    data.temperature = dht.readTemperature();
    data.humidity = dht.readHumidity();
    data.pressure = bmp.readPressure() / 100.0;
    data.altitude = bmp.readAltitude(1013.25);
    data.airQuality = analogRead(35);  // MQ-135
    data.timestamp = millis();

    return data;
}

void publishSensorData(SensorData data) {
    char payload[256];
    sprintf(payload,
        "{\"temp\":%.2f,\"hum\":%.2f,\"pres\":%.2f,\"alt\":%.2f,\"aqi\":%d}",
        data.temperature, data.humidity, data.pressure,
        data.altitude, data.airQuality
    );

    mqtt.publish("sensor/environment", payload);
    Serial.println("Sent: " + String(payload));
}

Conclusion

ESP32 is an ideal choice for IoT development. Key takeaways:

  1. WiFi Stability - Implement reconnection mechanisms
  2. Low-Power Design - Deep sleep and fast wake-up
  3. Communication Protocols - MQTT for cloud, LoRa for long range
  4. OTA Updates - Remote maintenance capability
  5. Error Handling - Watchdog timer and exception recovery

At BASHCAT, we specialize in ESP32 IoT development and can help you rapidly build stable and reliable IoT solutions. Feel free to contact us!

$ tail -n 1 /var/log/bashcat/posts

More from the workshop.