Week 11 – Networking and Communications

This week is about networking and communication between microcontroller nodes.

The objective is to learn wire and wireless communication.

Networking and Communications

AI prompt: "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

Group Task:
  • Send a message between two projects
Individual Task:
  • Design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s)

Weekly Activity Log
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

The group assignment was to communicate between any 2 projects already made. For the assignment purpose, we communicated between

Group Assignment


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

Image Source : Slides Eric Pan

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.

Block Diagram
Schematic
I2C Hub / Junction

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.

I2C Pin Mismatch

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 Connector Pin Out Image Source

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.

Main Controller : XIAO ESP32C6

// 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
}

  
Subordinate Controller : ATtiny1624

// 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 Blink

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.

Network Details
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
MAIN : XIAO ESP32C6 Code
#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
}
  
Subordinate 1 : ATtiny1624
#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
  }
}
  
Subordinate 2: XIAO ESP32C6
#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.

Subordinate 2 : XIAO ESP32C6 is not Blinking

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
  • I2C Scanner Code - run on Main Controller
    
    #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
    }
    
    I2C Scanner

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.

Network Details
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
Subordinate 2 : ATtiny1624
#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
  }
}
  
I2C Communication

Adding Local Input and Outputs

Heart Beat Blinking

Links

totem.labs Saheen Link Saheen Link Saheen Link Adrian Rico Link All Protocols in 6 mins