Wireless RC Robot with Arduino and XBees

Pages
Contributors: bboyho
Favorited Favorite 2

Experiment 2: Wirelessly Driving Forward

Introduction

Now that we have successfully sent a character, the next step is to have the controller receive an input before sending a character. Once the receiving robot receives the character, we'll have the motors drive forward.

Parts Needed

You will need the following parts from the required materials:

  • 2x XBees (Configured to Series 1 Firmware)
  • 1x Cerberus Cable (or 1x micro-B USB and 1x mini-B USB Cable)
  • 1x Assembled Shadow Chassis w/ RedBot Mainboard
  • 4x AA Batteries
  • 1x Assembled Wireless Joystick
  • 1x LiPo Battery

2.1: Remote Control w/ the SAMD21

For this part of the experiment, we are going to send a character from the controller based on the input. For simplicity, we will send a character when a button is pressed (D2) and the left joystick is in the up position.

Hardware Hookup

If you have not already, connect the controller to your computer via USB cable.

micro-B USB Cable inserting into Wireless Joystick

Open the Sketch

Copy and paste the following code into the Arduino IDE. Remember to select the SAMD21 DEV Breakout for the controller, select the COM port that it enumerated on, flip the Wireless Joystick's switch to the ON position, and hit upload.

language:c
/* 2_1_Remote_Control_SAMD21.ino
   Remote Control SAMD21 Example
   Written by: Ho Yun Bobby Chan
   Date: 2/15/19
   SparkFun Electronics

   license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
   Do whatever you'd like with this code, use it for any purpose.
   Please attribute and keep this license.

   This is example code for the Wireless Joystick with SAMD21. Any character entered through the
   Serial Monitor or when a condition statement is satisfied will be sent to the hardware UART pins.
   Assuming that you have a pair of XBees Series 1 modules (or Series 3 modules configured with 802.15.4 protocol) on the
   same channel, a character will be transmitted wirelessly between the XBees. The receiving
   XBee will then pass the character to the an ATmega328P microcontroller to move the robot forward.

   Pressing down on D2 (if you soldered the joystick on the right or a button) will check
   the joystick on the left. A character will be transmitted when moving the joystick:

       up = forward

   The RedBot will need to be programmed to read those values.

   Note: You may need to connect A5 to the XBee Series 3's reset pin on the Wireless Joystick
   for certain XBee Series 3 modules. For more details, check out the xbee3_RESET() function.
*/

#define FORWARD_REVERSE_JOYSTICK A3   // Pin used for left joystick's y-component
#define TURN_JOYSTICK A2   // Pin used for left joystick x-component

int buttonACCELERATE_State; //value to store the state of the button press
#define ACCELERATE_BUTTON 2 // Pin used for right trigger

// We'll store the the analog joystick values here
int16_t forward_reverse_Stick_value;
int16_t turnStick_value;

char c_data;//send values through the serial monitor for debugging

//LED to check if the LED is initialized.
const int status_LED = 13;

//needed for certain XBee Series 3 modules
#define xbee_reset A5

void setup() {

  SerialUSB.begin(9600);// Initialize Serial Monitor for DEBUGGING

  //Uncomment this if you want to wait until the serial monitor is open.
  //while (!SerialUSB); //Wait for Serial Monitor to Open

  SerialUSB.println("Wireless Joystick Controller Initializing");
  Serial1.begin(9600); // Start serial communication with XBee at 9600 baud

  xbee3_RESET();//in case XBee3 has issues initializing, hardware reset

  pinMode(ACCELERATE_BUTTON, INPUT_PULLUP); // Enable pullup resistor for accelerate button D2

  //Status LED to see if the RedBot is initializing
  pinMode(status_LED, OUTPUT);
  for (int i = 0; i < 3; i++) {
    digitalWrite(status_LED, HIGH);//set Status LED on
    delay(50);
    digitalWrite(status_LED, LOW); //set Status LED off
    delay(50);
  }

  SerialUSB.println("Wireless Joystick Controller's XBee Ready to Communicate");
  delay(10);
}//end setup

void loop() {

  //initialize variables to read buttons
  buttonACCELERATE_State = digitalRead(ACCELERATE_BUTTON);
  /***button1state
    - LOW or 0 means pressed
    - HIGH or 1 means not pressed
   ****/
  //Store values read joystick
  forward_reverse_Stick_value = analogRead(FORWARD_REVERSE_JOYSTICK);
  turnStick_value = analogRead(TURN_JOYSTICK);

  //send commands via serial monitor for testing here
  if (SerialUSB.available()) {
    c_data = SerialUSB.read();//take character from serial monitor and store in variable

    Serial1.print(c_data);//send to XBee

    //echo back what was sent to serial monitor
    SerialUSB.println("Sending Character Here, ");
    SerialUSB.println(c_data);
    digitalWrite(status_LED, HIGH); //turn ON Status LED
  }

  if (buttonACCELERATE_State == LOW) {
    SerialUSB.println("Accelerate Button has been pressed!");

    if (forward_reverse_Stick_value > 1000) {
      SerialUSB.println("Forward");
      Serial1.print('A');//transmit to RedBot via XBees on the same channel
      digitalWrite(status_LED, HIGH); //turn ON Status LED
    }

    //Debug left analog joystick here
    //Boundaries vary depending on the joystick's read value
    //You may need to adjust the values in the condition statements to calibrate
    //Additional condition statements will need to be written for pivoting
    //and turning in reverse
    SerialUSB.print("forward_reverse_Stick_value  = "); //~1023 up, ~7-9 down
    SerialUSB.println(forward_reverse_Stick_value);
    SerialUSB.println("turnStick_value = "); //~1023 left, ~5-6 right
    SerialUSB.println(turnStick_value);
  }

  delay(100); //add short delay for LED for feedback, this can be commented out if it is affecting performance
  digitalWrite(status_LED, LOW); //turn OFF Status LED
}//end loop

void xbee3_RESET() {

  //HARDWARE RESET
  /*
    - XBee Series 3 Hardware Reference Manual
    - Pg 31 Power Supply Design recommends decoupling capacitor between Vcc and GND.
      Tested with 10uF capacitor and without. This was not necessary.
    - Pg 60 Brown Out Detection. This is REQUIRED. Add a jumper between the XBee's Reset and A5
    https://www.digi.com/resources/documentation/digidocs/pdfs/90001543.pdf

    - Power cycle XBee Series 3 by grounding RESET Pin to avoid dicontinuities in ramp up and brown out detection
    https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2017/06/14/rmu_e203_avdd_ramp-j176

    - Minimum Time to Force Reset:
    - EFM32 devices = 50ns; EFM32PG/JG: Pearl and Jade Gecko =100ns
    https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2016/07/22/minimum_reset_holdt-PglD
  */
  pinMode(xbee_reset, OUTPUT);
  digitalWrite(xbee_reset, HIGH);
  delayMicroseconds(1);
  digitalWrite(xbee_reset, LOW);
  delayMicroseconds(1);
  digitalWrite(xbee_reset, HIGH);

  /*
    //SOFTWARE RESET
    //Software reset does not work with XBee Series 3... Needs a hardware reset
    delay(500);//wait until XBee Series 3 to start up after hardware reset
    Serial1.write("+++"); //Enter Command Mode
    delay(500);//short delay as the XBee gets into command mode
    Serial1.write("ATFR");//AT command for software reset
    Serial1.write("\r");//carriage return
    Serial1.write("\n");//new line
  */
}

Code to Note

Adding on to the code in 1.1, we'll set up the pins for the joystick. Since the joystick has two potentiometers to determine it's position, we'll set up the analog read pins and have a variable to store the readings. We'll also set up the pin for the button connected to D2 and create a variable to determine the button state.

After initializing the joystick, we'll read the button state and the analog pins before storing the values in their respective variables. We'll leave the section of code that takes in serial characters from the serial monitor for debugging. Finally, we'll check if the button D2 has been pressed and send a character when the joystick is in the up position. If we are connected to a computer, we'll echo the character back for debugging.

2.2 Robot Receive w/ the ATmega328P

For this part of the experiment, we are going to receive a character from the controller and have the RedBot drive forward.

Hardware Hookup

If you have not already, connect the Redbot to your computer via USB cable.

mini-B USB Cable inserting to RedBot

Open the Sketch

Copy and paste the following code into the Arduino IDE. Remember, we will need to select Arduino/Genuino Uno for the RedBot mainboard, select the corresponding COM port that it enumerated on, and flip the POWER switch to the ON position. Once these settings are adjusted, you can hit the upload button.

language:c
/* 2_2_Robot_Receive_ATmega328P.ino
   Robot Receive ATmega328P Example
   Written by: Ho Yun Bobby Chan
   Date: 2/15/19
   SparkFun Electronics

   license: Creative Commons Attribution-ShareAlike 4.0 (CC BY-SA 4.0)
   Do whatever you'd like with this code, use it for any purpose.
   Please attribute and keep this license.

   The first step to controlling the RedBot remotely is to first drive it
   from the Serial Monitor in a tethered setup. This is example code
   for the RedBot Mainboard with ATmega328P. After uploading this sketch,
   keep the RedBot tethered to your computer with the USB cable. Flip the
   switches to the respective sides: MOTOR => RUN and POWER => ON. You
   will also need to have UART flipped to the XBee_SW_Serial side.

   Assuming that you have a pair of XBees 1s (or 3 configured with
   802.15.4 protocol) on the same channel, a character will be
   transmitted wirelessly between the XBees. Any charactered received
   from the XBee connected to the software serial defined pins will
   be passed to the Serial Monitor. For troubleshooting, any character
   sent through the Serial Monitor will be echoed back. Try testing the
   controller to see if the robot will move forward or sending the following
   character through the Serial Monitor.

       A = forward

  If your motors are not moving forward when you send the forward command,
  simply flip the wiring. You can adjust the code but that would require
  adjusting more than one line of code. This does not account for motor
  intensity like the example that is used with the Wireless Joystick Example
  and RedBot Experiment 9.

  WARNING: Make sure to flip the switch to the XBEE_SW_SERIAL when
  you are uploading to the RedBot Mainboard. You will have issues uploading
  code and possibly brick your XBee.
*/

#include <RedBot.h>  //include RedBot library
RedBotMotors motors; //make instance of RedBot

char c_data;  // variable for holding incoming data from XBee to Arduino

// We'll use RedBot SoftwareSerial to communicate with the XBee:
// For Atmega328P's
// XBee's DOUT (TX) is connected to pin 14 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 15 (Arduino's Software TX)
#include <RedBotSoftwareSerial.h>
RedBotSoftwareSerial RedBotXBee; //make instance of Software Serial, pins     defined already in modified Software Serial Library

//LED to check if the LED is initialized.
const int status_LED = 13;

void setup() {
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  RedBotXBee.begin(9600);// Initialize SW for XBee for receiving serial
  Serial.begin(9600);// Initialize HW for Serial Monitor for DEBUGGING

  //Status LED to see if the RedBot is initializing
  pinMode(status_LED, OUTPUT);
  for (int i = 0; i < 3; i++) {
    digitalWrite(status_LED, HIGH);//set Status LED on
    delay(50);
    digitalWrite(status_LED, LOW); //set Status LED off
    delay(50);
  }

  Serial.println("RedBot Initialized!");
}//end setup

void loop() {

  if (RedBotXBee.available() || Serial.available()) {
    if (RedBotXBee.available()) {
      c_data = RedBotXBee.read();//store received value from XBee into variable

    }

    else if (Serial.available()) {
      c_data = Serial.read();//store received value from Serial Monitor into variable

    }

    Serial.println("Character Received, ");
    Serial.write(c_data);//send it out to serial monitor
    Serial.println();
    digitalWrite(status_LED, HIGH); //turn ON Status LED
    //delayMicroseconds(500);//add short delay for LED for feedback, this can be commented out if it is affecting performance

    if (c_data == 'A') {
      Serial.println("Drive Forward");
      RedBotXBee.write('A');
      digitalWrite(status_LED, HIGH); //turn ON Status LED

      motors.drive(255); //forward
   }
    else {
      //nothing was received, do nothing
    }


  }

  delay(100); // short pause so we are not constantly receiving characters
  motors.stop();//turn off motors
  digitalWrite(status_LED, LOW); //turn OFF Status LED
}//end loop

Code to Note

Since we are using the RedBot's motors, we'll need to set up the motors right after including the library. Again, we'll leave the section of code that takes in serial characters from the serial monitor for debugging. Note that this section of code also takes in characters received from the XBee and saves it in a variable.

Shortly after, we'll check to see what if the character matches what we sent from the controller. If the character matches, we'll move the wheels forward at full power. A short delay was added to have the robot move forward before shutting off the motors. For debugging, we'll send a message to the serial monitor indicating that we received a character to drive forward and light the LED briefly.


What You Should See

Disconnect the controller and robot from your computer. Insert the batteries to the boards. Pressing on the button connected to D2 and moving the left joystick to the up position will send a character out from the controller. The receiving RedBot should drive forward remotely!

Wireless Controller Commanding the Robot Moving Forward as a Demo