This week’s assignment was to design, build, and connect wired or wireless nodes with network addresses and input/output capabilities. My custom fish PCB uses the XIAO ESP32S3, so adding WiFi networking was straightforward.
1. Turning My Fish Board into a WiFi UDP Node


ESP32S3 WiFi Node Code
#include
#include
const char* ssid = "iPhone";
const char* password = "11111111";
const int BTN_PIN = D0;
const int LED_PIN = LED_BUILTIN;
WiFiUDP udp;
const unsigned int localPort = 1234;
const unsigned int sendPort = 1234;
IPAddress pcIP(172, 20, 10, 3);
int lastState = HIGH;
void setup() {
pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println("Node 1 booting...");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected!");
Serial.println(WiFi.localIP());
udp.begin(localPort);
}
void loop() {
int s = digitalRead(BTN_PIN);
if (s == LOW)
digitalWrite(LED_PIN, HIGH);
else
digitalWrite(LED_PIN, LOW);
if (lastState == HIGH && s == LOW) {
const char* msg = "node 1 button 0";
Serial.println(msg);
udp.beginPacket(pcIP, sendPort);
udp.print(msg);
udp.endPacket();
Serial.println("UDP message sent.");
}
lastState = s;
int packetSize = udp.parsePacket();
if (packetSize > 0) {
char buf[128];
int len = udp.read(buf, sizeof(buf) - 1);
if (len > 0) buf[len] = 0;
Serial.print("Received UDP: ");
Serial.println(buf);
}
}
Python UDP Listener
import socket
PORT = 1234
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
print("Listening UDP on port", PORT)
while True:
data, addr = s.recvfrom(1024)
print("From", addr, "->", data.decode(errors='ignore'))
2. Group Assignment — Two Boards Exchanging Data
Justin’s board uses an HC-SR04 ultrasonic sensor and sends distance values (e.g., DIST:23.4)
to my board via UDP. Both boards connect to my hotspot, forming a shared local network.
My Fish Board Receiver (OLED Display)
#include
#include
#include
#include
#include
#define W 128
#define H 64
const int SDA_PIN = D8;
const int SCL_PIN = D7;
TwoWire I2C(1);
Adafruit_SSD1306 display(W, H, &I2C, -1);
const int BTN[4] = { D0, D1, D2, D3 };
const char* LABEL[4] = { "RIVER", "LAKE", "OCEAN", "???" };
const char* ssid = "iPhone";
const char* password = "11111111";
WiFiUDP udp;
const unsigned int localPort = 1234;
void showText(const char* s) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(s, 0, 0, &x1, &y1, &w, &h);
int x = (W - (int)w) / 2;
int y = (H - (int)h) / 2;
display.setCursor(x, y);
display.println(s);
display.display();
}
void updateDisplayFromDistance(float d) {
if (d < 0)
showText("NO DATA");
else if (d < 10)
showText("TOO CLOSE");
else if (d < 30)
showText("NEAR");
else if (d < 100)
showText("FAR");
else
showText("OUT");
}
void setup() {
Serial.begin(115200);
delay(200);
I2C.begin(SDA_PIN, SCL_PIN, 100000);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
for (int i = 0; i < 4; i++) pinMode(BTN[i], INPUT_PULLUP);
showText("BOOTING");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(300);
showText("READY");
udp.begin(localPort);
}
void loop() {
static uint8_t last[4] = {HIGH, HIGH, HIGH, HIGH};
static unsigned long lastDebounce[4] = {0,0,0,0};
const unsigned long DEBOUNCE_MS = 30;
int packetSize = udp.parsePacket();
if (packetSize > 0) {
char buf[64];
int len = udp.read(buf, sizeof(buf) - 1);
if (len > 0) buf[len] = 0;
if (strncmp(buf, "DIST:", 5) == 0) {
float d = atof(buf + 5);
updateDisplayFromDistance(d);
}
}
}
Justin’s Ultrasonic Sender Node
#include
#include
const char* ssid = "iPhone";
const char* password = "11111111";
const int TRIG_PIN = D4;
const int ECHO_PIN = D5;
WiFiUDP udp;
const unsigned int sendPort = 1234;
IPAddress fishIP(172, 20, 10, 2);
const unsigned long MEASURE_INTERVAL = 500;
unsigned long lastMeasureTime = 0;
float measureDistanceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
if (duration == 0) return -1.0;
float distance = duration / 58.0;
return distance;
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(115200);
delay(1000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("Sense ready");
}
void loop() {
unsigned long now = millis();
if (now - lastMeasureTime >= MEASURE_INTERVAL) {
lastMeasureTime = now;
float d = measureDistanceCm();
char msg[32];
if (d < 0)
snprintf(msg, sizeof(msg), "DIST:-1");
else
snprintf(msg, sizeof(msg), "DIST:%.1f", d);
udp.beginPacket(fishIP, sendPort);
udp.write((const uint8_t*)msg, strlen(msg));
udp.endPacket();
Serial.print("Sent: ");
Serial.println(msg);
}
}