Week 11: Embedded Networking and Communications

Overview

This week I explored how microcontrollers can talk to each other using both wireless and wired protocols. I set up a network of three custom PCBs, each based on a different Seeed XIAO board: the ESP32C6, ESP32S3, and RP2040. Each board had a unique role and communicated with the others using either Wi-Fi, GPIO, or I2C.

I used HTTP over Wi-Fi to wirelessly control a NeoPixel ring from another board with physical buttons, and GPIO signaling to trigger events like sound or light feedback from a third board. I also integrated visual feedback through a 16x2 LCD screen using the I2C protocol on the ESP32S3 — connected via SDA and SCL.

Each of these boards was embedded in a custom-designed and milled PCB. The design and fabrication process was completed during Week 8: Electronics Production, and reused here to showcase real-world embedded communication.

Through this networked setup, I understood the trade-offs between protocols, the importance of voltage and pin configuration, and the joy of making things blink, beep, and display sarcastic messages when everything finally works.

Checklist

Group Assignment

As a group, we explored and tested several wired communication protocols between development boards, specifically UART, I2C, and SPI. Each team member contributed by wiring different microcontrollers, sending messages, and debugging signal transmission using serial terminals and oscilloscopes.

The testing was done in pairs or small teams and focused on point-to-point and master-slave communication using various board combinations. The results are documented on the Puebla lab's Week 11 page.

Communication Protocol Comparison

Protocol Type Wiring Speed Use Case
UART Serial (point-to-point) TX/RX + GND Medium Simple device-to-device messaging
I2C Serial (multi-master/slave) SDA/SCL + GND Medium Sensors, LCDs, displays
SPI Serial (master/slaves) MOSI/MISO/SCK/SS High Fast data transfer with multiple devices

Observations and Notes

My Takeaways

While our group focused entirely on wired communication, in my individual assignment I also tested wireless networking using Wi-Fi and HTTP. This let me control remote lighting effects and opened up possibilities for IoT-style interactions.

Additionally, I used I2C for local communication between my ESP32S3 and a 16x2 LCD display, wired through SDA and SCL. This gave useful visual feedback and reminded me to double-check voltage lines (spoiler: 3.3V won’t cut it for every screen).

ESP32C6 as Webserver for NeoPixel Ring

As part of the networking and communications week, I set up my ESP32C6 as a standalone web server that hosts a control panel for a NeoPixel LED ring. It allows users to trigger different light effects wirelessly through a web interface served directly by the microcontroller.

The ESP32C6 acts as a Wi-Fi client, connects to an existing network, and exposes a series of endpoints like rainbow, solid_white, etc., that trigger different lighting effects. The web interface includes buttons for each mode.

Code for ESP32C6 Webserver


      #include <WiFi.h>
      #include <Adafruit_NeoPixel.h>
      #include <WebServer.h>
      
      #define LED_PIN D0
      #define NUM_LEDS 16
      #define BRIGHTNESS 100
      
      const char* ssid = "XXXxxXXxx";
      const char* password = "XXXxxXXXXx";
      
      Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
      WebServer server(80);
      
      int currentEffect = 0;
      bool effectChanged = false;
      
      void setup() {
        Serial.begin(115200);
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED) delay(500);
      
        strip.begin();
        strip.setBrightness(BRIGHTNESS);
        strip.clear();
        strip.show();
      
        server.on("/", handleRoot);
        server.on("/rainbow", [](){ currentEffect = 1; effectChanged = true; server.send(200, "text/plain", "Rainbow Mode ON"); });
        server.on("/solid_white", [](){ currentEffect = 2; effectChanged = true; server.send(200, "text/plain", "Solid White ON"); });
        server.on("/running_white", [](){ currentEffect = 3; effectChanged = true; server.send(200, "text/plain", "Running White LED ON"); });
        server.on("/gradient", [](){ currentEffect = 4; effectChanged = true; server.send(200, "text/plain", "Gradient Effect ON"); });
        server.on("/orange_glow", [](){ currentEffect = 5; effectChanged = true; server.send(200, "text/plain", "Orange Glow Mode ON"); });
        server.begin();
      }
        

Code Breakdown – ESP32C6 Webserver

Below is an explanation of how the code for my ESP32C6 webserver works. Each section is grouped by function, with beginner-friendly descriptions of what each chunk does and how it contributes to building a wireless LED controller.

1. Libraries and Setup

#include <WiFi.h> – Enables Wi-Fi functions on the ESP32
#include <Adafruit_NeoPixel.h> – Lets us control the NeoPixel LED ring
#include <WebServer.h> – Turns the ESP32 into a basic web server that can respond to browser requests

2. Pin Definitions and Wi-Fi Credentials

We define the pin where the NeoPixel ring is connected (D0), how many LEDs it has, and how bright they should be. The ssid and password store the login info for your Wi-Fi network.

3. Creating the LED Strip and Web Server

We create a strip object to control the NeoPixels, and a server object listening on port 80 (the default for web browsers). We also create two variables: currentEffect to store the selected lighting mode, and effectChanged to detect changes.

4. Connecting to Wi-Fi

The board attempts to connect to the specified Wi-Fi network. While it waits, it prints dots to the serial monitor. Once connected, it prints the assigned IP address so we can access the web server from a browser.

5. Initializing the LED Ring

The LED ring is set up: brightness is applied, all LEDs are turned off using clear(), and changes are shown with show().

6. Defining Web Routes

Each route is a different webpage path like /rainbow or /solid_white. These trigger lighting effects. The root path / shows a webpage with clickable buttons. When a button is clicked, the server changes the lighting mode.

7. Main Loop

The loop() function constantly checks for browser input via server.handleClient(). Based on which effect is active, it runs the appropriate function to generate the LED animation. If a new effect is selected, the current one stops cleanly.

8. Light Effect Functions

Each animation (rainbow, solid white, running white, gradient, orange glow) is coded separately for better organization. All of them include server.handleClient() to keep the web server responsive during animations.

In Short:

Web Interface and Light Effects

Once the webserver is running, this part of the code handles two key things:

Web Interface Setup

void handleRoot() {
          server.send(200, "text/html",
            "<html><body style='font-family:sans-serif;'>"
            "<h2>Neopixel Control Panel</h2>"
            "<button onclick=\"fetch('/rainbow')\">Rainbow</button><br><br>"
            "<button onclick=\"fetch('/solid_white')\">Solid White</button><br><br>"
            "<button onclick=\"fetch('/running_white')\">Running White</button><br><br>"
            "<button onclick=\"fetch('/gradient')\">Gradient</button><br><br>"
            "<button onclick=\"fetch('/orange_glow')\">Orange Glow</button>"
            "</body></html>");
        }
        

This function builds a basic webpage with buttons. When a button is clicked, it sends a request to the ESP32 to change the light mode. The page is clean and simple — no external libraries, just raw HTML and JavaScript fetch().

Web Interface Screenshot

Rainbow Mode

void rainbowCycle(int wait) {
          for (int j = 0; j < 256 && currentEffect == 1; j++) {
            for (int i = 0; i < NUM_LEDS; i++) {
              strip.setPixelColor(i, Wheel((i * 256 / NUM_LEDS + j) & 255));
            }
            strip.show();
            delay(wait);
            server.handleClient();
          }
        }
        

Cycles through rainbow colors by shifting hues across the LED ring. Gives a colorful dynamic effect — great for showing off addressable LEDs.

Solid White

void solidWhite() {
          strip.fill(strip.Color(255, 255, 255));
          strip.show();
          while (currentEffect == 2) server.handleClient();
        }
        

All LEDs are set to white and stay that way until another effect is triggered. Clean and minimal.

Solid White Effect

Running White

void runningWhite() {
          while (currentEffect == 3) {
            for (int i = 0; i < NUM_LEDS; i++) {
              strip.clear();
              strip.setPixelColor(i, strip.Color(255, 255, 255));
              strip.show();
              delay(100);
              server.handleClient();
              if (effectChanged) { effectChanged = false; return; }
            }
          }
        }
        

A single white light travels around the ring like a chasing light. Resets each loop.

Gradient Run

void gradientRun() {
          strip.clear();
          for (int i = 0; i < NUM_LEDS && currentEffect == 4; i++) {
            float ratio = (float)i / (NUM_LEDS - 1);
            int r = 255;
            int g = int(255 * ratio);
            int b = 0;
            strip.setPixelColor(i, strip.Color(r, g, b));
            strip.show();
            delay(100);
            server.handleClient();
            if (effectChanged) { effectChanged = false; return; }
          }
        }
        

LEDs gradually shift from red to yellow across the ring, leaving a solid trail behind. Simple but elegant transition.

Orange Glow

void orangeGlow() {
          while (currentEffect == 5) {
            float level = (sin(millis() / 1000.0 * PI) + 1) / 2;
            int brightness = 30 + int(level * 100);
            for (int i = 0; i < NUM_LEDS; i++) {
              strip.setPixelColor(i, strip.Color(brightness, brightness / 2, 0));
            }
            strip.show();
            delay(30);
            server.handleClient();
            if (effectChanged) { effectChanged = false; return; }
          }
        }
        

Creates a smooth pulsing orange effect, simulating a glow like firelight. Uses a sine wave to change brightness gradually.

Lessons Learned

Download Project Files

All MicroPython codes are available below: