Ardubot + Xbee + Wiimote

This is a little project I have been working on recently. It’s my first real attempt at an arduino project.

I will have pictures and a site about it up soon.

Objective:
To control a robot built on the ardubot platform from a remote station with a Wiimote.

Hardware:
Ardubot platform-
Arduino Duemilanove
XBee Pro 900 XSC
9V battery pack

Ardubot basestation-
Arduino Duemilanove
XBee Pro 900 XSC
Wiimote (wired into an adafruit prototyping shield)

Code -

Here is the code for the ardubot platform. It’s a bit rough, but so is my C.

/*
Ardubot v0.1
by Jon Heard
crow@inthecrowsnest.com

The bot side of the software reads the XBee serial data,
splits the string, and controls the motors through a case.
The data from the base station is transmitted in the 
"A,255\r\n" format, the first character being the direction
of travel, and the interger being the PWM value to control
the motors. The PWM value is the radius of movement on the 
Wiichuck mapped to values 0-255 as transmitted by the base 
station. 

Code adapted from
Didier Stevens 
*/



int PIN_HBRIDGE_1A = 9;
int PIN_HBRIDGE_2A = 6;
int PIN_HBRIDGE_3A = 5;
int PIN_HBRIDGE_4A = 3;
char directions[6];
char dir;
int x;
int inByte = -1;
char inString[3];
int stringPos =0;
int radius;


void BotStop(){
  digitalWrite(PIN_HBRIDGE_3A, LOW);
  digitalWrite(PIN_HBRIDGE_4A, LOW);
  digitalWrite(PIN_HBRIDGE_1A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, LOW);
}

void BotForward(){
  digitalWrite(PIN_HBRIDGE_3A, HIGH);
  digitalWrite(PIN_HBRIDGE_4A, LOW);
  digitalWrite(PIN_HBRIDGE_1A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, HIGH);
}

void BotReverse(){
  digitalWrite(PIN_HBRIDGE_3A, LOW);
  digitalWrite(PIN_HBRIDGE_4A, HIGH);
  digitalWrite(PIN_HBRIDGE_1A, HIGH);
  digitalWrite(PIN_HBRIDGE_2A, LOW);
}

void BotRightTurn(int l, int r){
  digitalWrite(PIN_HBRIDGE_4A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, LOW);
  analogWrite(PIN_HBRIDGE_3A, l);
  analogWrite(PIN_HBRIDGE_1A, r);
}

void BotLeftTurn(int l, int r){
  digitalWrite(PIN_HBRIDGE_3A, LOW);
  digitalWrite(PIN_HBRIDGE_1A, LOW);
  analogWrite(PIN_HBRIDGE_4A, l);
  analogWrite(PIN_HBRIDGE_2A, r);
}

void BotForwardRight(int r){
  digitalWrite(PIN_HBRIDGE_3A, HIGH);
  digitalWrite(PIN_HBRIDGE_4A, LOW);
  digitalWrite(PIN_HBRIDGE_1A, LOW);
  analogWrite(PIN_HBRIDGE_2A, r);
}

void BotForwardLeft(int l){
  digitalWrite(PIN_HBRIDGE_1A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, HIGH);
  analogWrite(PIN_HBRIDGE_3A, l);
  digitalWrite(PIN_HBRIDGE_4A, LOW);
}

void BotReverseRight(int r){
  digitalWrite(PIN_HBRIDGE_3A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, LOW);
  digitalWrite(PIN_HBRIDGE_4A, HIGH);
  analogWrite(PIN_HBRIDGE_1A, r);
}

void BotReverseLeft(int l){
  digitalWrite(PIN_HBRIDGE_3A, LOW);
  digitalWrite(PIN_HBRIDGE_2A, LOW);
  digitalWrite(PIN_HBRIDGE_1A, HIGH);
  analogWrite(PIN_HBRIDGE_4A, l);
}

void handleSerial(){
  inByte = Serial.read();
  if ((inByte >= 65) && (inByte <= 74)){
    dir = inByte;
  }
  if (( inByte >= '0') && (inByte <= '9')){
    inString[stringPos] = inByte;
    stringPos++;
  }
  if (inByte == '\r'){
   radius = atoi(inString);
   stringPos = 0;
  }
}


void setup() {
  pinMode(PIN_HBRIDGE_1A, OUTPUT);
  pinMode(PIN_HBRIDGE_2A, OUTPUT);
  pinMode(PIN_HBRIDGE_3A, OUTPUT);
  pinMode(PIN_HBRIDGE_4A, OUTPUT);
  Serial.begin(9600);
  Serial.print("Hail!");
  BotStop();
}

void loop(){ 
  handleSerial();
switch (dir) {
  case 'A':
  BotForward();
  break;
  case 'B':
  BotReverse();
  break;
  case 'C':
  BotRightTurn(radius, radius);
  break;
  case 'D':
  BotLeftTurn(radius, radius);
  break;
  case 'E':
  BotForwardRight(radius);
  break;
  case 'F':
  BotForwardLeft(radius);
  break;
  case 'G':
  BotReverseRight(radius);
  break;
  case 'H':
  BotReverseLeft(radius);
  break;
  default:
  BotStop();
}
}

Here is the code for basestation.

/*
Ardubot Base Station v 0.1
by Jon Heard
crow@inthecrowsnest.com

The base station code was borrowed heavily from
Tim Herzel. Much thanks to him.

The base station code reads the X,Y of the WiiChuck and analyses
the data, assigns a direction code, and then computes the radius
of the joysticks movement and maps it to values 0-255 for the PWM
of the ardubot motors.

 *
 * This file is an adaptation of the code by these authors:
 * Tod E. Kurt
 *
 * The Wii Nunchuck reading code is taken from Windmeadow Labs
 * 
 */
#include <Wire.h>
#include <WiiChuck.h>
#include <math.h>
#define MAXANGLE 90
#define MINANGLE -90
WiiChuck chuck = WiiChuck();
int angleStart, currentAngle;
int tillerStart = 0;
double angle;
long x;
long y;
long r;
int maxx;
int maxy;
char dir;
char dirState;

void setup() {
  Serial.begin(9600);
  chuck.begin();
  chuck.update();
}


void loop() {
  delay(20);
  chuck.update(); 
  x = (int)chuck.readJoyX() *2;
  y = (int)chuck.readJoyY() *2;
 
  if (y> 40 && -40<= x && x <= 40){
    Serial.print("A");
} else {
  if (y <-40 && -40 <= x && x <= 40){
    Serial.print("B");
} else {
    if (-40 < y && y <=40 && x >= 40){
      Serial.print("C");
    } else {
      if (-40 < y && y <= 40 && x <= -40){
        Serial.print("D");
      } else {
        if (y >= 40 && x>= 40){
          Serial.print("E");
        } else {
          if (y >= 40 && x<-40){
            Serial.print("F");
          } else {
            if (y < -40 && x >= 40){
              Serial.print("G");
            } else {
              if (y < -40 && x < -40){
                Serial.print("H");
              } else {
                Serial.print("I");
              }
            }
          }
        }
      }
    }
}
}
  r = sqrt(sq(x) + sq(y));
  r = map(r,0,220,0,255);
  Serial.print(",");
  Serial.println(r);
}

Initial results:
Control is good. I’m able to drive it around my house no problem. Battery life hasn’t been tested and will be addressed at a later time.

Future improvements:
Addition of sensors, possibility of automation.
Possibly of a readout on the base station for the sensors.

Comments, Questions and Suggestions are welcome.

Looking forward to seeing the videos.

I see that you are communicating between the 2 Arduinos, using the XBees, at 9600 baud. Have you tried running them at faster speeds?

Some observations about the code. The receiver has a handleSerial method. That method does not call Serial.available(). I'm thinking that it should. There are lots of magic numbers in the code. A month from now, will you remember what 65 or 74 represent? I'd use either the character that the ASCII code represents ('A') or a #define statement (#define LetterA 65) and replace the 65 with LetterA.

After adding a character to inString, you need to add a null in the next position. Otherwise, the atoi function won't know where the string ends.

    inString[stringPos] = inByte;
    stringPos++;
    inString[stringPos] = '\0';   // Add a null terminator to the string

Why, on the receiver, does loop start with a delay statement? Why does the robot send a message ("Hail!") that the receiver ignores?

Other than these minor issues, the code is well organized. Subroutines perform one action, and that action is reflected in the name.

Good job!

Thank you for you input.

The particular model of XBee I'm using is limited to 9600 baud, I have 2 series 2 xbees but am unable to get them to work. The XBee-PRO XSC 900 work fine for the moment.

The delay on the receiver code got there by some copy pasting of code, should be elsewhere.

The "Hail!" message is there for when the bot is hooked up via cable and I'm watching on serial monitor. I guess I could put in some sort of start up verification on the receiver.

I'll make revisions and whatnot shortly. Just happy to have it working up to this point.

Any suggestions on adapting the code to allow transmission of sensor data in between the controller transmissions?

Adding sensor data ratchets up the complexity, considerably. Two way communication always does.

Each Arduino will need to stamp the messages it sends with a unique ID, and only concern itself with messages it receives from the other one.

The robot and base station must not block waiting for the other to send or read data.

The messages need to be kept short, so that reading what is available is quick.

Other than that, it's not really that difficult. Since you've made it this far, you should have no trouble making the feedback loop work.