Networking and Communications

In this week's assignment, I explored serial communication between two microcontrollers - a my board Seeed Studio XIAO RP2040 and an Arduino Uno. i created a chat system where the two devices could exchange messages bidirectionally, and later evolved into a button-controlled stepper motor system.

Components Used

Library installation

my XIAO RP2040

Raspberry Pi-based microcontroller board with multiple serial options

Library installation

Arduino Uno

Classic ATmega328P board for receiving serial commands

Library installation

Jumper Wires

For making serial connections between boards

Library installation

Stepper Motor

for moving

Library installation

Microstep Driver

For programming and power supply

Library installation

my building in button xiao

fro operation the stepper motor

Initial Challenges Encountered

🚫 Challenge 1: XIAO RP2040 Hardware Serial Not Working

The first major challenge I faced was that the hardware serial pins (RX/TX) on my XIAO RP2040 were not functioning properly. When I tried to use the default Serial1 on pins d6 and d7, I couldn't establish communication with the Arduino Uno.

Symptoms:

  • No data transmission between devices
  • Serial monitor showing no incoming messages
  • Hardware serial pins appeared to be unresponsive

✅ Solution 1: Implementing SoftwareSerial

To overcome the hardware serial issue, I decided to use the SoftwareSerial library, which allowed me to use any digital pins for serial communication.

Steps taken:

  1. Added SoftwareSerial.h
  2. Selected alternative pins (D4 and D5 on XIAO)
  3. Configured SoftwareSerial with same baud rate on both devices

Understanding SoftwareSerial Library

What is SoftwareSerial? The SoftwareSerial library allows you to create serial ports on any digital pins of your microcontroller. This is especially useful when:

  • Your hardware serial pins are already in use
  • You need multiple serial connections
  • Hardware serial pins are not functioning properly (as in my case)
  • You want flexibility in pin assignment

🚫 Challenge 2: Arduino Uno Conflicts

The second challenge arose when trying to use certain pins on the Arduino Uno for SoftwareSerial. Some pin combinations caused conflicts with other functionalities or simply didn't work reliably.

Issues faced:

  • Interference with hardware serial (pins 0 and 1)
  • Some pin combinations resulted in garbled data
  • Timing issues with certain pin selections

✅ Solution 2: Finding Compatible Pin Configuration

Through experimentation, I found that using pins 7 and 8 on the Arduino Uno for SoftwareSerial provided stable communication.

Final working configuration:

  • XIAO RP2040: D4 (RX) and D5 (TX)
  • Arduino Uno: Pin 8 (RX) and Pin 7 (TX)
  • Proper cross-connection: TX→RX and RX→TX

Connection

Library installation
XIAO RP2040 Connection Arduino Uno
D5 (TX) Pin 8 (RX)
D4 (RX) Pin 7 (TX)
GND GND

Final Working Code

XIAO RP2040 Code

// XIAO RP2040 - Simple Chat
#include <SoftwareSerial.h>

// Define SoftwareSerial pins
const int RX_PIN = 6;  // D4 on XIAO RP2040
const int TX_PIN = 7;  // D5 on XIAO RP2040
SoftwareSerial mySerial(RX_PIN, TX_PIN);

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
 
  Serial.println("XIAO Chat Ready!");
  Serial.println("Type messages to send to Arduino Uno");
 
  // Say hello
  mySerial.println("Hello from XIAO!");
}

void loop() {
  // If we receive something from Uno, display it
  if (mySerial.available()) {
    String message = mySerial.readStringUntil('\n');
    Serial.print("Uno says: ");
    Serial.println(message);
  }
 
  // If user types something, send it to Uno
  if (Serial.available()) {
    String message = Serial.readStringUntil('\n');
    mySerial.println(message);
    Serial.print("You said: ");
    Serial.println(message);
  }
}

Arduino Uno Code

#include <SoftwareSerial.h>

// Use pins that work for you
const int RX_PIN = 8;  // Connect to XIAO TX (D5)
const int TX_PIN = 7;  // Connect to XIAO RX (D4)
SoftwareSerial mySerial(RX_PIN, TX_PIN);

void setup() {
  Serial.begin(9600);    // For Serial Monitor
  mySerial.begin(9600);  // For XIAO communication
 
  Serial.println("Arduino Uno Chat Ready!");
  Serial.println("Type in Serial Monitor to send to XIAO");
 
  // Send hello through SoftwareSerial
  mySerial.println("Hello from Arduino!");
}

void loop() {
  // Check for messages from XIAO
  if (mySerial.available()) {
    String message = mySerial.readStringUntil('\n');
    Serial.print("XIAO says: ");
    Serial.println(message);
  }
 
  // Check for input from Serial Monitor
  if (Serial.available()) {
    String message = Serial.readStringUntil('\n');
    mySerial.println(message);
    Serial.print("You sent: ");
    Serial.println(message);
  }
}

Video Demonstration

IAO RP2040 Button Control to Arduino Uno Stepper Motor

Creating a button-controlled stepper motor system with serial communication between two microcontrollers

What I Built

I created a networked system where a button on my XIAO RP2040 controls a stepper motor connected to an Arduino Uno. When the button is pressed, the XIAO sends serial commands to the Uno, which then moves the stepper motor between 0° and 90° positions.

How I Connected Everything

The project involves two separate circuits connected via serial communication:

XIAO RP2040 Connections:

Component XIAO Pin Function
Button D2 User input (with internal pull-up)
TX (Serial) D5 (GPIO 7) Send data to Uno
RX (Serial) D4 (GPIO 6) Receive data from Uno
GND GND Common ground

Arduino Uno to Stepper Driver:

Component Arduino Pin Function
STEP Pin 2 Step signal to driver
DIR Pin 3 Direction control
ENABLE A0 Enable/disable motor
TX Pin 1 Send data to XIAO
RX Pin 0 Receive data from XIAO

Important: TX from XIAO connects to RX on Uno, and RX from XIAO connects to TX on Uno (crossed connection). Don't forget to connect the grounds together!

Complete wiring setup

How it's Works

The system operates using serial communication protocol:

  1. Button press on XIAO triggers a toggle between 0° and 90° positions
  2. XIAO sends serial command ("1,0" or "1,90") via SoftwareSerial on D4/D5
  3. Arduino Uno receives command through hardware serial (pins 0/1)
  4. Uno processes command and moves stepper motor to specified angle
  5. Built-in LED on XIAO indicates current position (ON = 90°, OFF = 0°)

XIAO RP2040 Code

This code handles button input and sends serial commands:

#include <SoftwareSerial.h>

// Define SoftwareSerial on D4(RX) and D5(TX)
const int RX_PIN = 6;  // D4 on XIAO RP2040
const int TX_PIN = 7;  // D5 on XIAO RP2040
SoftwareSerial mySerial(RX_PIN, TX_PIN);

const int BUTTON_PIN = 2;     // Button on D2 (GPIO 2)
const int LED_PIN = LED_BUILTIN;  // Built-in LED for status
bool motorPosition = false;   // false = 0°, true = 90°

void setup() {
  // Initialize USB Serial for debugging
  Serial.begin(115200);
  while (!Serial && millis() < 5000);
  
  // Initialize SoftwareSerial for Arduino Uno communication
  mySerial.begin(9600);
  
  // Setup button and LED
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  
  delay(1000);
  
  Serial.println("XIAO RP2040 Ready");
  Serial.println("Using D4(RX) and D5(TX) for serial communication");
  Serial.println("Press button D2 to toggle motor position");
}

void loop() {
  // Button handling with debouncing
  static bool lastButtonState = HIGH;
  static unsigned long lastDebounceTime = 0;
  const unsigned long debounceDelay = 50;
  
  bool reading = digitalRead(BUTTON_PIN);
  
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    static bool buttonState = HIGH;
    
    if (reading != buttonState) {
      buttonState = reading;
      
      if (buttonState == LOW) {
        // Button pressed - toggle position
        motorPosition = !motorPosition;
        
        if (motorPosition) {
          mySerial.println("1,90");
          Serial.println("Sent to Uno: 1,90");
          digitalWrite(LED_PIN, HIGH);
        } else {
          mySerial.println("1,0");
          Serial.println("Sent to Uno: 1,0");
          digitalWrite(LED_PIN, LOW);
        }
        
        // Wait for button release
        while (digitalRead(BUTTON_PIN) == LOW) {
          delay(10);
        }
      }
    }
  }
  
  lastButtonState = reading;
}

Arduino Uno Code (Simplified)

This code receives commands and controls the stepper motor:

#include <SoftwareSerial.h>

// Constants and definitions
const int numSteppers = 1;
const int STEP_PINS[numSteppers] = {2};
const int DIR_PINS[numSteppers] = {3};
const int ENABLE_PINS[numSteppers] = {A0};

// Position tracking
long currentStepperPositions[numSteppers] = {0};
int stepperSpeed[numSteppers] = {500};

// Microstep settings
const int MICROSTEPS = 16;
const int STEPS_PER_REV = 12.5 * MICROSTEPS;

void setup() {
  Serial.begin(9600);
  
  // Initialize stepper motor pins
  pinMode(STEP_PINS[0], OUTPUT);
  pinMode(DIR_PINS[0], OUTPUT);
  pinMode(ENABLE_PINS[0], OUTPUT);
  
  // Enable the stepper motor
  digitalWrite(ENABLE_PINS[0], LOW);
  
  Serial.println("Arduino Uno Stepper Controller Ready");
}

void loop() {
  if (Serial.available()) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    
    Serial.println("Received: " + input);
    processCommand(input);
  }
}

void processCommand(String command) {
  int commaIndex = command.indexOf(',');
  if (commaIndex > 0) {
    String motorStr = command.substring(0, commaIndex);
    String angleStr = command.substring(commaIndex + 1);
    
    int motorNum = motorStr.toInt();
    float angle = angleStr.toFloat();
    
    if (motorNum == 1) {
      Serial.print("Moving to angle: ");
      Serial.println(angle);
      moveToAngle(angle);
    }
  }
}

void moveToAngle(float angle) {
  long targetSteps = angleToSteps(angle);
  moveToPosition(targetSteps);
}

long angleToSteps(float angle) {
  return (long)((STEPS_PER_REV * angle) / 360.0);
}

void moveToPosition(long targetSteps) {
  long stepsToMove = targetSteps - currentStepperPositions[0];
  
  if (stepsToMove == 0) return;
  
  // Set direction
  bool forward = (stepsToMove > 0);
  digitalWrite(DIR_PINS[0], forward ? HIGH : LOW);
  
  // Calculate delay
  int stepDelayMicros = 1000000 / stepperSpeed[0];
  
  // Move the motor
  long stepsAbsolute = abs(stepsToMove);
  for (long i = 0; i < stepsAbsolute; i++) {
    digitalWrite(STEP_PINS[0], HIGH);
    delayMicroseconds(2);
    digitalWrite(STEP_PINS[0], LOW);
    delayMicroseconds(stepDelayMicros - 2);
  }
  
  // Update position
  currentStepperPositions[0] = targetSteps;
  
  Serial.print("Moved to position: ");
  Serial.print(currentStepperPositions[0]);
  Serial.println(" steps");
}

Video Demonstration

Communication Protocol Explained

XIAO RP2040 Side:

  • Uses SoftwareSerial library for flexible pin assignment
  • Monitors button state with debouncing to prevent false triggers
  • Sends formatted commands: "1,angle" (e.g., "1,90" or "1,0")
  • Uses built-in LED to indicate current position

Arduino Uno Side:

  • Uses hardware serial for reliable communication
  • Parses incoming commands to extract motor number and angle
  • Converts angle to steps based on microstepping configuration
  • Controls stepper motor using STEP and DIR signals

Serial Configuration:

  • Baud rate: 9600 (must match on both devices)
  • Data format: "motor_number,angle" (e.g., "1,90")
  • Line ending: '\n' (newline character)
  • Full duplex communication (both directions possible)

Challenges I Faced

Serial Pin Selection

Initially tried using the default Serial1 on XIAO (GP0/GP1) but had conflicts. Solved by switching to SoftwareSerial on D4/D5.

Button Debouncing

The button was triggering multiple times per press. Implemented proper debouncing with a 50ms delay to ensure clean state changes.

Cross-Connection Confusion

Initially connected TX to TX and RX to RX, which didn't work. Remembered that serial communication requires crossed connections (TX to RX).

What I Learned

This project taught me several important concepts about networking and communications:

  • Serial communication requires proper TX/RX cross-connection
  • SoftwareSerial provides flexibility when hardware serial pins are occupied
  • Proper command formatting and parsing is crucial for reliable communication
  • Ground connection between devices is essential for serial communication
  • Debouncing is critical for reliable button input