Week 11 - Networking and communications

Group assignment for this week is on the Chaihuo Week 11 group assignment page. The rest of this page is my individual work.

Individual assignment

My individual network is a wearable-to-host link for Brain Fog Insight Companion: MAX30102 on the band, Beetle over I2C, then BLE to a C3 bridge, then UART to the WROOM host.

I proved the packet format first with the WROOM acting as a direct BLE client. When the host also had to stay on WiFi for cloud voice calls, BLE and WiFi on the same chip made the UI stall. I moved BLE reception to a XIAO ESP32-C3 and kept the WROOM on UART only.

Assessment itemWhere it is covered
Demonstrate workflows used in network design Network diagram, design workflow list, node split (wearable / C3 / host), and the C3 bridge decision after WiFi load appeared.
Implement and interpret networking / communication protocols I2C sensor bus, BLE advertisement + GATT notify, UART forwarding, JSON packet format, and BAND,LINK,* status lines. Group I2C capture interpretation is under group assignment.
Connect nodes with addresses and local I/O Three nodes in the node map; each has a fixed name, UUID, I2C address, or UART pin pair.
Board of my own design WROOM host PCB documented in Week 16.
Hero shot, source code, problems and fixes Hardware photos and live-test video; BrainFog_PIO firmware downloads; problems table below.

Network design workflow

  1. Name the nodes and what each one is allowed to do.
  2. Wire local I/O on each node before testing the radio link.
  3. Use BLE between band and bridge because the band moves.
  4. Lock addresses early: device name, service UUID, characteristic UUID, I2C 0x57.
  5. Prove sensor read, then BLE packet format, then move BLE off WROOM when WiFi voice goes live.

Wearable wiring and BLE transmit path

The bracelet is a Beetle ESP32-C6 plus a MAX30102 module. The sensor talks to the Beetle on I2C; the Beetle sends readings out on BLE.

MAX30102 signalBeetle connection
SDAGPIO6
SCLGPIO7
VCC3V3
GNDGND
I2C address0x57

In firmware (BrainFog_PIO/beetle) the Beetle starts I2C on GPIO6/7, calls MAX30102.getHeartbeatSPO2() about once per second, and builds a JSON line with seq, hr, spo2, and temp. That line leaves the band in two BLE forms:

The band advertises as BrainFog_Beetle. GPIO15 blinks a status LED while I check the link.

Why I added the C3 bridge

The first version had WROOM scan for BrainFog_Beetle and parse notify packets itself. That was enough for Week 11. Later the same board also ran WiFi for cloud voice. BLE scan plus WiFi on one ESP32 made the host freeze during calls.

C3 now owns BLE: scan, filter by name, parse JSON, forward lines on UART at 115200 baud. WROOM only reads Serial1 and runs the screen plus cloud voice. Wiring matches BrainFog_PIO/c3_ble_bridge and Week 16: C3 D6 / GPIO21 TX to WROOM GPIO39 RX, C3 D7 / GPIO20 RX to WROOM GPIO38 TX.

Network diagram

Network diagram showing wearable BLE transmitter, C3 bridge, and WROOM host receiver
Three-node network: MAX30102 over I2C to Beetle, BLE to C3 bridge, UART to WROOM host.

Node map

NodeBoardLocal I/ORole in the network
Wearable transmitter DFRobot Beetle ESP32-C6 MAX30102 on I2C, status LED on GPIO15 Reads MAX30102 over I2C, then transmits HR / SpO2 / temperature over BLE advertisement and GATT notify.
C3 BLE bridge XIAO ESP32-C3 UART TX/RX to host, optional USB debug serial Receives wearable BLE, parses JSON, forwards lines to WROOM over UART.
Host receiver ESP32-WROOM on my custom host PCB Screen UI, USB serial debug, WiFi / cloud voice path Reads UART lines from C3 and updates the host application. No direct BLE workload in the final setup.

Addressing and protocol

Address / IDValueWhy it matters
BLE device nameBrainFog_BeetleThe C3 bridge only accepts packets from this advertised name.
Service UUID12345678-1234-1234-1234-1234567890abDefines the GATT service on the wearable.
Characteristic UUID12345678-1234-1234-1234-1234567890acCarries the JSON notify packets.
MAX30102 I2C address0x57Local sensor bus address on the wearable node.
C3 to WROOM UART115200 baud, C3 D6 / GPIO21 TX to GPIO39 RX, C3 D7 / GPIO20 RX to GPIO38 TXWired hop between bridge and host.
Advertisement payload0xFF 0xFF 'B' 'F' + seq + hr + spo2 + tempLets C3 parse wearable data from manufacturer data.
Packet format{"seq":N,"hr":72,"spo2":98,"temp":32.5}Parsed on the bridge and forwarded to the host.
Link status lineBAND,LINK,UP / BAND,LINK,DOWNTells the host whether the wearable link is currently alive.
{"seq":12,"hr":72,"spo2":98,"temp":32.5}

On the C3 bridge I read manufacturer bytes after the BF marker as seq (16-bit), hr, spo2, and temp (deci-degrees). When GATT is connected, the same fields arrive as JSON. The host side prints parsed numbers like SEQ=12 HR=72 SPO2=98 TEMP=32.5. Wrong device name or UUID means no connect; a dead sensor bus still sends JSON with zeros.

Hero shot and live test

Wearable band and desktop host hardware setup
Hero shot of the wearable-to-host communication setup used for the individual assignment.
Finished wearable bracelet product
Finished wearable bracelet product used as the transmitter node in the network.
Host receiver on breadboard during testing
Host receiver side during the breadboard testing phase, before the full desktop enclosure was integrated.
Wearable BLE live test. The band sends sensor data while the host receives and prints parsed values.

Board I designed and made

The host receiver runs on my own final-project host PCB, not on a bare commercial dev board alone. The board design, pin map, and assembly are documented in Week 16. For Week 11 I used that host PCB as the receiving node in this wearable network.

Week 16 host KiCad source

Firmware

Network firmware lives in PlatformIO project BrainFog_PIO. Downloads below are copies of the files I flash on hardware.

PlatformIO folderBoardNetwork job
beetle/ DFRobot Beetle ESP32-C6 MAX30102 I2C read, BLE advertise + GATT notify JSON transmit.
c3_ble_bridge/ XIAO ESP32-C3 Scan/connect to BrainFog_Beetle, parse packets, forward JSON and BAND,LINK,* over UART.
wroom/ ESP32-WROOM host PCB Passive UART consumer on Serial1; screen UI and WiFi/cloud voice run here without direct BLE.

Bring-up started with a direct WROOM BLE receiver sketch. The shipped project uses the three-folder split because BLE plus WiFi on one WROOM stalled during voice calls.

Download beetle main.cpp Download C3 bridge main.cpp Download early WROOM BLE RX test

Problems and fixes

ProblemWhat I changed
Host could scan many BLE devices but connected to the wrong one Filtered on the fixed name BrainFog_Beetle before attempting connection.
Connection dropped when the band moved away Added rescan logic on disconnect.
Wearable reboot loop on battery power during BLE startup Added bulk capacitance near the Beetle power input before retesting the link.
HR / SpO2 looked valid on the wearable serial log but not on the host Checked service and characteristic UUIDs on both sides; the notify callback had to match the TX characteristic.
WROOM stalled when BLE and WiFi/cloud voice ran together Moved BLE scan and parsing to the XIAO ESP32-C3 bridge and sent cleaned UART lines to WROOM.