As it was foretold, the second posting…

It took me a while to post again, mostly because I was trying to iron out a few wrinkles in my robot before I started going on about how great it is. There are still a few bugs, but here it is anyway…

A couple of years ago I bought a Nerf Stampede, inspired by my mate Josh. When my girlfriend, now wife, told me that I was too old to buy nerf guns, I replied “But all the other kids have one”. As this wasn’t the compelling argument that I thought it was, I was forced to disregard her objections and rolling eyes, and get one anyway.

I won’t go into detail regarding the modifications that I did, there are plenty of sites that talk about modding nerf guns, but essentially, I removed the air restrictor, put in a stronger spring, added a relay and connection for an external firing signal, and a connection for an external 12V battery.

Naturally, my next thought was to stick it on a robot that could do my evil bidding.

Here are a couple of pictures of the first iteration (remote controlled over local wifi; nerf stampede cannon; laptop webcam for telecontrol)

ver 1 - side view ver 1 - angled view

So, I wanted the robot to have sufficient power to propel itself, including the modified Nerf Stampede and any associated electronics/control equipment at a quick walking speed (aiming for minimum 7.5km/h), and have enough battery life to have some fun (30 minutes).

I wanted the robot to be controlled over a wireless network.

Originally, the robot was to use geared DC motors, rubber wheels and a custom build frame. I later received a powerchair (Jazzy 1122) which was kindly donated to science.

The Jazzy was quite old and had not been used for years, so the batteries were almost completely dead (enough life left to commission the robot, but not enough for prolonged testing). I was fortunate enough to receive some second-hand 50Ah 12V deep cycle batteries (two of), which are performing admirably. I love scrounged parts, and I’m glad I didn’t have to buy NEW batteries, which are quite expensive.

While I initially tried to use a very rudimentary interface board (Velleman k8055), this caused me to waste more time than necessary, just trying to get the inputs and outputs required. Had I been driving the motors directly using a power mosfet circuit (PWM H-bridge), this board would have been fine, but I later decided that building such a high current board would be a hassle, and decided to hijack the existing powerchair control circuit.

I ended up doing this by using an Arduino Mega 2560 R3. These boards have multiple PWM outputs, and by running the PWM signals through a low pass RC filter, you can convert it to a DC voltage that varies proportionally with PWM duty cycle.

I wish I had discovered Arduino long ago. I love it seriously, the possibilities are endless.

Electronics of Jazzy 1122

Below is a link to the datasheet to the joystick used on THIS PARTICULAR jazzy 1122 works (there are a few variations on the pilot system, but this seems to be a fairly common joystck).

D50800-03[1] – joystick potentiometer info

Here are some photos showing the connection inside the controller. I drilled a hole in the casing for the controller, and ran some network cable through, with a 2.54mm plug on each end, so that I can easily disconnect the chair controller from the robot control box. The white plug is my plug in, the green plug is the original connection from the joystick to the chair controller.

SAMSUNG SAMSUNG

20131005_16320220131005_163152

This controller has two outputs per axis on the joystick. As far as I can tell, each of these outputs must be at 2.5V (centre position), give or take, in order for the controller to not throw up an error when you turn it on. The two outputs on each axis then vary proportional to deflection of the joystick (at FULL position, one goes up to about 3.89V, the other down to about 1.11V). I didn’t do extensive testing, but I think that if these don’t both change at the required rate (i.e. if one were to stay at 2.5v), the chair will not perform properly.

This controller was configured with two speed settings, and both were programmed to be VERY slow. I was lent one of the small computers required to program these settings, and have since re-configured the controller so that Speed 1 is “quick” and Speed 2 is “very [dangerous, do not use inside or near your car] quick”.

Hijacking the controls of the jazzy, by sending fake signals to the joystick pins, I was able to make use of the motor drivers that come with the chair. These fake signals took the form of 2.5V DC voltages, which varied up to 3.89V or down to 1.11V, as described above.

Circuit design

I put in RC filters to convert the 5V PWM from the Arduino into 0-~5V analogue voltages that the chair controller will accept:

schematic extract

R=1000 Ohm

C=100 uF

And I used the following duty cycle set points to convert controller inputs into signals for the chair:

Upper 3 = 202 = 3.89 V

Upper 2 = 177 = 3.43 V

Upper 1 = 151 = 2.96 V

Neutral = 127 = 2.5 V

Lower 1 = 101 = 2.04 V

Lower 2 = 77 = 1.57 V

Lower 3 = 51 = 1.11 V

Note: I’ve just added a Map function to the code, to allow linear conversion of the controller inputs to the wheelchair input signals (instead of using the 3 step system shown above).

NOTE: if you build a similar circuit for a powerchair, you may need to fiddle with the PWM set points to get precisely the right output voltages, depending on the chair controller and your low pass filter design.

So once the electronics were set up, I just needed to send my inputs to the Arduino. Initially I just connected the Arduino to an old laptop via USB, and sent the commands over the serial connection. The laptop was connected to my wifi, and I wrote some software to send commands from a PS2 controller connected to another laptop. Using the webcam on the robot’s laptop meant that I could skype in and see through the robot’s eyes, to drive it remotely. Phew! that’s a bit convoluted. So:

PS2 —(USB)—> laptop –(wifi)—> laptop —(usb)—> arduino —(analogue signals)—>Chair controller.

Here’s a video:   (Remote Control Jazzy 1122 + Modded Nerf Stampede)

UPGRADES:

I decided that that was all a bit too involved. I put an ethernet shield on the arduino, connected it to a wifi router mounted on the robot (for better range and portability).

I also added a 24V-12V converter, so that I could run the Stampede, Wifi router, and arduino all off the powerchair batteries. This saves me having to worry about multiple batteries. The photo below shows the Power Strip on the right, with a (BLUE) Low Power / Continuous Use terminals, to provide power to the arduino (and anything else that I decide to install on the robot) which has low current draw with little in the way of fluctuations. The (ORANGE) High/Intermittent Power strip is for things like the nerf gun, which will draw more power, but may vary greatly. I intend to swap the low power circuit over to 5V by adding a second converter, as I’m currently having some issues with the current power converter.

20131005_163030

The next photo shows an isolation switch (little white one), the 24V-12V converter, a software kill switch (big red one, obviously), an LED to confirm when the isolation switch is on, and a terminal for connecting the electronics box too the powerchair batteries.

20131005_162947

I printed mounting brackets for the breadboard, powerstrip, router, etc, on my 3D printer (I’ll post about this later, see some of my miscelanious designs here):

20131005_162951 20131005_163036

As you can see, my style of electronics is best described as “Frankensteining bits together as I go”.

All of this allowed me to first control the robot using a PS2 controller connected to my laptop, which was connected to the robot’s Wifi router…

Jazzy 1122 wireless robot

… Then I modified the arduino code to accept UDP signals from an app on my phone (using “IPGamepad” on android (see also), but I intend to write my own app, to be better suited to my specific needs). This is cool, because I can now just turn on the switch, and while I’m opening up the app on my phone, the wifi router turns on and the arduino connects, making the robot ready for action.

Smartphone controlled powerchair

That’s all for now. I will post more as I finish the current round of upgrades (I’ve just added an LCD screen which glows a reassuring blue and displays useful information). I’ve posted a copy of the Revision 3 arduino code below, which works with IPGamepad. If anyone wants it, I’ll see if I can put up the software that I used for sending the commands from a PS2 controller connected to a computer.

Key Lessons Learnt:

  1. Kill Switches!!! When I first got the robot running, I had a few little glitches, which meant that on a few rare occasions, it went insane and tried to kill me. This inspired me to install the kill switch. Unfortunately it’s not a batter isolation switch (didn’t want to spend the money), it’s just a software kill switch that just sets the outputs to neutral and stops the robot from accepting new inputs, but it does the job.
  2. Arduino is very powerful for this sort of hobby robotics, and cheap too. There might be other types of boards out there, but I’m not familiar with them. They are cheap enough that there’s no excuse to try to boot-strap other bits of technology together to make your device, just fork out the money for an arduino board and you will save yourself a lot of heartache.
  3. Hot Glue Guns (the type you use for art and craft) are extremely useful for electronics projects. I used one to insulate soldered joins between two wires (to stop them from touching other circuits in the same enclosure). I also used them to hold my breadboard in place, loose alligator clip connections in place, etc. Firm enough to hold parts of a prototype circuit in place, but not so permanent that you can’t peel the glue off at the end of the project.
  4. Electric wheelchair repair companies can be a potential source of second hand parts and batteries. They are often replacing clients’ chairs, and use the old ones for spares.

Reference Info:

Arduino Code (latest fully tested UDP version)

//!!!!! DEFINE NETWORK STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

IPAddress ip(10, 1, 1, 177);
unsigned int localPort = 8888;      // local port to listen on

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

//!!!!! END DEFINE NETWORK STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//!!!!! DEFINE ROBOT STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//define variables here
int ledPin = 13;
int val = -1;
int vX;
int vY;
int vF;

int Fore_PWM = 4;
int Aft_PWM = 5;
int Left_PWM = 7;
int Right_PWM = 6;
int Fire_OUT = 22;
int Fire_IN = 23;
int Fore_IN = A1;
int Left_IN = A2;
int kill_switch = 24; //interupt pin for kill switch, normally high

//int Manual_IN=28; //switch for manual control. closed circuit for manual

int AnalogLower = 0;
int AnalogUpper = 1023;
int PWMLower = 0;
int PWMUpper = 255;
int VoltsLower = 0;
int VoltsUpper = 500;

int JazzyUpper3   = 202;
int JazzyUpper2   = 177;
int JazzyUpper1   = 151;
int JazzyNeutral = 127;
int JazzyLower1   = 101;
int JazzyLower2   = 77;
int JazzyLower3   = 51;

//int JazzyUpper3   = 212;
//int JazzyUpper2   = 186;
//int JazzyUpper1   = 160;
//int JazzyNeutral = 135;
//int JazzyLower1   = 108;
//int JazzyLower2   = 82;
//int JazzyLower3   = 57;

unsigned long lastTick=millis();
String Temp = “”;

//!!!!! END DEFINE ROBOT STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//!!!!! DEFINE IPGAMEPAD STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

const int PACKET_SIZE = 4; //Size of the joystick data packet
byte joystick_data[PACKET_SIZE]; //Byte array for incoming data – [0] = leftUP, [1] = leftRIGHT, [2] = rightUP, [3] = rightRIGHT

int gamepadUpper3   = 219;
int gamepadUpper2   = 182;
int gamepadUpper1   = 145;
int gamepadLower1   = 111;
int gamepadLower2   = 74;
int gamepadLower3   = 37;

//!!!!! END DEFINE IPGAMEPAD STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void setup() {
//!!!!! SETUP NETWORK STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  Serial.begin(9600);
 
  Serial.println(Ethernet.localIP());
  Serial.println(localPort);
  Serial.println(“Robot control system initialized.”);
 
  String inData = “”;
//!!!!! END SETUP NETWORK STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//!!!!! SETUP ROBOT STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
pinMode(ledPin,OUTPUT);

pinMode(Fire_OUT, OUTPUT);
digitalWrite(Fire_OUT,LOW);

pinMode(Fire_IN,INPUT);
pinMode(Fore_IN,INPUT);
pinMode(Left_IN,INPUT);
pinMode(kill_switch,INPUT);
//pinMode(Manual_IN,INPUT);

pinMode(Fore_PWM, OUTPUT); // sets the pin as output
analogWrite(Fore_PWM, JazzyNeutral); //sets reference voltage level= 125

pinMode(Aft_PWM, OUTPUT); // sets the pin as output
analogWrite(Aft_PWM, JazzyNeutral); //sets reference voltage level = 125

pinMode(Left_PWM, OUTPUT); // sets the pin as output
analogWrite(Left_PWM, JazzyNeutral); //sets reference voltage level= 125

pinMode(Right_PWM, OUTPUT); // sets the pin as output
analogWrite(Right_PWM, JazzyNeutral); //sets reference voltage level= 125

lastTick = millis();
//!!!!! END SETUP ROBOT STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

}

void loop() {
 
 
//    if (digitalRead(Manual_IN)==HIGH)
//  {
//    analogWrite(Fore_PWM, map(analogRead(Fore_IN), AnalogLower, AnalogUpper, PWMLower, PWMUpper));
//    analogWrite(Aft_PWM, map((256-analogRead(Fore_IN)), AnalogLower, AnalogUpper, PWMLower, PWMUpper));
//    analogWrite(Left_PWM, map(analogRead(Left_IN), AnalogLower, AnalogUpper, PWMLower, PWMUpper));
//    analogWrite(Right_PWM, map((256-analogRead(Left_IN)), AnalogLower, AnalogUpper, PWMLower, PWMUpper));
//
//  }
 
if (millis()-lastTick>100){
   timeout();
}
 
  // if there’s data available, read a packet
 
int packetSize = Udp.parsePacket();
if(packetSize)
{
  Udp.read(joystick_data, PACKET_SIZE); //read data into joystick_data array
//     Serial.println(“Contents:”);
//     Serial.println(“jostick_data[]: “);
//     Serial.println(joystick_data[0]);
//     Serial.println(joystick_data[1]);
//     Serial.println(joystick_data[2]);
//     Serial.println(joystick_data[3]);

    lastTick = millis();

vY=(long)joystick_data[0];
vX=(long)joystick_data[1];
vF=(long)joystick_data[2];

// !!!!!  DATA USE HERE   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

          if (digitalRead(kill_switch)==HIGH)//check for kill switch not hit (normally closed circuit, i.e. high), if not, deactivate robot
          {

          //CODE FOR COMMAND HERE!!!!!

              if (vX>gamepadUpper3)
              {
                //code here
                analogWrite(Right_PWM, JazzyUpper3);
                analogWrite(Left_PWM, JazzyLower3);
                
              }
              else if (vX>gamepadUpper2)
              {
                //code here
                analogWrite(Right_PWM, JazzyUpper2);
                analogWrite(Left_PWM, JazzyLower2);
              }
              else if (vX>gamepadUpper1)
              {
                //code here
                analogWrite(Right_PWM, JazzyUpper1);
                analogWrite(Left_PWM, JazzyLower1);
              }
              else if (vX<gamepadLower3)
              {
                //code here
                analogWrite(Right_PWM, JazzyLower3);
                analogWrite(Left_PWM, JazzyUpper3);
              }
              else if (vX<gamepadLower2)
              {
                //code here
                analogWrite(Right_PWM, (JazzyLower2));
                analogWrite(Left_PWM, (JazzyUpper2));
              }
              else if (vX<gamepadLower1)
              {
                //code here
                analogWrite(Right_PWM, (JazzyLower1));
                analogWrite(Left_PWM, (JazzyUpper1));
              }
              else
              {
                //code here
                analogWrite(Right_PWM, (JazzyNeutral));
                analogWrite(Left_PWM, (JazzyNeutral));
              }
              
              
              
              
              
              
              if (vY>gamepadUpper3)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyUpper3));
                analogWrite(Aft_PWM, (JazzyLower3));
                
              }
              else if (vY>gamepadUpper2)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyUpper2));
                analogWrite(Aft_PWM, (JazzyLower2));
              }
              else if (vY>gamepadUpper1)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyUpper1));
                analogWrite(Aft_PWM, (JazzyLower1));
              }
              else if (vY<gamepadLower3)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyLower3));
                analogWrite(Aft_PWM, (JazzyUpper3));
              }
              else if (vY<gamepadLower2)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyLower2));
                analogWrite(Aft_PWM, (JazzyUpper2));
              }
              else if (vY<gamepadLower1)
              {
                //code here
                analogWrite(Fore_PWM, (JazzyLower1));
                analogWrite(Aft_PWM, (JazzyUpper1));
              }
              else
              {
                //code here
                analogWrite(Fore_PWM, (JazzyNeutral));
                analogWrite(Aft_PWM, (JazzyNeutral));
              }
              
              
              
              if (vF>gamepadUpper3)
              {
                //code here
                digitalWrite(Fire_OUT,HIGH);
              }
              else
              {
                //code here
                digitalWrite(Fire_OUT,LOW);
              }

          }
          else
          {
            kill();
          }
    
            

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  }
  delay(10);
}

void kill(){
Serial.println(“kill switch hit”);
digitalWrite(Fire_OUT,LOW);
analogWrite(Fore_PWM, JazzyNeutral); //sets reference voltage level= 125
analogWrite(Aft_PWM, JazzyNeutral); //sets reference voltage level = 125
analogWrite(Left_PWM, JazzyNeutral); //sets reference voltage level= 125
analogWrite(Right_PWM, JazzyNeutral); //sets reference voltage level= 125
}

void timeout(){
digitalWrite(Fire_OUT,LOW);
analogWrite(Fore_PWM, JazzyNeutral); //sets reference voltage level= 125
analogWrite(Aft_PWM, JazzyNeutral); //sets reference voltage level = 125
analogWrite(Left_PWM, JazzyNeutral); //sets reference voltage level= 125
analogWrite(Right_PWM, JazzyNeutral); //sets reference voltage level= 125

}

Advertisements

2 thoughts on “As it was foretold, the second posting…

  1. Hi! Your YouTube videos and postings have sparked interest for my son & I to tackle this project on are recently acquired Jazzy. I would like to see if you can share information on how / what was needed to reprogram the controller for different speed settings.

    Thanks!

    • Hi jason,
      To change the settings on the Jazzy, you need a special device (it’s a little portable device with a screen and a couple of buttons) that plugs in and gives you access to the onboard computer’s settings. Not sure what the device is called or where you can buy one; I was loaned one by that care facility from where I acquired the Jazzy. The wheelchair maintenance technician organised it for me.
      I’d suggest talking to the original owners of the chair, to see who did their chair maintenance. Failing that, you could call up a local wheelchair sales/rent/maintenance shop, and see if you can bring the chair in and get them to hook up their computer to it. It only took a few minutes of exploring the settings, and I was able to increase the maximum speed and acceleration.
      One issue that I still have is that the chair gets an error when I try to drive up or down a steep slope, so I recommend seeing if the computer has a setting for maximum slope/grade/incline, and increasing it or disabling the setting altogether. Obviously this is an important feature if you’re riding in the chair and don’t want it to topple, but it’s just a hindrance if you live on a sloped block and don’t care if your robot accidentally tips over.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s