Buggy Wheelchair Robot
This is a Modification of an electric wheelchair.
I called it "Buggy" for two reasons. #1 My kids can still ride it around because I left the seat intact. #2 I haven't programmed anything for a long time.
I plan to create "jobs" for it in the near future.
I welcome any input. Keep in mind this is my first Instruct-able.
I found this chair on-line for a great price. The claim was that it was broken. The OEM battery charger had 2 volts output, the batteries are 24vdc. I found a 24VDC power supply and used the XLR plug that I cut from the charger and it worked fine. I did have to replace the batteries, they were old and tired.
I called it "Buggy" for two reasons. #1 My kids can still ride it around because I left the seat intact. #2 I haven't programmed anything for a long time.
I plan to create "jobs" for it in the near future.
I welcome any input. Keep in mind this is my first Instruct-able.
I found this chair on-line for a great price. The claim was that it was broken. The OEM battery charger had 2 volts output, the batteries are 24vdc. I found a 24VDC power supply and used the XLR plug that I cut from the charger and it worked fine. I did have to replace the batteries, they were old and tired.
Getting Power
This is the connector for the battery charger. It is a standard XLR microphone type plug. This can be connected at the joystick body or into the battery pack at the base.
With the center pin down, top left is green. This is Positive 24VDC. Top right is white. This is Negative 24VDC. The bottom center pin, from what I've read, is a programming pin to adjust various operating protocols for the drive systems. The manufacturer of this model doesn't share programming protocols.
With the center pin down, top left is green. This is Positive 24VDC. Top right is white. This is Negative 24VDC. The bottom center pin, from what I've read, is a programming pin to adjust various operating protocols for the drive systems. The manufacturer of this model doesn't share programming protocols.
Inside the Box
Upon power-up, the controller verifies that the joystick is centered. The voltage supplied at center is about 2.5 VDC. Knowing this I needed to allow the power-up to complete before I took control. So I used two DPDT Relays. Each directional feed from the joystick has two control lines, pins 2,4 and 5,7. I connected the common on the relay to the feed from the control board, the N.O. to the Arduino Uno circuit and the N.C. to the joystick.
Who's in Control
I had cut into the ribbon cable from the joystick to the controller. As shown in the schematic a few of the lines carry straight through. I soldered them all to a circuit board. This is the point that I broke out the directional lines to go to the relay bypass. Then as seen in the picture I had to cut the board in two to get it to fit into the joystick housing. I ran the relay control lines and the Arduino control line through a hole I drilled into the Joystick housing. Use a strain relief bushing and BE CAREFUL, when drilling. While in there I also soldered a 22 AWG 6 conductor (22/6) to the labeled test points that control power, on / off, speed control, 5 VDC supply and ground, and a spare conductor. These points can be found running down the left side of the control board. Then I closed and re-mounted the joystick on the wheelchair.
Connecting the Systems
Once our relays kick on and connect to the Arduino we will need to make sure we produce the 2.5vdc needed to keep the joystick from going in error mode. Remember, if the joystick is not centered (2.5Vcd) it will lockup and stop all operations. If you view the schematic showing pins 9 and 10 PWM, you will see how I connect the output of the Adruino to the input of the joystick. Using the joystick's 5vdc feed into the transistor and modulating it through the PWM output I can control the variance between the 4.0vdc and the 2.0vdc for forward / backward or left / right requests. This is controlled by activating the relay and then changing the PWM to get the direction that I want.
Aux Controls
Activating the unit and controlling the speed. Again using the source 5vdc from the joystick controller I am tripping a transistor to pulse each input. The power and speed buttons are momentary, in this case I will use the software to turn the output pin on the Arduino high then low with a 500ms delay, simulating a button press.
What's It Doing.
While working on this project I was given an old fire alarm panel. This panel had a Hitachi LCD display. This display, as I found is one of the most popular types available. So using one of the many examples on-line, I connected it to the Arduino. I use the LCD display to show me what the software is up to, Ie. on-line, power control, forward, backward, etc. I accomplished this using a simple LCD print command at each instruction. I also printed each "direction" to serial output as well.
Who, What, Where
The pin-out for the Arduino is as follows. Pin 0-1 are used as rx-tx. Pins 2,3,4,5,11,12,13 are used for the LCD. Pin 13, the onboard LED pin is connected to the LCD back-light. Pins 6+7 are for future use. Pin 8 trips the override relay. Pins 9+10 are the PWM outputs for directional control. I've used analog inputs 1,2,3 as outputs 14, 15, 16. Pin 14 is Speed up. Pin 15 is Speed down, and pin 16 is Power control.
Get Connected
During the programing I connected to the Arduino using the USB. But to control untethered I needed another option. Since being given the Fire Alarm panel and cabinet I chose to reuse what I had instead of buying new. So during the normal operation I unplugged the USB and connected a TTL to rs232 converter to pins 0+1. I also reused an old Ethernet to RS-232 converter connected to the TTL converter and a very old Linksys 80211b wireless router. I setup the router to bridge to my home network through a Linksys G router. I can telnet into the Arduino using a PC or cellphone on my wifi network and send commands to control the outputs. This connection is too slow however to make any program changes. All of my commands are one letter commands all caps. I.E. O=On, F=Forward, T= Take Control / Override.
Brains
I have pasted a copy of my program. Yes, I used Goto commands... Yes, I know many of you hate them. They worked for what I needed. I am open however to any constructive input on this project.
I haven't written a program since GWbasic. Old school I know. I realize this program looks alot like a basic flashback. It does however work just as I wanted.
/*
ROBOT DRIVE
Stephen J. Gardiner, CPS
Created 2012
Stephen J. Gardiner, CPS
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the LCD library
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int ledPin = 16; // the DRIVE SYSTEM POWER pin
const int lCdPin = 13; // lCdPin
const int redPin = 8; // OVER-RIDE ENGAGED
const int FASTPin = 14; // Speed-up
const int SLOWPin = 15; // Speed-Down
const int pwm1Pin = 9; // PWM Pin 1
const int pwm2Pin = 10; // PWM Pin2
int incomingByte; // a variable to read incoming serial data into
int OVERRIDEOFF;
void setup() {
Serial.begin(9600); // initialize serial communication:
// initialize the control pins as an outputs:
pinMode(ledPin, OUTPUT);
pinMode(lCdPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(FASTPin, OUTPUT);
pinMode(SLOWPin, OUTPUT);
pinMode(pwm1Pin, OUTPUT);
pinMode(pwm2Pin, OUTPUT);
lcd.begin(16,4); // set up the LCD's number of columns and rows:
lcd.setCursor(0, 0); // Print a message to the LCD.
digitalWrite(lCdPin, HIGH);
lcd.print(" ON-LINE ");
Serial.print("Robot Control v6.1" );
Serial.print("\r");
Serial.print(" ON-LINE" );
Serial.print("\r");
delay(1000);
digitalWrite(lCdPin, LOW);
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0,0);
// lcd.setCursor(0,3);
int val1 = 100; // variable to store DUTY CYCLE F + B
int val2 = 100; // variable to store DUTY CYCLE L + R
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO 2.5 (+/-)
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO 2.5 (+/-)
// see if there's incoming serial data:
if (Serial.available() > 0) {
// read the oldest byte in the serial buffer:
// Yes I know Goto Commands, Ya'll hatem' I works for me
incomingByte = Serial.read();
if (incomingByte == 'O') {goto DRIVEPOWER;}
if (incomingByte == 'T') {goto OVERRIDEON;}
if (incomingByte == 'C') {goto OVERRIDEOFF;}
if (incomingByte == 'I') {goto FASTER;}
if (incomingByte == 'D') {goto SLOWER;}
if (incomingByte == 'F') {goto FORWARD;}
if (incomingByte == 'B') {goto BACKWARD;}
if (incomingByte == 'L') {goto LEFT;}
if (incomingByte == 'R') {goto RIGHT;}
Serial.print("Robot Control v6.1 " );
lcd.print(" READY ");
Serial.print("\r");
return;
// if it's a capital O turn on the DRIVE:
{ DRIVEPOWER:
digitalWrite(ledPin, HIGH);
digitalWrite(lCdPin, HIGH);
lcd.print(" DRIVE SYSTEM POWER");
Serial.print("DRIVE SYSTEM POWER" );
delay(500);
lcd.setCursor(0,0);
digitalWrite(ledPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
int val = 169;
lcd.clear();
Serial.print("\r");
Serial.flush();
return;
}
{ OVERRIDEON:
// if it's an T OVERRIDE on:
digitalWrite(redPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" OVER-RIDE ENGAGED");
Serial.print("OVER-RIDE ENGAGED " );
delay(500);
lcd.setCursor(0,0);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
// if it's an C OVERRIDE off:
{ OVERRIDEOFF:
digitalWrite(redPin, LOW );
digitalWrite(lCdPin, HIGH);
lcd.print(" OVER-RIDE DIS-ENGAGED");
Serial.print("OVER-RIDE DIS-ENGAGED " );
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.print("\r");
Serial.flush();
return;
}
// if it's an F TO RAISE SPEED
{ FASTER:
digitalWrite(FASTPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" FASTER");
Serial.print("FASTER " );
delay(500);
lcd.setCursor(0,0);
digitalWrite(FASTPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
// if it's a capital S TO LOWER SPEED
{ SLOWER:
digitalWrite(SLOWPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" SLOWER");
Serial.print("SLOWER " );
delay(500);
lcd.setCursor(0,0);
digitalWrite(SLOWPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ FORWARD:
digitalWrite(lCdPin, HIGH);
lcd.print(" FORWARD");
Serial.print("FORWARD " );
int val2 = 200; // variable @ 200 DUTY CYCLE = FORWARD
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO FORWARD
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ BACKWARD:
digitalWrite(lCdPin, HIGH);
lcd.print(" BACKWARD");
Serial.print("BACKWARD " );
int val2 = 50; // variable @ 75 DUTY CYCLE = BACKWARD
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO BACKWARD
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ LEFT:
digitalWrite(lCdPin, HIGH);
lcd.print(" LEFT");
Serial.print("LEFT " );
int val1 = 50; // variable @ 100 DUTY CYCLE = LEFT
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO LEFT
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ RIGHT:
digitalWrite(lCdPin, HIGH);
lcd.print(" RIGHT");
Serial.print("RIGHT " );
int val1 = 220; // variable @ 200 DUTY CYCLE = RIGHT
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO RIGHT
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
}
}
I haven't written a program since GWbasic. Old school I know. I realize this program looks alot like a basic flashback. It does however work just as I wanted.
/*
ROBOT DRIVE
Stephen J. Gardiner, CPS
Created 2012
Stephen J. Gardiner, CPS
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the LCD library
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int ledPin = 16; // the DRIVE SYSTEM POWER pin
const int lCdPin = 13; // lCdPin
const int redPin = 8; // OVER-RIDE ENGAGED
const int FASTPin = 14; // Speed-up
const int SLOWPin = 15; // Speed-Down
const int pwm1Pin = 9; // PWM Pin 1
const int pwm2Pin = 10; // PWM Pin2
int incomingByte; // a variable to read incoming serial data into
int OVERRIDEOFF;
void setup() {
Serial.begin(9600); // initialize serial communication:
// initialize the control pins as an outputs:
pinMode(ledPin, OUTPUT);
pinMode(lCdPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(FASTPin, OUTPUT);
pinMode(SLOWPin, OUTPUT);
pinMode(pwm1Pin, OUTPUT);
pinMode(pwm2Pin, OUTPUT);
lcd.begin(16,4); // set up the LCD's number of columns and rows:
lcd.setCursor(0, 0); // Print a message to the LCD.
digitalWrite(lCdPin, HIGH);
lcd.print(" ON-LINE ");
Serial.print("Robot Control v6.1" );
Serial.print("\r");
Serial.print(" ON-LINE" );
Serial.print("\r");
delay(1000);
digitalWrite(lCdPin, LOW);
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0,0);
// lcd.setCursor(0,3);
int val1 = 100; // variable to store DUTY CYCLE F + B
int val2 = 100; // variable to store DUTY CYCLE L + R
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO 2.5 (+/-)
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO 2.5 (+/-)
// see if there's incoming serial data:
if (Serial.available() > 0) {
// read the oldest byte in the serial buffer:
// Yes I know Goto Commands, Ya'll hatem' I works for me
incomingByte = Serial.read();
if (incomingByte == 'O') {goto DRIVEPOWER;}
if (incomingByte == 'T') {goto OVERRIDEON;}
if (incomingByte == 'C') {goto OVERRIDEOFF;}
if (incomingByte == 'I') {goto FASTER;}
if (incomingByte == 'D') {goto SLOWER;}
if (incomingByte == 'F') {goto FORWARD;}
if (incomingByte == 'B') {goto BACKWARD;}
if (incomingByte == 'L') {goto LEFT;}
if (incomingByte == 'R') {goto RIGHT;}
Serial.print("Robot Control v6.1 " );
lcd.print(" READY ");
Serial.print("\r");
return;
// if it's a capital O turn on the DRIVE:
{ DRIVEPOWER:
digitalWrite(ledPin, HIGH);
digitalWrite(lCdPin, HIGH);
lcd.print(" DRIVE SYSTEM POWER");
Serial.print("DRIVE SYSTEM POWER" );
delay(500);
lcd.setCursor(0,0);
digitalWrite(ledPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
int val = 169;
lcd.clear();
Serial.print("\r");
Serial.flush();
return;
}
{ OVERRIDEON:
// if it's an T OVERRIDE on:
digitalWrite(redPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" OVER-RIDE ENGAGED");
Serial.print("OVER-RIDE ENGAGED " );
delay(500);
lcd.setCursor(0,0);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
// if it's an C OVERRIDE off:
{ OVERRIDEOFF:
digitalWrite(redPin, LOW );
digitalWrite(lCdPin, HIGH);
lcd.print(" OVER-RIDE DIS-ENGAGED");
Serial.print("OVER-RIDE DIS-ENGAGED " );
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.print("\r");
Serial.flush();
return;
}
// if it's an F TO RAISE SPEED
{ FASTER:
digitalWrite(FASTPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" FASTER");
Serial.print("FASTER " );
delay(500);
lcd.setCursor(0,0);
digitalWrite(FASTPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
// if it's a capital S TO LOWER SPEED
{ SLOWER:
digitalWrite(SLOWPin, HIGH );
digitalWrite(lCdPin, HIGH);
lcd.print(" SLOWER");
Serial.print("SLOWER " );
delay(500);
lcd.setCursor(0,0);
digitalWrite(SLOWPin, LOW);
delay(500);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ FORWARD:
digitalWrite(lCdPin, HIGH);
lcd.print(" FORWARD");
Serial.print("FORWARD " );
int val2 = 200; // variable @ 200 DUTY CYCLE = FORWARD
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO FORWARD
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ BACKWARD:
digitalWrite(lCdPin, HIGH);
lcd.print(" BACKWARD");
Serial.print("BACKWARD " );
int val2 = 50; // variable @ 75 DUTY CYCLE = BACKWARD
analogWrite(pwm2Pin,val2); // SET DRIVE- VOLTAGE TO BACKWARD
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ LEFT:
digitalWrite(lCdPin, HIGH);
lcd.print(" LEFT");
Serial.print("LEFT " );
int val1 = 50; // variable @ 100 DUTY CYCLE = LEFT
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO LEFT
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
{ RIGHT:
digitalWrite(lCdPin, HIGH);
lcd.print(" RIGHT");
Serial.print("RIGHT " );
int val1 = 220; // variable @ 200 DUTY CYCLE = RIGHT
analogWrite(pwm1Pin,val1); // SET DRIVE- VOLTAGE TO RIGHT
delay(1000);
lcd.setCursor(0,0);
digitalWrite(lCdPin, LOW);
lcd.clear();
Serial.flush();
Serial.print("\r");
return;
}
}
}
Extras
I have attached a zip file containing the schematics in a .sch format.
While working on this project and discussing it with friends I was told about modifications to these devices needed by various operators. These modifications vary from Sip and puff to chin controls. I hope that this project is a good starting point for these type of modifications.
While working on this project and discussing it with friends I was told about modifications to these devices needed by various operators. These modifications vary from Sip and puff to chin controls. I hope that this project is a good starting point for these type of modifications.