- Device name advertised as:
LEDneWF02001D0CDC48
- Wireshark says it's a
JMZengge
- UUID: FF00
- 0xFF22 - read/write without response notify
- 0xFF11 - write without response
- UUID: FFFF
- 0xFF02 - Read notify
- 0xFF01 - write
Based on previous escapades in BLE I assume you have to enable notifications by writing to one of the notification endpoints on FFFF.
Not sure if this is relevant: MTU: 200 bytes Command length: 8 Min interval: 16 (20 msec) Max interval: 24 (30 msec) Timeout multiplier: 600 (6 sec)
$ gatttool -I
[ ][LE]> connect 08:65:F0:0C:DC:48
Attempting to connect to 08:65:F0:0C:DC:48
Connection successful
[08:65:F0:0C:DC:48][LE]> primary
attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0011 uuid: 0000fe00-0000-1000-8000-00805f9b34fb
attr handle: 0x0012, end grp handle: 0x0017 uuid: 0000ffff-0000-1000-8000-00805f9b34fb
[08:65:F0:0C:DC:48][LE]> characteristics
handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x02, char value handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0009, char properties: 0x20, char value handle: 0x000a, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x16, char value handle: 0x000e, uuid: 0000ff22-0000-1000-8000-00805f9b34fb
handle: 0x0010, char properties: 0x04, char value handle: 0x0011, uuid: 0000ff11-0000-1000-8000-00805f9b34fb
handle: 0x0013, char properties: 0x12, char value handle: 0x0014, uuid: 0000ff02-0000-1000-8000-00805f9b34fb
handle: 0x0016, char properties: 0x0c, char value handle: 0x0017, uuid: 0000ff01-0000-1000-8000-00805f9b34fb
We sent:
0000 02 02 00 09 00 05 00 04 00 12 15 00 01 00 ..............
- Write Request 0x12
- Handle: 0x0015
- Service UUID 0xffff
- Characteristic: 0xff02
- UUID: 0x2902
We received:
0000 02 02 20 05 00 01 00 04 00 13 .. .......
- Write response: 0x13
- Handle: 0x0015
- Service: 0xffff
- Char: 0xff02
- UUID: 0x2902
Characteristic Configuration Client: 0x0001, Notification
We sent:
0000 02 02 00 13 00 0f 00 04 00 12 17 00 00 01 80 00 ................
0010 00 04 05 0a 81 8a 8b 96 ........
- Write request 0x12
- Handle: 0x0017
- Service: 0xffff
- UUID: 0xff01
Value is hex: 00 01 80 00 00 04 05 0a 81 8a 8b 96
which results in us being sent this over multiple frames:
0000 3e 00 04 00 1b 14 00 04 43 80 00 00 33 34 0a 7b >.......C...34.{
0010 22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c 6f 61 "code":0,"payloa
0020 64 22 3a 22 38 31 31 44 32 33 32 35 30 45 30 30 d":"811D23250E00
0030 33 32 33 32 46 46 30 30 30 32 30 30 33 30 38 39 3232FF0002003089
0040 22 7d "}
>C34 {"code":0,"payload":"811D23250E003232FF0002003089"}
04 43 80 00 00 33 34 0a 7b 22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c 6f 61 64 22 3a 22 38 31 31 44 32 33 32 35 30 45 30 30 33 32 33 32 46 46 30 30 30 32 30 30 33 30 38 39 22 7d
C 3 4 sp { " c o d e " : 0 , " p a y l o a d " : " 8 1 1 D 2 3 2 5 0 E 0 0 3 2 3 2 F F 0 0 0 2 0 0 3 0 8 9 " }
I would guess that this is an 8 byte header?
We then send:
0000 02 02 00 1b 00 17 00 04 00 12 17 00 00 02 80 00 ................
0010 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4 .......... .....
- Write request 0x12
- Handle: 0x0017
- Service: 0xffff
- UUID: 0xff01
Value in hex: 00 02 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4
We received a write response.
Then we send (to the same service and UUID):
Value in hex: 00 03 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4
We received a write response and then
In summary, these are the three payloads we sent:
00 01 80 00 00 04 05 0a 81 8a 8b 96
00 02 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4
00 03 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4
I think the first "on" request looks like this:
- Service: 0xffff
- UUID: 0xff01
Payload:
02 02 00 1c 00 18 00 04 00 12 17 00 00 04 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
which results in receiving this:
3e 00 04 00 1b 14 00 04 44 80 00 00 33 34 0c 7b22636f6465223a302c227061796c6f6164223a2238313144323432353045303033323332464630303032303033303841227d
>D34 {"code":0,"payload":"811D24250E003232FF000200308A"}
The I think the first "off" request looks like this:
- Service: 0xffff
- UUID: 0xff01
Payload:
02 02 00 1c 00 18 00 04 00 12 17 00 00 05 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
and we receive:3e 00 04 00 1b 14 00 04 45 80 00 00 33 34 0c 7b22636f6465223a302c227061796c6f6164223a2238313144323332353045303033323332464630303032303033303839227d
>E34 {"code":0,"payload":"811D23250E003232FF0002003089"}
If I look at just on the on/off messages I sent they look like this (including packet headers 02 02 00 1c 00 18
etc):
02 02 00 1c 00 18 00 04 00 12 17 00 00 04 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 05 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 06 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 07 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 08 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 09 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0a 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 0b 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0c 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 0d 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0e 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
Skipping all the preamble, let's look at the values of the write messages first. In a big list to see if we can spot the patterns. Starting from opening the app.
0028000000c0d0b1014160a1d12032806000fb3
0038000000c0d0b1014160a1d12032806000fb3
0004 8000000d0e0b3b23 000000 00000000 32 00 00 90
0005 8000000d0e0b3b24 000000 00000000 32 00 00 91
0006 8000000d0e0b3b23 000000 00000000 32 00 00 90
0007 8000000d0e0b3b24 000000 00000000 32 00 00 91
0008 8000000d0e0b3b23 000000 00000000 32 00 00 90
0009 8000000d0e0b3b24 000000 00000000 32 00 00 91
000a 8000000d0e0b3b23 000000 00000000 32 00 00 90
000b 8000000d0e0b3b24 000000 00000000 32 00 00 91
000c 8000000d0e0b3b23 000000 00000000 32 00 00 90
000d 8000000d0e0b3b24 000000 00000000 32 00 00 91
000e 8000000d0e0b3b23 000000 00000000 32 00 00 90
000f 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0010 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0011 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0012 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0013 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0014 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0015 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0016 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0017 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0018 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0019 8000000d0e0b3ba1 006464 00000000 00 00 00 a4
001a 8000000d0e0b3ba1 006464 00000000 00 00 00 a4
001b 8000000d0e0b3ba1 006464 00000000 00 00 00 a4
001c 8000000d0e0b3b24 000000 00000000 32 00 00 91
I ignore the first two packets, I expect they are some handshaking. Everything breaks up reasonably neatly as I have done with the spaces.
- First two bytes are a counter
- The next eight bytes seem to stay the same
- The next three bytes differ at the top (and the last packet which is an off I think) and the middle section which is where I was changing the colours. 6 bytes for colours would be RGB, but the numbers don't make sense)
- The last byte really looks like a checksum. It's always the same for each of the commands, so isn't effected by the counter.
Trace with times On 18:57
Full color 18:57 Press on Press red 18:58 5 times Dragged to red slid around colours fiddle with saturation brightness down, all down, up off
18:58:04 0005 8000000d0e0b3ba1 006464 00000000000000a4
18:58:06 0006 8000000d0e0b3ba1 006464 00000000000000a4
18:58:08 0007 8000000d0e0b3ba1 006464 00000000000000a4
18:58:24 0008 8000000d0e0b3ba1 006464 00000000000000a4
18:58:24 0009 8000000d0e0b3ba1 006464 00000000000000a4
18:58:25 000a 8000000d0e0b3ba1 006464 00000000000000a4
18:58:25 000b 8000000d0e0b3ba1 006464 00000000000000a4
18:58:46 000c 8000000d0e0b3ba1 40e464 0000000000000064
18:58:46 000d 8000000d0e0b3ba1 40e464 0000000000000064
18:58:58 000e 8000000d0e0b3ba1 43e464 0000000000000067
18:58:58 000f 8000000d0e0b3ba1 43e464 0000000000000067
18:59:01 0010 8000000d0e0b3ba1 52e464 0000000000000076
18:59:01 0011 8000000d0e0b3ba1 52e464 0000000000000076
18:59:03 0012 8000000d0e0b3ba1 5fe464 0000000000000083
18:59:04 0013 8000000d0e0b3ba1 5fe464 0000000000000083
18:59:05 0014 8000000d0e0b3ba1 6be464 000000000000008f
18:59:05 0015 8000000d0e0b3ba1 6be464 000000000000008f
18:59:06 0016 8000000d0e0b3ba1 706464 0000000000000014
18:59:06 0017 8000000d0e0b3ba1 706464 0000000000000014
18:59:08 0018 8000000d0e0b3ba1 766464 000000000000001a
18:59:08 0019 8000000d0e0b3ba1 766464 000000000000001a
18:59:10 001a 8000000d0e0b3ba1 7ee464 00000000000000a2
18:59:10 001b 8000000d0e0b3ba1 7ee464 00000000000000a2
18:59:11 001c 8000000d0e0b3ba1 856464 0000000000000029
18:59:11 001d 8000000d0e0b3ba1 856464 0000000000000029
18:59:14 001e 8000000d0e0b3ba1 8b6464 000000000000002f
18:59:14 001f 8000000d0e0b3ba1 8b6464 000000000000002f
18:59:14 0020 8000000d0e0b3ba1 91e464 00000000000000b5
18:59:14 0021 8000000d0e0b3ba1 91e464 00000000000000b5
18:59:15 0022 8000000d0e0b3ba1 9ae464 00000000000000be
18:59:15 0023 8000000d0e0b3ba1 9ae464 00000000000000be
18:59:15 0024 8000000d0e0b3ba1 9fe464 00000000000000c3
18:59:16 0025 8000000d0e0b3ba1 9fe464 00000000000000c3
18:59:16 0026 8000000d0e0b3ba1 aae464 00000000000000ce
18:59:16 0027 8000000d0e0b3ba1 aae464 00000000000000ce
18:59:17 0028 8000000d0e0b3ba1 006464 00000000000000a4
18:59:17 0029 8000000d0e0b3ba1 006464 00000000000000a4
18:59:17 002a 8000000d0e0b3ba1 006464 00000000000000a4
18:59:18 002b 8000000d0e0b3ba1 006464 00000000000000a4
18:59:19 002c 8000000d0e0b3ba1 006464 00000000000000a4
18:59:19 002d 8000000d0e0b3ba1 006464 00000000000000a4
18:59:25 002e 8000000d0e0b3ba1 005264 0000000000000092
18:59:25 002f 8000000d0e0b3ba1 005264 0000000000000092
18:59:26 0030 8000000d0e0b3ba1 003c64 000000000000007c
18:59:26 0031 8000000d0e0b3ba1 003c64 000000000000007c
18:59:27 0032 8000000d0e0b3ba1 002364 0000000000000063
18:59:27 0033 8000000d0e0b3ba1 002364 0000000000000063
18:59:30 0034 8000000d0e0b3ba1 006464 00000000000000a4
18:59:30 0035 8000000d0e0b3ba1 006464 00000000000000a4
18:59:49 0036 8000000d0e0b3ba1 006406 0000000000000046
18:59:49 0037 8000000d0e0b3ba1 006406 0000000000000046
18:59:53 0038 8000000d0e0b3ba1 006400 0000000000000040
18:59:53 0039 8000000d0e0b3ba1 006400 0000000000000040
18:59:56 003a 8000000d0e0b3ba1 006464 00000000000000a4
18:59:56 003b 8000000d0e0b3ba1 006464 00000000000000a4
Those colours just don't look right at all.
If you go here: https://jsbin.com/bezomev/edit?html,css,js,output and paste that col. in you see things like: https://imgur.com/a/mZloElF
Get another trace of just pressing the red, green, blue buttons. Strip out all the common parts, and we're left with...
00 64 64
00 64 64
00 64 64
00 64 64
3c 64 64
3c 64 64
3c 64 64
3c 64 64
78 64 64
78 64 64
78 64 64
78 64 64
78 64 64
... I don't think this is RGB at all.
What about HSL?
0 = red 120 = green 240 = blue
Let's convert the numbers in to decimal. I might not need all the columns, but try it anyway...
0 100 100
0 100 100
0 100 100
60 100 100
60 100 100
60 100 100
120 100 100
120 100 100
120 100 100
Welp, what if we multiplied the first column by 2?
0
0
0
120
120
120
240
240
240
That looks familiar, but, like, really though? Maybe first byte here is hue then?
Now we need to try it out with some brightness changes.
red red red blue blue blue green 50% sat 0% sat 50% sat 100% sat 75% sat 100% sat 50% bright 0% bright 25% bright 50% bright 75% bright 100% bright red
file: hsl_test.log
0004 800000 0d 0e0b3ba1 00 64 64 00 000000000000 a4
0005 800000 0d 0e0b3ba1 78 64 64 00 000000000000 1c
0006 800000 0d 0e0b3ba1 78 64 64 00 000000000000 1c
0007 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
0008 800000 0d 0e0b3ba1 3c 31 64 00 000000000000 ad
0009 800000 0d 0e0b3ba1 3c 31 64 00 000000000000 ad
000a 800000 0d 0e0b3ba1 00 00 64 00 000000000000 40
000b 800000 0d 0e0b3ba1 00 00 64 00 000000000000 40
000c 800000 0d 0e0b3ba1 3c 35 64 00 000000000000 b1
000d 800000 0d 0e0b3ba1 3c 35 64 00 000000000000 b1
000e 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
000f 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
0010 800000 0d 0e0b3ba1 3c 4c 64 00 000000000000 c8
0011 800000 0d 0e0b3ba1 3c 4c 64 00 000000000000 c8
0012 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
0013 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
0014 800000 0d 0e0b3ba1 3c 64 32 00 000000000000 ae
0015 800000 0d 0e0b3ba1 3c 64 32 00 000000000000 ae
0016 800000 0d 0e0b3ba1 3c 64 00 00 000000000000 7c
0017 800000 0d 0e0b3ba1 3c 64 00 00 000000000000 7c
0018 800000 0d 0e0b3ba1 3c 64 1c 00 000000000000 98
0019 800000 0d 0e0b3ba1 3c 64 1c 00 000000000000 98
001a 800000 0d 0e0b3ba1 3c 64 34 00 000000000000 b0
001b 800000 0d 0e0b3ba1 3c 64 34 00 000000000000 b0
001c 800000 0d 0e0b3ba1 3c 64 4a 00 000000000000 c6
001d 800000 0d 0e0b3ba1 3c 64 4a 00 000000000000 c6
001e 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
001f 800000 0d 0e0b3ba1 3c 64 64 00 000000000000 e0
0020 800000 0d 0e0b3ba1 00 64 64 00 000000000000 a4
Once more to really make sure. File:
Connect On green
Set Bri 100% Sat 100% Set bri 50% sat 100% Set bri 25% sat 100% Set bri 0% sat 100% Set bri 100% sat 100% Set red Set blue
File: finalhsltest.log
RGB: [0, 0, 0] RGB: [0, 255, 0] RGB: [0, 132, 0] RGB: [0, 61, 0] RGB: [0, 0, 0] RGB: [0, 255, 0] RGB: [255, 0, 0] RGB: [0, 0, 255]
Success!
It's taken pretty much all day, but I think I have the colour settings understood. I still can't send messages yet, but that can come next. It's HSV, where the first byte is H/2 (so it will fit in one byte).
Let's look at the white LEDs to start with.
White on. Click through the white colours from warm to cool. Brightness 100%, 75%, 50%, 25%, 0, 50 , 100 off
The first line is from the previous RGB experiments so we can compare and contrast.
0026 8000000d 0e0b3b a1 aae464 0000 0000000000 ce
0004 8000000d 0e0b3b 23 000000 0000 0000320000 90 - power on
0005 8000000d 0e0b3b b1 000000 0064 0000000000 50
0006 8000000d 0e0b3b b1 000000 1464 0000000000 64
0007 8000000d 0e0b3b b1 000000 2864 0000000000 78
0008 8000000d 0e0b3b b1 000000 3c64 0000000000 8c
0009 8000000d 0e0b3b b1 000000 5064 0000000000 a0
000a 8000000d 0e0b3b b1 000000 6464 0000000000 b4
000b 8000000d 0e0b3b b1 000000 6444 0000000000 94
000c 8000000d 0e0b3b b1 000000 642e 0000000000 7e
000d 8000000d 0e0b3b b1 000000 6415 0000000000 65
000e 8000000d 0e0b3b b1 000000 6401 0000000000 51
000f 8000000d 0e0b3b b1 000000 642e 0000000000 7e
0010 8000000d 0e0b3b b1 000000 6464 0000000000 b4
0011 8000000d 0e0b3b 24 000000 0000 0000320000 91 - power off
The white led settings are the two bytes after the RGB colours by the looks of it. They max out at 0x64 which is 100, so I would assume that it's a percentage, certainly for the brightness which is the 2nd of the two bytes. Also note that byte 10 has changed from 0xa1 to 0xb1 for white led control.
That was easy.
This is the effects stuff. From the main screen I did this...
speed 100 speed 50 speed 0 speed 10 speed 75 speed 100 brightness 75 b 50 b 25 b 0 b 25 b 50 b 75 b 100
click throu the effects 1 by 1 for 10 off
0002 800000 0c0d0b1014160a1e0a192907000fc4
0003 800000 0c0d0b1014160a1e0a192907000fc4
0012 800000 0d0e0b3ba1 3c6464 0000 0000000000 e0 - rgb command
000c 800000 0d0e0b3bb1 000000 642e 0000000000 7e - white command
cntr ?????? mode? e s b
0004 800000 04050b38 01 64 64
0005 800000 04050b38 01 32 64
0006 800000 04050b38 01 01 64
0007 800000 04050b38 01 33 64
0008 800000 04050b38 01 4d 64
0009 800000 04050b38 01 64 64
000a 800000 04050b38 01 64 4c
000b 800000 04050b38 01 64 32
000c 800000 04050b38 01 64 16
000d 800000 04050b38 01 64 00
000e 800000 04050b38 01 64 1a
000f 800000 04050b38 01 64 34
0010 800000 04050b38 01 64 4c
0011 800000 04050b38 01 64 64
0012 800000 04050b38 01 64 64
0013 800000 04050b38 02 64 64
0014 800000 04050b38 03 64 64
0015 800000 04050b38 04 64 64
0016 800000 04050b38 05 64 64
0017 800000 04050b38 06 64 64
0018 800000 04050b38 07 64 64
0019 800000 04050b38 08 64 64
001a 800000 04050b38 09 64 64
001b 800000 04050b38 0a 64 64
0005 800000 04050b38 01 64 64
0006 800000 04050b38 02 64 64
0007 800000 04050b38 03 64 64
0008 800000 04050b38 04 64 64
0009 800000 04050b38 71 64 64
001c 800000 0d0e0b3b240000000000000032000091 - off
Starting to look like we have 4 bytes counter, 3 bytes fixed as 80 00 00
over all commands. The the effects have a different 4 bytes to the colours, but seems fixed during all effects. Then a byte for effect number, a byte for speed and a byte for brightness. These packets are shorter than the colour packets and don't seem to have a checksum at all.
I have no interest in getting this working, so not going to spend time on it. If you're interested please get in touch.
This tool is quite hard to use in the app, and I'm not really sure how it works. I think it might be useful though, so I'm going to try and work it out. How hard can it be? It's probably a long list of pixel colours.
diy 1 - red diy 2 - green (might have missed the button here, I don't see it in the traces) diy 3 - blue slider to red slider to green slider to red click paint tool start at 12 o clock and draw round to 6 o clock green start at 12 go to 6 click erase start at 12, anti clockwise to 6 off
0001 8000000 40 50 a818a8b96
0002 8000000 c0 d0 b1014160a1e0b110807000f9 c7
0003 8000000 c0 d0 b1014160a1e0b110907000f9 d7
|-------fixed---------------| cnt? same cmd?
0202 00a5 00a1 0004 0012 1700 0004 800000 9697 0b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff00000 1333200 8f
0202 00a5 00a1 0004 0012 1700 0004 800000 9697 0b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff00000 1333200 8f
0202 00a5 00a1 0004 0012 1700 0006 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0007 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0008 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0009 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 000a 800000 9697 0b59 d40000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d400000 1333200 87
0202 00a5 00a1 0004 0012 1700 000b 800000 9697 0b59 d40000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d400000 1333200 87
0202 00a5 00a1 0004 0012 1700 000c 800000 9697 0b59 19d400 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d4000 1333200 c6
0202 00a5 00a1 0004 0012 1700 000d 800000 9697 0b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d4000 1333200 30
0202 001c 0018 0004 0012 1700 000e 800000 0d0e 0b3b 240000 000000 000032 000091 - off?
Notes:
- There is a new header First 12 bytes seem to be the same for each line of RGB data
- Then there is a counter by the looks of it (yes, this continues to increment from where the previous commands left off when changing modes)
- Then the classic 800000 we've seen before (what is this??)
- Then two bytes of something which is the same for the colour settings and different for the last command which I think must be an off
- There are 48 RGB tuples per line, and according to the app there are 48 RGB LEDs. So this is probably just a stream of colours for each LED in turn. Nice!
- Then there is a fixed tail. I don't think the last bit is a checksum, I think it's some kind of command indicator. edit: this is used for the modes in smear.
- I don't think moving the slider in the app sends any data, only the pattern is sent whole. Makes sense
Next test: its off smear All red (doy 1) All green All blue Erase all Red Draw 12 - 6 clock wise Erase 12 - 6 clock wise Green Draw 12 - 6 anti clock wise Erase 12 - 6 anti close wise off
(messed this up a bit, but should still be understandable. Clicked the wrong thing)
00028000000c0d0b1014160a1e0b360507000fbe
00038000000c0d0b1014160a1e0b360507000fbe
020200a500a1000 400121700 0004 800000 96970b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 01333200 8f
020200a500a1000 400121700 0005 800000 96970b59 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 01333200 8f
020200a500a1000 400121700 0006 800000 96970b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 01333200 8f
020200a500a1000 400121700 0007 800000 96970b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 000000 000000 000000 0000ff 0000ff 0000ff 01333200 92
020200a500a1000 400121700 0008 800000 96970b59 000000 000000 000000 000000 000000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 000000 000000 000000 000000 000000 000000 01333200 9a
020200a500a1000 400121700 0009 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000a 800000 96970b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 01333200 8f
020200a500a1000 400121700 000b 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000c 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000d 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000e 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000f 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0010 800000 96970b59 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 01333200 9f
020200a500a1000 400121700 0011 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0012 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0013 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0014 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0016 800000 96970b59 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 01333200 47
020200a500a1000 400121700 0017 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0018 800000 96970b59 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 33
020200a500a1000 400121700 0019 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 001a 800000 96970b59 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 33
001b 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90 - on
001c 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91 - off
All black Advanced Smear Mode Lightning 80 Lightning 100 Lightning 50 L 25 L 1 L 100 Bright 100 0 50 100 Stream one way Stream the other Strobe Jump off
0001 800000 04050a818a8b96
0002 800000 0c0d0b1014160a1e0d2c1307000fc4
0003 800000 0c0d0b1014160a1e0d2c1307000fc4
020200a500a1000 400121700 0019 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf - previous edits for comparison
020200a500a1000 400121700 0004 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0005 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0006 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 59 32 00 e5
020200a500a1000 400121700 0007 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 32 00 f0
020200a500a1000 400121700 0008 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0009 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 17 32 00 a3
020200a500a1000 400121700 000a 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 01 32 00 8d
020200a500a1000 400121700 000b 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 32 00 f0
020200a500a1000 400121700 000c 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 64 00 22
020200a500a1000 400121700 000d 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 00 00 be
020200a500a1000 400121700 000e 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 33 00 f1
020200a500a1000 400121700 000f 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 64 00 22
020200a500a1000 400121700 0010 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0011 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0012 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0013 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 01 24
020200a500a1000 400121700 0014 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 03 64 64 01 25
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 03 64 64 00 24
020200a500a1000 400121700 0016 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 04 64 64 00 25
020200a500a1000 400121700 0017 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 04 64 64 00 25
00188000000d0e0b3b240000000000000032000091 - off
- The packets are 170 bytes long
- There is a 12 byte header of some sort. Seems fixed.
- Then there is the counter. I'm reasonably sure this is a two byte counter
- Then there is a three byte "80 00 00" which we've seen elsewhere
- Then four bytes which stay the same
- Then 48 bytes of RRGGBB. Pixel zero is where the wire enters the metal circle and then with the stand on the left they go clockwise
- Then there are 5 bytes:
- mode (1 stream one way, 2 stream the other, 3 strobe, 4 jump)
- Speed (1 - 100%)
- Brightness (1-100%)
- ?
- ? checksum perhaps?
all red timer button time 14:18 create timer time set 14:30 to turn off confirm create timer 14:45 turn on create timer 15:00 turn on repeat sun, wed, sat turn on
0001 800000 04 05 0a 81 8a 8b 96
M? H?
0002 800000 0c 0d 0b 10 14 16 0a 1e 0e 11 31 07 00 0f c8
0003 800000 0c 0d 0b 10 14 16 0a 1e 0e 11 31 07 00 0f c8
0004 800000 0d 0e 0b 3b a1 00 64 64 00 00 00 00 00 00 00 a4
0005 800000 05 06 0a 22 2a 2b 0f 86
0006 800000 05 06 0a 11 1a 1b 0f 55
This is non-obvious. Not important for now.
Enable BT in app Scans Gets some basic info Click in Connects Turn off Back to main screen exit
- MTU negotiation 200 bytes. Later tablet asked for 256, but light said 200.
- A load of service discovery stuff I don't really understand. Not sure where the setting the notification is happening
- First outgoing request to light is: handle: 0x0017 service UUID: 0xffff, UUID: 0xff01. Value
0001 800000 04 05 0a 81 8a 8b 96
- Recvd handle value notification. Handle 0x0014. UUID 0xffff. UUID: 0xff02. Value
046d80000033340a7b22636f6465223a302c227061796c6f6164223a2238313144323336314630303046463030303030303032303033303433227d
- Sent (0xffff 0xff01)
0002 800000 0c 0d 0b 10 14 16 0a 1e 0e 39 2f 07 00 0f ee
- Sent (0xffff 0xff01)
0003 800000 0c 0d 0b 10 14 16 0a 1e 0e 39 2f 07 00 0f ee
- Sent (0xffff 0xff01)
0004 800000 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
(I think this is an on or an off) - Rxed (0xffff 0xff02)
046e 800000 33 34 0c 7b 22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c6f6164223a2238313144323436314630303046463030303030303032303033303434227d
Dunno. This is boring and hard.
0004 8000000d 0e0b3ba1 00646400 00000000 0000a4
0005 8000000d 0e0b3ba1 3c643000 00000000 0000ac
0006 8000000d 0e0b3ba1 78646400 00000000 00001c
0007 8000000d 0e0b3ba1 1e646400 00000000 0000c2
0008 8000000d 0e0b3ba1 00646400 00000000 0000a4
0011 8000000d 0e0b3ba1 00646400 00000000 0000a4
0012 8000000d 0e0b3ba1 00640000 00000000 000040
0013 8000000d 0e0b3ba1 00640000 00000000 000040
0014 8000000d 0e0b3ba1 00640000 00000000 000040
0015 8000000d 0e0b3ba1 00640000 00000000 000040
0016 8000000d 0e0b3ba1 00000000 00000000 0000dc
0017 8000000d 0e0b3ba1 00000000 00000000 0000dc
0018 8000000d 0e0b3ba1 00006400 00000000 000040
0019 8000000d 0e0b3ba1 00000100 00000000 0000dd
001a 8000000d 0e0b3ba1 00006400 00000000 000040
001b 8000000d 0e0b3ba1 00006400 00000000 000040
001c 8000000d 0e0b3ba1 00000100 00000000 0000dd
001d 8000000d 0e0b3ba1 00006400 00000000 000040
001e 8000000d 0e0b3ba1 00000100 00000000 0000dd
001f 8000000d 0e0b3ba1 00646400 00000000 0000a4
0020 8000000d 0e0b3ba1 00006400 00000000 000040
0021 8000000d 0e0b3ba1 3c643000 00000000 0000ac
0022 8000000d 0e0b3ba1 00006400 00000000 000040
0023 8000000d 0e0b3ba1 78646400 00000000 00001c
0024 8000000d 0e0b3ba1 00006400 00000000 000040
0025 8000000d 0e0b3ba1 1e646400 00000000 0000c2
0026 8000 000d 0e0b3ba1 00006400 00000000 000040
Dunno. It doesn't get solved by any of the online CRC calculators.
I loaded the app in to apktool
and looked in at the files. They don't make any sense to me.
I found a couple of interesting things that look relevant in case someone smarter than me can make sense of them.
The interesting stuff lives in /smali/com/zengge/hagallbjarkan/
Fun fact: Hagall Bjarkan is the who Bluetooth is named after.
Useful resources:
Files of interest:
- remoter/RemoterController.smali - defines
.method public static send(
- protocol/remoter/RemoterProtocol.smali - Seems to construct the byte array which gets sent to the BLE device
None of what follows should be considered truth. It's just my guess work.
In RemoteProtocol.smali
I found reference to "encryption" which is just xor.
It seems to pass something in to encrypt1
and then in to encrypt2
and then maybe CRC
?
I've added my best guess comments below (I have no idea what I'm doing). What's confusing me most, other than this strange code is that the packets aren't really encrypted since I can read perfectly logical values from the payload. So what is being encrypted? I assume nothing, and this is just a checksum. But then why so complicated?
If encoder
is what builds the byte array, where is that called from? It is called from send
in RemoterController.smali
. send
looks like this:
.method public static send(Landroid/content/Context;II[B)V // Called with a context?, two ints and a byte array
.locals 1
int-to-byte p2, p2 // int p2 is converted to a byte. I think this is the 2nd int passed in
const/16 v0, 0x1f // v0 is set to 0x1f v0 is the first local register
// encoder is called with p2, 0x1f, the first int, the byte array
invoke-static {p2, v0, p1, p3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encoder(BBI[B)[[B
move-result-object p1
invoke-static {p1}, Lcom/zengge/hagallbjarkan/utils/AdvertiseUtils;->createAdvertiseData03([[B)Landroid/bluetooth/le/AdvertiseData;
move-result-object p1
invoke-static {p0, p1}, Lcom/zengge/hagallbjarkan/utils/AdvertiseUtils;->advertiseData(Landroid/content/Context;Landroid/bluetooth/le/AdvertiseData;)V
return-void
.end method
So send
calls RemoterProtocol->encoder
with a byte, a byte, and int and a byte array, it returns a byte array. The returned array is then sent out via AdvertiseData in the Landroid stack. I assume this is just sending it over the air, but I don't get why it's called "Advertise". So perhaps this is wrong, or perhaps the send happens elsewhere.
Anyway, the thing I think I need to know is what is getting passed in to encoder
. Reading this: https://github.com/JesusFreke/smali/wiki/Registers#how-method-parameters-are-passed-into-a-method
.method public static encoder(BBI[B)[[B
.locals 7
const/16 v0, 0x9
const/4 v1, 0x0
if-nez p3, :cond_0 // if the byte array is not zero
new-array p3, v0, [B
goto :goto_0
:cond_0 // we have been passed an array with something in
array-length v2, p3 // v2 is now array len
if-ge v2, v0, :cond_1 // if len > 0x9 jump to cond_1
new-array v2, v0, [B // not bigger than 9, so new byte array v2 0x9 long
array-length v3, p3 // v3 now len of passed in array
invoke-static {p3, v1, v2, v1, v3}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
// p3 = passed in array
// v1 = 0x0
// v2 = len of array
// v1 = 0x0
// v3 = 0x9
// this is obvs copying the passed array to a new bigger array
move-object p3, v2 // v2 len of array is now p3 len of array
goto :goto_0
:cond_1 // We were passed an array bigger than 0x9
array-length v2, p3 // v2 = len of array
if-gt v2, v0, :cond_2 // if array len > 0x9 goto cond_2
:goto_0 // v3 is array len, p3 is an array of size 0x9 bytes
const/16 v2, 0x1a
new-array v3, v2, [B // v3 is new array of 0x1a long bytes
const/16 v4, 0x5a // v4 is 0x5a
aput-byte v4, v3, v1 // put 0x5a in position 0x0 of array v3
const/4 v4, 0x1 // v1 = 0x1
const/16 v5, 0x71 // v5 = 0x71
aput-byte v5, v3, v4 // put 0x71 in position 0x1 of array v3
const/4 v4, 0x2
aput-byte v1, v3, v4 // put 0x0 in position 0x2 of array v3
const/4 v4, 0x3
const/16 v5, 0x11
aput-byte v5, v3, v4 // put 0x11 in pos 0x3
const/4 v4, 0x4
aput-byte v1, v3, v4 // put 0x0 in pos 0x4
const/4 v5, 0x5
aput-byte p1, v3, v5 // put p1 (0x1f) I think in pos 0x5 (2nd param reg)
const/4 p1, 0x6
invoke-static {}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->nextSn()B // I think this might be the counter?
move-result v5
aput-byte v5, v3, p1 // put v5 (from nextsn) in 0x6 let's assume this is the counter
const/4 p1, 0x7 // pos 7 in p1 now
const/high16 v5, 0xff0000 //
and-int/2addr v5, p2 // add p2 to v5. p2 is 3rd param, an int. Called from RemoterController `send` method.
shr-int/lit8 v5, v5, 0x10 // vx, vy, vz - Shift vy right by the positions specified by vz (0x10) and store the result into vx. (could be encoding grb?)
int-to-byte v5, v5
aput-byte v5, v3, p1
const p1, 0xff00
and-int v5, p2, p1
const/16 v6, 0x8
shr-int/2addr v5, v6
int-to-byte v5, v5
aput-byte v5, v3, v6
and-int/lit16 p2, p2, 0xff
int-to-byte p2, p2
aput-byte p2, v3, v0
const/16 p2, 0xa
aput-byte v1, v3, p2
const/16 v0, 0xb
aput-byte v1, v3, v0
const/16 v0, 0xc
aput-byte p0, v3, v0
const/16 p0, 0xd
array-length v0, p3
invoke-static {p3, v1, v3, p0, v0}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
const/16 p0, 0x16
aput-byte v1, v3, p0
new-instance p3, Ljava/util/Random; // I dont see any evidence of random number usage in the HCI logs?
invoke-direct {p3}, Ljava/util/Random;-><init>()V
invoke-virtual {p3}, Ljava/util/Random;->nextInt()I
move-result p3 // p3 is now a random int
and-int/lit16 p3, p3, 0xff // adds 0xff to the random number and puts it in p3
int-to-byte p3, p3 // convert p3 to a byte
const/16 v0, 0x17
aput-byte p3, v3, v0 // put value of p3 in to array v3 at location v0 (0x17)
aget-byte p3, v3, v0 // get from byte array v3 at location v0 (0x17) store in p3 (i.e. put it in then take it back out again?)
// call encrypt1 with the array (v3), our randomnumber + 0xff (p3), p2 is 0xa I think, p0 is 0x16
invoke-static {v3, p3, p2, p0}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encrypt1([BBII)V
invoke-static {v3, v4, v0}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encrypt2([BII)V
invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->toCrc([B)I
move-result p0
const/16 p2, 0x18
and-int/2addr p1, p0
shr-int/2addr p1, v6
and-int/lit16 p1, p1, 0xff
int-to-byte p1, p1
aput-byte p1, v3, p2
const/16 p1, 0x19
and-int/lit16 p0, p0, 0xff
int-to-byte p0, p0
aput-byte p0, v3, p1
invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->reversal([B)V
sget-object p0, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->TAG:Ljava/lang/String;
new-instance p1, Ljava/lang/StringBuilder;
invoke-direct {p1}, Ljava/lang/StringBuilder;-><init>()V
const-string p2, " HEX "
invoke-virtual {p1, p2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invoke-static {v3, v2}, Lcom/zengge/hagallbjarkan/utils/ConvertUtil;->byte2HexStr([BI)Ljava/lang/String;
move-result-object p2
invoke-virtual {p1, p2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invoke-virtual {p1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p1
invoke-static {p0, p1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->toBytes([B)[[B
move-result-object p0
return-object p0
:cond_2
new-instance p0, Ljava/lang/RuntimeException;
const-string p1, "Overflow param."
invoke-direct {p0, p1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V
throw p0
.end method
.method private static encrypt1([BBII)V
// I think this means we have a byte array and two ints. One of the ints might be the length of the array. Returns VOID, so must edit the array in place?
// called from above with encrypt1 with the array (v3), our randomnumber + 0xff (p3), p2 is 0xa I think, p0 is 0x16
// p0 = byte array
// p1 = random number + 0xff
// p2 = 0xa (dec 10)
// p3 = 0x16 (dec 22)
.locals 1
:goto_0
if-gt p2, p3, :cond_0 // If we're past the end of the array(?) jump to cond_0 = return
aget-byte v0, p0, p2 // Load v0 with a byte from p2 in array p0.
xor-int/2addr v0, p1 // xor the loaded value with whatever p1 is (random + 0xff? but that wouldnt fit in a byte)
int-to-byte v0, v0 // convert it to a byte
aput-byte v0, p0, p2 // put it back where it came from?
add-int/lit8 p2, p2, 0x1 // Increment the counter by 1
goto :goto_0 // Go around again
:cond_0
return-void
.end method
.method private static encrypt2([BII)V
.locals 4
move v0, p1
:goto_0
if-gt v0, p2, :cond_0
aget-byte v1, p0, v0
sget-object v2, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->args:[B
sub-int v3, v0, p1
aget-byte v2, v2, v3
xor-int/2addr v1, v2
int-to-byte v1, v1
aput-byte v1, p0, v0
add-int/lit8 v0, v0, 0x1 // add one to the position and go around again
goto :goto_0
:cond_0
return-void
.end method
.method private static toCrc([B)I
.locals 3
const/4 v0, 0x0
move v1, v0
:goto_0
array-length v2, p0
add-int/lit8 v2, v2, -0x2
if-ge v0, v2, :cond_0
aget-byte v2, p0, v0
add-int/2addr v1, v2
add-int/lit8 v0, v0, 0x1
goto :goto_0
:cond_0
const p0, 0xffff
and-int/2addr p0, v1
return p0
.end method
This is a bit more useable. It converts back to something which looks more like code. I compared the bytes I thought the apktool would generate to the same thing here, and I was pretty close, so I think they are the same, but this one is a lot easier to read. I'm going to try using this instead.
Here's the same encoder from above, but decomplied by jadx:
public class RemoterProtocol {
private static final String TAG = "com.zengge.hagallbjarkan.protocol.remoter.RemoterProtocol";
private static final byte[] args = {17, 34, 4, 8, -103, 4, 36, 22, 4, -86, -69, -52, -16, 96, 97, -51, -49, Byte.MIN_VALUE, 53, 42};
private static byte count = 1;
public static byte[][] encoder(byte b2, byte b3, int i, byte[] bArr) {
if (bArr == null) {
bArr = new byte[9];
} else if (bArr.length < 9) {
byte[] bArr2 = new byte[9];
System.arraycopy(bArr, 0, bArr2, 0, bArr.length);
bArr = bArr2;
} else if (bArr.length > 9) {
throw new RuntimeException("Overflow param.");
}
System.arraycopy(bArr, 0, r3, 13, bArr.length);
encrypt1(r3, r3[23], 10, 22);
encrypt2(r3, 4, 23);
int crc = toCrc(r3);
byte[] bArr3 = {90, 113, 0, 17, 0, b3, nextSn(), (byte) ((16711680 & i) >> 16), (byte) ((i & CipherSuite.DRAFT_TLS_DHE_RSA_WITH_AES_128_OCB) >> 8), (byte) (i & 255), 0, 0, b2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) (new Random().nextInt() & 255), (byte) (((65280 & crc) >> 8) & 255), (byte) (crc & 255)};
reversal(bArr3);
String str = TAG;
Log.i(str, " HEX " + ConvertUtil.byte2HexStr(bArr3, 26));
return toBytes(bArr3);
}
private static void encrypt1(byte[] bArr, byte b2, int i, int i2) {
while (i <= i2) {
bArr[i] = (byte) (bArr[i] ^ b2);
i++;
}
}
private static void encrypt2(byte[] bArr, int i, int i2) {
for (int i3 = i; i3 <= i2; i3++) {
bArr[i3] = (byte) (bArr[i3] ^ args[i3 - i]);
}
}
private static byte nextSn() {
byte b2;
synchronized (RemoterProtocol.class) {
b2 = count;
count = (byte) (b2 + 1);
}
return b2;
}
private static void reversal(byte[] bArr) {
int i = 2;
while (i < bArr.length - 1) {
byte b2 = bArr[i];
int i2 = i + 1;
bArr[i] = bArr[i2];
bArr[i2] = b2;
i = i2 + 1;
}
}
private static byte[][] toBytes(byte[] bArr) {
byte[][] bArr2 = (byte[][]) Array.newInstance(byte.class, bArr.length / 2, 2);
for (int i = 0; i < bArr2.length; i++) {
byte[] bArr3 = bArr2[i];
int i2 = i * 2;
bArr3[0] = bArr[i2];
bArr3[1] = bArr[i2 + 1];
}
return bArr2;
}
private static int toCrc(byte[] bArr) {
int i = 0;
for (int i2 = 0; i2 < bArr.length - 2; i2++) {
i += bArr[i2];
}
return 65535 & i;
}
}
What surprises me is how much CRC and encryption stuff is seemingly performed. For all that messing about, I was still able to manually decode the protocol for light colour. There is something going on here that I don't understand, and it's probably to do with those 8 bytes at the start which seem to be static.
- You've got to pass in a max 9 element array to encoder.
- It then copies your whole 9 byte array in to an array called r3 starting at position 13(?? the array is only 9 long. Decompile error?)
I took another look at the Smali code since the Java wasn't making much sense. It looks like the decompiler got confused. There are references to encryption libraries in Java which in Smali look like simple bitshifts (but contain the letters "shr" so maybe misinterpreted as a encryption routine?)
const/16 v2, 0x1a
new-array v3, v2, [B
- This new array is then filled up with (* indicates see notes):
Example real message:
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14
00 10 80 00 00 0d 0e 0b 3b a1 3c 4c 64 00 00 00 00 00 00 00 c8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
0 1 2 3 4 5 6 7 8 9 A B c d e f 10 11 12 13 14 15 16 17 18 19 1A
5A 71 00 11 00 P1 NEXTSN *FF *FF *FF 00 00 Bp0 * * * * * * * * * 00 *
Note A - byte 8 (starting at 1)
- 0xFF0000 AND (byte) p2
- shift right 0x10 = 16 places
- regardless of the input p2, the answer will always be 0xFF?
- So what's the point?
- Could be a code bug, could be a decompile bug
Note B - byte 9
- Decompiled java talks about an encryption routine, but the smali doesn't?
- 0xff00 && p2 - which is a byte, so max 0xff? =p5
- shift right by 0x8
- Output is 0xFF regardless?
- Add to array at 0x8
- Why?
Note C - byte 10
- p2 AND 0xFF
- Add to array at v0 which is 0x9
Note D - byte 14
- Create a new array v0 which is p3 long
- Call arraycopy (src, srcpos, dest, destpos, num bytes to copy).
- Which works out to be copy (p3, 0, our working array, 0xd, len(p3)
- So copy all the bytes from the array passed in at the start in to here starting at 0xD
- I assume this is the max 9 long array created earlier
- then set p0 0x16 which is 9 after 0xd
Note E - byte 24
- Get a random int in to p3
- Set p3 to be int rand AND 0xff (so last two digits)
- Store our random number byte in position 0x17 (used for xor)
- Gets it back out again in to p3
- Calls "encrypt1" with main array, random & FF, 0xa, 0x16) (line 206)
- encypt 1: if 0xa > 0x16 (p2) return void (line 278)
- encrypt1(array, rand, start, stop)
- 0xa is a const at this point, as is 0x16. These might be array lengths?
- So in this case we carry on
- load v0 with value of array[p2 (0x16)]
- xor it with p1 (0xa)
- put it back
- add 1 to p2
- So
encrypt1
xors the array values from p2 0xa to p3 0x16 with a random number which is stored in the main array at 0x17 where it is anded with FF. - On to
encrypt2
. Called with (main array, v4 0x4, v0 0x17 (pos of rand but also len of array?)) - v0 is 0x4
- if v0 0x4 > p2 0x17 return. So a loop over bytes 4 to 17?
- v1 = byte at array[counter v0]
- load v2 with the array which is created at the top of the file (20 bytes):
0x11 | 17
0x22 | 34
0x4 | 4
0x8 | 8
-0x67 | -103
0x4 | 4
0x24 | 36
0x16 | 22
0x4 | 4
-0x56 | -86
-0x45 | -69
-0x34 | -52
-0x10 | -16
0x60 | 96
0x61 | 97
-0x33 | -51
-0x31 | -49
-0x80 | -128
0x35 | 53
0x2a | 42
- What the hell is this? Is it an encryption key? Let's call it that
- set v3 to zero. 0x4 minus 0x4 (i.e. at the start this is zero)
- set v2 to the encryption key array
- set v2 to byte v3 of the encyption key array
- v0 is counter
- xor byte v1 with byte v2. v1 is
array_p0[v0]
. v2 isencryption_array[v0 - p1]
- put the result in main array[v0]
- increment v0 by 1
It runs it through "reversal" which starts at position "2" so 3rd byte and loops until one before the end. That is it skips the first 2 elements and the last element. It just swaps bytes around, it doesn't reverse the whole array.
That would give us:
5A 71 11 00 1F 00 FF 00 FF FF 00 00 00 FF 00 00 00 00 00 00 00 00 00 FF FF FF FF
If we want to try and work backwards, The "set the colour" messages are 21 bytes long. 4 bytes of counter 7 bytes of wtf 1 byte of something related to what it's doing 5 bytes of colour 5 bytes of 0 1 byte checksum
0010 8000000d0e0b3b b1 000000 6464 0000000000 b4 0006 8000000d0e0b3b a1 006464 0000 0000000000 a4
I found an SDK! http://cnwifidevsdk.magichue.net:4000/ble/
I'm not certain yet if it's useful, or even the right one. But here's some things which might be useful:
public static ZGHBDevice newDevice(byte[] bArr, ScanResult scanResult) {
int i;
if (bArr == null || bArr.length < 32) {
return null;
}
try {
LinkedList linkedList = new LinkedList();
LinkedList linkedList2 = new LinkedList();
int i2 = 0;
do {
int i3 = bArr[i2] & 255;
if (i3 == 0) {
break;
}
int i4 = i2;
int i5 = i2;
int i6 = bArr[i2 + 1] & 255;
int i7 = i3 - 1;
byte[] bArr2 = new byte[i7];
System.arraycopy(bArr, i5 + 2, bArr2, 0, i7);
linkedList.add(Integer.valueOf(i6));
linkedList2.add(bArr2);
i = i4 + i3 + 1;
i2 = i;
} while (i != bArr.length);
if (linkedList.size() != linkedList2.size()) {
return null;
}
if (linkedList.contains(22)) {
if (linkedList.contains(255)) {
String str = TAG;
Log.i(str, "correct packets");
int index = getIndex(linkedList, 22);
int index2 = getIndex(linkedList, 255);
byte[] bArr3 = (byte[]) linkedList2.get(index);
byte[] bArr4 = (byte[]) linkedList2.get(index2);
Log.i(str, " buffer : " + e.a(bArr3));
if (bArr3.length == 16 || bArr3.length == 29) {
int manufacturer = getManufacturer(bArr3);
int manufacturer2 = getManufacturer(bArr4);
if (manufacturer == -1 || manufacturer2 == -1) {
return null;
}
ZGHBDevice zGHBDevice = new ZGHBDevice(scanResult);
setDeviceInfo(zGHBDevice, scanResult.getDevice().getName(), manufacturer, bArr3);
if (zGHBDevice.getBleVersion() >= 5) {
byte b = bArr3[14];
byte b2 = bArr3[15];
zGHBDevice.setCheckKeyFlag(b);
zGHBDevice.setFirmwareFlag(b2);
}
int length = bArr4.length - 3;
byte[] bArr5 = new byte[length];
System.arraycopy(bArr4, 3, bArr5, 0, length);
zGHBDevice.setState(bArr5);
setDeviceName(linkedList, linkedList2, zGHBDevice);
return zGHBDevice;
}
return null;
}
return null;
} else if (linkedList.contains(255)) {
int index3 = getIndex(linkedList, 255);
if (index3 == -1) {
return null;
}
byte[] bArr6 = (byte[]) linkedList2.get(index3);
if (bArr6.length != 29) {
return null;
}
int manufacturer3 = getManufacturer(bArr6);
if (manufacturer3 == -1) {
return null;
}
ZGHBDevice zGHBDevice2 = new ZGHBDevice(scanResult);
setDeviceInfo(zGHBDevice2, scanResult.getDevice().getName(), manufacturer3, bArr6);
if (zGHBDevice2.getBleVersion() >= 5) {
byte b3 = bArr6[14];
byte b4 = bArr6[15];
zGHBDevice2.setCheckKeyFlag(b3);
zGHBDevice2.setFirmwareFlag(b4);
byte[] bArr7 = new byte[11];
System.arraycopy(bArr6, 16, bArr7, 0, 11);
zGHBDevice2.setState(bArr7);
byte[] bArr8 = new byte[2];
bArr8[0] = bArr[27];
bArr8[1] = bArr[28];
zGHBDevice2.setRfu(bArr8);
} else {
int length2 = bArr6.length - 16;
byte[] bArr9 = new byte[length2];
System.arraycopy(bArr6, 16, bArr9, 0, length2);
zGHBDevice2.setRfu(bArr9);
}
setDeviceName(linkedList, linkedList2, zGHBDevice2);
return zGHBDevice2;
} else {
return null;
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return null;
}
}
I have spent the last week reverse engineering what I think is the protocol from the disassembled Android app. I went through the smali byte code line by line and ported it to Python. It's included in this repo, but its nowhere near functional. Or tested.
Do you know why it's not tested? Well stay tuned...
Let's imagine that you're a manufacter of low cost consumer lighting products. You've got a new product that you want to launch to the market, it's a BT LE controlled light and you're going to need a microcontroller with an RF remote and you're going to need an Android app and an iOS app.
So you go to your head of software engineering and say "We need some control software for this new light we've got coming to the market. You've got 2 weeks, 2 people and no money. Get to work!".
Your engineering team meet and they talk about what's needed. One of the topics of conversation is how BT LE is a pretty unreliable communication mechansim and in order to make things more reliable and also a bit more secure, to stop all those naughty home automation nerds controlling your lights with their software, you should implement some form of encryption and checksum on the conversation. Everyone agrees this is a good idea.
You spend a few hours working out a protocol which is easy enough to be implemented on the low end MCU you're working with and their very poorly documented SDK and you break off and one engineer goes to write the Android and iOS apps and one engineer goes off to write the firmware for the microcontroller.
After a couple of days your engineer working on the firmware comes to a realisation that she's not getting paid enough for this crap, and that since her manager is an idiot and the app writer only knows how to use point-and-click app toolkits, no one is ever going to check that the C++ code. So why bother spending all that time implementing and testing the encryption and CRC checking code, why not just assume it's correct and carry out the instructions? Stupider like a FOX!
So that's what they did.
Once you connect to the device, set the MTU to 200, enable the notification on 0x0015 and perhaps send a couple of handshake packets, you're off to the races and you can send colour changing packets with scant regard for the packet counter at the start or the checksum at the end. Just set them to FF, no one will ever know.
And that is the story of how I wasted two weeks learning to read smali code and trying to make sense of an overly complex protocol.
I don't suppose this is the last chapter of the story, but it is at least a beginning.
Thanks for reading this far. I hope you had fun, I know I did.