Week 11: Networking and Communications
Introduction
Low-Level Communication Methods
These methods are based on electrical signals and are typically used for direct control or simple data transfer between devices:
GPIO (Digital Communication)
Uses HIGH/LOW signals. While simple, it can still represent control logic between a main controller and peripheral devices.
PWM (Pulse Width Modulation)
Encodes information in the duty cycle of a signal. Although commonly used for control (like LED brightness or motor speed), it can also be interpreted as a communication method where the peripheral device decodes signal timing.
DAC (Digital-to-Analog Conversion)
Produces variable voltage levels. This can act as a form of analog communication where continuous signals represent data between devices.
Protocol-Based Communication
These methods define rules for data exchange and are designed for reliable communication between multiple devices:
I²C (Inter-Integrated Circuit)
Two-wire protocol (SDA, SCL) that allows multiple devices using addressing.
- One main device + multiple peripherals
SPI (Serial Peripheral Interface)
High-speed communication with dedicated lines (MOSI, MISO, SCK, CS).
- One main device + multiple peripherals (each with select line)
UART (Serial Communication)
Asynchronous communication using TX/RX.
- Point-to-point (main ↔ peripheral)
Networking Communication (IoT & Wireless)
Beyond local communication, embedded systems can connect through networking technologies, enabling distributed systems:
WiFi
Enables devices to connect to local networks or the internet. Ideal for IoT systems with cloud integration.
Bluetooth / BLE
Short-range communication, commonly used for direct interaction with smartphones.
MQTT (Message Queuing Telemetry Transport)
Lightweight publish/subscribe protocol. Devices communicate through a broker, allowing scalable and decoupled systems.
HTTP/HTTPS
Request-response model used for web services and APIs.
WebSockets (WS/WSS)
Enables real-time bidirectional communication between clients and devices.
XIAO ESP32-C6
Diagram of connections
This diagram represents the wiring of the system powered by a Seeed Studio XIAO ESP32-C6. The red 3.3V line and black GND line form a common power rail that distributes energy to two WS2812B NeoPixels, an OLED display, and a ESP32-WROOM-32 dev module. The green signal line originates from pin D3, passing through a 220Ω resistor (used to protect the data pin from voltage spikes) before reaching the DIN port of the first NeoPixel; the signal then chains from DOUT to the next pixel's DIN. This setup allows the XIAO to act as the controller of the ESP32 and the Neopixels, managing both local visual feedback.
Programming
To code, I used ARDUINO IDE. ARDUINO IDE is a free, open-source application for Windows, macOS, and Linux, used to write, compile, and upload code to Arduino boards. It provides a text editor, toolbar, and serial monitor, supporting C/C++ to create ".ino" sketch files for controlling microcontrollers.
XIAO ESP32-C6 Board
1. First, we have to create a sketch in Arduino IDE.
XIAO ESP32-C6 Board
2. Then, we have go to the board manager and write XIAO. After doing that, a library will appear, its name is Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower, III, we must install it.
Uploading
3. After installing the XIAO ESP32-C6 Board, we must click on the tab that says select board and write Seeed XIAO ESP32-C6, then select the PORT where our microcontroller is connected and upload the information. We can also click Tools, then Board and then the library Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower, III and there look for the XIAO. Both ways will let us set our board as a XIAO.
4. Before uploading our code to the microcontroller we should use the verify tool, that compiles the code before uploading it in order to detect mistakes or problems. The verify tool is the one in the top with the check.
5. Finally, to upload our code we must click the upload tool, that is the one with the arrow pointing to the right. If our code is right, it shall compile. To get the , we must click on Tools in the top menu and select Serial Monitor.
My Code
~ Network: WiFi connection to MQTT Broker (EMQX).
~ System: MQTT Callback for "Macarena" mode and Gamer effects.
~ Setup: PIN_BOTON (D0), PIN_RGB (D3), NUM_LEDS (2).
#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>
// -------- WIFI --------
const char* ssid = "iPhone de Derek";
const char* password = "9414902012";
// -------- MQTT --------
const char* mqttServer = "broker.emqx.io";
const int mqttPort = 1883;
// -------- PINES --------
#define PIN_BOTON D0
#define PIN_RGB D3
#define NUM_LEDS 2
#define LED 23
// -------- OBJETOS --------
WiFiClient esp32Client;
PubSubClient mqttClient(esp32Client);
Adafruit_NeoPixel pixels(NUM_LEDS, PIN_RGB, NEO_GRB + NEO_KHZ800);
// -------- VARIABLES --------
int var = 0;
String resultS = "";
uint32_t pixelHue = 0;
bool lastState = HIGH;
bool modoMacarena = false;
void wifiInit() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void callback(char* topic, byte* payload, unsigned int length) {
resultS = "";
for (int i = 0; i < length; i++) {
resultS += (char)payload[i];
}
if (String(topic) == "xiao/boton") {
if (resultS == "Macarena") {
modoMacarena = true;
for (int i = 0; i < NUM_LEDS; i++) {
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
}
pixels.show();
} else {
modoMacarena = false;
}
}
var = atoi(resultS.c_str());
}
void reconnect() {
while (!mqttClient.connected()) {
String clientId = "Kamilovich-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
mqttClient.subscribe("xiao/boton");
} else {
delay(3000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(LED, OUTPUT);
pixels.begin();
pixels.setBrightness(50);
wifiInit();
mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setCallback(callback);
}
void loop() {
if (!mqttClient.connected()) { reconnect(); }
mqttClient.loop();
if (!modoMacarena) { efectoGamer(); }
leerBoton();
if (var == 0) { digitalWrite(LED, LOW); }
else if (var == 1) { digitalWrite(LED, HIGH); }
}
void leerBoton() {
bool estado = digitalRead(PIN_BOTON);
if (estado == LOW && lastState == HIGH) {
mqttClient.publish("xiao/boton", "Macarena");
delay(200);
}
lastState = estado;
}
void efectoGamer() {
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate < 15) return;
for (int i = 0; i < pixels.numPixels(); i++) {
int hueOffset = i * (65536 / pixels.numPixels());
pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue + hueOffset)));
}
pixels.show();
pixelHue += 256;
lastUpdate = millis();
}
Neopixels code
if (String(topic) == "xiao/boton") {
if (resultS == "Macarena") {
modoMacarena = true;
for (int i = 0; i < NUM_LEDS; i++) {
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
}
pixels.show();
} else {
modoMacarena = false;
}
}
Macarena.This code processes incoming messages from an MQTT topic to trigger a specific lighting state. When the topic xiao/boton receives the string Macarena, it sets the boolean message modoMacarena to true and executes a for loop that iterates through every Neopixel in the strip, assigning each a solid red color using the pixels.setPixelColor(index, color) function with RGB values of (255, 0, 0). The pixels.show() command is then called to push this data from the microcontroller to the actual hardware, instantly illuminating the strip. If any other message is received, the mode is disabled, and the final line converts the incoming string into an integer using atoi() to maintain compatibility with a secondary Neopixel control on pin 23.
void loop() {
if (!mqttClient.connected()) { reconnect(); }
mqttClient.loop();
if (!modoMacarena) { efectoGamer(); }
leerBoton();
if (var == 0) { digitalWrite(LED, LOW); }
else if (var == 1) { digitalWrite(LED, HIGH); }
}
!Macarena.The loop() function acts as the central execution hub for the system, continuously ensuring that the connection to the MQTT broker is active by calling reconnect() whenever the client is disconnected. It maintains background MQTT processes through mqttClient.loop() and conditionally executes the efectoGamer() LED sequence only if the modoMacarena flag is false, effectively allowing external commands to override the default visual behavior. Finally, it monitors physical input via leerBoton() and performs real-time hardware control by toggling the state of a specific LED pin based on the value of the var variable received from the network.
void efectoGamer() {
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate < 15) return;
for (int i = 0; i < pixels.numPixels(); i++) {
int hueOffset = i * (65536 / pixels.numPixels());
pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue + hueOffset)));
}
pixels.show();
pixelHue += 256;
lastUpdate = millis();
}
efectoGamer(). function implements a non-blocking rainbow animation by utilizing millis() to track elapsed time, allowing the sequence to update every 15 milliseconds without halting the program. It uses pixels.numPixels() to scale calculations across the hardware, while pixels.setPixelColor() assigns colors generated by pixels.ColorHSV() to each LED. These colors are processed through pixels.gamma32() to correct for human light perception, ensuring a naturally vibrant look. To complete the cycle, pixels.show() pushes the data to the LEDs, pixelHue increments by 256 to shift the spectrum, and lastUpdate resets the timing gate to maintain a consistent animation speed.
Neopixels Library
To set the neopixels, we first need to install the library in Arduino. For that we have to go to the Library Manager and write Adafruit Neopixel, then intall the library named like that.
ESP32 WROOM 32
Diagram of connections
This diagram shows the I2C communication setup between an ESP32-WROOM-32 development board and an OLED display. The blue line connects the SDA (Serial Data) pin of the OLED to GPIO 21 on the ESP32, while the yellow line connects the SCL (Serial Clock) pin to GPIO 22. These two pins are the standard hardware I2C default for the ESP32, allowing the microcontroller to send graphical data and text to the screen using a synchronous serial protocol.
Programming
To code, I used ARDUINO IDE. ARDUINO IDE is a free, open-source application for Windows, macOS, and Linux, used to write, compile, and upload code to Arduino boards. It provides a text editor, toolbar, and serial monitor, supporting C/C++ to create ".ino" sketch files for controlling microcontrollers.
ESP32
1. First, we have to create a sketch in Arduino IDE.
ESP32
2. Then, we have go to the board manager and write ESP32. After doing that, a library will appear, its name is esp32 by Espressif Systems, we must install it.
Uploading
3. After installing the ESP32 Board, we must click on the tab that says select board and write ESP32 Dev Module, then select the PORT where our microcontroller is connected and upload the information.
4. Before uploading our code to the microcontroller we should use the verify tool, that compiles the code before uploading it in order to detect mistakes or problems. The verify tool is the one in the top with the check.
5. Finally, to upload our code we must click the upload tool, that is the one with the arrow pointing to the right. If our code is right, it shall compile. To get the , we must click on Tools in the top menu and select Serial Monitor.
My Code
~ Device: ESP32-WROOM-32 Receiver.
~ Display: SSD1306 OLED via I2C (SDA: 21, SCL: 22).
~ Action: Real-time visualization of messages from topic xiao/boton.
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// -------- CONFIGURACIÓN OLED --------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// -------- WIFI & MQTT --------
const char* ssid = "iPhone de Derek";
const char* mqttServer = "broker.emqx.io";
WiFiClient esp32Client;
PubSubClient mqttClient(esp32Client);
void actualizarOLED(String mensaje) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.println(mensaje);
display.display();
}
void callback(char* topic, byte* payload, unsigned int length) {
String mensaje = "";
for (int i = 0; i < length; i++) { mensaje += (char)payload[i]; }
actualizarOLED(mensaje);
}
void reconnect() {
while (!mqttClient.connected()) {
String clientId = "Kamo_Display_" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
mqttClient.subscribe("xiao/boton");
} else { delay(3000); }
}
}
void setup() {
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { for(;;); }
display.clearDisplay();
actualizarOLED("Esperando WiFi...");
wifiInit();
mqttClient.setServer(mqttServer, 1883);
mqttClient.setCallback(callback);
}
void loop() {
if (!mqttClient.connected()) { reconnect(); }
mqttClient.loop();
}
OLED code
void actualizarOLED(String mensaje) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.println(mensaje);
display.display();
}
actualizarOLED(). The actualizarOLED() function manages the physical rendering of data on the display by first executing display. clearDisplay() to wipe the existing buffer, preventing new text from overlapping with old pixels. It then prepares the visual layout by resetting the text "anchor" to the top-left corner with display. setCursor(0, 0), setting the font scale via display. setTextSize(1), and defining the pixel state with display. setTextColor(SSD1306_WHITE). After the string is written to the internal memory buffer using display. println(mensaje), the function calls display.display(), which is the critical final step that pushes the processed data from the ESP32’s RAM to the OLED hardware, making the message visible to the user.
void callback(char* topic, byte* payload, unsigned int length) {
String mensaje = "";
for (int i = 0; i < length; i++) { mensaje += (char)payload[i]; }
actualizarOLED(mensaje);
}
callback().The callback() function serves as the system's message processor, automatically triggering whenever a new message is received from the subscribed MQTT topic. It begins by initializing an empty string and using a for loop to iterate through the incoming payload, casting each byte into a character to reconstruct the full text message. Once the loop finishes and the complete mensaje is assembled, the function immediately passes this string to actualizarOLED(), ensuring that the hardware display is updated in real-time with the data received from the network.
Adafruit Library
To set the OLED, we first need to install the library in Arduino. For that we have to go to the Library Manager and write Adafruit GFX and the Adafruit SSD1306, then intall the library named like that.
Learning outcomes
Since I’m studying mechatronics engineering, I already had some prior exposure to electronics manufacturing; however, this was a wonderful opportunity to see the AMS1117 regulator in action and learn about programming Neopixels, which will be a very useful tool for my project.