Week 11 Fab Academy 2026 · Lab Rwanda

Networking &
Communications

Wireless ESP-NOW communication between the custom ESP32-S3 board (transmitter) and a Heltec LoRa V3 (receiver) — streaming live potentiometer angle and DHT sensor readings peer-to-peer over 2.4 GHz, addressed by MAC.

TransmitterCustom ESP32-S3
ReceiverHeltec LoRa V3
ProtocolESP-NOW
AddressingMAC Address
Status✓ Link Established
ESP-NOWHeltec V3ESP32-S3MAC AddressPeer-to-PeerTransmitterReceiverPotentiometerDHT Sensor2.4 GHzNo Router ESP-NOWHeltec V3ESP32-S3MAC AddressPeer-to-PeerTransmitterReceiverPotentiometerDHT Sensor2.4 GHzNo Router
Overview

Introduction

Networking week moves the microcontroller beyond its own sensors — making boards talk to each other. For this week I set up a wireless ESP-NOW link between my custom ESP32-S3 board and a Heltec LoRa V3 (which is also ESP32-based). The ESP32-S3 acts as the transmitter, sending the potentiometer angle and DHT temperature/humidity it already measured in Week 09. The Heltec acts as the receiver, decoding the incoming struct and printing the live values to the Serial Monitor.

ESP-NOW is Espressif's connectionless wireless protocol — no router, no IP stack, no pairing handshake. Every device is addressed purely by its factory-burned MAC address. The workflow had three clear milestones: reading the Heltec receiver MAC address to use as the peer address, programming the ESP32-S3 transmitter, then programming the Heltec receiver and watching the data arrive.

Transmitter
ESP32-S3
Custom Board
Potentiometer → Angle DHT → Temp + Humidity
ESP-NOW · 2.4 GHz
Receiver
Heltec LoRa V3
ESP32-S3 inside
Decode struct Serial Monitor output
This Week

Assignments

Group Assignment
Send a message between two projects.

As a group we demonstrated a message passing between two student boards over different protocols such as SPI and I2C — verifying data arrived correctly.

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

Individually, I used the custom ESP32-S3 board as a wireless sensor transmitter node and the Heltec LoRa V3 as the receiver node — each uniquely identified by its hardware MAC address on the ESP-NOW network.

Concepts

Why ESP-NOW?

ESP-NOW is a connectionless protocol by Espressif that runs on the ESP32's built-in 2.4 GHz Wi-Fi radio — without needing a router, access point, or IP address. Devices address each other directly by their 48-bit hardware MAC address.

Connectionless & Fast
No pairing, no handshake, no DHCP. The transmitter registers the receiver's MAC as a peer and sends immediately. Latency is under 10 ms — far faster than MQTT or HTTP over Wi-Fi, and works with no network infrastructure at all.
🏷️
MAC Addressing
Every ESP32 has a unique 48-bit MAC address burned into eFuse at the factory. The receiver's MAC is read first and hardcoded into the transmitter as the peer address — this hardware address is the network identity for this assignment.
📦
Struct Payload
ESP-NOW sends raw bytes up to 250 bytes per packet. A shared C struct is defined identically on both devices — potentiometer angle, temperature, humidity — so the receiver can cast the received bytes straight back into readable values.
Process

Step-by-Step

Both sensor inputs were already working from Week 09. This week was purely about firmware — reading the receiver MAC, programming the transmitter with that address, then programming the receiver and closing the wireless link.

Phase 01
Read the Heltec Receiver MAC Address Node identity · Peer address · Serial output
Step 01
Install Heltec Board Support in Arduino IDE
Added the Heltec ESP32 board package via the Arduino IDE Board Manager. Searched for Heltec ESP32 Series Dev-boards and installed it. This gives access to the correct board target for the Heltec LoRa V3 and ensures the right pin definitions load for the device.
Arduino IDEBoard ManagerHeltec ESP32
🖼️
Add image — Board Manager with Heltec package installed
Step 02
Upload MAC Reader Sketch to Heltec
Uploaded a minimal sketch to the Heltec that reads its Wi-Fi MAC address using WiFi.macAddress() and prints it to Serial. This value is essential — it must be hardcoded byte-by-byte into the transmitter as the ESP-NOW peer address. Getting it right before writing any radio code avoids a silent link failure later.
WiFi.macAddress()WIFI_STA modeSerial Monitor
#include <WiFi.h> void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); // required to read the MAC Serial.print("Receiver MAC Address: "); Serial.println(WiFi.macAddress()); } void loop() {}

Result: The Serial Monitor printed the Heltec MAC in the format XX:XX:XX:XX:XX:XX. This value was noted and converted to the byte array used in the transmitter's receiverMAC[].

🖼️
Add image — Serial Monitor showing Heltec MAC address
Phase 02
Program the ESP32-S3 Transmitter ESP-NOW TX · Sensor struct · Send callback
Step 03
Define the Shared Data Struct
Defined a C struct struct_message with three fields — potentiometer angle (int), DHT temperature (float), and DHT humidity (float). The identical struct must be declared in both the transmitter and receiver sketches so both sides interpret the raw bytes the same way.
structtypedefshared layout
// Shared struct — identical on TX and RX sides typedef struct struct_message { int angle; // potentiometer degrees 0–180 float temperature; // DHT temperature °C float humidity; // DHT relative humidity % } struct_message; struct_message myData;
Step 04
Write the Full Transmitter Firmware
The transmitter initialises ESP-NOW, registers the Heltec MAC as a peer, then every 2 seconds reads the potentiometer and DHT, fills the struct, and calls esp_now_send(). A send callback reports whether each packet was acknowledged or dropped.
esp_now_init()esp_now_add_peer()esp_now_send()OnDataSent
// ESP32-S3 Custom Board — ESP-NOW Transmitter // Payload: potentiometer angle + DHT temp & humidity #include <esp_now.h> #include <WiFi.h> #include <DHT.h> #define POT_PIN 35 // potentiometer wiper ADC pin #define DHT_PIN 14 // DHT data pin #define DHT_TYPE DHT11 // *** Replace with your Heltec receiver MAC bytes *** uint8_t receiverMAC[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; typedef struct struct_message { int angle; float temperature; float humidity; } struct_message; struct_message myData; DHT dht(DHT_PIN, DHT_TYPE); void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("Delivery: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL"); } void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); Serial.print("TX MAC: "); Serial.println(WiFi.macAddress()); if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed"); return; } esp_now_register_send_cb(OnDataSent); esp_now_peer_info_t peerInfo = {}; memcpy(peerInfo.peer_addr, receiverMAC, 6); peerInfo.channel = 0; peerInfo.encrypt = false; if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println("Failed to add peer"); return; } dht.begin(); Serial.println("ESP-NOW TX ready"); } void loop() { int raw = analogRead(POT_PIN); myData.angle = map(raw, 0, 4095, 0, 180); myData.temperature = dht.readTemperature(); myData.humidity = dht.readHumidity(); Serial.print("TX → Angle: "); Serial.print(myData.angle); Serial.print("° Temp: "); Serial.print(myData.temperature); Serial.print("°C Hum: "); Serial.print(myData.humidity); Serial.println("%"); esp_now_send(receiverMAC, (uint8_t *)&myData, sizeof(myData)); delay(2000); }

Critical: Replace the receiverMAC[] bytes with the exact values read from the Heltec in Step 02. An incorrect peer address causes packets to transmit without any receiver ever hearing them — no error is thrown.

🖼️
Add image — ESP32-S3 Serial Monitor showing TX packets every 2 s
Phase 03
Program the Heltec Receiver & View Results ESP-NOW RX · OnDataRecv callback · Live display
Step 05
Write the Heltec Receiver Firmware
The receiver sketch initialises ESP-NOW and registers an OnDataRecv callback. Whenever a packet arrives, the callback casts the raw byte pointer to struct_message and prints all three sensor values along with the sender's MAC address to the Serial Monitor.
esp_now_register_recv_cb()OnDataRecvmemcpystruct cast
// Heltec LoRa V3 — ESP-NOW Receiver // Receives sensor struct from ESP32-S3 custom board #include <esp_now.h> #include <WiFi.h> typedef struct struct_message { int angle; float temperature; float humidity; } struct_message; struct_message incomingData; void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) { memcpy(&incomingData, data, sizeof(incomingData)); Serial.println("--- Packet received ---"); Serial.print(" From : "); for (int i = 0; i < 6; i++) { Serial.printf("%02X", mac[i]); if (i < 5) Serial.print(":"); } Serial.println(); Serial.print(" Angle : "); Serial.print(incomingData.angle); Serial.println(" deg"); Serial.print(" Temp : "); Serial.print(incomingData.temperature); Serial.println(" C"); Serial.print(" Hum : "); Serial.print(incomingData.humidity); Serial.println(" %"); Serial.println(); } void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); Serial.print("RX MAC: "); Serial.println(WiFi.macAddress()); if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed"); return; } esp_now_register_recv_cb(OnDataRecv); Serial.println("ESP-NOW RX listening..."); } void loop() { // All work happens inside the OnDataRecv callback }
Step 06
Upload to Heltec & Open Serial Monitor
Uploaded the receiver sketch to the Heltec LoRa V3, selected its COM port, and opened the Serial Monitor at 115200 baud. With both boards powered, the Heltec immediately began printing incoming packets every ~2 seconds — angle, temperature, and humidity — from the ESP32-S3, along with the sender MAC address confirming the source.
Heltec V3Serial Monitor115200 baudLive packets

Link established: The Heltec Serial Monitor displayed correctly structured packets arriving every ~2 seconds with accurate angle, temperature, and humidity values — and the sender MAC confirming data was genuinely received over the air from the ESP32-S3 custom board.

🖼️
Add image — Heltec Serial Monitor showing received packets
🖼️
Add image — Both boards powered side by side
Step 07
Verify Live Data End-to-End
Rotated the potentiometer on the ESP32-S3 and watched the angle value update live on the Heltec Serial Monitor — confirming full end-to-end data flow over ESP-NOW. Breathed lightly on the DHT sensor to see the humidity value rise on the receiver side, verifying both sensor readings travel correctly through the wireless link with no wires between the two boards.
Live angle updateDHT over airEnd-to-end verified
🖼️
Add image — Heltec Serial Monitor with data changing as pot rotates
Results

Link Summary

Protocol
ESP-NOW
Frequency
2.4GHz
Addressing
48-bitMAC
Max payload
250bytes
TX interval
2s
Payload fields
3values
Router needed
✗ None
Link status
✓ Active
TX ESP32-S3 Custom Board
RoleTransmitter
Input 1Potentiometer (angle)
Input 2DHT11 (T + RH)
Send functionesp_now_send()
Node IDMAC (factory)
RX Heltec LoRa V3
RoleReceiver
OutputSerial Monitor
CallbackOnDataRecv()
Decodememcpy → struct
Node IDMAC (factory)
Takeaways

Conclusion

This week connected everything from the previous weeks — the custom board, the sensors, and the firmware — into a real networked system. The ESP32-S3 stopped being a standalone sensor reader and became a wireless node transmitting live data over the air to a second device. That shift from local to networked is one of the most important transitions in embedded systems design.

Reading the Heltec MAC address first was a necessary first step: ESP-NOW is a purely MAC-address-based protocol — there is no discovery, no IP, no DNS. You must know the receiver's exact 6-byte address before writing a single line of transmitter code. It makes the concept of a network address very concrete and tangible.

ESP-NOW proved to be an excellent match for this kind of local sensor telemetry — no router dependency, minimal setup overhead, and the struct-based payload keeps both sides of the code clean and symmetric. The OnDataSent and OnDataRecv callbacks give immediate confirmation of delivery without any extra test equipment.

ESP-NOW ESP32-S3 Heltec LoRa V3 MAC Address Peer-to-Peer Struct Payload Sensor Telemetry No Router Arduino IDE Serial Monitor
← Week 09 · Input Devices All Assignments →