Week 11 β Networking and Communications
This week is about networking and communication between microcontroller nodes.
The objective is to learn wire and wireless communication.
AI prompt ChatGPT: "Can you give me my avatar an animated landscape image (I don't want realistic image)- where in I am in my week 11 of fab academy - "networking and communication" and I am trying I2C protocol, Bluetooth and Meshtastic LoRA communication. PLease use the attached images as reference and make avatar."
Assignment Overview
- Send a message between two projects and document your work to the group work page and reflect on your individual page what you learned
- Design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s)
| Day | Activity | Status |
|---|---|---|
| Thursday | Week Planning π , LAB Activity π and Documentation π | Completed |
| Class - Wire and Wireless Communications | Completed | |
| Friday | LAB Activity π - Charlieplexing (ATTiny 1624) Debugging and Power-up | completed |
| Saturday | LAB Activity π - I2C communication between XIAO ESP32C6 and Charlieplexing (ATTiny 1624) | Completed |
| Sunday | Documentation π | Completed |
| Monday | LAB Activity π - I2C hub design and milling | In progress |
| Reflection and Learnings π‘ | Pending | |
| Complete Documentation - Regional Review π | In progress | |
| Tuesday | Regional Review π¨βπ«π¬ | Pending |
| Lab Activity π - Design π - LoRA with Meshtastic | Pending | |
| Watch Prof. Neilβs 2025 Lectures πΊ | Pending | |
| Wednesday | Go the Extra Mile - On Final ProjectβοΈ | Pending |
| NuEval - Final Documentation π | Pending | |
| Make instagram Reel π¬π | Pending |
Group Assignment
For the group assignment, our objective was to communicate between any 2 projects already made. So to make two different projects communicate with each other they has to decide or agree on the way to exchange data, then ensured that both systems could understand the information being sent, and finally verified that messages were being transmitted reliably between them.
Main Learning points:
- Agreement on common data exchange protocol.
- Configured both systems to send and receive data.
- Validated successful end-to-end communication.
Individual Assignment
Connect multiple nodes, there needs to be an address, can be a network address or bus address, connect nodes, connect two processor I make, or connect to phone, there has to be an address that I need ot talk to it, and local input going and coming into the network and output.
I2C Communication
I decided to use my microcontroller board from my production week 8 and charliplexing board from output week 10 for wired communicate assignment. While designing my week 8 electronic production I added totaL I2C communication JST connector to interface other modules. And while designing charlieplexing board week 10 output devices I keep I2C for communication.
I used I2C communication between XIAO ESP32C6 and ATtiny 1426 microcontroller, to connect multiple I2C nodes I designed and milled I2C hub to that I can connect other sensors such as GY-BMI160 6DOF and MAX30102 in future and test it out.
For this assignment propose, I have connected a pulse sensors as local input and charlieplexing as output device. The pulse sensor is an analog sensor and which is connected to XIAO ESP32C6 and ATtiny 1426 is driving charlieplexing LEDs in Multiplexing operation.
What Went Wrong - I2C Hub / Junction
I reversed / mirrored the I2C pin-out in the schematics, which resulted in wiring mismatch using standard I2C female-to-female wire.
What I Learned - I2C Hub / Junction
Always use standard wire sequence in the I2C connector, even though the connector in on top layer or on bottom layer always keep Pin 1 GND, Pin 2 - 3V3, Pin 3- SDA. and PIN 4 - SCL. Following this will always be correct because the connector can only get inserted in one direction, it has two grooves on the top of the connector which matches with the slots on the matting connector.
I2C communication between Two Nodes
Below I used two nodes one is XIAO ESP32C6 and one is ATtiny1624. I have Push button on XIAO ESP32C6 connected to D8 Pin which is active high and I have Status LED at pin PA5 on ATtiny1624 board. Thus the in this I2C communication when I press the button on XIAO ESP32C6 board the LED on the ATtiny1624 will blink.
ChatGPT AI prompt: "I want to do I2C communication between ATTiny1624 and XIAO ESP32C6. I have a button connected to D8 of XIAO ESP32C6 and LED connected to PA5 of ATTiny1624. So if I press push button it should turn on LED on ATtiny. Can you help me with both the program and also add comments in each line about its explanation."
// ESP32C6
#include
#define SUB_ADDR 0x08 // Address of ATtiny (must match slave)
#define BUTTON_PIN D8 // D8 pin where button is connected
void setup() {
Serial.begin(115200);
// Start serial monitor for debugging
pinMode(BUTTON_PIN, INPUT);
// Configure button pin as INPUT (NO internal pull-up/pull-down)
// External 10k pull-down handles default LOW state
// Default = LOW, pressed = HIGH
Wire.begin(22, 23);
// Initialize I2C (SDA=22, SCL=23)
Serial.println("ESP32 Main Ready");
}
void loop() {
int buttonState = digitalRead(BUTTON_PIN);
// Read button state
Wire.beginTransmission(SUB_ADDR);
// Start communication with slave
if (buttonState == HIGH) {
// Button pressed
Wire.write(1);
// Send value '1' β turn LED ON
Serial.println("Button Pressed β LED ON");
}
else {
// Button released
Wire.write(0);
// Send value '0' β turn LED OFF
Serial.println("Button Released β LED OFF");
}
Wire.endTransmission();
// End I2C transmission
delay(100);
// Small delay to avoid flooding I2C bus
}
// ATtiny1624
#include
#define SUB_ADDR 0x08 // Must match Main
#define LED_PIN PIN_PA5 // PA5
volatile uint8_t receivedData = 0;
// Store incoming data (volatile because used in interrupt)
void receiveEvent(int howMany) {
// This function runs automatically when data is received
if (Wire.available()) {
receivedData = Wire.read();
// Read incoming byte
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);
// Set LED pin as output
Wire.begin(SUB_ADDR);
// Initialize as I2C slave
Wire.onReceive(receiveEvent);
// Register function to run when data arrives
}
void loop() {
if (receivedData == 1) {
digitalWrite(LED_PIN, HIGH);
// Turn LED ON
}
else {
digitalWrite(LED_PIN, LOW);
// Turn LED OFF
}
}
I2C communication between Three Nodes
Now My Instructor Saheen asked me to add one more node and to the I2C communication. First, I added another XIAO ESP32C6 to as Subordinate and made three micro-controller network.
Network functioning is when I short-press (less than 0.5 sec) button on main micro-controller (XIAO ESP32C6) the status LED on Subordinate 1 (ATtiny1624) will flash twice and when I long-press (more than 0.5 sec) button on main microcontroller (XIAO ESP32C6) the status LED on Subordinate 2 (XIAO ESP32C6) will flash twice.
| Device | Controller | Button | Status LED | I2C Pins |
|---|---|---|---|---|
| Main | XIAO ESP32C6 | D8 (Active High) | D7 | SDA β D4 SCL β D5 |
| Subordinate 1 | ATtiny1624 | - | PA5 | SCL β PB0 SDA β PB1 |
| Subordinate 2 | XIAO ESP32C6 | D3 (Active Low) | D0 | SDA β D4 SCL β D5 |
ChatGPT AI prompt: "Please use Main/Subordinate terminology instead if Master/Slave terminology: I have three microcontrollers Boards: microcontroller 1 (Main): XIAO ESP32C6 which has Push button connected to D8 and it is Active High. It has LED connected on pin D8. microcontroller 2 (Subordinate 1) : ATtiny1426 which has LED connected on PA5. Microcontroller 3 (Subordinate 2) : ESP32C6 which has Push button connected to D3 it is Active low. and LED on D0. All three microcontrollers are connected via I2C communication and SDA and SCL lines are pulled-up to 3V3. I wan to do a basic communication between these three, when I short press push button on microcontroller 1 the LED of Subordinate 1 shall Blink twice. and when I long press push button on microcontroller 1 the LED of Subordinate 2 shall Blink twice."
#include // Include I2C library
#define BUTTON_PIN D8 // Button connected to D8 (Active HIGH)
#define LED_PIN D7 // LED connected to D7
#define SUB1_ADDR 0x08 // I2C address of Subordinate 1 (ATtiny)
#define SUB2_ADDR 0x09 // I2C address of Subordinate 2 (ESP32)
unsigned long pressStart = 0; // Stores time when button was pressed
bool prevState = LOW; // Stores previous button state for edge detection
void setup() {
pinMode(BUTTON_PIN, INPUT); // Button uses external pull-down β no internal pull-up
pinMode(LED_PIN, OUTPUT); // LED pin set as output
Wire.begin(22, 23); // Initialize I2C as Main β SDA=22, SCL=23
Serial.begin(115200); // Serial monitor for debugging
}
void loop() {
bool state = digitalRead(BUTTON_PIN); // Read current button state
// Detect button press (LOW β HIGH transition)
if (state && !prevState) {
pressStart = millis();
// Record time when button was pressed
}
// Detect button release (HIGH β LOW transition)
if (!state && prevState) {
unsigned long duration = millis() - pressStart; // Calculate how long button was held
if (duration < 500) {
// SHORT PRESS (<500ms)
sendCommand(SUB1_ADDR, 1); // Send command 1 to Subordinate 1
Serial.println("SHORT PRESS β Sub1");
}
else {
// LONG PRESS (>500ms)
sendCommand(SUB2_ADDR, 2); // Send command 2 to Subordinate 2
Serial.println("LONG PRESS β Sub2");
}
}
prevState = state; // Update previous state for next loop iteration
}
// ββ Function to send I2C command βββββββββββββ
void sendCommand(uint8_t addr, uint8_t cmd) {
Serial.print("Sending to: ");
Serial.println(addr);
Wire.beginTransmission(addr); // Start communication with given address
Wire.write(cmd); // Send command byte
Wire.endTransmission(); // End transmission β actually sends data
}
#include
#define LED_PIN PIN_PA5 // PA5 pin where LED is connected
#define I2C_ADDR 0x08 // I2C address for this device
volatile uint8_t command = 0; // Stores received command (volatile β used in interrupt)
// ββ I2C Receive Callback βββββββββββββββββββ
void receiveEvent(int howMany) {
if (Wire.available()) {
command = Wire.read(); // Read incoming command byte
}
}
void setup() {
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
Wire.begin(I2C_ADDR); // Initialize as I2C Subordinate
Wire.onReceive(receiveEvent); // Register callback when data is received
}
void loop() {
if (command == 1) {
// If command is 1 β blink LED
blinkTwice(); // Call blink function
command = 0; // Reset command after execution
}
}
// ββ Blink LED twice ββββββββββββββββββββββββ
void blinkTwice() {
for (int i = 0; i < 2; i++) {
digitalWrite(LED_PIN, HIGH); // Turn LED ON
delay(200); // Keep it ON for 200ms
digitalWrite(LED_PIN, LOW); // Turn LED OFF
delay(200); // Keep it OFF for 200ms
}
}
#include
#define LED_PIN D2 // LED connected to D0
#define I2C_ADDR 0x09 // I2C address for this device
volatile uint8_t command = 0; // Stores received command
// ββ I2C Receive Callback βββββββββββββββββββ
void receiveEvent(int len) {
Serial.println("Data received!");
while (Wire.available()) {
command = Wire.read();
Serial.print("Command = ");
Serial.println(command);
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
Wire.begin((uint8_t)I2C_ADDR); // Initialize as I2C Subordinate // Wire.begin(I2C_ADDR, 22, 23);
Wire.setClock(100000);
Wire.onReceive(receiveEvent); // Register receive callback
}
void loop() {
while (Wire.available()) {
command = Wire.read(); // Read incoming command byte
Serial.println(command);
if (command == 2) {
// If command is 2 β blink LED
blinkTwice();
command = 0; // Reset after execution
delay(10); // allow system to stabilize
}
}
}
// ββ Blink LED twice ββββββββββββββββββββββββ
void blinkTwice() {
for (int i = 0; i < 2; i++) {
digitalWrite(LED_PIN, HIGH); // LED ON
delay(200);
digitalWrite(LED_PIN, LOW); // LED OFF
delay(200);
}
}
Testing the I2C Network
After flashing code is all three microcontroller, I connected them with the I2C hub that I made for testing at the short press of button on Main XIAO ESP32C6 the onboard status LED of Subordinate 1 - ATtiny1624 blinked twice however at the long press of button on main XIAO ESP32C6 the status LED of Subordinate 2 - XAIO ESP32C6 did not blinked.
What Went Wrong --> Subordinate 2: XIAO ESP32C6
XIAO ESP32C6 does not work as Subordinate!
What I Learned --> Debugging
I am not able to resolve this issue yet but diving into this issue resolution - I learned below things.
Hardware Checks
- I2C connection / connecter pin sequence [GND, 3V3, SDA, SCL]- Checked
- Only One pull-up resistors between 3V3 and SDA in network - Checked
- Only One pull-up resistors between 3V3 and SCL in network - Checked
- Voltage between Supply and GND which is 3.3 V - Checked
- All nodes in network has common ground - Checked
- Status LED connection on ubordinate 2: XIAO ESP32C6 - Checked
- Replaced the Subordinate 2 with another XIAO ESP32C6, still not working- Checked
Software Checks
- Main code -> confirmed that long press is actually triggered - Checked
Serial.println("LONG PRESS TRIGGERED"); - Main code -> confirmed command is sent - Checked
Serial.print("Sending to: ");Serial.println(addr);
- Subordinate 2 -> added "Serial.println" inside Subordinate 2 - No response in serial print
Serial.println("Data received!"); - Address of Subordinate in Main confirmed - Checked
#define SUB2_ADDR 0x09
- Subordinate 2 address confirmed - Checked
#define I2C_ADDR 0x09
- Run I2C Scanner - Subordinate 2 XIAO ESP32C6 is not sending its Address 0X09
#include
void setup() {
Serial.begin(115200); // Start serial monitor
Wire.begin(22, 23); // Initialize I2C as Main (SDA=22, SCL=23)
Serial.println("\nI2C Scanner Starting...");
}
void loop() {
byte error, address;
int count = 0;
Serial.println("Scanning..."); // Scan all possible I2C addresses (1 to 126)
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address); // Try to communicate with this address
error = Wire.endTransmission(); // 0 = success (device responded)
if (error == 0) {
Serial.print("Found device at 0x");
if (address < 16) Serial.print("0"); // Formatting for single digit hex
Serial.println(address, HEX); // Print address in HEX
count++;
}
}
if (count == 0) {
Serial.println("No I2C devices found\n");
} else {
Serial.println("Scan complete\n");
}
delay(2000); // Wait 2 seconds before next scan
}
My Finding : This issue persist with all XIAO ESP32C6 boards that I checked. The same board works as Main however it does not work as Subordinate in the network.
Thus, I replaced the Subordinate 2 with ATtiny1624 and I was able to communicate between three nodes. Below is the network details and the arduino code.
My instructor Saheen Palayi identified the rootcause. We need to use Espressif-Ide to program the Subordinate XIAO ESP32C6 to work as receiver. I plane to do this and document it after my final project completion.
| Device | Controller | Button | Status LED | I2C Pins |
|---|---|---|---|---|
| Main | XIAO ESP32C6 | D8 (Active High) | D7 | SDA β D4 SCL β D5 |
| Subordinate 1 | ATtiny1624 | - | PA5 | SCL β PB0 SDA β PB1 |
| Subordinate 2 | ATtiny1624 | - | PA3 | SDA β PB0 SCL β PB1 |
#include
#define LED_PIN PIN_PA3 // PA5 pin where LED is connected
#define I2C_ADDR 0x09 // I2C address for this device
volatile uint8_t command = 0; // Stores received command (volatile β used in interrupt)
// ββ I2C Receive Callback βββββββββββββββββββ
void receiveEvent(int howMany) {
if (Wire.available()) {
command = Wire.read(); // Read incoming command byte
}
}
void setup() {
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
Wire.begin(I2C_ADDR); // Initialize as I2C Subordinate
Wire.onReceive(receiveEvent); // Register callback when data is received
}
void loop() {
if (command == 2) {
// If command is 2 β blink LED
blinkTwice(); // Call blink function
command = 0; // Reset command after execution
}
}
// ββ Blink LED twice ββββββββββββββββββββββββ
void blinkTwice() {
for (int i = 0; i < 2; i++) {
digitalWrite(LED_PIN, HIGH); // Turn LED ON
delay(200); // Keep it ON for 200ms
digitalWrite(LED_PIN, LOW); // Turn LED OFF
delay(200); // Keep it OFF for 200ms
}
}
Adding Local Input and Outputs
Now I tried to add pulse/heart beat sensor as an analog input to my main board XIAO ESP32C6 and blink the charlieplexing matrix based on heart beats.
Pulse/heart beat Sensor β ESP32C6 reads analog β sends level via I2C β ATtiny lights bars of Charlieplexing Matrix
- XIAO ESP32C6 β Main (sender)
- ATtiny1624 β Subordinate (receiver + LED driver)
ChatGPT AI prompt: "Hey, Can you give me two codes one for XIAO ESP32C6 and one for ATtiny 1624. I have connected them using I2C and Pins used in ATTiny are SDA is PB1 and SCL is PB0. I want to Blink Charleiplexing, with raw value changing between 1550 to 1670, the bars rising and falling according to these raw values. Please update the threshold to 1640 for the pulse sensor. The Raw values are changing between 1550 to 1670 with by heart beats. Please give ATtiny 1624 code with explanation comments in each lines. Instead of Master and Slave please use Main/Subordinate terminology."
#include <Wire.h>
#define PulseSensorPin 0 // Analog input pin
#define I2C_ADDR 0x08 // Subordinate address
int Signal;
// Your observed range
#define MIN_SIGNAL 1550
#define MAX_SIGNAL 1670
void setup() {
Serial.begin(115200);
Wire.begin(22, 23);
// Initialize I2C as Main (SDA=22, SCL=23)
Serial.println("ESP32 Main Ready");
}
void loop() {
Signal = analogRead(PulseSensorPin);
// Read pulse signal
Serial.println(Signal);
// Map signal to 0β5 levels (bars)
int level = map(Signal, MIN_SIGNAL, MAX_SIGNAL, 0, 5);
// Constrain to safe range
level = constrain(level, 0, 5);
// Send level to subordinate
Wire.beginTransmission(I2C_ADDR);
Wire.write(level);
Wire.endTransmission();
delay(30);
}
#include <Wire.h>
#define I2C_ADDR 0x08
#define THRESHOLD 1640
// ββ Charlieplex Pins βββββββββββββββββββββββββ
const uint8_t CHARLIE_PINS[] = { PIN_PA1, PIN_PA2, PIN_PA3, PIN_PA6, PIN_PA7 };
const uint8_t NUM_PINS = 5;
// ββ LED Struct βββββββββββββββββββββββββββββββ
struct LED {
uint8_t anode;
uint8_t cathode;
};
// ββ LED Mapping (your tested mapping) βββββββ
const LED LED_TABLE[5][4] = {
{ {PIN_PA2, PIN_PA3}, {PIN_PA1, PIN_PA3}, {PIN_PA7, PIN_PA3}, {PIN_PA6, PIN_PA3} },
{ {PIN_PA3, PIN_PA2}, {PIN_PA1, PIN_PA2}, {PIN_PA7, PIN_PA2}, {PIN_PA6, PIN_PA2} },
{ {PIN_PA3, PIN_PA1}, {PIN_PA2, PIN_PA1}, {PIN_PA7, PIN_PA1}, {PIN_PA6, PIN_PA1} },
{ {PIN_PA3, PIN_PA7}, {PIN_PA2, PIN_PA7}, {PIN_PA1, PIN_PA7}, {PIN_PA6, PIN_PA7} },
{ {PIN_PA3, PIN_PA6}, {PIN_PA2, PIN_PA6}, {PIN_PA1, PIN_PA6}, {PIN_PA7, PIN_PA6} },
};
// ββ Active LED Buffer βββββββββββββββββββββββ
struct ActiveLED {
uint8_t row;
uint8_t col;
};
ActiveLED activeBuffer[20];
uint8_t activeCount = 0;
// ββ Received signal βββββββββββββββββββββββββ
volatile uint16_t signalValue = 0;
// ββ ALL OFF βββββββββββββββββββββββββββββββββ
void allOff() {
for (uint8_t i = 0; i < NUM_PINS; i++) {
pinMode(CHARLIE_PINS[i], INPUT);
}
}
// ββ CLEAR BUFFER ββββββββββββββββββββββββββββ
void clearAll() {
activeCount = 0;
allOff();
}
// ββ ADD LED TO BUFFER βββββββββββββββββββββββ
void setLED(uint8_t row, uint8_t col, bool state) {
uint8_t r = row - 1;
uint8_t c = col - 1;
if (state) {
if (activeCount < 20) {
activeBuffer[activeCount].row = r;
activeBuffer[activeCount].col = c;
activeCount++;
}
}
}
// ββ MULTIPLEX ENGINE ββββββββββββββββββββββββ
void multiplex() {
if (activeCount == 0) {
allOff();
return;
}
for (uint8_t i = 0; i < activeCount; i++) {
LED led = LED_TABLE[activeBuffer[i].row][activeBuffer[i].col];
allOff();
pinMode(led.anode, OUTPUT);
pinMode(led.cathode, OUTPUT);
digitalWrite(led.anode, HIGH);
digitalWrite(led.cathode, LOW);
delayMicroseconds(400);
}
}
// ββ RECEIVE I2C DATA ββββββββββββββββββββββββ
void receiveEvent(int howMany) {
if (Wire.available() >= 2) {
uint8_t high = Wire.read();
uint8_t low = Wire.read();
signalValue = (high << 8) | low;
}
}
// ββ TURN ON FULL MATRIX βββββββββββββββββββββ
void turnAllOn() {
clearAll();
for (uint8_t r = 1; r <= 5; r++) {
for (uint8_t c = 1; c <= 4; c++) {
setLED(r, c, true);
}
}
}
// ββ SETUP βββββββββββββββββββββββββββββββββββ
void setup() {
allOff();
Wire.begin(I2C_ADDR);
Wire.onReceive(receiveEvent);
}
// ββ LOOP ββββββββββββββββββββββββββββββββββββ
void loop() {
if (signalValue > THRESHOLD) {
// FULL MATRIX ON
turnAllOn();
unsigned long start = millis();
while (millis() - start < 300) {
multiplex();
}
// OFF phase
clearAll();
start = millis();
while (millis() - start < 200) {
multiplex();
}
}
else {
clearAll();
multiplex();
}
}
Downloads and Useful Links
- I2C Hub KiCad design files
- FA26 Networking and Communications Class
- XIAO LoRa Boothcamp
- Getting Started - Meshtastic
- Seeed Meshtasic Contest
- Reference Assignment 1
- Reference Assignment 2
- Friend Finder reference totem.labs
- Final Project Reference
- Reference Interactive textile designer & sensor spinster
- Link Invasive connector
- All Communication Protocols in 6 mins