Wireless RC Robot with Arduino and XBees
Experiment 3: Wirelessly Controlling a Robot
Introduction
Now that we are able to wirelessly move our robot forward, we will want additional commands for turning and driving in reverse.
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
3.1: Full Remote Control w/ the SAMD21
For this part of the experiment, we will repeat the steps in 2.1 to transmit commands for turning or driving in reverse.
Hardware Hookup
Reconnect the controller to your computer via USB cable.
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.
/* 3_1_Full_Remote_Control_SAMD21.ino Full 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 right = forward turn right down = reverse left = forward turn left center = coast When D2 is not being pressed, a character will be sent to stop the motors. 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. */ int prev_buttonACCELERATE_State; //value to store the previous state of the button press int current_buttonACCELERATE_State; //value to store the current state of the button press // 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 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 Controller 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 current_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); } if (current_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 //delayMicroseconds(500);//add short delay for LED for feedback, this can be commented out if it is affecting performance } else if ( turnStick_value < 20) { SerialUSB.println("Turn Right"); Serial1.print('B'); 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 } else if (forward_reverse_Stick_value < 20) { SerialUSB.println("Reverse"); Serial1.print('C'); 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 } else if (turnStick_value > 1000) { SerialUSB.println("Turn Left"); Serial1.print('D'); 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 } else { SerialUSB.println("Coast"); digitalWrite(status_LED, HIGH); //turn ON Status LED Serial1.print('J'); } //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); } else {//current_buttonACCELERATE_State == HIGH //if not sending a command to drive, automatically have the robot stop moving SerialUSB.println("Stop"); digitalWrite(status_LED, HIGH); //turn ON Status LED Serial1.print('K'); } prev_buttonACCELERATE_State = current_buttonACCELERATE_State; //save current state 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
To add more commands, we simply add more if()
statements to check the position of the joystick on the right, down, and left positions. If the joystick is in any of those position, send a command to move the RedBot forward toward the right, reverse, and forward toward the left, respectively. If the joystick is in the center, we'll send a command to have the RedBot coast a bit. Otherwise, we'll continually send a command to have the RedBot stop when we are not pressing on D2.
3.2 Full Robot Control w/ the ATmega328P
For this part of the experiment, we will repeat the steps in 2.2 to receive the commands before moving the robot. Instead of just driving forward, the robot can now turn, and reverse.
Hardware Hookup
Reconnect the Redbot to your computer via USB cable.
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.
/* 3_2_Full_Robot_Control_ATmega328P.ino Full Robot Control 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 B = forward turn right C = reverse D = forward turn left J = coast K = stop 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. */ 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) 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 if (c_data == 'B') { Serial.println("Turn Right"); RedBotXBee.write('B'); digitalWrite(status_LED, HIGH); //turn ON Status LED motors.leftMotor(-200); // Turn on left motor power (motorPower = ) motors.rightMotor(100); // Turn on right motor power (motorPower = ) } else if (c_data == 'C') { Serial.println("Reverse"); RedBotXBee.write('C'); digitalWrite(status_LED, HIGH); //turn ON Status LED motors.drive(-255); //reverse } else if (c_data == 'D') { Serial.println("Turn Left"); RedBotXBee.write('D'); digitalWrite(status_LED, HIGH); //turn ON Status LED motors.leftMotor(-100); // Turn on left motor power (motorPower = ) motors.rightMotor(200); // Turn on right motor power (motorPower = ) } else if (c_data == 'J') { Serial.println("Coast"); RedBotXBee.write('J'); digitalWrite(status_LED, HIGH); //turn ON Status LED motors.coast(); } else if (c_data == 'K') { Serial.println("Stop"); RedBotXBee.write('K'); digitalWrite(status_LED, HIGH); //turn ON Status LED motors.stop(); } } //delay(100); // short pause so we are not constantly receiving characters digitalWrite(status_LED, LOW); //turn OFF Status LED }//end loop
Code to Note
Similar to 3.1, we'll add additional if()
statements to move the RedBot if the character matches what was sent. We'll adjust the motor power and direction for each condition when we receive the command to forward toward the right, reverse, forward toward the left, coast, or stop.
What You Should See
Disconnect the controller and robot from your computer. Press on D2 and move the joystick around. You should see the RedBot move toward the direction relative to the position of the joystick!