Week 11 — Networking & Communications
Group assignment
- Send a message between two projects.
- Document your work on the group work page and reflect on your individual page about what you learned.
Individual assignment
- Design, build, and connect wired or wireless node(s) with network or bus addresses and a local input and/or output device(s).
Learning outcomes
- Demonstrate workflows used in network design.
- Implement and interpret networking protocols and/or communication protocols.
Checklist
- Linked to the group assignment page.
- Documented the project and what I learned from implementing networking and/or communication protocols.
- Explained the programming processes I used.
- Ensured and documented that addressing for the boards works.
- Outlined problems and how I fixed them.
- Included design files or original source code.
- Included a hero shot of the networking and communication setup.
Documentation
For this week's assignment, we need to establish communication between two devices without using an external router, which means direct communication.
So far, I have two design boards based on the ESP32 C3, and one Arduino Uno. I will try to connect these boards using several protocols:
1. UART
2. I2C
3. Bluetooth LE
4. WIFI
5. ESP-Now
First four protocols I will try just basic packages and ESP-Now. I want to explore it a little bit deeper.


1) UART
Use this as a one-way test: ESP32-C3 sends text, Uno receives and prints it.
For more details about the XIAO ESP32C3 board interfaces, including UART and I2C, I referred to the official Seeed Studio wiki: XIAO ESP32C3 Getting Started.
Wiring
- XIAO D6 (TX) → Uno D10 (SoftwareSerial RX)
- XIAO GND → Uno GND
- Optional for future two-way: Uno D11 (SoftwareSerial TX) → XIAO D7 (RX) through proper level-safe wiring

Before moving to more advanced communication tests, I first wanted to make a very simple check that UART and I2C communication between two boards was working correctly. For this step, I asked AI to generate basic test code for a XIAO ESP32-C3 and an Arduino Uno.
AI request:
write a code for testing two boards, ESP32, XIAO C3, and Arduino Uno, which can get connected by UART and I2C.
Sender: XIAO ESP32-C3
#include <Arduino.h>
#include <HardwareSerial.h>
HardwareSerial MySerial(1);
void setup() {
Serial.begin(115200); // USB Serial Monitor
MySerial.begin(9600, SERIAL_8N1, D7, D6); // RX, TX on XIAO C3
Serial.println("ESP32-C3 UART sender started");
}
void loop() {
static int counter = 0;
String msg = "UART hello " + String(counter++);
MySerial.println(msg);
Serial.println("Sent: " + msg);
delay(1000);
}
Receiver: Arduino Uno
#include <SoftwareSerial.h>
SoftwareSerial espSerial(10, 11); // RX, TX
void setup() {
Serial.begin(115200); // Serial Monitor
espSerial.begin(9600); // UART from ESP32-C3
Serial.println("UNO UART receiver started");
}
void loop() {
if (espSerial.available()) {
String msg = espSerial.readStringUntil('\n');
Serial.print("Received: ");
Serial.println(msg);
}
}
As a result, we can see the exchange of messages in the serial monitor from sender to receiver using the UART protocol.

2) I2C
Use this as a one-way test: ESP32-C3 is controller/master, Uno is target/slave.
For more details about the XIAO ESP32C3 board interfaces, including UART and I2C, I referred to the official Seeed Studio wiki: XIAO ESP32C3 Getting Started.
Wiring
- XIAO D4 (SDA) → Uno A4 (SDA)
- XIAO D5 (SCL) → Uno A5 (SCL)
- XIAO GND → Uno GND

Sender: XIAO ESP32-C3
#include <Arduino.h>
#include <Wire.h>
#define SDA_PIN D4
#define SCL_PIN D5
#define UNO_ADDR 0x08
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("ESP32-C3 I2C sender started");
}
void loop() {
static uint8_t counter = 0;
Wire.beginTransmission(UNO_ADDR);
Wire.write(counter);
byte err = Wire.endTransmission();
Serial.print("Sent byte: ");
Serial.print(counter);
Serial.print(" I2C status: ");
Serial.println(err);
counter++;
delay(1000);
}
Receiver: Arduino Uno
#include <Wire.h>
volatile int lastByte = -1;
volatile bool newData = false;
void receiveEvent(int howMany) {
while (Wire.available()) {
lastByte = Wire.read();
newData = true;
}
}
void setup() {
Serial.begin(115200);
Wire.begin(0x08); // I2C slave address
Wire.onReceive(receiveEvent);
Serial.println("UNO I2C receiver started");
}
void loop() {
if (newData) {
noInterrupts();
int value = lastByte;
newData = false;
interrupts();
Serial.print("Received byte: ");
Serial.println(value);
}
}
The same result here is a successful exchange of packages from sender to receiver using the I2C protocol.

3) Bluetooth Low Energy
Simplest BLE arrangement:
For more details about Bluetooth usage on the XIAO ESP32C3, I referred to the official Seeed Studio wiki: XIAO ESP32C3 Bluetooth Usage.
- Sender C3 = BLE server/peripheral
- Receiver C3 = BLE client/central
- Sender updates a characteristic once per second
- Receiver prints notifications
What to do:
- Upload the server sketch to one XIAO ESP32-C3 first
- Open Serial Monitor and leave it running
- Upload the client sketch to the other XIAO ESP32-C3
- No wires between the two boards
After exploring documentation for Bluetooth Low Energy and Wi-Fi UDP packet transfer, I decided to compose the prompt for AI on how to build similar code.
Ai request:
Based on these documentation pages for XIAO ESP32C3 for Bluetooth Low Energy and Wi-Fi:
https://wiki.seeedstudio.com/XIAO_ESP32C3_Bluetooth_Usage/
https://wiki.seeedstudio.com/XIAO_ESP32C3_WiFi_Usage/
Compose a simple code for me: how can I change packages, and how can I test this protocol between two ESP32C3 devices
Sender: ESP32-C3 BLE server
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "12345678-1234-1234-1234-1234567890ab"
#define CHARACTERISTIC_UUID "abcdefab-1234-1234-1234-abcdefabcdef"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) override {
deviceConnected = true;
}
void onDisconnect(BLEServer* pServer) override {
deviceConnected = false;
BLEDevice::startAdvertising();
}
};
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE server...");
BLEDevice::init("Yaro-C3-BLE-Server");
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->addDescriptor(new BLE2902());
pCharacteristic->setValue("BLE hello 0");
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
Serial.println("BLE server ready, advertising...");
}
void loop() {
static int counter = 0;
String msg = "BLE hello " + String(counter++);
pCharacteristic->setValue(msg.c_str());
if (deviceConnected) {
pCharacteristic->notify();
Serial.println("Notified: " + msg);
} else {
Serial.println("Waiting for BLE client...");
}
delay(1000);
}
Receiver: ESP32-C3 BLE client
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEClient.h>
#include <BLERemoteCharacteristic.h>
#include <BLEAdvertisedDevice.h>
#define SERVICE_UUID "12345678-1234-1234-1234-1234567890ab"
#define CHARACTERISTIC_UUID "abcdefab-1234-1234-1234-abcdefabcdef"
static BLEAddress *serverAddress = nullptr;
static BLERemoteCharacteristic *remoteCharacteristic = nullptr;
static bool doConnect = false;
static bool connected = false;
static BLEAdvertisedDevice *myDevice = nullptr;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) override {
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {
Serial.print("Found BLE server: ");
Serial.println(advertisedDevice.getName().c_str());
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
BLEDevice::getScan()->stop();
}
}
};
void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("BLE notify: ");
for (size_t i = 0; i < length; i++) {
Serial.print((char)pData[i]);
}
Serial.println();
}
bool connectToServer() {
BLEClient *pClient = BLEDevice::createClient();
if (!pClient->connect(myDevice)) return false;
BLERemoteService *pRemoteService = pClient->getService(SERVICE_UUID);
if (pRemoteService == nullptr) return false;
remoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
if (remoteCharacteristic == nullptr) return false;
if (remoteCharacteristic->canNotify()) {
remoteCharacteristic->registerForNotify(notifyCallback);
}
connected = true;
return true;
}
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE client...");
BLEDevice::init("");
BLEScan *pScan = BLEDevice::getScan();
pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pScan->setActiveScan(true);
pScan->start(5, false);
}
void loop() {
if (doConnect && !connected) {
if (connectToServer()) {
Serial.println("Connected to BLE server");
} else {
Serial.println("BLE connect failed");
}
doConnect = false;
}
if (!connected) {
BLEDevice::getScan()->start(5, false);
}
delay(1000);
}

At this stage, I encounter a problem when two devices on the opposite side of the laptop can't clearly connect. Probably because of interference from the electromagnetic wave from the laptop, but when I put devices together and physically create an obstacle between the laptop and the devices with my hand, it started working.


4) Wi-Fi UDP
Simplest Wi-Fi method: UDP. The easiest setup is:
For more details about Wi-Fi usage on the XIAO ESP32C3, I referred to the official Seeed Studio wiki: XIAO ESP32C3 Wi-Fi Usage.
- Receiver C3 creates its own Wi-Fi network with SoftAP
- Sender C3 joins that network and sends UDP packets
- Receiver prints packets in Serial Monitor
What to do
- Upload the receiver/AP sketch first
- Then upload the sender/station sketch
- No router needed
Receiver: ESP32-C3 SoftAP + UDP listener
#include <Arduino.h>
#include "Network.h"
#include "WiFi.h"
NetworkUDP udp;
const char* ssid = "YaroUDP";
const char* password = "12345678";
IPAddress local_IP(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
const uint16_t udpPort = 1234;
void setup() {
Serial.begin(115200);
delay(1000);
Network.begin();
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ssid, password);
udp.begin(udpPort);
Serial.println("UDP receiver started");
Serial.print("AP IP: ");
Serial.println(WiFi.softAPIP());
}
void loop() {
int packetSize = udp.parsePacket();
if (packetSize) {
char incoming[255];
int len = udp.read(incoming, sizeof(incoming) - 1);
if (len > 0) incoming[len] = '\0';
Serial.print("Received UDP: ");
Serial.println(incoming);
}
}
At this stage I can see that a UDP server is started and in my Wi-Fi network list I saw the new Wi-Fi access point - YaroUDP


Sender: ESP32-C3 station + UDP sender
#include <Arduino.h>
#include "Network.h"
#include "WiFi.h"
NetworkUDP udp;
const char* ssid = "YaroUDP";
const char* password = "12345678";
IPAddress receiverIP(192, 168, 4, 1);
const uint16_t udpPort = 1234;
void setup() {
Serial.begin(115200);
delay(1000);
Network.begin();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to AP");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected to AP");
Serial.print("My IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
static int counter = 0;
String msg = "UDP hello " + String(counter++);
udp.beginPacket(receiverIP, udpPort);
udp.print(msg);
udp.endPacket();
Serial.println("Sent UDP: " + msg);
delay(1000);
}

5) ESP-NOW
I found several sources of ESP-NOW protocol examples of how to do it from Espressif system, also on GitHub, and on the Seed Studio wiki page.
- Official Arduino ESP32 ESP-NOW docs: includes example sketches.
https://docs.espressif.com/projects/arduino-esp32/en/latest/api/espnow.html
- Official one-to-one example: ESP_NOW_Serial.ino on Espressif GitHub.
https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP_NOW/examples/ESP_NOW_Serial/ESP_NOW_Serial.ino
- Seeed XIAO ESP32 ESP-NOW guide: shows the idea specifically for XIAO boards.
https://wiki.seeedstudio.com/xiao_esp32s3_espnow
ESP-NOW is a wireless communication protocol defined by espressif that enables direct, fast, and low-power control of smart devices without the need for a router.It can coexist with Wi Fi and Bluetooth LE, supporting multiple series of SoCs such as Lexin ESP8266, ESP32, ESP32-S, and ESP32-C. ESP-NOW is widely used in fields such as smart home appliances, remote control, and sensors.
Following characteristics
According to the MAC address connection method, pairing can be done quickly without network conditions, and devices can be connected in single to many, single to single, many to single, and many to many ways
ESP-NOW is a wireless communication protocol based on the data link layer, which simplifies the five layer OSI upper layer protocol into one layer without the need to add packet headers and unpacks layer by layer. It greatly alleviates the lag and delay caused by packet loss during network congestion and has a higher response speed
Compared to Wi-Fi and Bluetooth
Wi-Fi: ESP-NOW supports point-to-point communication between devices, so it has lower power consumption and higher transmission speed, and also has a longer communication distance.
Bluetooth: ESP-NOW does not require a pairing process, making it simpler and easier to use ESP-NOW, so it has lower power consumption and higher transmission speed.
But ESP-NOW is suitable for application scenarios that require fast, reliable, low-power, and point-to-point communication, while Bluetooth and Wi Fi are more suitable for complex network environments and scenarios with a large number of devices.
What I need
- 2 XIAO ESP32 boards
- 2 USB cables
- Arduino IDE
At this moment, I need to identify the MAC addresses of each board and use these MAC addresses in the code. I asked AI about how to extract the MAC address:\
How to extract MAC address from each Xiao ESP32 C3 board?
Result:
#include <WiFi.h>
void setup() {
Serial.begin(115200);
delay(1000);
WiFi.mode(WIFI_STA); // set station mode
delay(100);
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop() {
}
My address is successfully extracted from the Serial Monitor:
YaroBoard 1 MAC Address (Sender): E8:F6:0A:14:67:B4
And YaroBoard 2 MAC Address (Receiver): 1C:DB:D4:EB:CB:68


I found official documentation that the sender device is XIAO USB S3 but I need to change it for XIAO USB C3 so I ask AI to modify the existing code to use my C3 board instead of S3.
This is a code for ESP32-C6, but I have ESP32-C3. I need to change it for my sender device as ESP32-C3. Modify it.
Result for YaroBoard 1 (SENDER):
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#define ESPNOW_WIFI_CHANNEL 0
#define MAX_ESP_NOW_MAC_LEN 6
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define NO_PMK_KEY false
typedef uint8_t XIAO;
typedef int XIAO_status;
// MAC address of the RECEIVER ESP32-C3 (YaroBoard 2)
static uint8_t Receiver_XIAOC3_MAC_Address[MAX_ESP_NOW_MAC_LEN] = {0x1C, 0xDB, 0xD4, 0xEB, 0xCB, 0x68};
esp_now_peer_info_t peerInfo;
typedef struct message_types {
char device[MAX_CHARACTERS_NUMBER];
char Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types Personal_XIAOC3_Information;
void espnow_init();
void espnow_deinit();
void SenderXIAOC3_MACAddress_Requir();
void SenderXIAOC3_Send_Data();
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status);
void Association_ReceiverXIAOC3_peer();
void setup() {
Serial.begin(BAUD);
delay(1000);
SenderXIAOC3_MACAddress_Requir();
espnow_init();
esp_now_register_send_cb(SenderXIAOC3_Send_Data_cb);
Association_ReceiverXIAOC3_peer();
}
void loop() {
SenderXIAOC3_Send_Data();
delay(4000);
}
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
char macStr[18];
Serial.print("Packet to: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
tx_info->des_addr[0], tx_info->des_addr[1], tx_info->des_addr[2],
tx_info->des_addr[3], tx_info->des_addr[4], tx_info->des_addr[5]);
Serial.println(macStr);
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
Serial.println();
}
void Association_ReceiverXIAOC3_peer() {
Serial.println("Attempting to associate peer for XIAOC3...");
memset(&peerInfo, 0, sizeof(peerInfo));
peerInfo.channel = ESPNOW_WIFI_CHANNEL;
peerInfo.encrypt = NO_PMK_KEY;
memcpy(peerInfo.peer_addr, Receiver_XIAOC3_MAC_Address, 6);
esp_err_t addPressStatus = esp_now_add_peer(&peerInfo);
if (addPressStatus != ESP_OK) {
Serial.print("Failed to add peer: ");
Serial.println(addPressStatus);
} else {
Serial.println("Successful to add peer");
}
}
void SenderXIAOC3_Send_Data() {
strcpy(Personal_XIAOC3_Information.device, "XIAOC3");
strcpy(Personal_XIAOC3_Information.Trag, "Hello, I'm sender");
esp_err_t sendStatus = esp_now_send(
Receiver_XIAOC3_MAC_Address,
(uint8_t *)&Personal_XIAOC3_Information,
sizeof(Personal_XIAOC3_Information)
);
if (sendStatus == ESP_OK) {
Serial.println("Sent with success");
} else {
Serial.print("Error sending message: ");
Serial.println(sendStatus);
}
}
void SenderXIAOC3_MACAddress_Requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[MAX_ESP_NOW_MAC_LEN];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void espnow_init() {
XIAO_status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
void espnow_deinit() {
XIAO_status espnow_sign = esp_now_deinit();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully deinitialized!");
} else {
Serial.println("The ESP-NOW deinitialization failed!");
}
}
Libraries used
#include "WiFi.h"
#include "esp_now.h"
The sender code uses the WiFi.h and esp_now.h libraries. WiFi.h is used to configure the ESP32-C3 board in station mode and read its MAC address.
esp_now.h is used to initialize ESP-NOW communication, add a peer device, send data, and register
the callback function for delivery status.
Main idea
This project uses two XIAO ESP32-C3 boards:
- one board acts as the sender
- one board acts as the receiver
The sender transmits a short message to the receiver using ESP-NOW and the receiver prints the received data in the Serial Monitor.
The Core Functions
espnow_init()
Role: Initialize the ESP-NOW function.
Return value:
- initialization successful: ESP_OK
- initialization failed: other error code
espnow_deinit()
Role: De-initialize the ESP-NOW function. All information related to paired devices will be deleted.
Return value:
- deinitialization successful: ESP_OK
SenderXIAOC3_MACAddress_Requir()
Role: Set the Wi-Fi mode to STA (Station Mode) and obtain the MAC address of the sender board, then print it to the Serial Monitor.
SenderXIAOC3_Send_Data()
Role: Prepare and send a specific message from the sender XIAO ESP32-C3 to the receiver XIAO ESP32-C3.
SenderXIAOC3_Send_Data_cb()
Role: This is a callback function. When it is executed, it prints whether the message was successfully delivered and to which MAC address it was sent.
Association_ReceiverXIAOC3_peer()
Role: is the send callback. It reports whether the message delivery was successful and shows the destination MAC address.
If more receivers are needed, more peer nodes can be created and added in the code.
esp_now_register_send_cb()
Role: Register a callback function to verify whether the message has been sent to the MAC layer.
Return value:
- MAC layer successfully received data: ESP_NOW_SEND_SUCCESS
- otherwise: ESP_NOW_SEND_FAIL
------------------------------------------------------------------------
Default Variables
#define ESPNOW_WIFI_CHANNEL
Role: Defines the Wi-Fi channel where both sender and receiver are located.
#define MAX_ESP_NOW_MAC_LEN
Role: Defines the MAC address length.
#define MAX_CHARACTERS_NUMBER
Role: Defines the maximum number of accepted or transmitted characters.
#define BAUD 115200
Role: Sets the Serial Monitor baud rate.
static uint8_t Receiver_XIAOC3_MAC_Address[MAX_ESP_NOW_MAC_LEN]
Role: Stores the MAC address of the receiver XIAO ESP32-C3. It serves as the recipient address.
Supplement: Please note that this must be replaced with your own receiver board MAC address and cannot be copied directly from another project.
NO_PMK_KEY
Role: Chooses to pair devices without encryption.
Upload was successful but delivery failed because I didn't upload any code to my receiver board, and it is not connected as well.

Also, you can see that my first board connected as usbmodem1101.
When I connected the second board and the second Arduino IDE window, I saw that it's connected with a different address (usbmodem2101). I mean two boards connected, and I can program the second board on the second window without messing with the first one.

Also after connecting the receiver board, the serial monitor from the first board indicates the delivery is successful.

Code for Receiver Board (YaroBoard 2 Receiver)
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#define ESPNOW_WIFI_CHANNEL 0
#define MAX_ESP_NOW_MAC_LEN 6
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define NO_PMK_KEY false
typedef uint8_t XIAO;
typedef int status;
// Put here the MAC address of the SENDER ESP32-C3
static uint8_t XIAOC3_Sender_MAC_Address[MAX_ESP_NOW_MAC_LEN] = {0xE8, 0xF6, 0x0A, 0x14, 0x67, 0xB4};
esp_now_peer_info_t peerInfo_sender;
typedef struct receiver_meesage_types {
char Reveiver_device[MAX_CHARACTERS_NUMBER];
char Reveiver_Trag[MAX_CHARACTERS_NUMBER];
} receiver_meesage_types;
receiver_meesage_types XIAOC3_RECEIVER_INFORATION;
typedef struct message_types {
char Sender_device[MAX_CHARACTERS_NUMBER];
char Sender_Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types XIAOC3_SENDER_INFORATION;
void Receiver_MACAddress_requir();
void espnow_init();
void espnow_deinit();
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len);
void ReceiverXIAOC3_Send_Data();
void ReceiverXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status);
void Association_SenderXIAOC3_peer();
void setup() {
Serial.begin(BAUD);
delay(1000);
Receiver_MACAddress_requir();
espnow_init();
esp_now_register_recv_cb(ReceiverXIAOC3_Recive_Data_cb);
esp_now_register_send_cb(ReceiverXIAOC3_Send_Data_cb);
Association_SenderXIAOC3_peer();
}
void loop() {
ReceiverXIAOC3_Send_Data();
delay(4000);
}
void espnow_init() {
status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
void espnow_deinit() {
status espnow_sign = esp_now_deinit();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully deinitialized!");
} else {
Serial.println("The ESP-NOW deinitialization failed!");
}
}
void Receiver_MACAddress_requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[MAX_ESP_NOW_MAC_LEN];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
memcpy(&XIAOC3_SENDER_INFORATION, incomingData, sizeof(XIAOC3_SENDER_INFORATION));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Sender_device: ");
Serial.println(XIAOC3_SENDER_INFORATION.Sender_device);
Serial.print("Sender_Trag: ");
Serial.println(XIAOC3_SENDER_INFORATION.Sender_Trag);
Serial.println();
}
void ReceiverXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
char macStr[18];
Serial.print("Packet to: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
tx_info->des_addr[0], tx_info->des_addr[1], tx_info->des_addr[2],
tx_info->des_addr[3], tx_info->des_addr[4], tx_info->des_addr[5]);
Serial.println(macStr);
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
Serial.println();
}
void ReceiverXIAOC3_Send_Data() {
strcpy(XIAOC3_RECEIVER_INFORATION.Reveiver_device, "XIAOC3");
strcpy(XIAOC3_RECEIVER_INFORATION.Reveiver_Trag, "I got it");
esp_err_t sendStatus = esp_now_send(
XIAOC3_Sender_MAC_Address,
(uint8_t *)&XIAOC3_RECEIVER_INFORATION,
sizeof(receiver_meesage_types)
);
if (sendStatus == ESP_OK) {
Serial.println("Sent with success: XIAOC3_RECEIVER_INFORATION_data1");
} else {
Serial.print("Error sending message: ");
Serial.println(sendStatus);
}
}
void Association_SenderXIAOC3_peer() {
Serial.println("Attempting to associate peer for XIAOC3...");
memset(&peerInfo_sender, 0, sizeof(peerInfo_sender));
peerInfo_sender.channel = ESPNOW_WIFI_CHANNEL;
peerInfo_sender.encrypt = NO_PMK_KEY;
memcpy(peerInfo_sender.peer_addr, XIAOC3_Sender_MAC_Address, 6);
esp_err_t addPressStatus = esp_now_add_peer(&peerInfo_sender);
if (addPressStatus != ESP_OK) {
Serial.print("Failed to add peer: ");
Serial.println(addPressStatus);
} else {
Serial.println("Successful to add peer");
}
}
**The Core Functions**
**espnow_init()**
**Role:** Initialize the ESP-NOW function.
**Return value:**
- initialization successful: ESP_OK
- initialization failed: other error code
espnow_deinit()
Role: De-initialize the ESP-NOW function. All information related to paired devices will be deleted.
Return value:
- deinitialization successful: ESP_OK
Receiver_MACAddress_requir()
Role: Set the Wi-Fi mode to STA (Station Mode) and obtain the MAC address of the receiver board, then print it to the Serial Monitor.
ReceiverXIAOC3_Recive_Data_cb()
Role: This is the receive callback function. When data arrives from the sender, it copies the incoming data into a message structure and prints the received content in the Serial Monitor.
ReceiverXIAOC3_Send_Data()
Role: Prepare and send a reply message from the receiver XIAO ESP32-C3 back to the sender XIAO ESP32-C3.
This function is only needed if two-way communication is used.
ReceiverXIAOC3_Send_Data_cb()
Role: This is a callback function. When it is executed, it prints whether the reply message was successfully delivered and to which MAC address it was sent.
Association_SenderXIAOC3_peer()
Role: Add the sender node as a peer device.
This is needed if the receiver also sends data back to the sender.
esp_now_register_recv_cb()
Role: Register a callback function to process incoming data when a message is received.
esp_now_register_send_cb()
Role: Register a callback function to verify whether the reply message has been sent to the MAC layer.
Return value:
- MAC layer successfully received data: ESP_NOW_SEND_SUCCESS
- otherwise: ESP_NOW_SEND_FAIL
Default Variables
#define ESPNOW_WIFI_CHANNEL
Role: Defines the Wi-Fi channel where both sender and receiver are located.
#define MAX_ESP_NOW_MAC_LEN
Role: Defines the MAC address length.
#define MAX_CHARACTERS_NUMBER
Role: Defines the maximum number of accepted or transmitted characters.
#define BAUD 115200
Role: Sets the Serial Monitor baud rate.
static uint8_t XIAOC3_Sender_MAC_Address[MAX_ESP_NOW_MAC_LEN]
Role: Stores the MAC address of the sender XIAO ESP32-C3. It serves as the destination address when the receiver sends a reply.
Supplement: This must be replaced with your own sender board MAC address and cannot be copied directly from another project.
NO_PMK_KEY
Role: Chooses to pair devices without encryption.
As a result, in the Serial Monitor I can see that the receiver initiated and it's able to receive packages from the sender.

Button LED Control
Next step, I want to send a signal from the receiver when the button is clicked, and this signal can control the LED on the receiver board.
Changes in the code will be follow:
On the sender
Add a button pin, read its state, and send "ON" or "OFF".
1. Add a button pin near the top
#define BUTTON_PIN D7
2. In setup(), set the button as input
pinMode(BUTTON_PIN, INPUT_PULLUP);
3. Replace current SenderXIAOC3_Send_Data() with this
void SenderXIAOC3_Send_Data() {
strcpy(Personal_XIAOC3_Information.device, "XIAOC3_YaroBoard1");
if (digitalRead(BUTTON_PIN) == LOW) {
strcpy(Personal_XIAOC3_Information.Trag, "ON");
} else {
strcpy(Personal_XIAOC3_Information.Trag, "OFF");
}
esp_err_t sendStatus = esp_now_send(
Receiver_XIAOC3_MAC_Address,
(uint8_t *)&Personal_XIAOC3_Information,
sizeof(Personal_XIAOC3_Information)
);
if (sendStatus == ESP_OK) {
Serial.print("Sent: ");
Serial.println(Personal_XIAOC3_Information.Trag);
} else {
Serial.print("Error sending message: ");
Serial.println(sendStatus);
}
}
4. Make the loop faster
Replace:
delay(4000);
with:
delay(100);
On the receiver
Stop sending reply messages and control an LED instead.
1. Add an LED pin near the top
#define LED_PIN D6
2. In setup(), set the LED as output
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
3. In the receiver, remove these parts completely
- XIAOC3_Sender_MAC_Address
- peerInfo_sender
- ReceiverXIAOC3_Send_Data()
- ReceiverXIAOC3_Send_Data_cb()
- Association_SenderXIAOC3_peer()
- esp_now_register_send_cb(...)
4. Replace the receiver callback with this
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
memcpy(&XIAOC3_SENDER_INFORATION, incomingData, sizeof(XIAOC3_SENDER_INFORATION));
Serial.print("Sender_device: ");
Serial.println(XIAOC3_SENDER_INFORATION.Sender_device);
Serial.print("Sender_Trag: ");
Serial.println(XIAOC3_SENDER_INFORATION.Sender_Trag);
if (strcmp(XIAOC3_SENDER_INFORATION.Sender_Trag, "ON") == 0) {
digitalWrite(LED_PIN, HIGH);
} else if (strcmp(XIAOC3_SENDER_INFORATION.Sender_Trag, "OFF") == 0) {
digitalWrite(LED_PIN, LOW);
}
Serial.println();
}
5. Empty the receiver loop
void loop() {
}
Then the full code for SENDER:\
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#define ESPNOW_WIFI_CHANNEL 0
#define MAX_ESP_NOW_MAC_LEN 6
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define NO_PMK_KEY false
#define BUTTON_PIN D7
typedef uint8_t XIAO;
typedef int XIAO_status;
// Receiver ESP32-C3 MAC address
static uint8_t Receiver_XIAOC3_MAC_Address[MAX_ESP_NOW_MAC_LEN] = {0x1C, 0xDB, 0xD4, 0xEB, 0xCB, 0x68};
esp_now_peer_info_t peerInfo;
typedef struct message_types {
char device[MAX_CHARACTERS_NUMBER];
char Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types Personal_XIAOC3_Information;
void espnow_init();
void SenderXIAOC3_MACAddress_Requir();
void SenderXIAOC3_Send_Data();
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status);
void Association_ReceiverXIAOC3_peer();
void setup() {
Serial.begin(BAUD);
delay(1000);
pinMode(BUTTON_PIN, INPUT_PULLUP);
SenderXIAOC3_MACAddress_Requir();
espnow_init();
esp_now_register_send_cb(SenderXIAOC3_Send_Data_cb);
Association_ReceiverXIAOC3_peer();
}
void loop() {
SenderXIAOC3_Send_Data();
delay(100);
}
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
char macStr[18];
Serial.print("Packet to: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
tx_info->des_addr[0], tx_info->des_addr[1], tx_info->des_addr[2],
tx_info->des_addr[3], tx_info->des_addr[4], tx_info->des_addr[5]);
Serial.println(macStr);
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
Serial.println();
}
void Association_ReceiverXIAOC3_peer() {
Serial.println("Attempting to associate peer for XIAOC3...");
memset(&peerInfo, 0, sizeof(peerInfo));
peerInfo.channel = ESPNOW_WIFI_CHANNEL;
peerInfo.encrypt = NO_PMK_KEY;
memcpy(peerInfo.peer_addr, Receiver_XIAOC3_MAC_Address, 6);
esp_err_t addPressStatus = esp_now_add_peer(&peerInfo);
if (addPressStatus != ESP_OK) {
Serial.print("Failed to add peer: ");
Serial.println(addPressStatus);
} else {
Serial.println("Successful to add peer");
}
}
void SenderXIAOC3_Send_Data() {
strcpy(Personal_XIAOC3_Information.device, "XIAOC3_YaroBoard1");
if (digitalRead(BUTTON_PIN) == LOW) {
strcpy(Personal_XIAOC3_Information.Trag, "ON");
} else {
strcpy(Personal_XIAOC3_Information.Trag, "OFF");
}
esp_err_t sendStatus = esp_now_send(
Receiver_XIAOC3_MAC_Address,
(uint8_t *)&Personal_XIAOC3_Information,
sizeof(Personal_XIAOC3_Information)
);
if (sendStatus == ESP_OK) {
Serial.print("Sent: ");
Serial.println(Personal_XIAOC3_Information.Trag);
} else {
Serial.print("Error sending message: ");
Serial.println(sendStatus);
}
}
void SenderXIAOC3_MACAddress_Requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[MAX_ESP_NOW_MAC_LEN];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void espnow_init() {
XIAO_status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
And for RECEIVER:\
\
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#define ESPNOW_WIFI_CHANNEL 0
#define MAX_ESP_NOW_MAC_LEN 6
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define LED_PIN D6
typedef int XIAO_status;
typedef struct message_types {
char device[MAX_CHARACTERS_NUMBER];
char Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types XIAOC3_SENDER_INFORATION;
void Receiver_MACAddress_requir();
void espnow_init();
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len);
void setup() {
Serial.begin(BAUD);
delay(1000);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Receiver_MACAddress_requir();
espnow_init();
esp_now_register_recv_cb(ReceiverXIAOC3_Recive_Data_cb);
}
void loop() {
}
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
memcpy(&XIAOC3_SENDER_INFORATION, incomingData, sizeof(XIAOC3_SENDER_INFORATION));
Serial.print("Sender_device: ");
Serial.println(XIAOC3_SENDER_INFORATION.device);
Serial.print("Sender_Trag: ");
Serial.println(XIAOC3_SENDER_INFORATION.Trag);
if (strcmp(XIAOC3_SENDER_INFORATION.Trag, "ON") == 0) {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED ON");
} else if (strcmp(XIAOC3_SENDER_INFORATION.Trag, "OFF") == 0) {
digitalWrite(LED_PIN, LOW);
Serial.println("LED OFF");
}
Serial.println();
}
void Receiver_MACAddress_requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[MAX_ESP_NOW_MAC_LEN];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void espnow_init() {
XIAO_status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
As a result I can control the receiver board LED by pressing the button on the sender board.

In the serial monitor of the sender, I can see that the message is sent as a packet message. In the receiver, I can see that the LED is on and off when I trigger the button.
Heart Rate Pulse Oximeter Sensor

Recently I received a MAX30102 heartbeat sensor. This sensor just needs to be connected to the finger tip and then it can measure the heart beat. It is an infrared sensor.
Give me a simple code on how can I test the MAX30102 heartbeat sensor.
The output contains several code blocks. To check the board and the sensor. On this stage, I need to check the board first: is it connected well or not?
#include <Wire.h>
#define SDA_PIN D4
#define SCL_PIN D5
void setup() {
Serial.begin(115200);
delay(1000);
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("I2C scanner started...");
}
void loop() {
byte error, address;
int count = 0;
Serial.println("Scanning...");
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
count++;
}
}
if (count == 0) {
Serial.println("No I2C devices found.");
} else {
Serial.println("Scan complete.");
}
Serial.println();
delay(2000);
}
As a result, in the serial monitor, I can see the I2C connection is established, and the device is found.

For checking the sensor I need to install the library MAX3010x

Code for checking the MAX30102 sensor
#include <Wire.h>
#include "MAX30105.h"
MAX30105 sensor;
#define SDA_PIN D4
#define SCL_PIN D5
void setup() {
Serial.begin(115200);
delay(1000);
Wire.begin(SDA_PIN, SCL_PIN);
if (!sensor.begin(Wire, I2C_SPEED_STANDARD)) {
Serial.println("Sensor not found. Check wiring.");
while (1);
}
sensor.setup();
sensor.setPulseAmplitudeRed(0x1F);
sensor.setPulseAmplitudeIR(0x1F);
sensor.setPulseAmplitudeGreen(0);
Serial.println("Place your finger on the sensor.");
}
void loop() {
long irValue = sensor.getIR();
Serial.print("IR value: ");
Serial.println(irValue);
delay(100);
}
This sensor can sense a disturbance in the air when there is nothing in front but when I use my finger tip and touch it, it starts demonstrating a different value


Next, I want to try to synchronize my heartbeat with the embedded LED on the board so I ask AI about different requests.
I need to convert this value to a blinking LED. I want the embedded LED to blink the same as my heartbeat, based on information from the sensor.
Now I want to transmit my data bit from one sender to the receiver, the same as I did before. You need to change the sender code and receiver code to blink an LED based on the data heart bit signal. The LED must blink on two boards together.
Code Sender
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"
#define ESPNOW_WIFI_CHANNEL 0
#define MAX_ESP_NOW_MAC_LEN 6
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define NO_PMK_KEY false
#define SDA_PIN D4
#define SCL_PIN D5
#define LED_PIN D6
typedef uint8_t XIAO;
typedef int XIAO_status;
// Receiver ESP32-C3 MAC address
static uint8_t Receiver_XIAOC3_MAC_Address[MAX_ESP_NOW_MAC_LEN] = {0x1C, 0xDB, 0xD4, 0xEB, 0xCB, 0x68};
esp_now_peer_info_t peerInfo;
MAX30105 sensor;
typedef struct message_types {
char device[MAX_CHARACTERS_NUMBER];
char Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types Personal_XIAOC3_Information;
long lastBeat = 0;
float beatsPerMinute = 0;
unsigned long ledOffTime = 0;
void espnow_init();
void SenderXIAOC3_MACAddress_Requir();
void SenderXIAOC3_Send_Data(const char *msg);
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status);
void Association_ReceiverXIAOC3_peer();
void setup() {
Serial.begin(BAUD);
delay(1000);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Wire.begin(SDA_PIN, SCL_PIN);
if (!sensor.begin(Wire, I2C_SPEED_STANDARD)) {
Serial.println("Sensor not found. Check wiring.");
while (1);
}
sensor.setup();
sensor.setPulseAmplitudeRed(0x1F);
sensor.setPulseAmplitudeIR(0x1F);
sensor.setPulseAmplitudeGreen(0);
SenderXIAOC3_MACAddress_Requir();
espnow_init();
esp_now_register_send_cb(SenderXIAOC3_Send_Data_cb);
Association_ReceiverXIAOC3_peer();
Serial.println("Place your fingertip on the sensor.");
}
void loop() {
long irValue = sensor.getIR();
if (millis() > ledOffTime) {
digitalWrite(LED_PIN, LOW);
}
if (irValue > 5000) {
if (checkForBeat(irValue)) {
long delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60.0 / (delta / 1000.0);
Serial.print("Beat detected! BPM: ");
Serial.println(beatsPerMinute);
digitalWrite(LED_PIN, HIGH);
ledOffTime = millis() + 80;
SenderXIAOC3_Send_Data("BEAT");
}
} else {
Serial.println("No finger detected");
digitalWrite(LED_PIN, LOW);
}
delay(20);
}
void SenderXIAOC3_Send_Data_cb(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
void Association_ReceiverXIAOC3_peer() {
Serial.println("Attempting to associate peer for XIAOC3...");
memset(&peerInfo, 0, sizeof(peerInfo));
peerInfo.channel = ESPNOW_WIFI_CHANNEL;
peerInfo.encrypt = NO_PMK_KEY;
memcpy(peerInfo.peer_addr, Receiver_XIAOC3_MAC_Address, 6);
esp_err_t addPressStatus = esp_now_add_peer(&peerInfo);
if (addPressStatus != ESP_OK) {
Serial.print("Failed to add peer: ");
Serial.println(addPressStatus);
} else {
Serial.println("Successful to add peer");
}
}
void SenderXIAOC3_Send_Data(const char *msg) {
strcpy(Personal_XIAOC3_Information.device, "XIAOC3_YaroBoard1");
strncpy(Personal_XIAOC3_Information.Trag, msg, MAX_CHARACTERS_NUMBER - 1);
Personal_XIAOC3_Information.Trag[MAX_CHARACTERS_NUMBER - 1] = '\0';
esp_err_t sendStatus = esp_now_send(
Receiver_XIAOC3_MAC_Address,
(uint8_t *)&Personal_XIAOC3_Information,
sizeof(Personal_XIAOC3_Information)
);
if (sendStatus == ESP_OK) {
Serial.print("Sent: ");
Serial.println(Personal_XIAOC3_Information.Trag);
} else {
Serial.print("Error sending message: ");
Serial.println(sendStatus);
}
}
void SenderXIAOC3_MACAddress_Requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[MAX_ESP_NOW_MAC_LEN];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void espnow_init() {
XIAO_status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
Code Receiver
#include <Arduino.h>
#include "WiFi.h"
#include "esp_now.h"
#define ESPNOW_WIFI_CHANNEL 0
#define BAUD 115200
#define MAX_CHARACTERS_NUMBER 20
#define LED_PIN D6
typedef int XIAO_status;
typedef struct message_types {
char device[MAX_CHARACTERS_NUMBER];
char Trag[MAX_CHARACTERS_NUMBER];
} message_types;
message_types XIAOC3_SENDER_INFORATION;
unsigned long ledOffTime = 0;
void Receiver_MACAddress_requir();
void espnow_init();
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len);
void setup() {
Serial.begin(BAUD);
delay(1000);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Receiver_MACAddress_requir();
espnow_init();
esp_now_register_recv_cb(ReceiverXIAOC3_Recive_Data_cb);
}
void loop() {
if (millis() > ledOffTime) {
digitalWrite(LED_PIN, LOW);
}
}
void ReceiverXIAOC3_Recive_Data_cb(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
memcpy(&XIAOC3_SENDER_INFORATION, incomingData, sizeof(XIAOC3_SENDER_INFORATION));
Serial.print("Sender_device: ");
Serial.println(XIAOC3_SENDER_INFORATION.device);
Serial.print("Sender_Trag: ");
Serial.println(XIAOC3_SENDER_INFORATION.Trag);
if (strcmp(XIAOC3_SENDER_INFORATION.Trag, "BEAT") == 0) {
digitalWrite(LED_PIN, HIGH);
ledOffTime = millis() + 80;
Serial.println("Heartbeat blink");
}
Serial.println();
}
void Receiver_MACAddress_requir() {
WiFi.mode(WIFI_STA);
delay(100);
uint8_t mac[6];
WiFi.macAddress(mac);
Serial.println();
Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void espnow_init() {
XIAO_status espnow_sign = esp_now_init();
if (espnow_sign == ESP_OK) {
Serial.println("The ESP-NOW is successfully initialized!");
} else {
Serial.println("The ESP-NOW initialization failed");
}
}
I'm very excited about the result because it's actually working. I can see the serial monitor sending packages, and I can see the LED indication. The most interesting thing is that I am touching my heart with my right hand, and I'm using my left hand with a sensor. I can see that I can feel and see my heart beating and the LED blinking simultaneously

Reflection
I tried to explore several protocols.
Wire protocols are very simple; they just need two or three wires. The code is also very simple to understand and to repeat.
Wireless communication is a bit more complex, and it requires its up some node's name or address, his name, and then some procedure for how to establish this connection and initialize the port, the mode, and the speed.
Also, for fast prototyping, I'm asking AI to generate code and explain to me what exactly is happening inside. Later on, I will dive deeper, but at this stage, I just need to make everything work.
Reference
Download the source files used in this assignment:
- 1. UART Sender - XIAO ESP32-C3
- 2. UART Receiver - Arduino Uno
- 3. I2C Sender - XIAO ESP32-C3
- 4. I2C Receiver - Arduino Uno
- 5. BLE Sender - XIAO ESP32-C3
- 6. BLE Receiver - XIAO ESP32-C3
- 7. Wi-Fi UDP Receiver - XIAO ESP32-C3
- 8. Wi-Fi UDP Sender - XIAO ESP32-C3
- 9. ESP-NOW Simple Sender - YaroBoard 1
- 10. ESP-NOW Simple Receiver - YaroBoard 2
- 11. ESP-NOW Button LED Sender - YaroBoard 1
- 12. ESP-NOW Button LED Receiver - YaroBoard 2
- 13. HeartBeat LED Test - MAX30102
- 14. ESP-NOW HeartBlink Sender - YaroBoard 1
- 15. ESP-NOW HeartBlink Receiver - YaroBoard 2