week11. Networking and Communications
Assignment
Group assignment
- send a message between two projects
Individual assignment
- design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s)
Group assignment
For more information about group projects, please see the group project page on the FabLab Kannai website. Group assignment page is here
🌐 Networked Communication Between Microcontroller and Web Server
This assignment explores the implementation of a wireless network node using XIAO ESP32-C3 that communicates with a remote server and displays data in a browser via real-time web protocols.
Building on the foundation from the week4 assignment page, the microcontroller is now mounted on the custom development board created in week8, and functions as part of a networked system that retrieves data from an external API and transmits it to a browser via a server pipeline.
This application was inspired by Fab Academy 2021's Nadieh, and showcases real-time communication between an embedded device and web client.
1. Node Design and Network Integration
The XIAO ESP32-C3 functions as a wireless network node by connecting to a Wi-Fi network and obtaining an IP address.
It acts as a client that fetches data (a joke) from a public API over HTTP.
Communication is established through a multi-stage data pipeline:
ESP32-C3 → Wi-Fi (HTTP) → Serial → Node.js → Socket.IO → Browser
┌──────────────────────┐
│ Joke API (Cloud) │
│ https://official... │
└────────────┬─────────┘
│ HTTP GET
▼
┌──────────────────────┐
│ ESP32-C3 (Wi-Fi) │
│ - Retrieves Joke │
│ - Outputs via UART │
└────────────┬─────────┘
│ Serial (USB)
▼
┌──────────────────────┐
│ Node.js Server │
│ - Reads serial input │
│ - Emits via Socket.IO │
└────────────┬─────────┘
│ WebSocket
▼
┌──────────────────────┐
│ Web Browser (UI) │
│ - Displays joke │
└──────────────────────┘
2. Data Transmission Behavior
- The microcontroller sends an HTTP request every 15 seconds to obtain new data from an external API.
- The received data is sent through its serial interface to a Node.js server.
- The server then emits the message to connected clients using WebSocket-style communication (via Socket.IO).
JavaScript and Socket Communication
JavaScript is used on the client side to receive real-time updates from the Node.js server and update the browser view without refreshing the page. This showcases a dynamic web communication model.
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript
Node.js and WebSocket Pipeline
Node.js enables server-side event-driven programming. In this project, it acts as the intermediary that receives data from the microcontroller (via serial) and relays it to the browser using socket.io
.
Reference:
https://nodejs.org/en/
Server Setup (macOS)
brew install node
npm install
node index.js
🧩 System Structure
realtime-communication/
├── node_modules/
├── public/
│ └── index.html # Web interface
├── index.js # Node.js server (receives & emits data)
├── package.json
├── package-lock.json
🖥 Node.js server (index.js)
This server listens on a serial port for incoming data from the microcontroller and broadcasts it to all connected browser clients.
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const { SerialPort } = require("serialport");
const { ReadlineParser } = require("@serialport/parser-readline");
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
app.use(express.static("public"));
async function findAndConnectSerialPort() {
try {
const ports = await SerialPort.list();
const targetPort = ports.find((port) => {
return (
port.path.includes("usbmodem") || // macOS
port.path.includes("usbserial") // Windows/Linux fallback
);
});
if (!targetPort) {
console.error("No compatible serial port found.");
return;
}
console.log(`✅ Found serial port: ${targetPort.path}`);
const port = new SerialPort({ path: targetPort.path, baudRate: 9600 });
const parser = port.pipe(new ReadlineParser({ delimiter: "\n" }));
parser.on("data", (data) => {
const trimmed = data.trim();
console.log("Received:", trimmed);
io.emit("joke", trimmed);
});
port.on("error", (err) => {
console.error("SerialPort Error:", err.message);
});
} catch (err) {
console.error("Failed to list serial ports:", err);
}
}
server.listen(3000, () => {
console.log("Server running at http://localhost:3000");
findAndConnectSerialPort();
});
🌐 Browser Client (index.html)
This file implements the real-time front-end logic that receives the joke from the server and updates the UI.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Joke Viewer</title>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<style>
body {
font-family: sans-serif;
background-color: #CEE6C1;
text-align: center;
padding: 50px;
}
h1 {
font-size: 2rem;
}
#joke {
font-size: 1.5rem;
margin-top: 30px;
color: #333;
padding: 20px;
border: 2px solid #eee;
background: #fff;
display: inline-block;
max-width: 600px;
white-space: pre-wrap;
word-wrap: break-word;
text-align: left;
}
</style>
</head>
<body>
<h1>🤣 Live Joke Display</h1>
<div id="joke">Waiting for joke...</div>
<script>
const socket = io();
socket.on("joke", (data) => {
document.getElementById("joke").textContent = data;
});
</script>
</body>
</html>
📡 ESP32-C3 Code: Wireless Node Behavior
This microcontroller program:
- connects to Wi-Fi
- accesses a cloud API every 15 seconds
- and relays the fetched content to a connected computer over the serial line.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// Wi-Fi Configuration
const char* ssid = "*******";
const char* password = "*******";
// Joke API
const String url = "https://official-joke-api.appspot.com/random_joke";
// Joke acquisition interval (milliseconds)
const unsigned long interval = 15000; // 15 sec.
unsigned long lastFetchTime = 0;
void setup() {
Serial.begin(115200);
Serial.println("Booting...");
Serial.println("Connecting to WiFi...");
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());
// Get the first joke right away
lastFetchTime = millis() - interval;
}
void loop() {
unsigned long currentTime = millis();
if (currentTime - lastFetchTime >= interval) {
lastFetchTime = currentTime;
String joke = getJoke();
Serial.println(joke);
}
}
String getJoke() {
Serial.println("Fetching joke...");
HTTPClient http;
http.useHTTP10(true);
http.begin(url);
int httpCode = http.GET();
if (httpCode <= 0) {
Serial.print("HTTP error: ");
Serial.println(httpCode);
http.end();
return "<HTTP error>";
}
String result = http.getString();
http.end();
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, result);
if (error) {
Serial.print("JSON parse error: ");
Serial.println(error.c_str());
return "<parse error>";
}
String setup = doc["setup"].as<String>();
String punchline = doc["punchline"].as<String>();
return setup + " / " + punchline;
}
Communication Flow Demonstration
The following video shows the ESP32-C3 functioning as a wireless communication node: retrieving data, transmitting it through a Node.js server, and displaying it in the browser in real time.
Afterthoughts
- This week's focus on networking and communication allowed me to build a full pipeline from microcontroller to web client.
- While it began as an output device experiment, it evolved into a system with layered data transmission, protocol bridging, and network awareness.
- The ESP32-C3 served as a capable networked node, showcasing how microcontrollers can participate in modern data systems.