Week 11 — Networking and communications
This week’s topic: Networking and communications.
Individual assignment
Your personal work for this week — notes, photos, design files, and reflections.
Group assignment
This group documentation focuses on direct communication between two nodes for the Week 11 topic Networking and communications.
Assignment brief
- Verify direct wired or wireless communication between two nodes.
- Document the setup, observed behavior, and communication signals.
Overview
Our group focused on a practical two-node communication test using two XIAO ESP32C3 boards. We implemented a BLE server-client pair, used OLED screens to show connection status, and captured I2C communication between a XIAO ESP32C3 and an OLED display with a logic analyzer.
This workflow let us document not just a successful connection, but also scanning, reconnect behavior, and the signal-level data behind the user-facing display updates.
For me, this group exercise was useful because it turned communication from an abstract topic into something visible. I understood more clearly that connection logic, retry behavior, and signal observation all matter at the same time when building a real multi-node system.
Experiment goals
- Build BLE communication between two XIAO ESP32C3 nodes.
- Use OLED displays to visualize connection, disconnection, and reconnection states.
- Observe I2C signal behavior during OLED updates and interpret the captured data.
Hardware used
- XIAO ESP32C3 × 2
- OLED display modules (I2C, address
0x3C) - IPEX antenna for BLE distance comparison
- Logic analyzer for I2C capture
- Breadboard and jumper wires
Key principles
- BLE roles: the server advertises a service, while the client scans and connects.
- I2C bus:
SDAcarries data andSCLcarries clock; ACK/NACK confirms transfer state. - Why two nodes matter: compared with a single-board demo, a dual-node test reveals real issues such as connection management, timeout, reconnection, and signal loss.
Communication notes
Week 11 covers both wired and wireless communication. In embedded systems, this matters because splitting a system into communicating modules can reduce signal interference and make each part easier to develop, test, and replace.
- UART is asynchronous serial communication with no shared clock.
- I2C is synchronous, uses
SDAandSCL, and addresses multiple devices on one bus. - SPI is synchronous, full duplex, and uses separate lines for data in, data out, clock, and chip select.
- BLE is a low-power wireless method suitable for short-range node-to-node communication.
For this assignment, the practical focus was on BLE as the main wireless channel and I2C as the wired interface used by the OLED display connected to the XIAO ESP32C3.
BLE two-node test
The server continuously advertised a fixed BLE service UUID. The client
scanned for the target device name thexiao, locked onto the
device, and initiated connection when found. OLED screens were used
to show states such as scanning, connected, and retrying after disconnect.
During the distance test, we also observed that the connection could drop beyond a certain range and then recover after the client resumed scanning and reconnected.
BLE implementation notes
Each node consisted of a XIAO ESP32C3 plus an OLED display. One node
acted as the BLE server and advertised its presence. The other acted
as the client, scanning nearby devices and connecting to the server
when the device name thexiao was found.
Because the XIAO ESP32C3 code uses BLE library objects extensively,
pointer syntax appears often in the sketch. For example,
BLEServer* pServer; means pServer stores the
memory address of a BLE server object, and -> is used
to access methods on that object.
BLEServer* pServer;
pServer = BLEDevice::createServer();
pServer->createService(SERVICE_UUID);
Server code excerpt
The server waits for a client connection, updates the OLED through BLE callbacks, and restarts advertising after disconnect.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
#define PIN_SDA 6
#define PIN_SCL 7
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define DEVICE_NAME "thexiao"
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BLEServer* pServer;
BLECharacteristic* pCharacteristic;
bool deviceConnected = false;
bool lastState = false;
void oledShow(const char* a, const char* b, const char* c, const char* d) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(a);
display.println(b);
display.println(c);
display.println(d);
display.display();
}
class ServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* server) override {
deviceConnected = true;
}
void onDisconnect(BLEServer* server) override {
deviceConnected = false;
delay(100);
pServer->getAdvertising()->start();
}
};
Client code excerpt
The client continuously scans for the target server, connects when found, reads the characteristic value, and restarts scanning after disconnect.
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <BLEClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define DEVICE_NAME_TARGET "thexiao"
BLEScan *pBLEScan = nullptr;
BLEClient *pClient = nullptr;
volatile bool wantConnect = false;
volatile bool connected = false;
BLEAddress *pTargetAddr = nullptr;
class ScanCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (connected || wantConnect) return;
if (!advertisedDevice.haveName()) return;
if (advertisedDevice.getName() != DEVICE_NAME_TARGET) return;
if (pBLEScan) pBLEScan->stop();
pTargetAddr = new BLEAddress(advertisedDevice.getAddress());
wantConnect = true;
}
};
void loop() {
if (wantConnect && !connected && pTargetAddr != nullptr) {
if (pClient->connect(*pTargetAddr)) {
wantConnect = false;
}
}
delay(50);
}
The full sketches also included OLED status updates, reconnect logic, and characteristic reads, which made the experiment easier to debug in real time.
I2C signal observation
We used a logic analyzer to inspect the I2C traffic between the XIAO ESP32C3
and the OLED display while the client device was running. In the captured data,
the OLED address 0x3C and subsequent write bytes could be identified.
Although there was some noise in the capture, the main packets and ACK responses were still recognizable, which confirmed that the communication path was functioning.
I also learned that reading analyzer data requires patience rather than expecting a perfectly clean screenshot. Even a slightly noisy capture can still teach me how to identify the expected address and judge whether the bus is basically working.
I2C packet interpretation
Since the BLE client kept scanning and updating the OLED, the logic analyzer captured frequent I2C activity. Two packets were identified during inspection.
| Packet | Observed bytes | Interpretation |
|---|---|---|
| Packet 1 | 0x3C WR, 0x40, 0x00, 0x26, 0x49, 0x49 |
Expected OLED write transaction with valid display data bytes. |
| Packet 2 | 0x23 WR, 0x80, 0x0F, 0xC1, 0x43, 0x45 |
Likely noise or an unintended decode, because that device was not part of the setup. |
The first packet matched the intended OLED address 0x3C
and behaved like a normal write transaction. Even though the second
operation appeared noisy, the expected first packet and visible ACK
responses indicated that the I2C communication itself was still working.
Binary to hexadecimal example
To interpret logic-analyzer output more confidently, we also reviewed a basic binary-to-hexadecimal conversion example:
Binary: 01001001
Split into 4-bit groups: 0100 1001
0100 = 4
1001 = 9
Hexadecimal = 0x49
This helped explain how bytes such as 0x49 appear in the
OLED data stream and how they map back to binary bit patterns.
Topology reference diagrams
Code summary
The server code initialized BLE advertising, created a service and characteristic, and updated the OLED when the connection state changed. The client code continuously scanned for the named device, connected when it was found, attempted recovery after disconnect, and displayed status on the OLED in real time.
This combination gave the group a complete communication loop: device discovery, connection, status feedback, disconnect handling, and signal-level inspection.
Result summary
- The group successfully demonstrated BLE communication between two XIAO ESP32C3 nodes.
- OLED feedback made the connection lifecycle easier to debug and present.
- I2C capture confirmed that OLED communication was active and interpretable.
- The experiment provided a useful foundation for future multi-device project integration.
- The added code review and packet interpretation made the documentation closer to an engineering log than a simple photo report.