PAGE 10 PAGE 12
WEEK #11

NETWORKING & COMMUNICATIONS

Making devices talk: Architecture, Nodes, I2C, and Wireless Protocols.

00. GROUP ASSIGNMENT

What I learned from the group assignment: We probed the analog levels and digital signals of an output device using an oscilloscope and a multimeter. My main takeaway was visualizing the difference between a constant voltage reading on a multimeter versus seeing the actual PWM square waves and voltage spikes on the oscilloscope when a motor is running. This taught me why calculating power limits is so critical before connecting outputs.

📂 OPEN GROUP ASSIGNMENT
⬇️ JUMP TO BOARD FILES & SCHEMATICS ⬇️

MISSION BRIEFING

This week's objective is to establish communication between at least two processors or devices. I tackled both wired and wireless protocols using the powerful ESP32-C6 microcontroller. The mission involves mapping the network architecture, wiring an I2C bus to extract data from a sensor and display it on an OLED, and setting up a wireless server.

01. NETWORK ARCHITECTURE & THE "SECOND NODE"

To understand how the communication works, we must define the Network Topology. A valid critique during my evaluation was: "It seems you only have one node PCB, and your computer... add at least another node."

To solve this, I expanded my network to include distinct microcontrollers and devices talking to each other.

  • Node 1 (The Master MCU): My custom ESP32-C6 board. It acts as the brain, polling data and hosting the wireless server.
  • Node 2 (Wired Slave MCU): The built-in controller inside the SH1106 OLED Display. It receives rendering commands via I2C.
  • Node 3 (Wired Sensor Node): The SHT31 IC. It calculates temperature and humidity and sends the raw bytes via I2C.
  • Node 4 (Wireless Client): A Smartphone connecting to the ESP32-C6's local IP address to read the live data stream.
Network Architecture Diagram
Architecture Diagram: The flow of data across multiple nodes in the local and wireless network.

02. PROTOCOL ANALYSIS: WHY MQTT?

During the development of the wireless communication, I evaluated different protocols. While I ultimately used an HTTP Web Server for quick visualization in this specific documentation, MQTT (Message Queuing Telemetry Transport) is the superior protocol for my Final Project's sensor network. Here is the technical justification:

1. Packets & Overhead

HTTP is a "heavy" protocol. Every time my ESP32 requests or sends data via HTTP, it must send massive textual headers (Cookies, User-Agent, Accept, etc.), often exceeding 500 bytes just to send a 2-byte sensor reading. MQTT, on the other hand, has a fixed, ultra-lightweight header of only 2 Bytes.

2. Speed & Latency

Because MQTT packets are tiny, the transmission speed is significantly faster, and it requires far less bandwidth. For a project like a "Smart Goal" where I need real-time target detection, the latency of opening and closing TCP connections for every HTTP request is unacceptable. MQTT keeps a persistent connection open to a Broker.

3. Publish / Subscribe Architecture

HTTP uses a strict Request/Response model (the client must ask for data). MQTT uses a Pub/Sub model. My ESP32 simply "Publishes" data to a topic (e.g., goal/sensor1), and any node "Subscribed" to that topic receives the data instantly without having to constantly ask for it.

03. WIRING DIAGRAMS

Before jumping into the code, here is the exact wiring diagram mapping the physical connections between the nodes. Both the OLED and the SHT31 sensor share the exact same I2C bus (SDA and SCL pins) without interfering with each other because they have different hardware addresses.

I2C Wiring Diagram
Wiring Diagram: Routing the 3.3V, GND, SDA, and SCL lines across the nodes.

04. LOCAL NETWORK: I2C BUS (WIRED)

The I2C (Inter-Integrated Circuit) protocol is perfect for connecting multiple devices using just a few pins. The ESP32 acts as the "Master" and calls out to different "Slave" nodes using unique hexadecimal addresses.

I routed 4 wires in parallel (VCC, GND, SDA, SCL) to hook up the SH1106G OLED (Address: 0x3C) and the SHT31 Sensor (Address: 0x44). The ESP32 asks the SHT31 for data, translates the bytes, and routes them to the OLED.

I2C IN ACTION: ESP32 fetching data from Node 3 (SHT31) and routing it to Node 2 (OLED).

05. WIRELESS NETWORK: HTTP SERVER

Moving from wires to invisible waves, I used the ESP32-C6's built-in Wi-Fi to create a local HTTP server on port 80. This allowed me to add my Smartphone and Laptop as new wireless nodes.

The ESP32 connects to my router and gets a local IP. I copied that IP into my browser. Using a C++ rawliteral string, I injected custom HTML and CSS directly from the microcontroller, including a <meta http-equiv="refresh" content="2"> tag so the client node auto-refreshes every 2 seconds to fetch live data!

WIRELESS ACCESS: Entering the local IP to load the live-refreshing Web Server.
SERVER INTERFACE

The HTML interface rendered directly from the ESP32 memory.

06. THE CODE VAULT (DEEP DIVE EXPLANATION)

To ensure my work is fully replicable, here is a detailed breakdown of how the code operates.

PART 1: THE I2C MULTI-DEVICE LOGIC

How the I2C Code Works:
  • Line 7-10: Defines the I2C Addresses. 0x3C is standard for SH1106 displays. 0x44 is the factory address for the SHT31 sensor.
  • setup(): Calls Wire.begin() to initialize the hardware I2C pins. It then pings the OLED to verify it's connected.
  • Wire.beginTransmission(0x44): The ESP32 opens a connection to the sensor node.
  • Wire.write(0x24) & Wire.write(0x00): These are specific HEX commands from the SHT31 datasheet that tell the sensor to take a "High Repeatability Measurement" without clock stretching.
  • Wire.requestFrom(0x44, 6): The ESP32 demands 6 bytes back (2 for Temp, 1 CRC, 2 for Hum, 1 CRC).
  • Bit Shifting: (data[0] << 8) | data[1] merges two 8-bit bytes into a single 16-bit integer so we can do the math to convert it to Celsius based on the datasheet's formula.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

#define i2c_Address 0x3C
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

#define SHT31_ADDR 0x44   

Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

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

  Wire.begin();

  if (!display.begin(i2c_Address, true)) {
    Serial.println("Fail to initialize OLED SH110X");
    while (1);
  }

  display.clearDisplay();
  display.setTextColor(SH110X_WHITE);
  display.setTextSize(1);
  display.setCursor(10, 20);
  display.println("Starting...");
  display.display();
  delay(1000);
}

void loop() {
  // SEND COMMAND TO SHT3X
  Wire.beginTransmission(SHT31_ADDR);
  Wire.write(0x24); // Measurement High Repeatability
  Wire.write(0x00);

  if (Wire.endTransmission() != 0) {
    Serial.println("Error sending command");
    return;
  }
  delay(20);

  // READ DATA (6 Bytes)
  Wire.requestFrom(SHT31_ADDR, 6);

  if (Wire.available() == 6) {
    uint8_t data[6];
    for (int i = 0; i < 6; i++) {
      data[i] = Wire.read();
    }

    uint16_t rawTemp = (data[0] << 8) | data[1];
    uint16_t rawHum  = (data[3] << 8) | data[4];

    float temperature = -45.0 + (175.0 * rawTemp / 65535.0);
    float humidity    = 100.0 * rawHum / 65535.0;

    // Show on OLED
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(10, 10);
    display.println("SHT3X Sensor Node");

    display.setTextSize(2);
    display.setCursor(10, 25);
    display.print(temperature, 1);
    display.print(" C");

    display.setCursor(10, 45);
    display.print(humidity, 1);
    display.print(" %");

    display.display();
  }
  delay(2000);
}

PART 2: THE HTTP WEB SERVER LOGIC

How the Server Code Works:
  • WiFi.begin(ssid, password): Connects the ESP32 to the local Wi-Fi router.
  • server.on("/", handleRoot): Defines the Routing. When a node navigates to the root IP address, the ESP32 triggers the handleRoot function.
  • handleRoot(): First, it calls readSHT3X() to ping the I2C sensor for fresh data. Then, it creates a massive String containing HTML code.
  • R"rawliteral(...)rawliteral": A C++ feature that allows multi-line strings without needing to escape quotation marks, perfect for writing raw HTML/CSS inside an Arduino sketch.
  • server.send(200, "text/html", html): The ESP32 acts as a true server, responding to the client's HTTP request with a 200 OK status code and pushing the constructed HTML string to their browser.
#include <Arduino.h>
#include <Wire.h>
#include <WiFi.h>
#include <WebServer.h>

#define SHT31_ADDR 0x44

const char* ssid = "RoyZ";
const char* password = "Zarate28";

WebServer server(80); // Port 80 is standard for HTTP

float temperature = 0.0;
float humidity = 0.0;

// Function to read sensor via I2C
void readSHT3X() {
  Wire.beginTransmission(SHT31_ADDR);
  Wire.write(0x24);
  Wire.write(0x00);
  if (Wire.endTransmission() != 0) return;
  delay(20);

  Wire.requestFrom(SHT31_ADDR, 6);
  if (Wire.available() == 6) {
    uint8_t data[6];
    for (int i = 0; i < 6; i++) { data[i] = Wire.read(); }
    uint16_t rawTemp = (data[0] << 8) | data[1];
    uint16_t rawHum  = (data[3] << 8) | data[4];

    temperature = -45.0 + (175.0 * rawTemp / 65535.0);
    humidity    = 100.0 * rawHum / 65535.0;
  }
}

// Function to serve the Web Page
void handleRoot() {
  readSHT3X(); // Get fresh data

  // Construct HTML with CSS and Auto-Refresh tag
  String html = R"rawliteral(
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="refresh" content="2"> 
    <title>SHT3X Monitor</title>
    <style>
      body { font-family: Arial; text-align: center; background-color: #111; color: white; margin-top: 50px;}
      .card { background: #222; display: inline-block; padding: 30px; border-radius: 20px; box-shadow: 0 0 20px rgba(255,255,255,0.1); }
      h1 { font-size: 32px; }
      p { font-size: 28px; margin: 20px 0; }
    </style>
  </head>
  <body>
    <div class="card">
      <h1>SHT3X Sensor</h1>
      <p>🌡 Temperature: )rawliteral";

  html += String(temperature, 1);
  html += R"rawliteral( °C</p>
      <p>💧 Humidity: )rawliteral";

  html += String(humidity, 1);
  html += R"rawliteral( %</p>
    </div>
  </body>
  </html>
  )rawliteral";

  server.send(200, "text/html", html); // Push to client
}

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

  Wire.begin();
  WiFi.begin(ssid, password);

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

  Serial.println();
  Serial.print("IP: ");
  Serial.println(WiFi.localIP()); // Print IP so user can connect

  // Start Server Routing
  server.on("/", handleRoot);
  server.begin();
  Serial.println("HTTP Server started");
}

void loop() {
  // Constantly listen for incoming clients (Laptops, Phones)
  server.handleClient();
}