Objectives
Individual assignment: design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s)
Group assignment: send a message between two projects
Group assignment
Group assignment pageNetworking and communications
Networking and communication involve connecting electronic devices like microcontrollers, sensors, and computers, so they can exchange data. Networking provides the structure: it includes physical connections (like wires or radio signals), hardware (like routers or antennas), and protocols (rules for sending data). Communication is the process that happens over this structure: devices send, receive, and interpret data using methods like serial communication (UART, I2C, SPI), wireless protocols (Wi-Fi, Bluetooth, LoRa), and networking stacks (like TCP/IP over the Internet). Depending on the setup, communication can be wired or wireless, synchronous or asynchronous, and involve simple point-to-point links or complex networks using routing and addressing. Systems are designed to handle errors, share channels, and maintain modularity, so multiple devices can interact reliably without interfering with each other. All of this is layered from the physical media (like copper wires or RF waves) up to the application (like a webpage or sensor reading) following the OSI model. Networking and communication are essential for building distributed systems, IoT devices, and interactive electronics.
This week, I want to learn about wireless communication, which may be benificial in upgrading my final project. I chose XIAO esp32 c6 microcontroller. Pin out diagram is given below.

XIAO ESP32C6 is an embedded development board that boasts outstanding RF performance, thanks to its support for both 2.4GHz Wifi - 802.11b/g/n and Bluetooth Low Energy (BLE) 5.0 dual wireless communication. This capability enables the XIAO ESP32C6 to provide reliable and high-speed wireless connectivity for a wide range of Internet of Things (IoT) applications. The board features an onboard ceramic antenna, which eliminates the need for an external antenna and simplifies the design process.
How microcontrollers communicate using WiFi ?
XIAO ESP32-C6 microcontrollers can wirelessly communicate with each other using Wi-Fi by connecting to a shared network or by creating one device as an Access Point (AP). In this setup, the AP microcontroller broadcasts a Wi-Fi network name (SSID) and requires a password for secure access. The other two microcontrollers act as clients (Stations), connecting to this network using the SSID and password. Once all devices are connected, they can communicate using different network protocols. HTTP (Hypertext Transfer Protocol) allows the clients to send requests (like sensor data or commands) to the server, which processes and responds accordingly. UDP (User Datagram Protocol) is faster and more lightweight, ideal for sending small packets directly without establishing a connection, though it doesn’t guarantee delivery. For more flexible multi-device communication, the devices can use MQTT (Message Queuing Telemetry Transport), where one device acts as a broker or connects to an external broker, and each ESP32-C6 can publish or subscribe to specific data topics. Alternatively, the microcontrollers can use ESP-NOW, a fast, connectionless protocol that uses MAC addresses to directly send small messages between devices, without needing an SSID or password at all.
Designing the PCB
To design a PCB in KiCad, we begin by creating a new project, which generates two main files: one for the schematic (.kicad_sch) and another for the PCB layout (.kicad_pcb). In the Schematic Editor, you add components by pressing A, wire them using W, and include power symbols like +5V or GND.Running the Electrical Rules Checker (ERC) helps catch common mistakes.

Once complete, we transfer the schematic to the PCB Editor using the "Update PCB from Schematic" tool. In the PCB Editor, drew the board outline on the Edge.Cuts layer. Then route the copper traces using the Route Tracks tool. Finally, a Design Rule Check (DRC) helps ensure there are no layout violations. After verifying the design, we can generate Gerber files for fabrication.

I used Gerber2png software to convert my PCB design's Gerber files into PNG images. After the conversion, I obtained separate images for the trace layer and the outline layer. The trace layer showed the copper connections on the board, illustrating how the components were electrically linked. The outline layer displayed the board’s physical boundary, which I had drawn on the Edge.Cuts layer in KiCad.


I then uploaded the trace and outline images into mods CE, setting them up for PCB milling.
Milling the PCB
I wanted to make two PCBs. I made one in Modela MDX-20 milling machine and other one in X tool.
Rolland Modela MDX-20 milling machine
I used the FR-1 copper-clad board . I installed a 1/64-inch end mill for milling the traces and carefully set the X, Y, and Z origins, making sure the bit just touched the board’s surface. Using mods CE , I loaded my PNG file, selected the "PCB traces (1/64)" process, and generated the toolpath. I sent the job to the machine and monitored it as it milled the traces. Once done, I changed the bit to a 1/32-inch mill for cutting the board outline, reset the Z-axis, and repeated the process with the appropriate settings. After milling, I removed the board, cleaned it with isopropyl alcohol, and inspected it for any imperfections.


X tool F1 Ultra

I lifted the protective enclosure of the xTool F1 Ultra and placed my copper-clad board flat on the baseplate, making sure the blue laser spot landed clearly on the surface. To focus the laser, I held down the Up/Down button to adjust the height of the laser module. When the red and blue light spots overlapped perfectly, I knew the focus was set. The F1 Ultra is equipped with two types of lasers: a blue diode laser for cutting and engraving wood, acrylic, and plastics, and a infrared fiber laser for engraving metals and marking PCBs. For my PCB work, I used the infrared fiber laser, which is ideal for ablating thin copper layers and blue laser for outline cut. In xTool Creative Space (XCS), I used the left-side tools to create or import my PCB design onto the canvas. I selected the design object, used the top toolbar for edits, and set the engraving and cutting parameters on the right—adjusting power, speed, and number of passes. Before starting, I used the Framing function to preview the engraving area—the laser dot traced the border of the design directly on the board, helping me confirm its position.
I also made sure the xTool Smoke Purifier was connected to the exhaust port at the back of the F1 Ultra.
Once everything was set, I clicked Process in XCS, closed the protective enclosure, and hit Start in the software. When the display showed “Ready,” I pressed the Start/Stop button on the machine’s touchscreen controller to begin engraving. Once finished, the cutting process is loaded the same way.
The final board

Assembling the components
I had two PCBs to assemble as I was planning to add two nodes. A board was already built in electronics design week.I used the Interactive HTML BOM to identify and source the required components from FabStash. After receiving the parts, I soldered them onto the PCB. Post-soldering, I inspected the board both visually under a microscope to check for solder bridges, alignment, and cold joints, and electrically using a multimeter to verify continuity and proper connections.
Final PCB


Inspecting the PCBs


mistakes : I tried to connect VL53L0X sensor. But microcontroller didn't detect the device. With the help of Instructor, it was later found that the I2C connection were not proper.After I resoldered the connector, the sensor was detected by the board
Communication between the PCBs
esp-now
ESP-NOW is a wireless communication protocol developed by Espressif that allows ESP32 and other compatible devices to exchange data directly without relying on Wi-Fi or an internet connection. It operates over the 2.4 GHz frequency band and supports fast, low-power, and efficient peer-to-peer communication. Devices must be paired beforehand, but once paired, they can reconnect automatically even after a reset or power loss—no additional handshake is required. ESP-NOW supports both encrypted and unencrypted communication, allows a mix of secure and open peers, and can transmit payloads up to 250 bytes. It also includes a callback feature to notify whether a message was successfully delivered, making it well-suited for applications like sensor networks, remote monitoring, and other IoT systems that require quick, lightweight data exchange.
To connect xiao esp32 c6 boards we need mac address of the reciever. To find the mac address, I used the following code.Make sure to set the corret baud rate in serial monitor.
#include <WiFi.h> void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); Serial.println("Board B MAC Address:"); Serial.println(WiFi.macAddress()); } void loop() {}

Changing intesity of led in node using data from distance sensor in main board
I browsed to get code to connect the MCUs. There were mistakes. I copied the error messages and uploaded in chatgpt to correct the code. Then I used chatgpt to refer the code.
- Prompt 1: I want ESP32-C6 code to communicate using ESP-NOW.
- Prompt 2:I have a VL53L0X sensor and a red LED. I want to change the intensity of the LED with respect to the sensor.
- Prompt 3:The sensor is in one ESP32-C6 and the LED is in another ESP32-C6.
I wanted one MCU to send the data from VL53L0X time of flight sensor. Program of the sender is given below
#include <WiFi.h> #include <esp_now.h> #include <Wire.h> #include <VL53L0X.h> // Distance sensor VL53L0X sensor; // Receiver MAC address uint8_t peerAddress[] = { 0x7C, 0x2C, 0x67, 0x64, 0xA2, 0x34 }; // Change this to your receiver's MAC // Message to send typedef struct struct_message { int distance_cm; } struct_message; struct_message msg; void setup() { Serial.begin(115200); // I2C init Wire.begin(); // VL53L0X init sensor.setTimeout(500); if (!sensor.init()) { Serial.println("VL53L0X Init Failed"); while (1); } Serial.println("VL53L0X Ready"); // WiFi & ESP-NOW WiFi.mode(WIFI_STA); if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed"); return; } // Add peer esp_now_peer_info_t peerInfo = {}; memcpy(peerInfo.peer_addr, peerAddress, 6); peerInfo.channel = 0; peerInfo.encrypt = false; if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println("Failed to add peer"); return; } } void loop() { int distance = sensor.readRangeSingleMillimeters(); if (sensor.timeoutOccurred()) { Serial.println("Timeout"); return; } int distance_cm = distance / 10; msg.distance_cm = distance_cm; Serial.print("Sending distance (cm): "); Serial.println(distance_cm); esp_now_send(peerAddress, (uint8_t *)&msg, sizeof(msg)); delay(500); }
The receiver MCU needs to change the intensity of led according to the data from sender. Program code of receiver is given below.
#include <WiFi.h> #include <esp_now.h> #define LED_PIN 18 // Change this to your actual LED pin (must support PWM) // Struct to receive data typedef struct struct_message { int distance_cm; } struct_message; struct_message msg; // Callback when data is received void OnDataRecv(const esp_now_recv_info_t *recv_info, const uint8_t *incomingData, int len) { memcpy(&msg, incomingData, sizeof(msg)); Serial.print("Received distance: "); Serial.print(msg.distance_cm); Serial.println(" cm"); // Map 0–20 cm to 255–0 brightness int brightness = map(msg.distance_cm, 0, 20, 255, 0); brightness = constrain(brightness, 0, 255); // Write brightness to LED analogWrite(LED_PIN, brightness); } void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); WiFi.mode(WIFI_STA); WiFi.disconnect(); // Not connecting to WiFi AP if (esp_now_init() != ESP_OK) { Serial.println("Error initializing ESP-NOW"); return; } // Register receive callback esp_now_register_recv_cb(OnDataRecv); Serial.println("ESP-NOW Receiver Ready"); } void loop() { // Nothing needed here, all handled via callback }
The result is given below
Turning on neo pixel in nodes using push button in main board
I used chatgpt to get the code. The prompt is given below.
- I want to use ESP-NOW to communicate between three ESP32-C6 microcontrollers. The sender has a switch. When it's pressed the first time, the NeoPixel on Board 2 should light up. On the second press, the NeoPixel on Board 3 should light up. On the third press, both should turn off."
- The NeoPixel is connected to GPIO 18 on both receiver boards
- Here's the code I’m using to detect button presses and blink an LED based on how many times the button is pressed. Please adapt it for the ESP32-C6 and ESP-NOW
- I don't need an internal pull-up resistor for the button
- I want the NeoPixels to change colors when turned on. They should cycle through different colors one after another
- add the color cycling functionality into the code and give me final, working sender and receiver code for the ESP32-C6 boards using ESP-NOW
- On the third button press, both NeoPixels should turn off
- Why is my ESP32-C6 BOOT button acting like a regular switch instead of functioning like a BOOT button
- Here's my full ESP32-C6 receiver code using ESP-NOW and NeoPixel. It's giving an error about the callback function signature — how can I fix it
Sender
#include <WiFi.h> #include <esp_now.h> uint8_t board2Address[] = {0x8C, 0xBF, 0xEA, 0xCB, 0x7E, 0xC8}; // Board 2 (NeoPixel) uint8_t board3Address[] = {0x7C, 0x2C, 0x67, 0x64, 0xBA, 0xF8}; // Board 3 (NeoPixel) int pressCount = 0; bool lastButtonState = HIGH; const int buttonPin = 9; void setup() { Serial.begin(115200); pinMode(buttonPin, INPUT); // External pull-down assumed WiFi.mode(WIFI_STA); if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed!"); return; } esp_now_peer_info_t peerInfo = {}; memcpy(peerInfo.peer_addr, board2Address, 6); peerInfo.channel = 0; peerInfo.encrypt = false; esp_now_add_peer(&peerInfo); memcpy(peerInfo.peer_addr, board3Address, 6); esp_now_add_peer(&peerInfo); } void loop() { bool currentButtonState = digitalRead(buttonPin); if (lastButtonState == HIGH && currentButtonState == LOW) { pressCount++; uint8_t msgToBoard2 = 0; uint8_t msgToBoard3 = 0; if (pressCount % 3 == 1) { msgToBoard2 = 1; // Activate board 2 msgToBoard3 = 0; esp_now_send(board2Address, &msgToBoard2, sizeof(msgToBoard2)); esp_now_send(board3Address, &msgToBoard3, sizeof(msgToBoard3)); Serial.println("➡️ Sent activation to Board 2"); } else if (pressCount % 3 == 2) { msgToBoard2 = 0; msgToBoard3 = 1; // Activate board 3 esp_now_send(board2Address, &msgToBoard2, sizeof(msgToBoard2)); esp_now_send(board3Address, &msgToBoard3, sizeof(msgToBoard3)); Serial.println("➡️ Sent activation to Board 3"); } else { msgToBoard2 = 0; msgToBoard3 = 0; esp_now_send(board2Address, &msgToBoard2, sizeof(msgToBoard2)); esp_now_send(board3Address, &msgToBoard3, sizeof(msgToBoard3)); Serial.println("⛔ Sent deactivation to both boards"); } delay(200); // Debounce } lastButtonState = currentButtonState; }

Receiver
#define NEO_RMT // Use RMT for ESP32-C6 NeoPixel support #include <WiFi.h> #include <esp_now.h> #include <Adafruit_NeoPixel.h> #include "esp_sleep.h" #define LED_PIN 18 #define NUM_PIXELS 1 Adafruit_NeoPixel pixels(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800); bool active = false; int colorIndex = 0; void setup() { Serial.begin(115200); // Prevent sleep issues with RMT on ESP32-C6 esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); pixels.begin(); pixels.clear(); pixels.show(); WiFi.mode(WIFI_STA); WiFi.disconnect(); // Important for ESP-NOW only mode if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed!"); return; } // Register ESP-NOW receive callback esp_now_register_recv_cb(OnDataRecv); Serial.println("Receiver ready – waiting for data..."); } void loop() { if (active) { setColor(colorIndex); colorIndex = (colorIndex + 1) % 6; delay(500); } else { pixels.clear(); pixels.show(); delay(100); } } // ESP-NOW receive callback void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) { if (incomingData != nullptr && len > 0) { if (incomingData[0] == 1) { active = true; Serial.println("🟢 Activated – cycling NeoPixel!"); } else if (incomingData[0] == 0) { active = false; Serial.println("🔴 Deactivated – NeoPixel off."); } } } void setColor(int index) { uint32_t color; switch (index) { case 0: color = pixels.Color(255, 0, 0); break; // Red case 1: color = pixels.Color(0, 255, 0); break; // Green case 2: color = pixels.Color(0, 0, 255); break; // Blue case 3: color = pixels.Color(255, 255, 0); break; // Yellow case 4: color = pixels.Color(0, 255, 255); break; // Cyan case 5: color = pixels.Color(255, 0, 255); break; // Magenta } pixels.setPixelColor(0, color); pixels.show(); }
And the result
Hero shot

Files
DesignfilesConclusion
This week, I explored wireless communication using ESP-NOW with the XIAO ESP32-C6 microcontrollers, and it was a valuable learning experience that I believe can benefit my final project. I designed and fabricated two custom PCBs using both the Roland MDX-20 milling machine and the xTool F1 Ultra. I successfully established ESP-NOW communication between the boards by using their MAC addresses to send data directly without relying on a Wi-Fi network. I also implemented a system where pressing a button on one board sends a signal to two receivers, each with a NeoPixel that changes color when selected—demonstrating how nodes can wirelessly communicate and respond to specific commands. In another test, I transmitted distance data from a VL53L0X sensor to a receiver that adjusted LED brightness in real time.