Skip to content

11. Networking and Communications

Group assignment:

  • Send a message between two projects.

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 Highlight

Highlights of I2C Communication Between XIAO RP2040 and XIAO SAMD21

Objective:
Successfully establish I2C communication between two microcontroller boards (XIAO RP2040 as Controller and XIAO SAMD21 as Peripheral) to send and receive messages.

Steps:
1. Hardware Setup:
- Connected SDA & SCL pins of both boards.
- Added DFPlayer Mini, speaker, and LED to the peripheral (SAMD21) so that we can play a sound whenever it receives a message form the mater board.

  1. Code Implementation:
  2. RP2040(Master):
    • Sends user-input messages via I2C to the peripheral/slave.
    • Uses Wire.h library for I2C communication.
  3. SAMD21(Slave):

    • Receives messages and triggers LED & DFPlayer (plays audio).
    • Uses SoftwareSerial for DFPlayer communication.
  4. Outcome:

  5. Successful Unidirectional communication via Serial Monitor.
  6. Peripheral/slave responds by lighting an LED & playing audio upon receiving messages.

Challenges & Learnings:

  • Proper I2C address matching is crucial (both boards must use the same address).
  • SoftwareSerial conflicts can occur if not properly initialized.
  • Real-time debugging via Serial Monitor helps verify communication.

This exercise strengthened our understanding of I2C protocols and inter-board communication in embedded systems.

Here is the link to our group assignment.

Individual Assignment

For this week, I decided to work on controlling the music via wifi network. I used Xiao ESP32S3, DFPlayer Mini and a speaker. I want to integrate this in my final project as well and I have also simulated Blynk and ESP32 board in previous week. I switched up my board to Xiao ESP32S3 as it has same pins like Xiao RP2040 and hence it can be used with muy PCB board.

I already learned how to set up my DFPlayer Mini and a speaker with my Xiao RP2040 in Out devices week. Here is the link for the same.

This is the circuit connection I made.

Then I refer Music Player widget documentation which is available in Blynk and I also learned some of the functions available for DFPlayer mini form here. I wrote my own code using these to resources.

Here is how I set up Blynk app on my Mobile Phone. - Install Blynk App form here.

  • I then created an account to use Blynk app.

  • Before uploading my own program, I tested the wifi connection using the ready made code available in Blynk app so that I understand how to set up wifi connection between my Board and my Blynk app on my phone.

  • Here is the template code Blynk Provides when we use their ready made template.

/*************************************************************

  This is a simple demo of sending and receiving some data.
  Be sure to check out other examples!
 *************************************************************/

/* Fill-in information from Blynk Device Info here */
#define BLYNK_TEMPLATE_ID           "TMPL3gVIgbDe5"
#define BLYNK_TEMPLATE_NAME         "Quickstart Device"
#define BLYNK_AUTH_TOKEN            "Dyq_EP2dii5IGTCi9B38PNimMUxejAd_"

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial


#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

BlynkTimer timer;

// This function is called every time the Virtual Pin 0 state changes
BLYNK_WRITE(V0)
{
  // Set incoming value from pin V0 to a variable
  int value = param.asInt();

  // Update state
  Blynk.virtualWrite(V1, value);
}

// This function is called every time the device is connected to the Blynk.Cloud
BLYNK_CONNECTED()
{
  // Change Web Link Button message to "Congratulations!"
  Blynk.setProperty(V3, "offImageUrl", "https://static-image.nyc3.cdn.digitaloceanspaces.com/general/fte/congratulations.png");
  Blynk.setProperty(V3, "onImageUrl",  "https://static-image.nyc3.cdn.digitaloceanspaces.com/general/fte/congratulations_pressed.png");
  Blynk.setProperty(V3, "url", "https://docs.blynk.io/en/getting-started/what-do-i-need-to-blynk/how-quickstart-device-was-made");
}

// This function sends Arduino's uptime every second to Virtual Pin 2.
void myTimerEvent()
{
  // You can send any value at any time.
  // Please don't send more that 10 values per second.
  Blynk.virtualWrite(V2, millis() / 1000);
}

void setup()
{
  // Debug console
  Serial.begin(115200);

  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  // You can also specify server:
  //Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, "blynk.cloud", 80);
  //Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, IPAddress(192,168,1,100), 8080);

  // Setup a function to be called every second
  timer.setInterval(1000L, myTimerEvent);
}

void loop()
{
  Blynk.run();
  timer.run();
  // You can inject your own code or combine it with other sketches.
  // Check other examples on how to communicate with Blynk. Remember
  // to avoid delay() function!
}
  • After that I created a project and added Music Player Widget to control music like playing, stopping, next and previous music. Then I added Slider Button so that I can control the volume of music.

  • I specified the virtual pin for Music Control Widget.

  • I then specified the virtual pin for Slider which will be used to increase or decrease the volume of music.

  • I then copied the template name, template ID and authentication key to my program so that my Board can connect to the my phone’s hotspot for wifi connection.

  • I noticed that I cannot really get the Authentication key form Blynk app on my phone but I can login to Blynk Desktop application and get the details as show below.

  • Then I uploaded my program and check if it is working correctly. It took me some time to get a program that works correctly. I also realized that asking chatGPT or Deepseek to write a code for me was not really a good idea unless you are familiar with all the functions available in the library that you are using. Because I was able to easily write my own program after learning all the functions which are clearly explained in DFPlayer Mini Documentation and Blynk Documentation.

  • Here is the video of controlling Music via my Blynk App using Music Player Widget.

  • This is the program I wrote for the same.
/* Fill-in information from Blynk Device Info here */
#define BLYNK_TEMPLATE_ID "TMPL3RChFD034"
#define BLYNK_TEMPLATE_NAME "NAP POD"
#define BLYNK_AUTH_TOKEN "wBKDIwuXVMXQLEJSb_QJpJKnXZ_sDJcK"
#define BLYNK_PRINT Serial
#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

// Initialize software serial on pins D0 (RX) and D1 (TX)
SoftwareSerial mySerial(D0, D1); 

// Create DFPlayer object
DFRobotDFPlayerMini myDFPlayer;

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "esp32";
char pass[] = "esp32369369";

BlynkTimer timer;

// Volume control from Blynk slider on V1
BLYNK_WRITE(V1) {
  int vol = constrain(param.asInt(), 0, 30);
  myDFPlayer.volume(vol);
  Serial.print(F("Volume set to: "));
  Serial.println(vol);
}
bool status = 1;
// This function is called every time the Virtual Pin 0 state changes

BLYNK_WRITE(V0) {   
  // Called when the datastream virtual pin V0 is updated by Blynk.Console, Blynk.App, or HTTP API. 
  String value = param.asStr();
  if (value == "play") {
    if (status){
      Serial.println("'play' button pressed");
      myDFPlayer.play();  //Play the first mp3
      delay(1000);
      status = !status; // Toggle pause state
    }else if(!status) {
      myDFPlayer.start();  //start the mp3 from the pause
      delay(1000);
    }

  } else if (value == "stop") {
    Serial.println("'stop' button pressed");
    myDFPlayer.pause();  //pause the mp3

  } else if (value == "prev") {
    Serial.println("'prev' button pressed");
    myDFPlayer.previous();  //Play previous mp3
    delay(1000);

  } else if (value == "next") {
    Serial.println("'next' button pressed");
    myDFPlayer.next();  //Play next mp3
    delay(1000);

  } else {
    Serial.print("V0 = '");
    Serial.print(value);
    Serial.println("'");    
  }
}

void setup(){
  // Debug console
  Serial.begin(115200);
  mySerial.begin(9600);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  // Initialize DFPlayer
  if (!myDFPlayer.begin(mySerial)) {
    Serial.println(F("DFPlayer initialization failed!"));
    Serial.println(F("1. Check RX/TX connections (must be crossed)"));
    Serial.println(F("2. Insert SD card with MP3 files"));
    while(true);  // Halt if initialization fails
  }
}

void loop(){
  Blynk.run();
  timer.run();
}

Libraries I used in the program are:

i. DFRobotDFPlayerMini.h – Controls the DFPlayer Mini module for MP3 playback.

ii. SoftwareSerial.h – Enables serial communication on custom pins (for DFPlayer).

iii. WiFi.h – Provides Wi-Fi connectivity for the ESP32.

iv. WiFiClient.h – Handles Wi-Fi client connections.

v. BlynkSimpleEsp32.h – Integrates Blynk IoT platform with ESP32 for remote control.

How does my program work?

i. Setup and Initialization - WiFi and Blynk: Connects to Wi-Fi (ssid & pass) and initializes Blynk using the Auth Token.

  • DFPlayer Mini: Initializes via SoftwareSerial (D0-RX, D1-TX). Checks for SD card and valid MP3 files; halts if initialization fails.

ii. Blynk Virtual Pin Controls

  • V1 (Slider) → Adjusts volume (0-30).

  • V0 (Buttons) → Handles playback commands:

  • play → Starts/resumes playback.

  • stop → Pauses playback.

  • prev/next → Skips tracks.

iii. Main Loop - Continuously runs Blynk.run() to listen for remote commands.

Here is another assignment for Networking and communication week as Blynk does not really satisfy the requirement.

I set up a I2C communication between my custom Xiao RP2040 board and Arduino. Here, Arduino Uno is I2C Master and my xiao RP2040 is I2C Slave. I will send color command from Serial port open by Ardunio Uno and it will be send to the color command to Xioa RP2040 via I2C communication line. Once my Xiao RP2040 gets the command it will out the color on two strips of Addressable RGB Leds connected to its pin D0 and D1.

  • Here is the set up.

  • Here is a clear circuit connection.

  • Here is the I2C Master Program for Arduino Uno.

#include <Wire.h>

const int slaveAddress = 0x08; // I2C address of the slave device (XIAO RP2040)
String inputMessage = "";      // Variable to store user input

void setup() {
  Wire.begin();                // Initialize I2C as master
  Serial.begin(9600);        // Start Serial communication for input/output

  Serial.println("Neopixel Control via I2C");
  Serial.println("Available commands:");
  Serial.println("red, green, blue, yellow, purple, cyan, white, off");
  Serial.println("Type a color command and press Enter:");
}

void loop() {
  // Wait for user input from Serial Monitor
  if (Serial.available()) {
    inputMessage = Serial.readStringUntil('\n'); // Read message until Enter key
    inputMessage.trim(); // Remove any accidental whitespace or newline
    inputMessage.toLowerCase(); // Convert to lowercase for easier comparison

    // Validate the input against known color commands
    if (inputMessage == "red" || inputMessage == "green" || inputMessage == "blue" || 
        inputMessage == "yellow" || inputMessage == "purple" || inputMessage == "cyan" || 
        inputMessage == "white" || inputMessage == "off") {

      // Begin I2C transmission
      Wire.beginTransmission(slaveAddress);
      Wire.write(inputMessage.c_str()); // Send as C-style string
      Wire.endTransmission();

      // Confirm message was sent
      Serial.print("Command sent to XIAO: ");
      Serial.println(inputMessage);
    }
    else {
      Serial.println("Invalid command. Available options:");
      Serial.println("red, green, blue, yellow, purple, cyan, white, off");
    }

    // Prompt for next command
    Serial.println("\nEnter another color command:");
  }
}
  • Here is the I2C Slave Program for Xiao RP2040.
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

// Define the number of NeoPixels and the pins 
#define PIN_STRIP1 D0
#define PIN_STRIP2 D1
#define I2C_ADDRESS 0x08

// Initialize the NeoPixel objects for both strips
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(NUM_PIXELS, PIN_STRIP1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(NUM_PIXELS, PIN_STRIP2, NEO_GRB + NEO_KHZ800);

// Define colors
uint32_t RED = strip1.Color(255, 0, 0);    // Red
uint32_t BLUE = strip1.Color(0, 0, 255);   // Blue
uint32_t GREEN = strip1.Color(0, 255, 0);  // Green
uint32_t YELLOW = strip1.Color(255, 255, 0); // Yellow
uint32_t PURPLE = strip1.Color(128, 0, 128); // Purple
uint32_t CYAN = strip1.Color(0, 255, 255);   // Cyan
uint32_t WHITE = strip1.Color(255, 255, 255);// White

String receivedCommand = ""; // Stores the received I2C command

// Function to clear all pixels on both strips
void clearPixels() {
  for (int i = 0; i < NUM_PIXELS; i++) {
    strip1.setPixelColor(i, 0);
    strip2.setPixelColor(i, 0);
  }
  strip1.show();
  strip2.show();
}

// Function to set all pixels to a specific color
void setAllPixels(uint32_t color) {
  for (int i = 0; i < NUM_PIXELS; i++) {
    strip1.setPixelColor(i, color);
    strip2.setPixelColor(i, color);
  }
  strip1.show();
  strip2.show();
}

// Function to blink all pixels with a specific color
void blinkColor(uint32_t color, int blinkDuration = 300) {
  // Fade in
  for (int brightness = 0; brightness <= 255; brightness += 50) {
    strip1.setBrightness(brightness);
    strip2.setBrightness(brightness);
    setAllPixels(color);
    delay(blinkDuration/5);
  }
  // Fade out
  for (int brightness = 255; brightness >= 0; brightness -= 50) {
    strip1.setBrightness(brightness);
    strip2.setBrightness(brightness);
    setAllPixels(color);
    delay(blinkDuration/5);
  }
  clearPixels();
}

// Function to handle received I2C commands
void handleCommand(String cmd) {
  cmd.toLowerCase();

  if (cmd == "red") {
    setAllPixels(RED);
  } 
  else if (cmd == "blue") {
    setAllPixels(BLUE);
  }
  else if (cmd == "green") {
    setAllPixels(GREEN);
  }
  else if (cmd == "yellow") {
    setAllPixels(YELLOW);
  }
  else if (cmd == "purple") {
    setAllPixels(PURPLE);
  }
  else if (cmd == "cyan") {
    setAllPixels(CYAN);
  }
  else if (cmd == "white") {
    setAllPixels(WHITE);
  }
  else if (cmd == "blink red") {
    blinkColor(RED);
  }
  else if (cmd == "blink blue") {
    blinkColor(BLUE);
  }
  else if (cmd == "blink green") {
    blinkColor(GREEN);
  }
  else if (cmd == "off") {
    clearPixels();
  }
  else {
    // Unknown command - could add error handling here
  }
}

// Callback function for received I2C data
void receiveEvent(int byteCount) {
  receivedCommand = "";
  while (Wire.available()) {
    char c = Wire.read();
    receivedCommand += c;
  }
  handleCommand(receivedCommand);
}

void setup() {
  // Initialize both NeoPixel strips
  strip1.begin();
  strip2.begin();
  clearPixels(); // Ensure all pixels are off at the start

  // Initialize I2C communication
  Wire.begin(I2C_ADDRESS);
  Wire.onReceive(receiveEvent);

  // Set brightness (0-255)
  strip1.setBrightness(50);
  strip2.setBrightness(50);
}

void loop() {
}
  • Here is Brief description of how it works. Here’s a concise explanation of how the I2C communication works between your Arduino Uno (Master) and XIAO RP2040 (Slave):

  • Arduino Uno (Master)

  • Sends color commands (“red”, “blue”, etc.) via I2C when you type them in the Serial Monitor.
  • Uses Wire.beginTransmission() to start communication and Wire.write() to send data.

  • XIAO RP2040 (Slave)

  • Listens for I2C commands at address 0x08.
  • When data is received, it triggers receiveEvent(), which reads the command.
  • The command is processed in handleCommand(), setting Addressable RGB LEDs to the requested color.

How Data Flows 1. User Input
- I type "blue" in the Arduino Serial Monitor. - The Uno sends this string over I2C to the XIAO.

  1. I2C Transmission
  2. The XIAO detects incoming data and runs receiveEvent().
  3. The command ("blue") is stored in receivedCommand.

  4. Command Execution

  5. handleCommand() checks the string and calls setAllPixels(BLUE).
  6. Both NeoPixel strips turn blue.

  7. Effects

  8. If you send "blink red", the XIAO runs blinkColor(RED) for a fading effect.
  9. "off" clears all LEDs.

Key Features

  • Non-blocking: The XIAO uses interrupts, so it responds immediately to I2C commands.
  • Extensible: Easy to add new colors/effects by expanding handleCommand().
  • Two-way Control: You could modify it to send status back to the Uno (e.g., “Command Executed!”).

Wiring Notes

  • Connect I2C pins:
  • Arduino Uno (SDA: A4, SCL: A5) → XIAO RP2040 (SDA: D4, SCL: D5).
  • Share GND between boards.

  • Working videos of the I2C communication between Arduino Uno(Master) and Xiao RP2040(Slave).

Program Files

i.Blynk Wifi template program file.

ii. Program code for controlling music (Xioa ESP32S3 + DFPlayerMini + Speaker) from Blynk App.

iii. I2C Master code for Arduino and I2C Slave code for Xiao rp2040.