Module 7 - MQTT, HTTP, WIFI

7.1 Introduction: The IoT Communication Stack

For an IoT device to be useful, it needs to communicate. This communication happens in layers, much like a conversation. You need to have connectivity, then you need a common language to request things (web communication), and sometimes a specialized shorthand (messaging).

7.2 Local Network Connectivity with Wi-Fi

Wi-Fi is a technology based on the IEEE 802.11 standards that enables wireless data exchange. The ESP32 supports common standards like 802.11b, 802.11g, and 802.11n, operating in the 2.4 GHz frequency band.

ESP32 Wi-Fi Modes
  1. Station Mode (STA): The ESP32 acts as a client, connecting to an existing access point (AP) like your home or university router. This is the most common mode for devices that need internet access.
  2. Access Point Mode (AP): The ESP32 creates its own Wi-Fi network, allowing other devices (like your phone or laptop) to connect directly to it. This is useful for initial device configuration or creating isolated local networks.
  3. STA + AP Mode: The ESP32 can do both simultaneously, connecting to one network while also providing its own. This allows it to act as a range extender or a bridge between networks.
Example
#include <WiFi.h>

// --- Replace with your network credentials ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// -----------------------------------------

void setup() {
  Serial.begin(115200); // Allow serial to initialize

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

  // Start the Wi-Fi connection
  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 successfully!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // The main work is done in setup for this example.
  // In a real application, you would do your networking tasks here.
  delay(10000);
}

7.3 Web Communication with HTTP/HTTPS

HTTP

HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the World Wide Web. It operates on a request-response model.

HTTPS

HTTPS is simply HTTP Secure. It adds a layer of SSL/TLS encryption to the conversation. This prevents anyone from eavesdropping on the data exchanged between the client and server, which is crucial for protecting sensitive information like passwords or personal data.

Example
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// --- Replace with your network credentials ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// -----------------------------------------

// The URL of the API we want to request data from
const char* api_url = "http://jsonplaceholder.typicode.com/posts/1";

void setup() {
  Serial.begin(115200);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // Check if we are connected to WiFi before proceeding
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    Serial.println("Making HTTP GET request...");
    http.begin(api_url); // Initialize the HTTP request
    
    int httpCode = http.GET(); // Send the GET request

    if (httpCode > 0) { // Check if the request was successful
      Serial.printf("HTTP Response code: %d\n", httpCode);

      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString(); // Get the response payload as a string
        
        // --- Parse the JSON response ---
        JsonDocument doc;
        DeserializationError error = deserializeJson(doc, payload);

        if (error) {
          Serial.print("deserializeJson() failed: ");
          Serial.println(error.c_str());
        }
        else {
          // Extract the value associated with the "id" and "title" key
        
          int postId = doc["id"];
          const char* title = doc["title"];
  
          Serial.printf("Post ID: %d\n", postId);
          Serial.printf("Title: %s\n", title);
        }
      }
    } else {
      Serial.printf("HTTP GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end(); // Free resources
  } else {
    Serial.println("WiFi not connected.");
  }

  // Wait 30 seconds before the next request
  delay(30000); 
}

7.4 Efficient IoT Messaging with MQTT

MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for constrained devices and unreliable networks, making it perfect for IoT. Instead of the request-response model, MQTT uses a publish/subscribe (pub/sub) model.

MQTT Components
MQTT Concepts
MQTTS

Just like HTTPS, MQTTS is MQTT secured with TLS encryption to protect data confidentiality and integrity.

Example
#include <WiFi.h>
#include <PubSubClient.h>

// --- Replace with your network credentials ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// -----------------------------------------

// --- MQTT Broker Configuration ---
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;

// --- Topics ---
// Topic to publish messages to
const char* publish_topic = "Digilab/Modul7/status"; 
// Topic to subscribe to for incoming messages
const char* subscribe_topic = "Digilab/Modul7/command"; 

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

// This function is called whenever a message arrives on a subscribed topic
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // Example: Turn on a built-in LED if the message is "ON"
  if (message == "ON") {
    Serial.println("Turning on lamp");
    digitalWrite(LED_BUILTIN, HIGH);
  } else if (message == "OFF") {
    Serial.println("Turning off lamp");
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);
    
    // Attempt to connect
    if (mqttClient.connect(clientId.c_str())) {
      Serial.println("connected");
      // Subscribe to the command topic upon connection
      mqttClient.subscribe(subscribe_topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected!");
  
  // Configure MQTT client
  mqttClient.setServer(mqtt_server, mqtt_port); 
  mqttClient.setCallback(callback);
}

void loop() {
  // Ensure the MQTT client is connected
  if (!mqttClient.connected()) {
    reconnect();
  }
  mqttClient.loop(); // This allows the client to process incoming messages

  // --- Publish a message every 10 seconds ---
  static unsigned long lastMsg = 0;
  unsigned long now = millis();
  if (now - lastMsg > 10000) {
    lastMsg = now;
    
    char msg[50];
    snprintf(msg, 50, "Uptime: %lu seconds", millis() / 1000);
    
    Serial.printf("Publishing message to %s: %s\n", publish_topic, msg);
    mqttClient.publish(publish_topic, msg);
  }
}