Reverse Engineering the Dynamic Controls Shark Joystick

No, not a joystick that lets you drive a shark. It’s a joystick for a mobility scooter or powerchair, as is used by people with disabilities.

There are a lot of resources on the internet that claim that the joystick uses CAN-Bus. This is because the signal is differential (there’s a “high” and “low” data line, and they are inversions of each other). However, I don’t think that this is the case. The microcontroller used in the joystick is the ATMega8, which doesn’t have a CAN controller. There are no CAN controllers or tranceivers in the joystick. On top of that, my friend Seth‘s Saleae logic analyzer can’t make head nor tails of the protocol using the normal CAN analysis modes.

So if it’s not CAN, what is it? There is an LM339 in the joystick, and another one in the motor control unit. These are quad differential comparators, and would be pretty handy if you wanted to hack your own differential serial lines, for noise immunity.

The asynchronous serial decoder of the logic analyzer did manage to decode the serial bit stream at 40000bps.

If the joystick data lines are unplugged and it is powered up, the only output is

t '129' '137' '134' '128' '133' '138' '166' '130' '196' '15'

repeated every 20ms (19.96937, actually, but who’s counting?).

If the joystick is plugged in, the startup does this:

t '129' '137' '134' '128' '133' '138' '166' '130' '196' '15'
'5' '130' '248' '15'
` '192' '191' '192' '141' '128' '140' '128' '199' '15'
a '128' '128' '128' '128' '128' '128' '128' '158' '15'
` '192' '191' '192' '141' '128' '140' '128' '199' '15'
a '128' '192' '128' '128' '128' '128' '128' '222' '26' '133' '167' '185' '15'
` '192' '191' '192' '141' '128' '140' '128' '199' '15'
a '128' '192' '128' '128' '128' '128' '128' '222' '15'
` '192' '191' '192' '141' '128' '140' '128' '199' '15'

I wrote a little script that parses the CSV output of the logic analyzer software and just prints a newline after each ’15’.

The main thing to notice about this is that the same initialization value is sent, but then it falls into a sort of call and response, with every other line starting with ‘a’ or ‘`’.

My guess is that these are communications passing back and forth between the joystick and the motor controller. I logged 20 seconds of the wheelchair sitting still and then counted all the unique messages that passed between the joystick and the motor controller.

Assuming that the above startup sequence is call and response, the message starting with ‘t’ and the ones starting with ‘`’ are the joystick, and the ones starting with ‘a’ are from the motor driver.

I sorted the commands out and counted the unique messages. They break down like this:


1 ` '191' '190' '192' '189' '128' '132' '128' '161' '15'
1 ` '191' '191' '192' '186' '128' '132' '128' '163' '15'
1 ` '192' '191' '192' '128' '128' '140' '128' '212' '15'
1 ` '192' '191' '192' '130' '128' '132' '128' '218' '15'
1 '5' '130' '248' '15'
1 a '128' '192' '128' '128' '128' '128' '128' '222' '26' '133' '167' '185' '15'
1 t '129' '137' '134' '128' '133' '138' '166' '130' '196' '15'
2 ` '191' '190' '192' '174' '128' '132' '128' '176' '15'
3 ` '191' '191' '192' '184' '128' '140' '128' '157' '15'
10 a '128' '192' '128' '128' '128' '128' '128' '222' '15'
14 ` '192' '190' '192' '135' '128' '132' '128' '214' '15'
20 a '146' '128' '128' '128' '128' '128' '128' '140' '26' '133' '167' '185' '15'
21 a '128' '128' '128' '128' '128' '128' '128' '158' '15'
22 ` '192' '191' '192' '129' '128' '132' '128' '219' '15'
34 ` '191' '191' '192' '176' '128' '132' '128' '173' '15'
37 ` '192' '191' '192' '128' '128' '132' '128' '220' '15'
40 ` '191' '190' '192' '190' '128' '132' '128' '160' '15'
57 ` '191' '190' '192' '182' '128' '132' '128' '168' '15'
70 ` '191' '191' '192' '185' '128' '132' '128' '164' '15'
139 ` '191' '190' '192' '183' '128' '132' '128' '167' '15'
290 ` '191' '191' '192' '184' '128' '132' '128' '165' '15'
294 ` '191' '190' '192' '191' '128' '132' '128' '159' '15'
954 a '146' '128' '128' '128' '128' '128' '128' '140' '15'

Most of the unique messages are from the joystick, and almost all of the messages sent back are the one at the bottom, with 954 occurances.

Let’s compare the messages from the motor controller. Bear in mind that nothing is moving at this point.


1 '5' '130' '248' '15'
1 a '128' '192' '128' '128' '128' '128' '128' '222' '26' '133' '167' '185' '15'
10 a '128' '192' '128' '128' '128' '128' '128' '222' '15'
20 a '146' '128' '128' '128' '128' '128' '128' '140' '26' '133' '167' '185' '15'
21 a '128' '128' '128' '128' '128' '128' '128' '158' '15'
954 a '146' '128' '128' '128' '128' '128' '128' '140' '15'

The first one is the startup acknowledgement. It never occurs again.

The next one and the one that occurs 20 times look similar, in that they both have four extra numbers in them. The first, second, and eighth values are the only ones that vary between them.

The one that occurs ten times mtches the one that occurs 21 times, except for the second and eighth fields, and doesn’t have the extended part.

The third through seventh fields are always ‘128’.

The first and second fields are always ‘128’, ‘146’, or ‘192’. The eighth field is always ‘222’, ‘140’, or ‘158’. The nineth field is either 26 or the end of transmission marker ’15’.

So what does all this mean?

I suspect that at least one of the values has something to do with the battery. The battery connects to the motor driver, but there is a battery level display on the joystick, so the motor driver must communicate some battery level information to the joystick.

The messages from the joystick have a similar pattern.


1 ` '191' '190' '192' '189' '128' '132' '128' '161' '15'
1 ` '191' '191' '192' '186' '128' '132' '128' '163' '15'
1 ` '192' '191' '192' '128' '128' '140' '128' '212' '15'
1 ` '192' '191' '192' '130' '128' '132' '128' '218' '15'
2 ` '191' '190' '192' '174' '128' '132' '128' '176' '15'
3 ` '191' '191' '192' '184' '128' '140' '128' '157' '15
14 ` '192' '190' '192' '135' '128' '132' '128' '214' '15'
22 ` '192' '191' '192' '129' '128' '132' '128' '219' '15'
34 ` '191' '191' '192' '176' '128' '132' '128' '173' '15'
37 ` '192' '191' '192' '128' '128' '132' '128' '220' '15'
40 ` '191' '190' '192' '190' '128' '132' '128' '160' '15'
57 ` '191' '190' '192' '182' '128' '132' '128' '168' '15'
70 ` '191' '191' '192' '185' '128' '132' '128' '164' '15'
139 ` '191' '190' '192' '183' '128' '132' '128' '167' '15'
290 ` '191' '191' '192' '184' '128' '132' '128' '165' '15'
294 ` '191' '190' '192' '191' '128' '132' '128' '159' '15'

The first and second values vary, but only slightly. The third value does not vary. the fourth value varies over the range 128-191. The fifth value does not vary. The sixth value varies, the seventh does not, the eighth does.

The joystick is a 4-axis device, with three buttons and a pair of binary inputs. My hope is that the values reported are something like the four joystick axes and the buttons, but the numbers don’t quite line up, as there are more buttons (5, three buttons plus two input jacks) than values left over after subtracting the joystick axes. Unless the jacks are in parallel with the buttons, that’s not what’s going on.

Recording from the data lines while the joystick is held in the forward position gives these unique lines:


1 ` '128' '191' '192' '134' '128' '132' '128' '150' '15'
1 ` '128' '192' '192' '130' '128' '132' '128' '153' '15'
1 ` '128' '192' '192' '132' '128' '132' '128' '151' '15'
1 ` '128' '193' '192' '131' '128' '132' '128' '151' '15'
1 ` '129' '191' '192' '165' '128' '132' '128' '246' '15'
1 ` '131' '191' '192' '170' '128' '132' '128' '239' '15'
1 ` '133' '191' '192' '176' '128' '132' '128' '231' '15'
1 ` '135' '190' '192' '191' '128' '132' '128' '215' '15'
1 ` '137' '190' '192' '190' '128' '132' '128' '214' '15'
1 ` '140' '190' '192' '135' '128' '132' '128' '138' '15'
1 ` '142' '191' '192' '130' '128' '132' '128' '140' '15'
1 ` '144' '191' '192' '156' '128' '132' '128' '240' '15'
1 ` '146' '191' '192' '156' '128' '132' '128' '238' '15'
1 ` '148' '191' '192' '149' '128' '132' '128' '243' '15'
1 ` '150' '191' '192' '150' '128' '132' '128' '240' '15'
1 ` '152' '191' '192' '164' '128' '132' '128' '224' '15'
1 ` '154' '191' '192' '186' '128' '132' '128' '200' '15'
1 ` '157' '191' '192' '153' '128' '132' '128' '230' '15'
1 ` '160' '191' '192' '136' '128' '132' '128' '244' '15'
1 ` '163' '191' '192' '145' '128' '132' '128' '232' '15'
1 ` '166' '191' '192' '153' '128' '132' '128' '221' '15'
1 ` '169' '191' '192' '161' '128' '132' '128' '210' '15'
1 ` '172' '191' '192' '145' '128' '132' '128' '223' '15'
1 ` '174' '191' '192' '179' '128' '132' '128' '187' '15'
1 ` '177' '191' '192' '139' '128' '132' '128' '224' '15'
1 ` '179' '191' '192' '139' '128' '132' '128' '222' '15'
1 ` '180' '191' '192' '188' '128' '132' '128' '172' '15'
1 ` '182' '191' '192' '181' '128' '132' '128' '177' '15'
1 ` '184' '191' '192' '172' '128' '132' '128' '184' '15'
1 ` '186' '191' '192' '172' '128' '132' '128' '182' '15'
1 ` '188' '191' '192' '140' '128' '132' '128' '212' '15'
1 ` '189' '191' '192' '156' '128' '132' '128' '195' '15'
1 ` '190' '191' '192' '132' '128' '132' '128' '218' '15'
1 ` '190' '191' '192' '147' '128' '132' '128' '203' '15'
1 ` '190' '191' '192' '163' '128' '132' '128' '187' '15'
1 ` '190' '191' '192' '171' '128' '132' '128' '179' '15'
1 ` '190' '191' '192' '172' '128' '132' '128' '178' '15'
1 ` '190' '191' '192' '181' '128' '132' '128' '169' '15'
1 ` '190' '191' '192' '190' '128' '132' '128' '160' '15'
1 ` '191' '191' '192' '141' '128' '132' '128' '208' '15'
1 ` '191' '191' '192' '173' '128' '132' '128' '176' '15'
1 ` '192' '191' '192' '133' '128' '140' '128' '207' '15'
1 ` '192' '191' '192' '135' '128' '132' '128' '213' '15'
1 ` '192' '191' '192' '150' '128' '132' '128' '198' '15'
1 '5' '130' '248' '15'
1 a '128' '192' '128' '128' '128' '128' '128' '222' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '134' '230' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '141' '223' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '145' '219' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '129' '142' '222' '15'
1 a '146' '128' '128' '160' '132' '128' '135' '229' '15'
1 a '146' '128' '128' '176' '128' '128' '145' '219' '15'
1 a '146' '128' '132' '160' '128' '128' '146' '218' '15'
1 a '146' '129' '128' '160' '128' '128' '141' '223' '15'
1 a '146' '132' '128' '160' '128' '128' '143' '221' '15'
1 a '146' '132' '128' '160' '130' '128' '146' '218' '15'
1 t '129' '137' '134' '128' '133' '138' '166' '130' '196' '15'
2 ` '128' '191' '192' '133' '128' '132' '128' '151' '15'
2 ` '128' '191' '192' '135' '128' '132' '128' '149' '15'
2 ` '128' '192' '192' '134' '128' '132' '128' '149' '15'
2 ` '128' '194' '192' '130' '128' '132' '128' '151' '15'
2 ` '191' '191' '192' '188' '128' '132' '128' '161' '15'
2 ` '192' '191' '192' '131' '128' '132' '128' '217' '15'
2 ` '192' '191' '192' '140' '128' '132' '128' '208' '15'
2 a '146' '128' '128' '160' '128' '128' '146' '218' '26' '133' '167' '185' '15'
3 ` '128' '191' '192' '131' '128' '132' '128' '153' '15'
3 ` '192' '191' '192' '143' '128' '132' '128' '205' '15'
3 a '146' '128' '128' '128' '128' '128' '128' '140' '26' '133' '167' '185' '15'
4 a '146' '128' '128' '160' '128' '128' '130' '234' '15'
4 a '146' '128' '128' '160' '128' '128' '131' '233' '15'
4 a '146' '128' '128' '160' '128' '128' '136' '228' '15'
5 ` '128' '191' '192' '132' '128' '132' '128' '152' '15'
5 ` '128' '192' '192' '128' '128' '132' '128' '155' '15'
5 a '146' '128' '128' '160' '128' '128' '129' '235' '15'
5 a '146' '128' '128' '160' '128' '128' '133' '231' '15'
5 a '146' '128' '128' '160' '128' '128' '134' '230' '15'
5 a '146' '128' '128' '160' '128' '128' '138' '226' '15'
6 ` '128' '193' '192' '130' '128' '132' '128' '152' '15'
6 ` '192' '191' '192' '134' '128' '132' '128' '214' '15'
6 a '146' '128' '128' '160' '128' '128' '132' '232' '15'
6 a '146' '128' '128' '160' '128' '128' '137' '227' '15'
7 a '146' '128' '128' '160' '128' '128' '139' '225' '15'
7 a '146' '128' '128' '160' '128' '128' '141' '223' '15'
8 a '146' '128' '128' '160' '128' '128' '140' '224' '15'
9 ` '128' '193' '192' '129' '128' '132' '128' '153' '15'
9 ` '128' '193' '192' '132' '128' '132' '128' '150' '15'
9 ` '192' '191' '192' '141' '128' '140' '128' '199' '15'
9 a '146' '128' '128' '160' '128' '128' '145' '219' '15'
10 a '128' '192' '128' '128' '128' '128' '128' '222' '15'
10 a '146' '128' '128' '160' '128' '128' '135' '229' '15'
10 a '146' '128' '128' '160' '128' '128' '142' '222' '15'
10 a '146' '128' '128' '160' '128' '128' '143' '221' '15'
12 ` '128' '192' '192' '135' '128' '132' '128' '148' '15'
12 ` '128' '193' '192' '133' '128' '132' '128' '149' '15'
12 a '146' '128' '128' '160' '128' '128' '128' '236' '15'
12 a '146' '128' '128' '160' '128' '128' '144' '220' '15'
15 ` '128' '193' '192' '128' '128' '132' '128' '154' '15'
20 ` '128' '193' '192' '134' '128' '132' '128' '148' '15'
21 a '128' '128' '128' '128' '128' '128' '128' '158' '15'
24 ` '128' '194' '192' '129' '128' '132' '128' '152' '15'
25 ` '192' '191' '192' '141' '128' '132' '128' '207' '15'
26 ` '192' '191' '192' '142' '128' '132' '128' '206' '15'
30 ` '192' '191' '192' '133' '128' '132' '128' '215' '15'
33 ` '192' '191' '192' '132' '128' '132' '128' '216' '15'
63 ` '128' '194' '192' '128' '128' '132' '128' '153' '15'
64 ` '128' '193' '192' '135' '128' '132' '128' '147' '15'
126 a '146' '128' '128' '128' '128' '128' '128' '140' '15'
135 a '146' '128' '128' '160' '128' '128' '146' '218' '15'

Breaking out the motor driver packets gives:


1 a '128' '192' '128' '128' '128' '128' '128' '222' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '134' '230' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '141' '223' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '128' '145' '219' '26' '133' '167' '185' '15'
1 a '146' '128' '128' '160' '128' '129' '142' '222' '15'
1 a '146' '128' '128' '160' '132' '128' '135' '229' '15'
1 a '146' '128' '128' '176' '128' '128' '145' '219' '15'
1 a '146' '128' '132' '160' '128' '128' '146' '218' '15'
1 a '146' '129' '128' '160' '128' '128' '141' '223' '15'
1 a '146' '132' '128' '160' '128' '128' '143' '221' '15'
1 a '146' '132' '128' '160' '130' '128' '146' '218' '15'
2 a '146' '128' '128' '160' '128' '128' '146' '218' '26' '133' '167' '185' '15'
3 a '146' '128' '128' '128' '128' '128' '128' '140' '26' '133' '167' '185' '15'
4 a '146' '128' '128' '160' '128' '128' '130' '234' '15'
4 a '146' '128' '128' '160' '128' '128' '131' '233' '15'
4 a '146' '128' '128' '160' '128' '128' '136' '228' '15'
5 a '146' '128' '128' '160' '128' '128' '129' '235' '15'
5 a '146' '128' '128' '160' '128' '128' '133' '231' '15'
5 a '146' '128' '128' '160' '128' '128' '134' '230' '15'
5 a '146' '128' '128' '160' '128' '128' '138' '226' '15'
6 a '146' '128' '128' '160' '128' '128' '132' '232' '15'
6 a '146' '128' '128' '160' '128' '128' '137' '227' '15'
7 a '146' '128' '128' '160' '128' '128' '139' '225' '15'
7 a '146' '128' '128' '160' '128' '128' '141' '223' '15'
8 a '146' '128' '128' '160' '128' '128' '140' '224' '15'
9 a '146' '128' '128' '160' '128' '128' '145' '219' '15'
10 a '128' '192' '128' '128' '128' '128' '128' '222' '15'
10 a '146' '128' '128' '160' '128' '128' '135' '229' '15'
10 a '146' '128' '128' '160' '128' '128' '142' '222' '15'
10 a '146' '128' '128' '160' '128' '128' '143' '221' '15'
12 a '146' '128' '128' '160' '128' '128' '128' '236' '15'
12 a '146' '128' '128' '160' '128' '128' '144' '220' '15'
21 a '128' '128' '128' '128' '128' '128' '128' '158' '15'
126 a '146' '128' '128' '128' '128' '128' '128' '140' '15'
135 a '146' '128' '128' '160' '128' '128' '146' '218' '15'

Now it seems that every value varies at least once. I’m not sure yet what to make of this, but I feel like I’m on the right track.