Arduino controlled wheelchair

Power wheelchair board for REAL info!

POWERCHAIR MENU! www.wheelchairdriver.com/powerchair-stuff.htm

Re: Arduino controlled wheelchair

Postby gcebiker » 08 Oct 2015, 12:21

Im not sure ill be able to measure the voltage swings.

The way it was wired up this afternoon, it was ok just sitting on the bench talking to the Laptop but when i moved the joystick i got motor errors (as they were not plugged in)

I did not take the board out and im not keen to either in case i damage the LCD / Cable, it has not happened many times but enough that i now dont move stuff unless i have to.

Ill open it back up and if i can get some readings ill post them here
Could you stick a link up for anyone following this tangent ..in case they are interested in your Controllers ?

Cheers
Tony
Gold Coast, Australia
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby woodygb » 08 Oct 2015, 23:03

I'm not sure ..I'd need to check ...but I think the ERROR messages have no effect on the actual joystick voltage being produced...it's just not acted on.
You should have been able to program the controller without connecting the brake wires or motors just ignore the ERROR messages .

Blue Tooth remote control link....R/C is very similar.
viewtopic.php?f=2&t=4479
User avatar
woodygb
 
Posts: 7128
Joined: 12 Mar 2011, 18:45
Location: Bedford UK

Re: Arduino controlled wheelchair

Postby gcebiker » 14 Oct 2015, 13:55

Work in progress,

Posting here if anyone else is working on detecting/modifying the serial data between a Dynamic Shark wheelchair controller and the Dynamic Shark Motor driver.

In prep for vision assist modules, stop assist to prevent damage to house walls / aid spastisity difficulties.

The people i am trying to help are not intellectually injured, its external control / communication , trapping them inside their bodies.

Hoping to further the work of Gizmosmith

This is my first go at writing original Arduino Code (rather than tweaking something else i have found)
this is also posted over on the Arduino forums.




Combining tested packets, getting ready to do
Read state (on/off)
Read On/off button
then write appropriate packet or stop serial write , ie digitalWrite (txPin, Low);

Code: Select all
// This sketch is to emulate the packet of data that the Shark Joystick sends
/******************************************************************************
    * Author: Tony Matthews ammatthews at gmail dot com
    * License: FreeBSD
    ******************************************************************************/
/******************************************************************************
Copyright (c) 2015, Tony Matthews
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

*******************************************************************************/

// This code is incomplete and still under revision.
// Its also got junk that may not be needed,
// Early testing phases.
// Successful output of Data packet for Shark Wheelchair controller.
// Not tested on chair, just on the Logic Analyzer.
// Pulse at one second for testing, chair sends pulse every 17ish microseconds

#include <SoftwareSerial.h>

#define rxPin 10      // Digital Pin10 for Receive from controller.
// this will have to be changed for the GCDuiNode as it uses this pin to communicate with the RF24

#define txPin 11      // Digital Pin11 Transmit from Shark Joystick to Controller.

// int sharkDataInt [10];    //Declare size of data packet sent from Shark Joystick
int joyStartByte;    //  was/is to setup/call function from new "void sharkJoyInt" .h file yet to be written.

// Names for Pins and what they are connected to
const int yPot = A0;    // Y Pot input value
const int xPot = A1;    // X Pot input value

// not used yet but will be one day...not that i ever dial my chair down...what's the point of going slow :P
int maxPot = A2;      // Max speed dial pot


// Names for analogRead values of Analog Pots
int yPotVal;
int xPotVal;
int maxSpeedVal;

// Names for Mapped analogRead Values of Pots
int yPotValMapped;
int xPotValMapped;


// Size of data packet, tho the packets are not constrained to this size.
// This size for this packet but probably not for others.
unsigned char data[11];
unsigned long timer = 0;

// set up a new serial port
SoftwareSerial sharkSerial (rxPin, txPin, true);    // TRUE sets this to invert software serial output.






void setup() {
  // put your setup code here, to run once:

// define pin modes for tx, rx:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  // set the data rate for the SoftwareSerial port
  sharkSerial.begin(38400);
}

void loop() {
  // Need to add
  // Check for state, is Shark Joy On or OFF ??
  // If OFF and button press "on" - run function 'sharkStartUp'
  // If ON and button press "off" - stop serial output
  // If ON and no button "on/off" - run function  'sharkOnMode'
 
 

}

void sharkStartup (){ //startup set of iverted serial data
// data set is incomplete, i will need to detect joy xy ..i think
// work in progress

  { 
    yPotVal = analogRead(yPot);
    xPotVal = analogRead(xPot);
    yPotValMapped = map(yPotVal, 0, 1023, 128, 255); // Map min/max pot values to match min/max original Shark joystick values.
    xPotValMapped = map(xPotVal, 0, 1012, 128, 255); // Map X values
  }


  {
    //build serial packet
    data[0] = (0x74);   // Joystik packets start with this " ' " not accepted had to use hex)
    data[1] = 130;     // Joystick Y value
    data[2] = 133;     // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;     // default horn off, horn on value is 130
    data[6] = 205;     // joystick On Value ?
    data[7] = 160;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;     // ?
    data[10] = 15;       // all packets end with this identifier

  }
  for (unsigned char i = 0; i < 11; i++)
    sharkSerial.write(data[i]);


  //  delay(17);    //Delay to next packet being sent.
}
 void sharkOnMode (){
  {
    yPotVal = analogRead(yPot);
    xPotVal = analogRead(xPot);
    yPotValMapped = map(yPotVal, 0, 1023, 128, 255); // Map min/max pot values to match min/max original Shark joystick values.
    xPotValMapped = map(xPotVal, 0, 1012, 128, 255); // Map X values
  }
  {
    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this " ' " not accepted had to use hex
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 192;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 212;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
             sharkSerial.write(data[i]);
    }
   
  delay(1) ; // 1.718ms till next "a" packet
  {
   
    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 146;     //
    data[2] = 128;     //
    data[3] = 192;      // 
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 204;
 
    for (unsigned char i = 0; i < 9; i++)
             sharkSerial.write(data[i]);
    }
     delay(1);
     
     sharkSerial.write(0x0f);
     
   
  delay(17);    //Delay next packet 17ms input from Shark joystick

}
   
 

User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 18 Oct 2015, 08:05

No response from joystick as yet.
Success...it turns "on" (starts writing the inverted serial data)
Success it turns "off" (stops writing)

Data Stream on Saleae Logic4 looks ok...but the RX is high when it needs to start low, go high when powered and stay high till turned off.
..still trying to find out how to do it,

Ive tried rx on pin 10 - opposite happens, starts low and wont let itself go high..ever.
(currently runs RX on Hardware UART pin0)

Ive go to be getting close for sure ?, its my first time coding ...please be gentle if i have missed something obvious...or even not so obvious.
Any constructive feed back is much appreciated.
Code: Select all
[code]


// This sketch is to emulate the packet of data that the Shark Joystick sends
/******************************************************************************
    * Author: Tony Matthews ammatthews at gmail dot com
    * License: FreeBSD
    ******************************************************************************/
/******************************************************************************
Copyright (c) 2015, Tony Matthews
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

*******************************************************************************/

// This code is incomplete and still under revision.
// Its also got junk that may not be needed,
// Early testing phases.


#include <SoftwareSerial.h>

// Names for Pins and what they are connected to
const int rxPin = 0;      // Digital Pin 0 for Receive from Motor controller.
// getting ready to use GCDuiNode with the RF24 or Wii Nunchuck (currently hard wired)

const int txPin = 1;          // Digital Pin 1 Transmit from Shark Joystick to Motor Controller.
const int onOffPin = 2;        // Digital Pin 2 is the software on/off button
boolean onOffstate = LOW;      //name holder for what the state is at the moment.

const int yPot = A0;    // Y Pot input value
const int xPot = A1;    // X Pot input value

const int maxPot = A2;      // Max speed dial pot


// Names for analogRead values of Analog Pots
int yPotVal;
int xPotVal;
int maxSpeedVal;

// Names for Mapped analogRead Values of Pots
int yPotValMapped;
int xPotValMapped;


// Size of data packet, tho the packets are not constrained to this size.
// This size for this packet but probably not for others.
unsigned char data[11];
unsigned long timer = 0;

// set up a new serial port
// digitalWrite(rxPin, LOW); //attempting to get RX to start low before going high when 'on'state
SoftwareSerial sharkSerial (rxPin, txPin, true);    // TRUE sets this to invert software serial output.
// i may need to comment out some lines of the .file to let the RX start off low.





void setup() {
  // define pin modes for tx, rx:
  // not sure its needed, seems to work when commented out
  pinMode(rxPin, INPUT);      // software serial inverted recieve pin
  pinMode(txPin, OUTPUT);     // softwaer serial inverted transmit pin
  pinMode(13, OUTPUT);        // Arduino led to monitor on or off state.
  pinMode(onOffPin, INPUT_PULLUP);    // Software on/off button, turn on internal pull up resistors.

  //i think .end is in right place, may need to go higher in script ?
  Serial.end(); // allow software serial to use Hardware UART

  // start & set the data rate for the SoftwareSerial port
  sharkSerial.begin(38400);
}

void loop() {
  // Starts in Off state from physical power on.
  // Check state, if "on" , "turn off".
  if (digitalRead(onOffPin) == LOW && onOffstate == HIGH)
  {
    onOffstate = !onOffstate;    //toggles onOffstate from HIGH to LOW (if it was HIGH when the on/off button is pressed)
    digitalWrite(txPin, LOW);    // hold txPin low
    digitalWrite(13, onOffstate);    // Turn off the LED on arduino as indication of state
    delay (300);       //debounce switch delay
  }


  // check state, if on/off button is pressed, change state to on and run start up sequence.
  else if (digitalRead(onOffPin) == LOW && onOffstate == LOW)
  {
    onOffstate = !onOffstate;     // toggle state to HIGH if in'off mode' and on/off button is pressed
    digitalWrite(13, onOffstate); // turn on LED pin to get visual indication of on/off state
    sharkStartup();              // call function sharkStartup()
    delay(11);    // delay 11ms before calling sharkOnMode() per next if statement.
  }


  if (onOffstate == HIGH)
  {
    digitalWrite(13, onOffstate);    //Keep arduino LED 'on' as indication of mode arduino is in
    sharkOnMode();                   // Call sharkOnMode() to write joystick data to inverted serial bus
  }


}

// Dynamic Shark start up sequence, inverted serial data.

void sharkStartup () {
  // data set is incomplete
  // work in progress
  // Power On sequence, High for .293seconds, low for 10.23ms, write 't' packet, low 2.3ms

  {
    digitalWrite(txPin, HIGH);
    delay(293);
    digitalWrite(txPin, LOW);
    delay(10);
  }

  {
    //build serial packet
    data[0] = (0x74);   // start identifier...handshake ? " t "
    data[1] = 130;      // Joystick Y value
    data[2] = 133;      // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;      // default horn off, horn on value is 130
    data[6] = 205;      // joystick On Value ?
    data[7] = 160;      // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;      // ?
    data[10] = 15;      // all packets end with this identifier

    for (unsigned char i = 0; i < 11; i++)
      sharkSerial.write(data[i]);
  }
  delay(2);       // delay before next handshake? packet

  // build next handshake packet
  {
    sharkSerial.write(0x05);
    sharkSerial.write(0x80);
    sharkSerial.write(0xFA);
  }
  delay(1);     // delay before close identifier "15"
  sharkSerial.write(0x0F);

  delay(11);    //delay 12ms before sending single sharkOnMode() packet.
  sharkOnMode(); // write one "0x60" packet and one "a" packet

  // delay(11);    //delay 11.71ms before sending single sharkThridpacket()
  // delay not needed as previous sharkOnMode() has delay at end already.
  sharkThirdpacket ();     // write one third packet

}




void sharkThirdpacket () {
  //this third packet of data is very similar to the sharkOnMode but
  //"0x60" section, 1.6ms pause low, "a" section, .6ms low, "26,133,167,185"

  {
    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 192;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 212;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
      sharkSerial.write(data[i]);
  }
  delay(1) ; // 1.718ms till next "a" packet
  {
    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 146;     //
    data[2] = 128;     //
    data[3] = 192;      //
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 204;

    for (unsigned char i = 0; i < 9; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);
  {
    // build "26,133,167,185" packet
    data[0] = 26;
    data[1] = 133;
    data[2] = 167;
    data[3] = 185;

    for (unsigned char i = 0; i < 4; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);
  sharkSerial.write(0x0f);

}




// function to write: emulated, inverted, serial data.
void sharkOnMode () {
  {
    yPotVal = analogRead(yPot);
    xPotVal = analogRead(xPot);
    yPotValMapped = map(yPotVal, 0, 1023, 128, 255); // Map min/max pot values to match min/max original Shark joystick values.
    xPotValMapped = map(xPotVal, 0, 1012, 128, 255); // Map X values
  }
  {

    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 192;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 212;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
      sharkSerial.write(data[i]);
  }

  delay(1) ; // 1.718ms till next "a" packet
  {

    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 146;     //
    data[2] = 128;     //
    data[3] = 192;      //
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 204;

    for (unsigned char i = 0; i < 9; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);

  sharkSerial.write(0x0f);


  delay(11);    //Delay next packet 17ms input from Shark joystick

}



[/code]
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby woodygb » 18 Oct 2015, 09:01

Could you not read the RX signal output with a pin and then write the required inverted signal via another?
User avatar
woodygb
 
Posts: 7128
Joined: 12 Mar 2011, 18:45
Location: Bedford UK

Re: Arduino controlled wheelchair

Postby gcebiker » 18 Oct 2015, 09:34

At present its on the UART pins, RX/TX ...started off today on pins 10/11
RX it stays high, pin10 it stayed low.

There is a work around involving commenting out two lines of the .h file, which pertain to the settings of the RX line (basically leaving the TX inverted and RX non inverted...i think...)

I dont have a MAX232 chip but if i do get one that might be the missing link (rather than using software inverted).

Also in one of the packets of data i had to write it line by line instead of (unsigned char i ...) which is how i do the other packets, got stuck in a loop so i changed it to the
line by line.
serial.Write(val);
serial.Write(val);

Ill try switching back to the unsigned char , method and pin 10/11 as at least i was getting a response from the base...incorrect but something :)
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 18 Oct 2015, 09:52

motor controller does not seem to send anything back to the joystick...tho i have not tried unplugging a brake line to find out if it would do.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 18 Oct 2015, 10:27

Yeah found some coding/timing errors (code putting timing out), its slow going.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby woodygb » 18 Oct 2015, 11:25

You could use a 74HC14A Hex inverter instead of the MAX232 chip.
User avatar
woodygb
 
Posts: 7128
Joined: 12 Mar 2011, 18:45
Location: Bedford UK

Re: Arduino controlled wheelchair

Postby gcebiker » 18 Oct 2015, 11:47

Cleaned up some tiny bits of code, timing is better.
Its odd, i thought maybe it (the arduino) needed more power, so i hooked up a plug pack to the development board...all it ouput was garbage...or was it.

I tested a few times, lipo battery, usb power via wall, usb power via computer...

A pin on the Saleae Logic4 or my wiring must have been loose or Ive had too long today staring at a computer screen...idk

Upshot....the Joystick when its 'off' is still sending a stream of 255,255,255,255, ...must be the "i am turned off do nothing" signal.

My best guess
i had taped the bare wire end sockets with sticky tape, which fell off but left a bit of sticky behind and i've now covered with heatshrink and gotten the tape glue off....i bet it was blocking the signal all the time. :oops:

So the sketch below is working for now, i will look at taking the factory joystick off from Motor controller and do tests without it being wired in parallel.
By 'working' i mean getting correct reads on the TX line with Logic4, no result from chair as yet.
Code: Select all

// This sketch is to emulate the packet of data that the Shark Joystick sends
/******************************************************************************
    * Author: Tony Matthews ammatthews at gmail dot com
    * License: FreeBSD
    ******************************************************************************/
/******************************************************************************
Copyright (c) 2015, Tony Matthews
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

*******************************************************************************/

// This code is incomplete and still under revision.
// Its also got junk that may not be needed,
// Early testing phases.


#include <SoftwareSerial.h>

// Names for Pins and what they are connected to
const int rxPin = 9;      // Digital Pin 0 for Receive from Motor controller.
// getting ready to use GCDuiNode with the RF24 or Wii Nunchuck (currently hard wired)

const int txPin = 12;          // Digital Pin 1 Transmit from Shark Joystick to Motor Controller.
const int onOffPin = 2;        // Digital Pin 2 is the software on/off button
boolean onOffstate = LOW;      //name holder for what the state is at the moment.

const int yPot = A0;    // Y Pot input value
const int xPot = A1;    // X Pot input value

const int maxPot = A2;      // Max speed dial pot


// Names for analogRead values of Analog Pots
int yPotVal;
int xPotVal;


// Names for Mapped analogRead Values of Pots
int yPotValMapped;
int xPotValMapped;


// Size of data packet, tho the packets are not constrained to this size.
// This size for this packet but probably not for others.
unsigned char data[12];
unsigned long timer = 0;

// set up a new port
SoftwareSerial sharkSerial (rxPin, txPin, true);    // TRUE sets this to invert software serial output.
// i may need to comment out some lines of the .file to let the RX start off low.





void setup() {
  // define pin modes for tx, rx:
  // not sure its needed, seems to work when commented out
  pinMode(rxPin, INPUT);      // software serial inverted recieve pin
  pinMode(txPin, OUTPUT);     // softwaer serial inverted transmit pin
  pinMode(13, OUTPUT);        // Arduino led to monitor on or off state.
  pinMode(onOffPin, INPUT_PULLUP);    // Software on/off button, turn on internal pull up resistors.

 
  // start & set the data rate for the SoftwareSerial port
  sharkSerial.begin(38400);
}

void loop() {
  // Starts in Off state from physical power on.
  // Check state, if "on" , "turn off".
  if (digitalRead(onOffPin) == LOW && onOffstate == HIGH)
  {
    onOffstate = !onOffstate;    //toggles onOffstate from HIGH to LOW (if it was HIGH when the on/off button is pressed)
    digitalWrite(txPin, LOW);    // hold txPin low
    digitalWrite(13, onOffstate);    // Turn off the LED on arduino as indication of state
    delay (300);       //debounce switch delay
  }


  // check state, if on/off button is pressed, change state to on and run start up sequence.
  else if (digitalRead(onOffPin) == LOW && onOffstate == LOW)
  {
    onOffstate = !onOffstate;     // toggle state to HIGH if in'off mode' and on/off button is pressed
    digitalWrite(13, onOffstate); // turn on LED pin to get visual indication of on/off state
    sharkStartup();              // call function sharkStartup()
    delay(11);    // delay 11ms before calling sharkOnMode() per next if statement.
  }


  if (onOffstate == HIGH)
  {
    digitalWrite(13, onOffstate);    //Keep arduino LED 'on' as indication of mode arduino is in
    sharkOnMode();                   // Call sharkOnMode() to write joystick data to inverted serial bus
  }


}

// Dynamic Shark start up sequence, inverted serial data.

void sharkStartup () {
  // data set is incomplete
  // work in progress
  // Power On sequence, High for .293seconds, low for 10.23ms, write 't' packet, low 2.3ms

  {
    digitalWrite(txPin, HIGH);
    delay(293);
    digitalWrite(txPin, LOW);
    delay(10);
  }

  {
    //build serial packet
    data[0] = (0x74);   // start identifier...handshake ? " t "
    data[1] = 130;      // Joystick Y value
    data[2] = 133;      // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;      // default horn off, horn on value is 130
    data[6] = 205;      // joystick On Value ?
    data[7] = 160;      // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;      // ?
    data[10] = 15;      // all packets end with this identifier

    for (unsigned char i = 0; i < 11; i++)
      sharkSerial.write(data[i]);
  }
  delay(2);       // delay before next handshake? packet

  // build next handshake packet
  {
    data[0] = 5;       //
    data[1] = 128;      //
    data[2] = 250;      //
 for (unsigned char i = 0; i < 3; i++)
      sharkSerial.write(data[i]);

    }
  delay(1);     // delay before close identifier "15"
  sharkSerial.write(0x0F);

  delay(11);    //delay 12ms before sending single sharkOnMode() packet.
  sharkOnMode(); // write one "0x60" packet and one "a" packet

  // delay(11);    //delay 11.71ms before sending single sharkThridpacket()
  // delay not needed as previous sharkOnMode() has delay at end already.
  sharkThirdpacket ();     // write one third packet

}




void sharkThirdpacket () {
  //this third packet of data is very similar to the sharkOnMode but
  //"0x60" section, 1.6ms pause low, "a" section, .6ms low, "26,133,167,185"

  {
    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 205;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 200;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
      sharkSerial.write(data[i]);
  }
  delay(1) ; // 1.718ms till next "a" packet
  {
    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 128;     //
    data[2] = 192;     //
    data[3] = 192;      //
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 158;    //

    for (unsigned char i = 0; i < 9; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);
  {
    // build "26,133,167,185" packet
    data[0] = 26;
    data[1] = 133;
    data[2] = 167;
    data[3] = 185;

    for (unsigned char i = 0; i < 4; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);
  sharkSerial.write(0x0f);

}




// function to write: emulated, inverted, serial data.
void sharkOnMode () {
  {
    yPotVal = analogRead(yPot);
    xPotVal = analogRead(xPot);
    yPotValMapped = map(yPotVal, 0, 1023, 128, 255); // Map min/max pot values to match min/max original Shark joystick values.
    xPotValMapped = map(xPotVal, 0, 1012, 128, 255); // Map X values
  }
  {

    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 192;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 191;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
      sharkSerial.write(data[i]);
  }

  delay(1) ; // 1.718ms till next "a" packet
  {

    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 128;     //
    data[2] = 192;     //
    data[3] = 192;      //
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 158;

    for (unsigned char i = 0; i < 9; i++)
      sharkSerial.write(data[i]);
  }
  delay(1);

  sharkSerial.write(0x0f);


  delay(11);    //Delay next packet 17ms input from Shark joystick

}


User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 19 Oct 2015, 07:16

Rewire testing points.

Took out old inline ones and added in "From base", "From Joystick"
Image
Joined together it runs as normal/disconnect to test individually via test lead.


Test Lead/lead to Arduino
Image
Plugs on the ends let me
a. plug it directly inline to Arduino development board,
inline i should be able to read the joystick values and then pass them (modified with sensor data) onto the Base controller.
(a bit of future planning)
b. still be able to measure inline with the little plug i made for the end.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 19 Oct 2015, 10:44

OH its getting closer, the rewire nearly did the trick.

First time with such a close match to desired output.
RX - Should go high with TX line on power up and stay high.
...those blips should be on the falling side (instead of the leading as pictured)

Image

If i can see that the Async Serial output, matches the factory Joystick..... (inverted ? or is it inverted TX and normal RX ?)
Then its on to see if i can get the base to respond.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 19 Oct 2015, 14:18

Tried, sendonlysoftwareserial, different power supplies, moving ground probe.

Before the rewire i had tried hardware Rx TX but thats not working as it is not inverted.
I dont know if it would matter if i called serial.end() and took over 0&1 with softwareserial (rx, tx, true);
Its not make any difference so far using any other of the pins i have tried.

Maybe if i get the emulator working as an inline repeater instead (i dont actually know if i can do such a thing) will i understand what is happening ?
...like thats going to be any easier... :?

I did see that i don't need to specify pinMode as it does it inside of softwareserial.h
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby woodygb » 19 Oct 2015, 15:43

Try.

NewSoftSerial myInvertedConn(0, 1, true); // this device uses inverted signaling

(0,1) used purely as an example ... use whatever pins are available.
NOTE :- Not all pins... as I recall..will work with software serial and this varies from board to board.

https://www.arduino.cc/en/Reference/SoftwareSerial
User avatar
woodygb
 
Posts: 7128
Joined: 12 Mar 2011, 18:45
Location: Bedford UK

Re: Arduino controlled wheelchair

Postby gcebiker » 19 Oct 2015, 15:48

I am, PC's off but recent code us a few posts up... So close I'm having trouble getting to sleep... Its1am here in Oz
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 19 Oct 2015, 23:43

Gotta go to work, i'll try Sofware Serial TX only inverted True, Hardware RX on pin 0 when i get home.
Nick Gammon has written a modified software serial , i need to read a bit more.

Tho when i tried it in the past, RX is held high constantly, Emulator needs to be low when 'off' and high when 'on'
Seems like i should call serialend(), digitalWrite(0, LOW) for "off" state, if statement.

And only do serial.begin as part of the sharkStartup() function.

Not sure, writing notes for myself so i don't forget what to try when i get home.
Please chime in if anyone else has any ideas.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 20 Oct 2015, 06:25

Manipulate UART on/off - Powering Emulator with Single Cell LiPo
- NO base response as yet.

Using SendOnlySoftwareSerial to control inverted TX

Code: Select all
if (digitalRead(onOffPin) == LOW && onOffstate == HIGH)
  {
    onOffstate = !onOffstate;    //toggles onOffstate from HIGH to LOW (if it was HIGH when the on/off button is pressed)
    Serial.end();        // Allow manual control of UART RX
    pinMode(rxPin, OUTPUT); // a bit of a work around to get RX UART to go LOW
    digitalWrite(rxPin, LOW);    //hold rxPin low when 'off'

    digitalWrite(txPin, LOW);    // hold txPin low when 'off'
    digitalWrite(13, onOffstate);    // Turn off the LED on arduino as indication of state
    delay (300);       //debounce switch delay
  }

Image
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 20 Oct 2015, 07:26

I might be barking up the wrong tree waiting for the base to control the RX...as it appears that the Joystick is controlling both lines. :o

Image
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 20 Oct 2015, 07:33

So, ill work on new sketch/modified old one.

Dual serial output, one INVERTED, one Non-Inverted.

See what happens
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 20 Oct 2015, 12:12

2ms out if you dont count the delay i have in there (i just cut and paste).
SendOnlySoftwareSerial - non inverted for ch3, inverted ch2



Code: Select all
  {
    // " ' " build serial packet
    data[0] = (0x60);   // Joystik packets start with this
    data[1] = yPotValMapped; // Joystick Y value
    data[2] = xPotValMapped; // Joystick X value
    data[3] = 255;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 205;      // Speed Fine Tune ?
    data[5] = 128;     // default horn off, horn on value is 130
    data[6] = 140;     // joystick On Value ?
    data[7] = 128;     // chair mode/ drive 128, tilt/aux output 129
    data[8] = 200;     // some other XY modifier..maybe acceleration/de settings
    data[9] = 15;       // all packets end with this identifier

    for (unsigned char i = 0; i < 10; i++)
      sharkSerialy.write(data[i]);
  }
  delay(1) ; // 1.718ms till next "a" packet
  {
    // " a " build serial packet
    data[0] = (0x61);   //
    data[1] = 128;     //
    data[2] = 192;     //
    data[3] = 192;      //
    data[4] = 128;      //
    data[5] = 128;     //
    data[6] = 128;     //
    data[7] = 128;     //
    data[8] = 158;    //

    for (unsigned char i = 0; i < 9; i++)
      sharkSerialy.write(data[i]);
  }


Image
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 20 Oct 2015, 13:41

Opps, wrong bit of code in previous post

Code: Select all
  {
    //build 'Yellow' serial packet
    data[0] = (0x74);   // start identifier...handshake ? " t "
    data[1] = 130;      // Joystick Y value
    data[2] = 133;      // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;      // default horn off, horn on value is 130
    data[6] = 205;      // joystick On Value ?
    data[7] = 160;      // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;      // ?
    data[10] = 15;      // all packets end with this identifier

    for (unsigned char i = 0; i < 11; i++)
      sharkSerialy.write(data[i]);
  }
  {
    //build 'Blue' serial packet
    data[0] = (0x74);   // start identifier...handshake ? " t "
    data[1] = 130;      // Joystick Y value
    data[2] = 133;      // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;      // default horn off, horn on value is 130
    data[6] = 205;      // joystick On Value ?
    data[7] = 160;      // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;      // ?
    data[10] = 15;      // all packets end with this identifier

    for (unsigned char i = 0; i < 11; i++)
      sharkSerialb.write(data[i]);
  }
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby Irving » 20 Oct 2015, 22:13

gcebiker wrote:Opps, wrong bit of code in previous post


try this... though I doubt you'll get the data streams to align exactly unless you do the 'bit-banging' yourself

Code: Select all
  {
    //build serial packet
    data[0] = (0x74);   // start identifier...handshake ? " t "
    data[1] = 130;      // Joystick Y value
    data[2] = 133;      // Joystick X value
    data[3] = 130;      // Max speed setting via turtle/hare buttons on joystick
    data[4] = 128;      // Speed Fine Tune ?
    data[5] = 136;      // default horn off, horn on value is 130
    data[6] = 205;      // joystick On Value ?
    data[7] = 160;      // chair mode/ drive 128, tilt/aux output 129
    data[8] = 128;      //?
    data[9] = 141;      // ?
    data[10] = 15;      // all packets end with this identifier
    for (unsigned char i = 0; i < 11; i++) {
      sharkSerialy.write(data[i]);
      sharkSerialb.write(data[i]);  }
  }
User avatar
Irving
 
Posts: 2114
Joined: 04 Dec 2012, 11:51
Location: NW London

Re: Arduino controlled wheelchair

Postby gcebiker » 21 Oct 2015, 01:03

only .2ms out of phase , thank you for the idea. Zoomed out it looked freakin awesome, i was sooo happy ...till i zoomed in a bit :cry:

Reading the motor controller on its own i get '255' on both ch every 20ms

so what i am seeing on the Logic4 must be an interference pattern (one canceling out part of the other and we only see the left over bits)

There is a lot of stuff inside the joystick, a combination of software and hardware is what i am reading, and the emulator is just software.
Closest i got was with single cell lipo.

Image

Ive got it wired now that i can try an inline read / re transmit and measure the result, now to write the sketch.

In looking back, its been an interesting first sketch to cut my teeth on.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 21 Oct 2015, 12:24

Ah HA !

I need one of these http://www.hobbytronics.co.uk/leonardo-canbus
Oh how i love the internet and gadgets, i think i was gestated in a Library..at the back of a Radio Shack.
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 21 Oct 2015, 12:56

gcebiker wrote:Ah HA !

I need one of these http://www.hobbytronics.co.uk/leonardo-canbus
Oh how i love the internet and gadgets, i think i was gestated in a Library..at the back of a Radio Shack.


Just ordered 3, cant wait.
No wonder i hit a snag, was only sending half of the signal !
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby LROBBINS » 21 Oct 2015, 16:01

Microchip has at least three Application Notes that you might find useful. They are:

    AN713 Controller Area Network (CAN) Basics

    AN228 A CAN Physical Layer Discussion

    AN754 Understanding Microchip’s CAN Module Bit Timing

The files are too big, even zipped, to post here, but they can be had at:

http://www.microchip.com/wwwcategory/TaxonomySearch.aspx?show=Application%20Notes&ShowField=no#

Of course, there's no guarantee that Dynamic's implementation conforms completely to the CAN standard.

Ciao,
Lenny
LROBBINS
 
Posts: 5789
Joined: 27 Aug 2010, 09:36
Location: Siena, Italy

Re: Arduino controlled wheelchair

Postby gcebiker » 22 Oct 2015, 01:49

Thank you lenny,
something to read while i wait for the boards to arrive.

Cheers
Tony
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby gcebiker » 23 Oct 2015, 10:03

Well...i am reinventing the wheel it seems with someone having done it 6! years ago.

Looks like a DX2 from his other videos, ive not seen a DX2, so ive no idea if he has hacked the BUS or the Joystick proper.

Amazing job he has done.
https://www.youtube.com/watch?v=VzyvzoxBWs8
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

Re: Arduino controlled wheelchair

Postby woodygb » 23 Oct 2015, 10:44

I've seen some of his video's before.... e.g.

https://www.youtube.com/watch?v=-2fZOeSrhTs

Click on his name for a few more.
User avatar
woodygb
 
Posts: 7128
Joined: 12 Mar 2011, 18:45
Location: Bedford UK

Re: Arduino controlled wheelchair

Postby gcebiker » 05 Nov 2015, 23:37

Leonardo CANBUS boards came a few days ago....
http://www.hobbytronics.co.uk/leonardo-canbus

Cant get a reading from the CAN-High, CAN-Low pins using the Saleae Logic4 Logic Analyzer.

Sketch i am using is.
Code: Select all
/***************************************************************************
  Hobbytronics Leonardo CAN-BUS board

  Send Test Data to other nodes
  
  Leonardo CAN BUS product page  http://www.hobbytronics.co.uk/leonardo-canbus
   
  Hobbytronics.co.uk
****************************************************************************/

#include <mcp_can.h>
#include <SPI.h>

int led = 23;

MCP_CAN CAN0(17);           // Set CS to pin 17

void setup()
{
   Serial.begin(9600);
   delay(3000);
   Serial.print("Can Test..\r\n");
   // init can bus, baudrate: 40000 baud
   if(CAN0.begin(CAN_40KBPS) == CAN_OK) Serial.print("can init ok!!\r\n");
   else Serial.print("Can init fail!!\r\n");
   pinMode(led, OUTPUT);
}

unsigned char stmp[8] = {0, 1, 2, 3, 4, 5, 6, 7};
void loop()
{
   // send data:  id = 0x00, standard frame, data len = 8, stmp: data buf
   digitalWrite(led, HIGH);   // turn the LED on   
   CAN0.sendMsgBuf(0x00, 0, 8, stmp); 
   delay(1000);
   digitalWrite(led, LOW);   // turn the LED off 
   delay(900);                       // send data every second
}



I was hoping to use the CAN to provide ASYNC Serial per the needs of the power chair base but cant even get a test sketch to output, so have to get that sorted first.

I am starting to see however, the can packets are not a cut and paste job of the Software Serial packets i nearly had working.

...got to get it wired for testing first, how can i possibly be mucking up where to stick two wires :(
http://greenmobility.com.au/rc-wheelchair-controller/
My YouTube Ch -- https://www.youtube.com/user/gcebiker
User avatar
gcebiker
 
Posts: 879
Joined: 11 Jul 2015, 14:20
Location: Gold Coast, Queensland, Australia.

PreviousNext

Return to Everything Powerchair

Who is online

Users browsing this forum: acid_coke, emilevirus, ricardoh, yeshelp and 81 guests

 

  eXTReMe Tracker