FAB ACADEMY 2026

WEEK 11

Networking and Communications

Group assignment:

What I Learned From Teamwork

Work in progress Week 08

Installing the MQTT Protocol

For this group assignment, we will install the MQTTX program by following the indicated steps.


Creating an MQTT connection for IoT communication on a local network

At this stage, a new connection was created in the MQTTX application in order to establish communication with a broker using the MQTT protocol. To do this, the basic parameters were configured, such as the connection name, the server address (host), the communication port (1883), and the client ID. This setup allows the system to connect correctly to the broker and send and receive data from external devices such as the ESP32-C3 programmed in Arduino IDE. This step is essential for enabling IoT communication and verifying real-time data flow between hardware and software.

Work in progress Week 11

We will use the general name ASSIGNMENT11 and keep the default options as shown in the image.
Then we will click NEW SUBSCRIPTION to create a new subscription, and in the topic field we will enter fabacademy/contador.
Finally, we will click CONNECT to establish the connection with the broker.

Work in progress Week 11

We verified that the shared data uses the topic fabacademy/contador and that it is set to the Plaintext option; then we checked the connections on more computers. Work in progress Week 11

Establishing the WiFi connection and sending data via MQTT

At this stage, the WiFi connection of the ESP32-C3 microcontroller was established, allowing it to access the local network and communicate with a broker through the MQTT protocol. Arduino IDE was used for programming, and the PubSubClient library was installed. This library makes it easy to implement MQTT communication on the ESP32, enabling functions such as connecting to the broker, publishing sensor data, and subscribing to different topics. Thanks to this implementation, the device can send information in real time, becoming part of a functional IoT system.

Work in progress Week 11

This test code was generated in Gemini to verify the WiFi connection and MQTT communication.

Code: MQTT Communication  Fuente : GEMINI

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// WiFi
const char* ssid = "TU_WIFI";
const char* password = "TU_PASSWORD";

// MQTT
const char* mqtt_server = "broker.emqx.io";

WiFiClient espClient;
PubSubClient client(espClient);

String mensaje = "";

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

  // OLED
  Wire.begin(4, 5);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();

  // WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // MQTT
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("XIAOClient")) {
      client.subscribe("fabacademy/servo");
    } else {
      delay(2000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  mensaje = "";

  for (int i = 0; i < length; i++) {
    mensaje += (char)payload[i];
  }

  Serial.println(mensaje);

  // Mostrar en OLED
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println(mensaje);
  display.display();
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

    

In the serial monitor, the connection and some messages sent by my classmates can be observed.

Work in progress Week 11

It is time to connect the two projects.

TRANSMITTER

In this case, my classmate will be the transmitter, and the PCB he will use is Adrian Torres's FABXIAO, specifically only the button connected to pin D7. To do this, he connected his board to his computer and uploaded the following code.

Code: TRANSMITTER  Source: Gemini

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

// WiFi
const char* ssid = "IoT_UP";
const char* password = "ti6WzfPsk3WnqZpt8d";

// MQTT
const char* mqtt_server = "broker.emqx.io";

WiFiClient espClient;
PubSubClient client(espClient);

int boton = D7;
int contador = 0;
bool estadoAnterior = HIGH;

void setup() {
  Serial.begin(115200);
  pinMode(boton, INPUT_PULLUP);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  client.setServer(mqtt_server, 1883);
}

void reconnect() {
  while (!client.connected()) {
    client.connect("XIAO_BOTON");
  }
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  bool estadoActual = digitalRead(boton);

  // Detecta pulsación
  if (estadoAnterior == HIGH && estadoActual == LOW) {
    contador++;

    String msg = String(contador);
    Serial.println(msg);

    client.publish("fabacademy/contador", msg.c_str());

    delay(300); // anti rebote
  }

  estadoAnterior = estadoActual;
}

    
Work in progress Week 11

RECEIVER

In this case, I will be the receiver and I will use the board that has the OLED, specifically pin D4 for SDA and pin D5 for SCL. To do this, I connected my board to my computer and uploaded the following code.

Code: RECEIVER   Source: Gemini

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// WiFi
const char* ssid = "IoT_UP";
const char* password = "ti6WzfPsk3WnqZpt8d";

// MQTT
const char* mqtt_server = "broker.emqx.io";

WiFiClient espClient;
PubSubClient client(espClient);

String mensaje = "";
bool nuevoMensaje = false;

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

  // OLED
  Wire.begin(D4, D5);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 20);
  display.println("Contador");
  display.display();

  // WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // MQTT
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("XIAO_OLED")) {
      client.subscribe("fabacademy/contador");
    } else {
      delay(2000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  mensaje = "";

  for (int i = 0; i < length; i++) {
    mensaje += (char)payload[i];
  }

  Serial.println(mensaje);
  nuevoMensaje = true;
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  if (nuevoMensaje) {
    display.clearDisplay();
    display.setTextSize(3);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(20, 20);
    display.println(mensaje);
    display.display();

    nuevoMensaje = false;
  }
}
    
Work in progress Week 11

The video shows the communication between the transmitter and the receiver, where pressing the transmitter button increases the counter and displays it on the receiver OLED. This confirms that the MQTT connection is working correctly and that the data is being transmitted effectively between both devices.

Some Failed Attempts

We encountered some issues when testing the connection with a microservo, since it did not rotate according to the data sent from another computer.

Conclusion

In conclusion, during this week I was able to understand and apply the use of the MQTT protocol to establish communication between two projects within a local network. First, I configured MQTTX to connect to the broker and monitor data in real time; then I verified the WiFi connection of the ESP32-C3 and the publication of messages using code in Arduino IDE. Finally, I confirmed the communication between a transmitter and a receiver, where pressing a button sent data that was then correctly displayed on an OLED screen. This whole process helped me better understand the integration between hardware, software, and IoT communication, as well as the importance of testing and adjustments to ensure stable system performance.




Individual assignment:

  • design, build and connect wired or wireless node(s) with network or bus addresses and a local input and/or output device(s).

Access Point Mode for My ESP32

At this stage, the ESP32-C3 microcontroller was configured in Access Point (AP) mode, allowing it to generate its own WiFi network without needing an external router. Using Arduino IDE, an embedded web server was implemented on the device, which can be accessed from a browser through a local IP address. This approach allows direct communication between the user and the ESP32 within a local wireless network, meeting the requirement of creating connected nodes without depending on the internet.

Work in progress Week 10

Since I only had the RP2040 board package installed, it was necessary to install the ESP32 package and look for the XIAO ESP32 C3.
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

I asked Gemini to generate a test code to start the network and let me connect from my phone to turn on the LED on my board, which is connected to pin D2.

Code: ESP32 Access Point  Fuente: Gemini

#include <WiFi.h>
#include <WebServer.h>

// Configura el nombre de tu red y la contraseña
const char* ssid = "Red_David_Fab";
const char* password = "fabacademy2026";

// Definimos el pin del LED (D2 en el XIAO es GPIO 4)
const int ledPin = 4; 

WebServer server(80);

// Esta función crea la página que verás en tu celular
void handleRoot() {
  String html = "<html><style>body{font-family:sans-serif; text-align:center; padding:50px;}";
  html += "button{padding:20px; font-size:20px; cursor:pointer;}</style>";
  html += "<body><h1>Control PCB</h1>";
  html += "<p><a href='/on'><button>ON LED</button></a></p>";
  html += "<p><a href='/off'><button>OFF LED</button></a></p>";
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleOn() {
  digitalWrite(ledPin, HIGH);
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleOff() {
  digitalWrite(ledPin, LOW);
  server.sendHeader("Location", "/");
  server.send(303);
}

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

  // Configuramos la placa como un Access Point (Emisor de señal)
  WiFi.softAP(ssid, password);

  Serial.println("Red iniciada");
  Serial.print("IP de la placa: ");
  Serial.println(WiFi.softAPIP()); // Esto mostrará 192.168.4.1

  // Definimos qué pasa cuando entramos a cada dirección
  server.on("/", handleRoot);
  server.on("/on", handleOn);
  server.on("/off", handleOff);

  server.begin();
}

void loop() {
  server.handleClient(); // Mantiene el servidor escuchando
}
    
Work in progress Week 11

We verified that the network started correctly and wrote down the IP: 192.168.4.1
Work in progress Week 11

From the phone, we connect to the WiFi network created by the ESP32 and then enter the IP address in our browser to access the control page. By clicking the "ON LED" button, the LED connected to pin D2 on the board turns on, and by clicking "OFF LED" it turns off. This confirms that the communication between the phone and the ESP32 through the Access Point is working correctly, allowing the hardware to be controlled wirelessly.

ESP32 Access Point result 1
ESP32 Access Point result 2

We asked Gemini to modify the web page colors and translate it into Spanish as a test

Code: AP  Edit: David Avila     Original code: Gemini

#include <WiFi.h>
#include <WebServer.h>

// Configura el nombre de tu red y la contraseña
const char* ssid = "Red_David_Fab";
const char* password = "fabacademy2026";

// Definimos el pin del LED (D2 en el XIAO es GPIO 4)
const int ledPin = 4; 

WebServer server(80);

// Esta función crea la página que verás en tu celular
void handleRoot() {
  String html = "<html><head><meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<style>";
  html += "body { font-family: 'Arial'; text-align: center; background-color: #f4f4f4; padding-top: 50px; }";
  html += "h1 { color: #333; }";
  html += ".button { display: inline-block; padding: 15px 25px; font-size: 24px; cursor: pointer; text-align: center; ";
  html += "text-decoration: none; outline: none; color: #fff; border: none; border-radius: 15px; box-shadow: 0 9px #999; margin: 10px; }";
  html += ".btn-on { background-color: #4CAF50; }"; // Verde
  html += ".btn-off { background-color: #f44336; }"; // Rojo
  html += ".button:active { box-shadow: 0 5px #666; transform: translateY(4px); }";
  html += "</style></head><body>";
  
  html += "<h1>Parametri-k Control Panel</h1>";
  html += "<p>Estado del LED en D2</p>";
  html += "<a href='/on' class='button btn-on'>ENCENDER</a>";
  html += "<a href='/off' class='button btn-off'>APAGAR</a>";
  
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleOn() {
  digitalWrite(ledPin, HIGH);
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleOff() {
  digitalWrite(ledPin, LOW);
  server.sendHeader("Location", "/");
  server.send(303);
}

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

  // Configuramos la placa como un Access Point (Emisor de señal)
  WiFi.softAP(ssid, password);

  Serial.println("Red iniciada");
  Serial.print("IP de la placa: ");
  Serial.println(WiFi.softAPIP()); // Esto mostrará 192.168.4.1

  // Definimos qué pasa cuando entramos a cada dirección
  server.on("/", handleRoot);
  server.on("/on", handleOn);
  server.on("/off", handleOff);

  server.begin();
}

void loop() {
  server.handleClient(); // Mantiene el servidor escuchando
}

The video shows that the servo motor moves correctly when the button is pressed, and the LED turns on while the servo motor is moving. This indicates that the code works correctly and that the output device is functioning properly.

Testing with My Input and Output Devices

I asked Gemini to create a web control panel for my LDR module and my microservo, so that the module would be shown in a graph like the serial plotter and the microservo would move to 45, 90, and 180 degrees. This was the result. Work in progress Week 11

Code: MicroServo + Module LDR  Edit: David Avila     Original code: Gemini

#include 
#include 
#include 

// --- Datos de tu Red ---
const char* ssid = "Red_David_Fab";
const char* password = "fabacademy2026";

// --- Pines de tu PCB (XIAO ESP32-C3) ---
const int ledPin = 4;   // D2 
const int ldrPin = 3;   // D1 (GPIO 3)
const int servoPin = 2; // D0 (GPIO 2)

WebServer server(80);
Servo myServo;

// --- Interfaz Web con Auto-Escala ---
void handleRoot() {
  String html = "";
  html += "";

  html += "

HEXAMODULAR PANEL

"; // Gráfico con estilo Osciloscopio html += "

LDR Real-Time Plotter

"; html += ""; html += "

Valor: --

"; html += "

Control LED

"; html += "ENCENDERAPAGAR
"; html += "

Control Servo

"; html += ""; html += ""; html += "
"; // JavaScript Inteligente html += ""; server.send(200, "text/html", html); } void handleLDR() { int lectura = analogRead(ldrPin); server.send(200, "text/plain", String(lectura)); } void setup() { Serial.begin(115200); pinMode(ledPin, OUTPUT); myServo.attach(servoPin); // Modo Access Point WiFi.softAP(ssid, password); Serial.println("--- SISTEMA INICIADO ---"); Serial.print("IP: "); Serial.println(WiFi.softAPIP()); // Rutas server.on("/", handleRoot); server.on("/readLDR", handleLDR); server.on("/on", [](){ digitalWrite(ledPin, HIGH); server.sendHeader("Location", "/"); server.send(303); }); server.on("/off", [](){ digitalWrite(ledPin, LOW); server.sendHeader("Location", "/"); server.send(303); }); server.on("/s45", [](){ myServo.write(45); server.sendHeader("Location", "/"); server.send(303); }); server.on("/s90", [](){ myServo.write(90); server.sendHeader("Location", "/"); server.send(303); }); server.on("/s180", [](){ myServo.write(180); server.sendHeader("Location", "/"); server.send(303); }); server.begin(); } void loop() { server.handleClient(); }

The video shows a control and monitoring panel from my phone connected to my PCB, which I customized thanks to Gemini's code.

Conclusions: