JorgePe / BOOSTreveng

Reverse engineering the LEGO BOOST Hub
MIT License
168 stars 35 forks source link

BOOST Color and Distance Sensor info #33

Open dlech opened 6 years ago

dlech commented 6 years ago

I've been spying on my BOOST Color and Distance sensor

Like EV3 UART sensors, Powered Up UART devices (can't say "sensors" anymore since BOOST Interactive Motor is also UART) send data about the device at 2400 baud until the brick syncs with the device.

Here is the data that I captured and the interpretation according to lms2012.h and d_uart_mod.c.

Recorded bytes and interpretation (click to expand)
40  MESSAGE_CMD + LENGTH_1 + CMD_TYPE
25  Type = 37; BOOST Color and Distance Sensor
9a  checksum; 0xff ^ 0x40 ^ 0x25
51  MESSAGE_CMD + LENGTH_4 + CMD_MODES (similar to EV3 CMD_MODES with 2 extra bytes)
07  7 + 1; Modes = 8 (value used on EV3)
07  7 + 1; Views = 8 (value used on EV3)
0a  10 + 1; Modes = 11 (value used on Powered Up)
07  7 + 1; Views = 8 (value used on Powered Up)
a3  checksum; 0xff ^ 0x51 ^ 0x07  ^ 0x07 ^ 0x0a ^ 0x07
52  MESSAGE_CMD + LENGTH_4 + CMD_SPEED
00  LSB
c2  ..
01  ..
00  MSB; BitRate = 115200
6e  checksum; 0xff ^ 0x52 ^ 0x00 ^ 0xc2 ^ 0x01 ^ 0x00
5f  MESSAGE_CMD + LENGTH_8 + CMD_UNK1 (new PF2 command)
00  LSB
00  ..
00  ..
10  MSB; ??? = 0x10000000
00  LSB
00  ..
00  ..
10  MSB; ??? = 0x10000000
a0  checksum; 0xff ^ 0x5f ^ 0x00 ^ 0x00 ^ 0x10 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x10
9a  MESSAGE_INFO + LENGTH_8 + MODE_2; really mode 10
20  MODE_PLUS_8 + INFO_NAME
43  'C'
41  'A'
4c  'L'
49  'I'
42  'B'
00  ''
00  ''
00  ''; Name = "CALIB"
00  checksum; 0xff ^ 0x9a ^ 0x20 ^ 0x43 ^ 0x41 ^ 0x4c ^ 0x49 ^ 0x42 ^ 0x00 ^ 0x00 ^ 0x00
9a  MESSAGE_INFO + LENGTH_8 + MODE_2; really mode 10
21  MODE_PLUS_8 + INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
ff  ..
7f  ..
47  MSB; RawMax = 65535.0
83  checksum; 0xff ^ 0x9a ^ 0x21 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0xff ^ 0x7f ^ 0x47
9a  MESSAGE_INFO + LENGTH_8 + MODE_2; really mode 10
22  MODE_PLUS_8 + INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
cd  checksum; 0xff ^ 0x9a ^ 0x22 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0xc8 ^ 0x42
9a  MESSAGE_INFO + LENGTH_8 + MODE_2; really mode 10
23  MODE_PLUS_8 + INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
ff  ..
7f  ..
47  MSB; SiMax = 65535.0
81  checksum; 0xff ^ 0x9a ^ 0x23 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0xff ^ 0x7f ^ 0x47
92  MESSAGE_INFO + LENGTH_4 + MODE_2; really mode 10
24  MODE_PLUS_8 + INFO_SYMBOL
4e  'N'
2f  '/'
41  'A'
00  ''; Symbol = "N/A"
69  checksum; 0xff ^ 0x92 ^ 0x24 ^ 0x4e ^ 0x2f ^ 0x41 ^ 0x00
8a  MESSAGE_INFO + LENGTH_2 + MODE_2; really mode 10
25  MODE_PLUS_8 + INFO_UNK1 (new PF2 info)
10  ???
00  ???
40  checksum; 0xff ^ 0x8a ^ 0x25 ^ 0x10 ^ 0x00
92  MESSAGE_INFO + LENGTH_4 + MODE_2; really mode 10
a0  MODE_PLUS_8 + INFO_FORMAT
08  DataSets = 8
01  Format = DATA16
05  Figures = 5
00  Decimals = 0
c1  checksum; 0xff ^ 0x92 ^ 0xa0 ^ 0x08 ^ 0x01 ^ 0x05 ^ 0x00
99  MESSAGE_INFO + LENGTH_8 + MODE_1; really mode 9
20  MODE_PLUS_8 + INFO_NAME
44  'D'
45  'E'
42  'B'
55  'U'
47  'G'
00  ''
00  ''
00  ''; Name = "DEBUG"
17  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1; really mode 9
21  MODE_PLUS_8 + INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
c0  ..
7f  ..
44  MSB; RawMax = 1023.0
bc  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1; really mode 9
22  MODE_PLUS_8 + INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ce  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1; really mode 9
23  MODE_PLUS_8 + INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; SiMax = 10.0
24  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1; really mode 9
24  MODE_PLUS_8 + INFO_SYMBOL
4e  'N'
2f  '/'
41  'A'
00  ''; Symbol = "N/A"
6a  checksum
89  MESSAGE_INFO + LENGTH_2 + MODE_1; really mode 9
25  MODE_PLUS_8 + INFO_UNK1 (new PF2 info)
10  ???
00  ???
43  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1; really mode 9
a0  MODE_PLUS_8 + INFO_FORMAT
02  DataSets = 2
01  Format = DATA16
05  Figures = 5
00  Decimals = 0
c8  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0; really mode 8
20  MODE_PLUS_8 + INFO_NAME
53  'S'
50  'P'
45  'E'
43  'C'
20  ' '
31  '1'
00  ''
00  ''; "SPEC 1"
53  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0; really mode 8
21  MODE_PLUS_8 + INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
7f  ..
43  MSB; RawMax = 255.0
7a  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0; really mode 8
22  MODE_PLUS_8 + INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
cf  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0; really mode 8
23  MODE_PLUS_8 + INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
7f  ..
43  MSB; SiMax = 255.0
78  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0; really mode 8
24  MODE_PLUS_8 + INFO_SYMBOL
4e  'N'
2f  '/'
41  'A'
00  ''; Symbol = "N/A"
6b  checksum
88  MESSAGE_INFO + LENGTH_2 + MODE_0; really mode 8
25  MODE_PLUS_8 + INFO_UNK1 (new PF2 info)
00  ???
00  ???
52  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0; really mode 8
a0  MODE_PLUS_8 + INFO_FORMAT
04  DataSets = 4
00  Format = DATA8
03  Figures = 3
00  Decimals = 1
c8  checksum
9f  MESSAGE_INFO + LENGTH_8 + MODE_7
00  INFO_NAME
49  'I'
52  'R'
20  ' '
54  'T'
78  'x'
00  ''
00  ''
00  ''; "IR Tx"
77  checksum
9f  MESSAGE_INFO + LENGTH_8 + MODE_7
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
ff  ..
7f  ..
47  MSB; RawMax = 65535.0
a6  checksum
9f  MESSAGE_INFO + LENGTH_8 + MODE_7
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e8  checksum
9f  MESSAGE_INFO + LENGTH_8 + MODE_7
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
ff  ..
7f  ..
47  SiMax = 65535.0
a4  checksum
97  MESSAGE_INFO + LENGTH_4 + MODE_7
04  INFO_NAME
4e  'N'
2f  '/'
41  'A'
00  ''; Symbol = "N/A"
4c  checksum
8f  MESSAGE_INFO + LENGTH_2 + MODE_7
05  INFO_UNK1
00  ???
04  ???
71  checksum
97  MESSAGE_INFO + LENGTH_4 + MODE_7
80  INFO_FORMAT
01  DataSets = 1
01  Format = DATA16
05  Figures = 5
00  Decimals = 0
ed  checksum
9e  MESSAGE_INFO + LENGTH_8 + MODE_6
00  INFO_NAME
52  'R'
47  'G'
42  'B'
20  ' '
49  'I'
00  ''
00  ''
00  ''; Name = "RGB I"
5f  checksum
9e  MESSAGE_INFO + LENGTH_8 + MODE_6
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
c0  ..
7f  ..
44  MSB; RawMax = 1023.0
9b  checksum
9e  MESSAGE_INFO + LENGTH_8 + MODE_6
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e9  checksum
9e  MESSAGE_INFO + LENGTH_8 + MODE_6
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
c0  ..
7f  ..
44  MSB = 1023.0
99  checksum
96  MESSAGE_INFO + LENGTH_4 + MODE_6
04  INFO_SYMBOL
52  'R'
41  'A'
57  'W'
00  ''; Symbol = "RAW"
29  checksum
8e  MESSAGE_INFO + LENGTH_2 + MODE_6
05  INFO_UNK1
10  ???
00  ???
64  checksum
96  MESSAGE_INFO + LENGTH_4 + MODE_6
80  INFO_FORMAT
03  DataSets = 3
01  Format = DATA16
05  Figures = 5
00  Decimals = 0
ee  checksum
9d  MESSAGE_INFO + LENGTH_8 + MODE_5
00  INFO_NAME
43  'C'
4f  'O'
4c  'L'
20  ' '
4f  'O'
00  ''
00  ''
00  ''; Name = "COL O"
4d  checksum
9d  MESSAGE_INFO + LENGTH_8 + MODE_5
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; RawMax = 10.0
02  checksum
9d  MESSAGE_INFO + LENGTH_8 + MODE_5
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ea  checksum
9d  MESSAGE_INFO + LENGTH_8 + MODE_5
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; SiMax = 10.0
00  checksum
95  MESSAGE_INFO + LENGTH_4 + MODE_5
04  INFO_SYMBOL
49  'I'
44  'D'
58  'X'
00  ''; Symbol = "IDX"
3b  checksum
8d  MESSAGE_INFO + LENGTH_2 + MODE_5
05  INFO_UNK1
00  ???
04  ???
73  checksum
95  MESSAGE_INFO + LENGTH_4 + MODE_5
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
03  Figures = 3
00  Decimals = 0
e8  checksum
94  MESSAGE_INFO + LENGTH_4 + MODE_4
00  INFO_NAME
41  'A'
4d  'M'
42  'B'
49  'I'; Name = 'AMBI'
6c  checksum
9c  MESSAGE_INFO + LENGTH_8 + MODE_4
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
e8
9c  MESSAGE_INFO + LENGTH_8 + MODE_4
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
eb  checksum
9c  MESSAGE_INFO + LENGTH_8 + MODE_4
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
ea  checksum
94  MESSAGE_INFO + LENGTH_4 + MODE_4
04  INFO_SYMBOL
50  'P'
43  'C'
54  'T'
00  ''; Symbol = 'PCT'
28  checksum
8c  MESSAGE_INFO + LENGTH_2 + MODE_4
05  INFO_UNK1
10  ???
00  ???
66  checksum
94  MESSAGE_INFO + LENGTH_4 + MODE_4
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
03  Figures = 3
00  Decimals = 0
e9  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3
00  INFO_NAME
52  'R'
45  'E'
45  'F'
4c  'L'
54  'T'
00  ''
00  ''
00  ''; Name = "REFLT"
2d  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
ef  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ec  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
ed  checksum
93  MESSAGE_INFO + LENGTH_4 + MODE_3
04  INFO_SYMBOL
50  'P'
43  'C'
54  'T'
00  ''; Symbol = "PCT"
2f  checksum
8b  MESSAGE_INFO + LENGTH_2 + MODE_3
05  INFO_UNK1
10  ???
00  ???
61  checksum
93  MESSAGE_INFO + LENGTH_4 + MODE_3
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
03  Figures = 3
00  Decimals = 0
ee  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
00  INFO_NAME
43  'C'
4f  'O'
55  'U'
4e  'N'
54  'T'
00  ''
00  ''
00  ''; Name = "COUNT"
26  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
ee  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ed  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
ec  checksum
92  MESSAGE_INFO + LENGTH_4 + MODE_2
04  INFO_SYMBOL
43  'C'
4e  'N'
54  'T'
00  ''; Symbol = "CNT"
30  checksum
8a  MESSAGE_INFO + LENGTH_2 + MODE_2
05  INFO_UNK1
08  ???
00  ???
78  checksum
92  MESSAGE_INFO + LENGTH_4 + MODE_2
80  INFO_FORMAT
01  DataSets = 1
02  Format = DATA32
04  Figures = 4
00  Decimals = 0
ea  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1
00  INFO_NAME
50  'P'
52  'R'
4f  'O'
58  'X'; Name = "PROX"
7b  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; RawMax = 10.0
06  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ee  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; SiMax = 10.0
04  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1
04  INFO_SYMBOL
44  'D'
49  'I'
53  'S'
00  ''; Symbol = "DIS"
34  checksum
89  MESSAGE_INFO + LENGTH_2 + MODE_1
05  INFO_UNK1
50  ???
00  ???
23  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
03  Figures = 3
00  Decimals = 0
ec  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
00  INFO_NAME
43  'C'
4f  'O'
4c  'L'
4f  'O'
52  'R'
00  ''
00  ''
00  ''; Name = "COLOR"
3a  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
01  INFO_RAW
00  LSB
00  ..
00  ..
00  MSB; RawMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; RawMax = 10.0
07  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
02  INFO_PCT
00  LSB
00  ..
00  ..
00  MSB; PctMin = 0.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
ef
98  MESSAGE_INFO + LENGTH_8 + MODE_0
03  INFO_SI
00  LSB
00  ..
00  ..
00  MSB; SiMin = 0.0
00  LSB
00  ..
20  ..
41  MSB; SiMax = 10.0
05  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0
04  INFO_SYMBOL
49  'I'
44  'D'
58  'X'
00  ''; Symbol = "IDX"
3e  checksum
88  MESSAGE_INFO + LENGTH_2 + MODE_0
05  INFO_UNK1
c4  ???
00  ???
b6  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
03  Figures = 3
00  Decimals = 0
ed  checksum
88  MESSAGE_INFO + LENGTH_2 + MODE_0
06  INFO_UNK2 (second new PF2 info - others are 05)
4f  ???
00  ???
3e  checksum
04  MESSAGE_SYS + BYTE_ACK
00  MESSAGE_SYS + BYTE_SYNC
dlech commented 6 years ago
  1. This should provide some insight to the "mystery modes" in ColorDistanceSensor.md, For example "IR Tx" mode sounds rather interesting.
  2. It would be nice to do this for other devices. I don't have WeDo 2.0 sensors, so the Interactive Motor is the only other one I could do. This is a rather time consuming processes, so if someone else wants to pitch in, that would be awesome.
  3. There are some new message types in Powered Up devices that aren't present in EV3 sensors, so if anyone has any theories on what these might be, I would love to hear your ideas.
Cheat-sheet for interpreting messages
// FIRST BYTE

// bits 7-6
#define   MESSAGE_SYS                   0x00    // System message   0b00 << 6
#define   MESSAGE_CMD                   0x40    // Command message  0b01 << 6
#define   MESSAGE_INFO                  0x80    // Info message     0b10 << 6
#define   MESSAGE_DATA                  0xC0    // Data message     0b11 << 6

// bits 5-3
#define   LENGTH_1                      0x00    // 1 byte           0b000 << 3
#define   LENGTH_2                      0x08    // 2 bytes          0b001 << 3
#define   LENGTH_4                      0x10    // 4 bytes          0b010 << 3
#define   LENGTH_8                      0x18    // 8 bytes          0b011 << 3
#define   LENGTH_16                     0x20    // 16 bytes         0b100 << 3
#define   LENGTH_32                     0x28    // 32 bytes         0b101 << 3

// MESSAGE_SYS bits 2-0
#define   BYTE_SYNC                     0x00    // Synchronization byte
#define   BYTE_ACK                      0x04    // Acknowledge byte
#define   BYTE_NACK                     0x02    // Not acknowledge byte (keep alive)

// MESSAGE_CMD bits 2-0
#define   CMD_TYPE                      0x00    // CMD command - TYPE     (device type for VM reference)
#define   CMD_MODES                     0x01    // CMD command - MODES    (number of supported modes 0=1)
#define   CMD_SPEED                     0x02    // CMD command - SPEED    (maximum communication speed)
#define   CMD_SELECT                    0x03    // CMD command - SELECT   (select mode)
#define   CMD_WRITE                     0x04    // CMD command - WRITE    (write to device)
#define   CMD_UNK1                      0x07    // CMD command - UNK1     (unknown Powered Up device info)

// MESSAGE_INFO and MESSAGE_DATA bits 2-0
#define   MODE_0                        0x00    // MODE 0 (or 8 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_1                        0x01    // MODE 1 (or 9 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_2                        0x02    // MODE 2 (or 10 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_3                        0x03    // MODE 3 (or 11 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_4                        0x04    // MODE 4 (or 12 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_5                        0x05    // MODE 5 (or 13 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_6                        0x06    // MODE 6 (or 14 if INFO_MODE_PLUS_8 bit is set)
#define   MODE_7                        0x07    // MODE 7 (or 15 if INFO_MODE_PLUS_8 bit is set)

// SECOND INFO BYTE

#define   INFO_NAME                     0x00    // INFO command - NAME    (device name)
#define   INFO_RAW                      0x01    // INFO command - RAW     (device RAW value span)
#define   INFO_PCT                      0x02    // INFO command - PCT     (device PCT value span)
#define   INFO_SI                       0x03    // INFO command - SI      (device SI  value span)
#define   INFO_SYMBOL                   0x04    // INFO command - SYMBOL  (device SI  unit symbol)
#define   INFO_UNK1                     0x05    // INFO command - UNK1    (unknown powered up-only info 1)
#define   INFO_UNK2                     0x06    // INFO command - UNK2    (unknown powered up-only info 2)
#define   INFO_MODE_PLUS_8              0x20    // Bit flag used in powered up devices to indicate that the mode is 8 + the mode specified in the first byte
#define   INFO_FORMAT                   0x80    // INFO command - FORMAT  (device data sets and format)

// INFO_FORMAT formats
#define   DATA8                         0x00    // 8-bit signed integer
#define   DATA16                        0x01    // 16-bit little-endian signed integer
#define   DATA32                        0x02    // 32-bit little-endian signed integer
#define   DATAF                         0x03    // 32-bit little-endian IEEE 754 floating point
dlech commented 6 years ago

DIY sniffer: https://ofalcao.pt/blog/2018/powered-up-interception-cable

dlech commented 6 years ago

Here is the results in table form:

Field Value
Type ID 37
Modes (on EV3) 11 (8)
Views (on EV3) 8 (8)
BitRate 115200
FwVersion 1.0.00.0000
HwVersion 1.0.00.0000
Unknown3 0x4f (79)
Unknown4 0x00 (0)
Mode Name RawMin...Max PctMin...Max SiMin...Max Symbol Unknown DataSets Format Figures Decimals
0 COLOR 0.0...10.0 0.0...100.0 0.0...10.0 IDX 0xc4, 0x00 (196, 0) 1 DATA8 3 0
1 PROX 0.0...10.0 0.0...100.0 0.0...10.0 DIS 0x50, 0x00 (80, 0) 1 DATA8 3 0
2 COUNT 0.0...100.0 0.0...100.0 0.0...100.0 CNT 0x08, 0x00 (8, 0) 1 DATA32 4 0
3 REFLT 0.0...100.0 0.0...100.0 0.0...100.0 PCT 0x10, 0x00 (16, 0) 1 DATA8 3 0
4 AMBI 0.0...100.0 0.0...100.0 0.0...100.0 PCT 0x10, 0x00 (16, 0) 1 DATA8 3 0
5 COL O 0.0...10.0 0.0...100.0 0.0...10.0 IDX 0x00, 0x04 (0, 4) 1 DATA8 3 0
6 RGB I 0.0...1023.0 0.0...100.0 0.0...1023.0 RAW 0x10, 0x00 (16, 0) 3 DATA16 5 0
7 IR Tx 0.0...65535.0 0.0...100.0 0.0...65535.0 N/A 0x00, 0x04 (0, 4) 1 DATA16 5 0
8 SPEC 1 0.0...255.0 0.0...100.0 0.0...255.0 N/A 0x00, 0x00 (0, 0) 4 DATA8 3 0
9 DEBUG 0.0...1023.0 0.0...100.0 0.0...1023.0 N/A 0x10, 0x00 (16, 0) 2 DATA16 5 0
10 CALIB 0.0...65535.0 0.0...100.0 0.0...65535.0 N/A 0x10, 0x00 (16, 0) 8 DATA16 5 0

Feel free to copy and paste this somewhere in the repo.

dlech commented 6 years ago

One pattern I noticed with the unknown values is that the output modes (COL O and IR Tx) seem to have the second value as non-zero while the other (input) modes seem to have the first value as non-zero.

dlech commented 6 years ago

Here is the BOOST Interactive motor dump:

click to expand
40  MESSAGE_CMD + LENGTH_1 + CMD_TYPE
26  Type = 38; BOOST Interactive Motor
99  checksum
49  MESSAGE_CMD + LENGTH_2 + CMD_MODES
03  3 + 1; Modes = 4
02  2 + 1; Views = 3
b7  checksum
52  MESSAGE_CMD + LENGTH_4 + CMD_SPEED
00  LSB
c2  ..
01  ..
00  MSB; BitRate = 115200
6e  checksum
5f  MESSAGE_CMD + LENGTH_8 + CMD_UNK1 (new PF2 command)
00  LSB
00  ..
00  ..
10  MSB; ??? = 0x10000000
00  LSB
00  ..
00  ..
10  MSB; ??? = 0x10000000
a0  checksum
93  MESSAGE_INFO + LENGTH_4 + MODE_3;
00  INFO_NAME
54  'T'
45  'E'
53  'S'
54  'T'; Name = "TEST"
7a  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3;
01  INFO_RAW
00  LSB
00  ..
c8  ..
c2  MSB; RawMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
e5  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3;
02  INFO_PCT
00  LSB
00  ..
c8  ..
c2  MSB; PctMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e6  checksum
9b  MESSAGE_INFO + LENGTH_8 + MODE_3;
03  INFO_SI
00  LSB
00  ..
c8  ..
c2  MSB; SiMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
e7  checsum
93  MESSAGE_INFO + LENGTH_4 + MODE_3;
04  INFO_SYMBOL
54  'T'
53  'S'
54  'T'
00  ''; Symbol = "TST"
3b  checksum
8b  MESSAGE_INFO + LENGTH_2 + MODE_3;
05  INFO_UNK1
00  ???
00  ???
71  checksum
93  MESSAGE_INFO + LENGTH_4 + MODE_3;
80  INFO_FORMAT
05  DataSets = 5
01  Format = DATA16
06  Digits = 6
00  Decimals = 0
ee  checksum
92  MESSAGE_INFO + LENGTH_4 + MODE_2
00  INFO_NAME
50  'P'
4f  'O'
53  'S'
00  ''; Name = "POS"
21  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
01  INFO_RAW
00  LSB
00  ..
b4  ..
c3  MSB; RawMin = -360.0
00  LSB
00  ..
b4  ..
43  MSB; RawMax = 360.0
e4  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
02  INFO_PCT
00  LSB
00  ..
c8  ..
c2  MSB; PctMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e7  checksum
9a  MESSAGE_INFO + LENGTH_8 + MODE_2
03  INFO_SI
00  LSB
00  ..
b4  ..
c3  MSB; SiMin = -360.0
00  LSB
00  ..
b4  ..
43  MSB; SiMax = 360.0
e6  checksum
92  MESSAGE_INFO + LENGTH_4 + MODE_2
04  INFO_SYMBOL
44  'D'
45  'E'
47  'G'
00  ''; Symbol = "DEG"
2f  checksum
8a  MESSAGE_INFO + LENGTH_2 + MODE_2
05  INFO_UNK1
08  ???
00  ???
78  checksum
92  MESSAGE_INFO + LENGTH_4 + MODE_2
80  INFO_FORMAT
01  DataSets = 1
02  Format = DATA32
06  Figures = 6
00  Decimals = 0
e8  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
00  INFO_NAME
53  'S'
50  'P'
45  'E'
45  'E'
44  'D'
00  ''
00  ''
00  ''; Name = "SPEED"
21  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
01  INFO_RAW
00  LSB
00  ..
c8  ..
c2  MSB; RawMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
e7  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
02  INFO_PCT
00  LSB
00  ..
c8  ..
c2  MSB; PctMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e4  checksum
99  MESSAGE_INFO + LENGTH_8 + MODE_1
03  INFO_SI
00  LSB
00  ..
c8  ..
c2  MSB; SiMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
e5  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1
04  INFO_SYMBOL
50  'P'
43  'C'
54  'T'
00  ''; Symbol = "PCT"
2d  checksum
89  MESSAGE_INFO + LENGTH_2 + MODE_1
05  INFO_UNK1
10  ???
00  ???
63  checksum
91  MESSAGE_INFO + LENGTH_4 + MODE_1
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
04  Figures = 4
00  Decimals = 0
eb  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
00  INFO_NAME
50  'P'
4f  'O'
57  'W'
45  'E'
52  'R'
00  ''
00  ''
00  ''; Name = "POWER"
38  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
01  INFO_RAW
00  LSB
00  ..
c8  ..
c2  MSB; RawMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; RawMax = 100.0
e6  checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
02  INFO_PCT
00  LSB
00  ..
c8  ..
c2  MSB; PctMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; PctMax = 100.0
e5 checksum
98  MESSAGE_INFO + LENGTH_8 + MODE_0
03  INFO_SI
00  LSB
00  ..
c8  ..
c2  MSB; SiMin = -100.0
00  LSB
00  ..
c8  ..
42  MSB; SiMax = 100.0
e4  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0
04  INFO_SYMBOL
50  'P'
43  'C'
54  'T'
00  ''; Symbol = "PCT"
2c  checksum
88  MESSAGE_INFO + LENGTH_2 + MODE_0
05  INFO_UNK1
00  ???
50  ???
22  checksum
90  MESSAGE_INFO + LENGTH_4 + MODE_0
80  INFO_FORMAT
01  DataSets = 1
00  Format = DATA8
04  Figures = 4
00  Decimals = 0
ea  checksum
88  MESSAGE_INFO + LENGTH_2 + MODE_0
06  INFO_UNK2 (second new PF2 info - others are 05)
06  ???
00  ???
77  checksum
04  MESSAGE_SYS + BYTE_ACK
00  MESSAGE_SYS + BYTE_SYNC
dlech commented 6 years ago
Field Value
Type ID 38
Modes 4
Views 3
BitRate 115200
FwVersion 1.0.00.0000
HwVersion 1.0.00.0000
Unknown3 0x06 (6)
Unknown4 0x00 (0)
Mode Name RawMin...Max PctMin...Max SiMin...Max Symbol Unknown DataSets Format Figures Decimals
0 POWER -100.0...100.0 -100.0...100.0 -100.0...100.0 PCT 0x00, 0x50 (0, 80) 1 DATA8 4 0
1 SPEED -100.0...100.0 -100.0...100.0 -100.0...100.0 PCT 0x10, 0x00 (16, 0) 1 DATA8 4 0
2 POS -360.0...360.0 -100.0...100.0 -360.0...360.0 DEG 0x08, 0x00 (8, 0) 1 DATA32 6 0
3 TEST -100.0...100.0 -100.0...100.0 -100.0...100.0 TST 0x00, 0x00 (0, 0) 5 DATA16 6 0
cpseager commented 6 years ago

Following this with interest! Not sure if you have already looked at the UART hardware circuit but I looked at the wedo2 and sensors a couple of years back and noticed the TX circuit was completely identical to EV3 circuit with one exception, the BAT54 diodes were not put on the board (even though the empty pcb pads were there for them!). The buffer used is type 74HC2G125 but it is the same circuit.

captureev3

However the RX circuit is different to EV3, in fact it is exactly the same circuit as the TX circuit with the buffer simply pointing the other way. This is a bit simpler than the EV3 RX circuit because it does not need to be able to support the old RCX sensors.

So basically the options are 1) turn buffer off, use both pins as ADC inputs (e.g. to identify motors/lights) 2) turn buffer off, use both pins as i/o for i2c (probably not used?) 3) turn buffer on, use as UART

I also took a tilt sensor apart and noticed that the 3-axis accelerometer chip was connected across the power rails directly. As this is a 3V device any voltage above 3V would permanently damage it. Therefore I assume that no higher voltage (e.g. 5V) will ever be put out of the V+ pin?

The serial interface in the tilt sensor is 100% identical to the serial EV3 sensors (e.g. gyro) too. A lot of circuit designs being reused!

capturegyro

Let me know if you want to borrow tilt/motion sensors for testing.

dlech commented 6 years ago

Thanks for the insights. :smile:

Part of my reason for doing this is too see how compatible Powered Up is with EV3. I was thinking it might be possible to use Powered Up sensors on EV3. But, it looks like the EV3 firmware will have to be modified to ignore the new message types. And as you pointed out, if 5V on pin 4 is a problem, then connecting Powered Up devices to EV3 is bad idea (it has 4V on pin 4). And since there is nothing connected to pins 1 and 2 on the tilt sensor schematic above, that tells me LEGO didn't plan on this anyway.

However, connecting EV3 sensors to Powered Up devices still seems like a possibility (assuming the Powered Up firmwares don't require any of the new UART messages).

FWIW, it looks like they did include the Schottky diodes in BOOST.

cpseager commented 6 years ago

Maybe the reel ran out when they were building my wedo! lego_batterybox05

cpseager commented 6 years ago

And since there is nothing connected to pins 1 and 2 on the tilt sensor schematic above,

Actually that is the serial circuit borrowed from the gyro schematic in the EV3 hardware developer pack, I didn't bother redrawing it accurately as it is much the same. In reality the tilt sensor has a capacitor to ground on each of the motor pins (1 and 2).

lego_tilt_sensors_exploded

JorgePe commented 6 years ago

Thanks guys. Lots of new info and much better than what I got on holidays with my cheap analyzer.

dlech commented 6 years ago

I think you can get the same information with your analyzer if you set it to 2400 baud or just use a USB serial adapter set to 2400 baud. I was hoping you might be able to get a dump of the two WeDo 2.0 sensors. (Although I'm sure I will buy some for myself eventually.)

JorgePe commented 6 years ago

I tried 2400 at start but never saw nothing... Not sure why but 300 gave more consistent data than everything else until I tried 115200. Will have to try again but now that holidays are gone it's tough to get time and room for it.

GianCann commented 4 years ago

I resume this post... I want to try to impersonate the Color Sensor with a different hardware.

Starting from the data captured by @dlech I have wrote this code:

import utime
import machine
from machine import UART
uart=UART(1,2400)

uart.init(baudrate=2400, bits=8, parity=None, stop=1,tx = 23, rx = 33)
uart.write(b'\x00')
uart.write(b'\x40\x25\x9A')
uart.write(b'\x51\x07\x07\x0a\x07\xA3')
uart.write(b'\x52\x00\xc2\x01\x00\x6E')
uart.write(b'\x5f\x00\x00\x00\x10\x00\x00\x00\x10\xA0')
uart.write(b'\x9a\x20\x43\x41\x4c\x49\x42\x00\x00\x00\x00')
uart.write(b'\x9a\x21\x00\x00\x00\x00\x00\xff\x7f\x47\x83')
uart.write(b'\x9a\x22\x00\x00\x00\x00\x00\x00\xc8\x42\xCD')
uart.write(b'\x9a\x23\x00\x00\x00\x00\x00\xff\x7f\x47\x81')
uart.write(b'\x92\x24\x4e\x2f\x41\x00\x69')
uart.write(b'\x8a\x25\x10\x00\x40')
uart.write(b'\x92\xa0\x08\x01\x05\x00\xC1')
uart.write(b'\x99\x20\x44\x45\x42\x55\x47\x00\x00\x00\x17')
uart.write(b'\x99\x21\x00\x00\x00\x00\x00\xc0\x7f\x44\xBC')
uart.write(b'\x99\x22\x00\x00\x00\x00\x00\x00\xc8\x42\xCE')
uart.write(b'\x99\x23\x00\x00\x00\x00\x00\x00\x20\x41\x24')
uart.write(b'\x91\x24\x4e\x2f\x41\x00\x6A')
uart.write(b'\x89\x25\x10\x00\x43')
uart.write(b'\x91\xa0\x02\x01\x05\x00\xC8')
uart.write(b'\x98\x20\x53\x50\x45\x43\x20\x31\x00\x00\x53')
uart.write(b'\x98\x21\x00\x00\x00\x00\x00\x00\x7f\x43\x7A')
uart.write(b'\x98\x22\x00\x00\x00\x00\x00\x00\xc8\x42\xCF')
uart.write(b'\x98\x23\x00\x00\x00\x00\x00\x00\x7f\x43\x78')
uart.write(b'\x90\x24\x4e\x2f\x41\x00\x6B')
uart.write(b'\x88\x25\x00\x00\x52')
uart.write(b'\x90\xa0\x04\x00\x03\x00\xC8')
uart.write(b'\x9f\x00\x49\x52\x20\x54\x78\x00\x00\x00\x77')
uart.write(b'\x9f\x01\x00\x00\x00\x00\x00\xff\x7f\x47\xA6')
uart.write(b'\x9f\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xE8')
uart.write(b'\x9f\x03\x00\x00\x00\x00\x00\xff\x7f\x47\xA4')
uart.write(b'\x97\x04\x4e\x2f\x41\x00\x4C')
uart.write(b'\x8f\x05\x00\x04\x71')
uart.write(b'\x97\x80\x01\x01\x05\x00\xED')
uart.write(b'\x9e\x00\x52\x47\x42\x20\x49\x00\x00\x00\x5F')
uart.write(b'\x9e\x01\x00\x00\x00\x00\x00\xc0\x7f\x44\x9B')
uart.write(b'\x9e\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xE9')
uart.write(b'\x9e\x03\x00\x00\x00\x00\x00\xc0\x7f\x44\x99')
uart.write(b'\x96\x04\x52\x41\x57\x00\x29')
uart.write(b'\x8e\x05\x10\x00\x64')
uart.write(b'\x96\x80\x03\x01\x05\x00\xEE')
uart.write(b'\x9d\x00\x43\x4f\x4c\x20\x4f\x00\x00\x00\x4D')
uart.write(b'\x9d\x01\x00\x00\x00\x00\x00\x00\x20\x41\x02')
uart.write(b'\x9d\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xEA')
uart.write(b'\x9d\x03\x00\x00\x00\x00\x00\x00\x20\x41\x00')
uart.write(b'\x95\x04\x49\x44\x58\x00\x3B')
uart.write(b'\x8d\x05\x00\x04\x73')
uart.write(b'\x95\x80\x01\x00\x03\x00\xE8')
uart.write(b'\x94\x00\x41\x4d\x42\x49\x6C')
uart.write(b'\x9c\x01\x00\x00\x00\x00\x00\x00\xc8\x42\xE8')
uart.write(b'\x9c\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xEB')
uart.write(b'\x9c\x03\x00\x00\x00\x00\x00\x00\xc8\x42\xEA')
uart.write(b'\x94\x04\x50\x43\x54\x00\x28')
uart.write(b'\x8c\x05\x10\x00\x66')
uart.write(b'\x94\x80\x01\x00\x03\x00\xE9')
uart.write(b'\x9b\x00\x52\x45\x45\x4c\x54\x00\x00\x00\x2D')
uart.write(b'\x9b\x01\x00\x00\x00\x00\x00\x00\xc8\x42\xEF')
uart.write(b'\x9b\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xEC')
uart.write(b'\x9b\x03\x00\x00\x00\x00\x00\x00\xc8\x42\xED')
uart.write(b'\x93\x04\x50\x43\x54\x00\x2F')
uart.write(b'\x8b\x05\x10\x00\x61')
uart.write(b'\x93\x80\x01\x00\x03\x00\xEE')
uart.write(b'\x9a\x00\x43\x4f\x55\x4e\x54\x00\x00\x00\x26')
uart.write(b'\x9a\x01\x00\x00\x00\x00\x00\x00\xc8\x42\xEE')
uart.write(b'\x9a\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xED')
uart.write(b'\x9a\x03\x00\x00\x00\x00\x00\x00\xc8\x42\xEC')
uart.write(b'\x92\x04\x43\x4e\x54\x00\x30')
uart.write(b'\x8a\x05\x08\x00\x78')
uart.write(b'\x92\x80\x01\x02\x04\x00\xEA')
uart.write(b'\x91\x00\x50\x52\x4f\x58\x7B')
uart.write(b'\x99\x01\x00\x00\x00\x00\x00\x00\x20\x41\x06')
uart.write(b'\x99\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xEE')
uart.write(b'\x99\x03\x00\x00\x00\x00\x00\x00\x20\x41\x04')
uart.write(b'\x91\x04\x44\x49\x53\x00\x34')
uart.write(b'\x89\x05\x50\x00\x23')
uart.write(b'\x91\x80\x01\x00\x03\x00\xEC')
uart.write(b'\x98\x00\x43\x4f\x4c\x4f\x52\x00\x00\x00\x3A')
uart.write(b'\x98\x01\x00\x00\x00\x00\x00\x00\x20\x41\x07')
uart.write(b'\x98\x02\x00\x00\x00\x00\x00\x00\xc8\x42\xEF')
uart.write(b'\x98\x03\x00\x00\x00\x00\x00\x00\x20\x41\x05')
uart.write(b'\x90\x04\x49\x44\x58\x00\x3E')
uart.write(b'\x88\x05\xc4\x00\xB6')
uart.write(b'\x90\x80\x01\x00\x03\x00\xED')
uart.write(b'\x88\x06\x4f\x00\x3E')
utime.sleep_ms(5)
uart.write(b'\x04')
uart.write(b'\x00')
starttime = utime.ticks_ms()
currenttime = starttime
while (currenttime-starttime) < 1000:
     utime.sleep_ms(5)
     data = uart.read(uart.any())
     if data.find(b'\x04') >= 0:
          connected = True
          currenttime = starttime+3000
     else:
          connected = False
          currenttime = utime.ticks_ms()

utime.sleep_ms(5)
if connected == True:
  uart.init(baudrate=115200, bits=8, parity=None, stop=1,tx = 23, rx = 33)
  for a in range(100000):
    print("Send value")
    value = (3 & 0xFF)
    payload = bytes([0xC0, value, 0xFF ^ 0xC0 ^ value])
    size = uart.write(payload)
    utime.sleep_ms(90)
else:
  print("Reset sensor")
  utime.sleep_ms(100)
  machine.reset()

unfortunately, the handshaking is not successful and I don't explain why since the byte stream is identical to that detected by @dlech

dlech commented 4 years ago

Some ideas:

  1. The device info section (at 2400 baud) should be repeated until a connection is made
  2. The last uart.write(b'\x00') doesn't seem right even though it is in the original data I posted.
GianCann commented 4 years ago
  1. The device info section (at 2400 baud) should be repeated until a connection is made

Yes. If the sensor don't receive ACK from the hub in 1 second, the device reboot and try a new attempt.

2. The last uart.write(b'\x00') doesn't seem right even though it is in the original data I posted.

Removed. But not change... I've added also a delay of 10ms between each sequence, but without success.

Still, it should work ...

dlech commented 4 years ago

Do you have a logic analyzer?

GianCann commented 4 years ago

No... do you think that this product it is good for me? https://www.amazon.it/AZDelivery-Logic-Analyzer-Parent/dp/B07ZDF18W1

GianCann commented 4 years ago

Boost Color Sensor data in Google Sheet format: https://docs.google.com/spreadsheets/d/12pC1mJYzqdmGBvYqUXoseGNsVBxPltWDnVcR6G_bLVA/edit?usp=sharing

dlech commented 4 years ago

do you think that this product it is good for me?

I really like https://www.saleae.com/ (if you have the budget for it).

The one you linked says it can do RS232 and has good reviews, so it probably will do the job just fine.

GianCann commented 4 years ago

I tried to connect the sensor to an USB-Serial adapter, to read the handshake data (2400 bps ,8n1). I powered the sensor with 3.3v. The Pin 1 & 2 are not connected. The RGB LEDs switch continuosly from red to green (with a small pause), but not data is showed in the Serial Terminal. Why?

dlech commented 4 years ago

Maybe Tx and Rx are backwards?

GianCann commented 4 years ago

@dlech I tried both combinations.

dlech commented 4 years ago

maybe hardware flow control is enabled?

dlech commented 4 years ago

is ground connected to both the power supply and the USB adapter?

GianCann commented 4 years ago

image

GianCann commented 4 years ago

Ok, finally!!! image

GianCann commented 4 years ago

@dlech there is an error in your dump ;)

... 52 'R' 45 'E' 45 'F' <<<<< This is 46!!! 4c 'L' 54 'T' ...

GianCann commented 4 years ago

Now, my sensor and the original Color Sensor write on serial interface the same data. The first is not identified, the second yes (also with only 4 wire connected 3,4,5 & 6).

So, what other thing I try to check?

GianCann commented 4 years ago

@Philo have send to me the data captured in the communication between sensor and hub (waiting Amazon deliver to me the Logic Analyzer).

After the communication switch to 115.200 bps, the hub start to send the NACK byte (0x02) at which one the sensor reply with this bytes sequence: Hub > 02 Sensor > 46 08 B1 D0 09 01 FF (delay of 0.2 ms) Sensor: 3C E4 (delay of 17 ms) Sensor > D0 09 01 FF 3B E3 (delay of 17 ms) Sensor > D0 09 01 FF 3C E4

Each NACK are sent every 100ms

The bytes D0 09 01 FF 3B E3 are the data message. D0 is the header 09 01 FF 3B is the raw data (color/ambient/distance?), (I think that the FF byte is the padding byte to complete the 4 bytes slot) E3 is the checksum (FF ^ D0 ^ 09 ^ 01 ^ FF ^ 3B)

I don't understand these bytes: 46 08 B1 D0 09 01 FF (INFO MESSAGE???) 3C E4 ???

Note: these bytes are not sent to every NACK from the hub

cityhubboostcolorsensor-full.zip

cpseager commented 4 years ago

46 08 B1 D0 09 01 FF (INFO MESSAGE???) 3C E4 ???

=

46 08 B1 // Device mode 8 (SPEC1) D0 09 01 FF 3C E4 //Normal sensor value changed response

cpseager commented 4 years ago

Mode 8 is the special combined distance and color sensor mode. The sensor does not start in this mode, so the hub is issuing a command to changeover into this mode (to which 46 08 B1 is the sensor "I've changed mode" response).

A sensor response is only sent back to the NACK poll when something actually changes. In mode 8 this is

e.g. D0 09 01 FF 3C E4

D0 color distance FF partial checksum

            if (partial > 0) {
                distance += 1.0 / partial;
            }
            distance = Math.floor(distance * 25.4) - 20;

This repo will probably help you understand what various bytes are: https://github.com/nathankellenicki/node-poweredup/blob/master/src/devices/colordistancesensor.ts

GianCann commented 4 years ago

Mode 8 is the special combined distance and color sensor mode. The sensor does not start in this mode, so the hub is issuing a command to changeover into this mode

Thank you @cpseager So, the HUB send a SELECT command to the sensor with a payload 0x08 after start the communication at 115200?

cpseager commented 4 years ago

Once the app (e.g. on ipad) is notified that a new sensor is connected to the hub, the app will send a 'change mode' bluetooth command to the hub, the hub then sends this serially to the sensor. So to emulate a sensor successfully you need to be able to emulate the sensor mode changes too (because each sensor mode sends differently formatted data back to the hub). For this boost color sensor mode 8 seems to be the most commonly mode used by the lego apps. Once you have a logic analyser you will be able to see this in more detail. Good luck!

GianCann commented 4 years ago

So to emulate a sensor successfully you need to be able to emulate the sensor mode changes too (because each sensor mode sends differently formatted data back to the hub). For this boost color sensor mode 8 seems to be the most commonly mode used by the lego apps.

Yes, I saw... However the code don't work because I don't receive the ACK message from the hub... I noted (from the data capture by Philo) that the initialization phase require about 3.1 seconds in total (from first byte 0x40 to the last 0x04) and some packet are delayed by 10ms pause between them. I haved insert this delay in the code (in the same point), but my initialization code have however a total time of 2.6 seconds (500ms delta) I don't undestand...

Today Amazon have delivery to me the logic analyzer: tonight, when I am go back to the home I will make some acquisitions and will compare the data with sigrok

import machine
import utime
from neopixel import NeoPixel
from machine import Pin
from machine import UART

# groove connector
#Pin26 (vicino a 5v)
#Pin32
txPin=32 #hub pin = 6
rxPin=26 #hub pin = 5

uart=UART(1,2400)
uart.init(baudrate=2400, bits=8, parity=None, stop=1,tx = txPin, rx = rxPin)
connected=False
while connected==False:
  print("INIT SENSOR")
  for i in range(5):
    np[i] = (ledLumin, 0, 0)
  np.write()
  utime.sleep_ms(25)
  for i in range(5):
    np[i] = (0, 0, 0)
  np.write()
  utime.sleep_ms(25)
  starSequence=utime.ticks_ms()
# INITIALIZATION SEQUENCE
  uart.write(b'\x00')
  utime.sleep_ms(5)
  uart.write(b'\x40\x25\x9A')
  uart.write(b'\x51\x07\x07\x0A\x07\xA3')
  uart.write(b'\x52\x00\xC2\x01\x00\x6E')
  uart.write(b'\x5F\x00\x00\x00\x10\x00\x00\x00\x10\xA0')
  utime.sleep_ms(10)
  uart.write(b'\x9A\x20\x43\x41\x4C\x49\x42\x00\x00\x00\x00')
  uart.write(b'\x9A\x21\x00\x00\x00\x00\x00\xFF\x7F\x47\x83')
  uart.write(b'\x9A\x22\x00\x00\x00\x00\x00\x00\xC8\x42\xCD')
  uart.write(b'\x9A\x23\x00\x00\x00\x00\x00\xFF\x7F\x47\x81')
  uart.write(b'\x92\x24\x4E\x2F\x41\x00\x69')
  uart.write(b'\x8A\x25\x10\x00\x40')
  uart.write(b'\x92\xA0\x08\x01\x05\x00\xC1')
  utime.sleep_ms(10)
  uart.write(b'\x99\x20\x44\x45\x42\x55\x47\x00\x00\x00\x17')
  uart.write(b'\x99\x21\x00\x00\x00\x00\x00\xC0\x7F\x44\xBC')
  uart.write(b'\x99\x22\x00\x00\x00\x00\x00\x00\xC8\x42\xCE')
  uart.write(b'\x99\x23\x00\x00\x00\x00\x00\x00\x20\x41\x24')
  uart.write(b'\x91\x24\x4E\x2F\x41\x00\x6A')
  uart.write(b'\x89\x25\x10\x00\x43')
  uart.write(b'\x91\xA0\x02\x01\x05\x00\xC8')
  utime.sleep_ms(10)
  uart.write(b'\x98\x20\x53\x50\x45\x43\x20\x31\x00\x00\x53')
  uart.write(b'\x98\x21\x00\x00\x00\x00\x00\x00\x7F\x43\x7A')
  uart.write(b'\x98\x22\x00\x00\x00\x00\x00\x00\xC8\x42\xCF')
  uart.write(b'\x98\x23\x00\x00\x00\x00\x00\x00\x7F\x43\x78')
  uart.write(b'\x90\x24\x4E\x2F\x41\x00\x6B')
  uart.write(b'\x88\x25\x00\x00\x52')
  uart.write(b'\x90\xA0\x04\x00\x03\x00\xC8')
  utime.sleep_ms(10)
  uart.write(b'\x9F\x00\x49\x52\x20\x54\x78\x00\x00\x00\x77')
  uart.write(b'\x9F\x01\x00\x00\x00\x00\x00\xFF\x7F\x47\xA6')
  uart.write(b'\x9F\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xE8')
  uart.write(b'\x9F\x03\x00\x00\x00\x00\x00\xFF\x7F\x47\xA4')
  uart.write(b'\x97\x04\x4E\x2F\x41\x00\x4C')
  uart.write(b'\x8F\x05\x00\x04\x71')
  uart.write(b'\x97\x80\x01\x01\x05\x00\xED')
  utime.sleep_ms(10)
  uart.write(b'\x9E\x00\x52\x47\x42\x20\x49\x00\x00\x00\x5F')
  uart.write(b'\x9E\x01\x00\x00\x00\x00\x00\xC0\x7F\x44\x9B')
  uart.write(b'\x9E\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xE9')
  uart.write(b'\x9E\x03\x00\x00\x00\x00\x00\xc0\x7F\x44\x99')
  uart.write(b'\x96\x04\x52\x41\x57\x00\x29')
  uart.write(b'\x8E\x05\x10\x00\x64')
  uart.write(b'\x96\x80\x03\x01\x05\x00\xEE')
  utime.sleep_ms(10)
  uart.write(b'\x9D\x00\x43\x4F\x4C\x20\x4F\x00\x00\x00\x4D')
  uart.write(b'\x9D\x01\x00\x00\x00\x00\x00\x00\x20\x41\x02')
  uart.write(b'\x9D\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xEA')
  uart.write(b'\x9D\x03\x00\x00\x00\x00\x00\x00\x20\x41\x00')
  uart.write(b'\x95\x04\x49\x44\x58\x00\x3B')
  uart.write(b'\x8D\x05\x00\x04\x73')
  uart.write(b'\x95\x80\x01\x00\x03\x00\xE8')
  utime.sleep_ms(10)
  uart.write(b'\x94\x00\x41\x4D\x42\x49\x6C')
  uart.write(b'\x9C\x01\x00\x00\x00\x00\x00\x00\xC8\x42\xE8')
  uart.write(b'\x9C\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xEB')
  uart.write(b'\x9C\x03\x00\x00\x00\x00\x00\x00\xC8\x42\xEA')
  uart.write(b'\x94\x04\x50\x43\x54\x00\x28')
  uart.write(b'\x8C\x05\x10\x00\x66')
  uart.write(b'\x94\x80\x01\x00\x03\x00\xE9')
  utime.sleep_ms(10)
  uart.write(b'\x9B\x00\x52\x45\x46\x4C\x54\x00\x00\x00\x2D')
  uart.write(b'\x9B\x01\x00\x00\x00\x00\x00\x00\xC8\x42\xEF')
  uart.write(b'\x9B\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xEC')
  uart.write(b'\x9B\x03\x00\x00\x00\x00\x00\x00\xC8\x42\xED')
  uart.write(b'\x93\x04\x50\x43\x54\x00\x2F')
  uart.write(b'\x8B\x05\x10\x00\x61')
  uart.write(b'\x93\x80\x01\x00\x03\x00\xEE')
  utime.sleep_ms(10)
  uart.write(b'\x9A\x00\x43\x4F\x55\x4E\x54\x00\x00\x00\x26')
  uart.write(b'\x9A\x01\x00\x00\x00\x00\x00\x00\xC8\x42\xEE')
  uart.write(b'\x9A\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xED')
  uart.write(b'\x9A\x03\x00\x00\x00\x00\x00\x00\xC8\x42\xEC')
  uart.write(b'\x92\x04\x43\x4E\x54\x00\x30')
  uart.write(b'\x8A\x05\x08\x00\x78')
  uart.write(b'\x92\x80\x01\x02\x04\x00\xEA')
  utime.sleep_ms(10)
  uart.write(b'\x91\x00\x50\x52\x4F\x58\x7B')
  uart.write(b'\x99\x01\x00\x00\x00\x00\x00\x00\x20\x41\x06')
  uart.write(b'\x99\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xEE')
  uart.write(b'\x99\x03\x00\x00\x00\x00\x00\x00\x20\x41\x04')
  uart.write(b'\x91\x04\x44\x49\x53\x00\x34')
  uart.write(b'\x89\x05\x50\x00\x23')
  uart.write(b'\x91\x80\x01\x00\x03\x00\xEC')
  utime.sleep_ms(10)
  uart.write(b'\x98\x00\x43\x4F\x4C\x4F\x52\x00\x00\x00\x3A')
  uart.write(b'\x98\x01\x00\x00\x00\x00\x00\x00\x20\x41\x07')
  uart.write(b'\x98\x02\x00\x00\x00\x00\x00\x00\xC8\x42\xEF')
  uart.write(b'\x98\x03\x00\x00\x00\x00\x00\x00\x20\x41\x05')
  uart.write(b'\x90\x04\x49\x44\x58\x00\x3E')
  uart.write(b'\x88\x05\xC4\x00\xB6')
  uart.write(b'\x90\x80\x01\x00\x03\x00\xED')
  uart.write(b'\x88\x06\x4F\x00\x3E')
  utime.sleep_ms(10)
  uart.write(b'\x04')
  utime.sleep_ms(5)
# END INITIALIZATION SEQUENCE  
  starttime = utime.ticks_ms()
  print(starttime-starSequence) # time requested for initialization sequence

  currenttime = starttime
  while (currenttime-starttime) < 2000:
    data = uart.read(uart.any())
    print(data)
    if data.find(b'\x04') >= 0:
      connected = True
      break
    else:
      currenttime = utime.ticks_ms()
    utime.sleep_ms(10)

utime.sleep_ms(10)
uart.init(baudrate=115200, bits=8, parity=None, stop=1,tx = txPin, rx = rxPin)

for i in range(5):
  np[i] = (0, ledLumin, 0)
np.write()

while True:
  data = uart.read(uart.any())

  if data.find(b'\x02') >= 0:
    print("Received NACK from the Hub")
    # send INFO message mode '8' 
    header = 0x46
    value1 = 0x08
    checksum = 0xFF ^ header ^ value1
    message = bytes([header, value1, checksum])
    size = uart.write(message)

    #send static data (only for test)   
    header = 0xD0
    value1 = 0x01
    value2 = 0x06
    value3 = 0xFF
    value4 = 0x03
    checksum = 0xFF ^ header ^ value1 ^ value2 ^ value3 ^ value4
    message = bytes([header, value1, value2, value3, value4, checksum])
    size = uart.write(message)
    utime.sleep_ms(50)
dracode commented 4 years ago

@GianCann

Today Amazon have delivery to me the logic analyzer: tonight, when I am go back to the home I will make some acquisitions and will compare the data with sigrok

Not sure if this will be of much help to you, but since you are going to be using Sigrok anyway, here is a little Sigrok plugin I wrote a while back that does some interpretation on the raw UART data. It will decode some of the ColorDistanceSensor messages within Sigrok for you, for example. It's not been updated in 16 months and is probably incomplete, but I think it helps.

https://github.com/dracode/sigrok-lego-boost

GianCann commented 4 years ago

Thank you @dracode

The problem, however, it's not the data, but the timing. The UART implementation for the ESP32, in MicroPython, use a buffer for the tx channel so the rest of the code is executed when the trasmission is not ended. I tried to initialize the UART with a txBuf=0, but there is no change.

My data, analyzed with PulseView don't have any "delay" between the various "sections" of bytes.

In this post seems to be a solution but I don't know how to rebuild the MicroPython core: https://forum.micropython.org/viewtopic.php?t=5739

Later today, I try to replicate the project in Arduino IDE.

GianCann commented 4 years ago

Finally, I have a project that work! https://youtu.be/rwldWM4Dkdw

dracode commented 4 years ago

Very nice!

mw75 commented 4 years ago

@GianCann Where to find the code and a hardware description of your example? I would like to port it to arduino to develop a complete powerd-up-device-simulation-library over time.

GianCann commented 4 years ago

@mw75 in the next days I put all code and info on my GitHub repository. Take patience ;)

ahmedjouirou commented 4 years ago

Hello every one , i wrapped up the interesting work done in this discussion , and i make up an Arduino Library (that works with all Arduino s version - using the main serial port + extra serial for debugging) which Emulates Lego Powered Up Color/Distance sensor, i also added another class for emulating Tilt sensor here's the repo :

https://github.com/ahmedjouirou/legopup_arduino

Example of usage:

`

include

include

SoftwareSerial DbgSerial(2, 8); // RX:2 TX:8

// Test sensing pin(s) const int AnalogPinSens = A5;

int8_t SensorX; int8_t SensorY;

LegoPupTilt lpup(&SensorX, &SensorY);

void setup() {

DbgSerial.begin(9600); DbgSerial.println("Hello !"); while (!DbgSerial) {

} }

void loop() {

lpup.Process();

if(lpup.IsConnected()){ //DbgSerial.println("Connected !"); } else{
//DbgSerial.println("Not Connected"); }

int sensorValue = analogRead(AnalogPinSens); SensorX = map(sensorValue, 0, 1023, -45, 45); SensorY = map(sensorValue, 0, 1023, -45, 45); }

`

ysard commented 2 years ago

Hi, I used the work done here, and especially that of @ahmedjouirou & @GianCann to make a complete library supporting (hopefully) all the features/modes of the "Color & Distance" and "Tilt" sensors. It is also compatible with PyBricks.

It is now easy to interface any electronic component to the PoweredUp Hub via an Arduino/ESP experiment board, and the implementation of other LEGO peripherals should be simplified.

I Hope it will be useful for some of you. Happy hacking ;)

https://github.com/ysard/MyOwnBricks

joehui commented 2 years ago

Finally, I have a project that work! https://youtu.be/rwldWM4Dkdw

@GianCann Hello, I wonder how you fixed your original issue. I am working on a similar project with the Spike Prime hub. My hardware is sending the same config data as the color sensor. But I receive no ACK from the hub at all. Also, do you know if your project works with Spike Prime hub?

Your help will be greatly appreciated. Thank you.

cpseager commented 2 years ago

@joehui Sadly GianCann passed away in 2020.

ysard commented 2 years ago

@joehui : Hi maybe you could take a look at https://github.com/ysard/MyOwnBricks/blob/db6b9ba5735cccc290d2dd2d3dc3dc154203d4c5/src/ColorDistanceSensor.cpp#L191 for a working init sequence.

and here https://github.com/ysard/MyOwnBricks/blob/db6b9ba5735cccc290d2dd2d3dc3dc154203d4c5/src/ColorDistanceSensor.cpp#L315 for the handling of NACKs and mode requests from the hub.

It's the same protocol for all hubs but be sure that the peripheral you're trying to emulate is supposed to be supported by the hub. See the following pages for a compatibility update :

For the moment you will notice that the compatibility information for the spike hubs seems to be missing (too recent hardware).

I had found the following image which summarized the situation in 2020 (hubs, peripherals, apps) (didn't find its origin). Modules have been added since then, but unfortunately I doubt that the compatibility table has evolved a lot.

PowerFunctions 2 0 compatibility sheet

Keep in mind that the support on hubs of new devices is a goodwill of LEGO, not a hardware limitation.

Pybricks firmware supports all PoweredUp devices : https://github.com/pybricks/pybricks-micropython/tree/master/pybricks/pupdevices

If you are sending the correct Boost Color and Distance Sensor | 88007 init sequence, and still receive nothing, I think there is a compatibility issue. Spike hubs support SPIKE Prime Color Sensor | 45678 which has its own init sequence. You will have to find it (it is possible by reading the pybricks sources, and by knowing the modes available on the peripheral).

For now you will notice that in projects such as mine the sequence is just took without really trying to dissect its meaning.

... or, you can just flash your hub with pybricks and continue what you are doing....

GL & HF

joehui commented 2 years ago

@ysard Thanks for your detailed reply. I actually figured out the issue with my project a while ago. I don't remember what the exact problem was, but it was some silly mistake in my code.

Anyway, the info you sent is used for other parts of my project.

Thank you.

ysard commented 2 years ago

@joehui : Happy to see that the problem is solved. So to resume, you have successfully emulated a sensor Boost Color and Distance Sensor | 88007 running on a Spike hub?

Also, it could be nice to update MyOwnBricks with a new example or supported hardware if it doesn't already exist ;)

joehui commented 2 years ago

@ysard I was actually emulating the color sensor (Sensor ID: 0x3D) that comes with the Robot Inventor kit running on its hub, which is basically the same as the one the Spike Prime's. And, yes, I was able to emulate it successfully using an ESP8266.

For the protocol, the only thing new that really matters as compared to EV3 is the combination mode sequence for Mode 0 as described in https://github.com/pybricks/technical-info/blob/master/uart-protocol.md regarding INFO_MODE_COMBO.

After the config info sequence, the hub will send the following command: { 4C 20 00 93 }
(0x4C=0b01001100 --> Write data of length 2.
0x20 probably means just means Reset the Combination to )

Sensor will reply with the following msg to confirm: { 44 20 9B }

Then hub will send a command to combine the multiple modes into 1 mode at Mode 0: { 5C 25 00 10 00 50 51 52 00 C5 } 5C -> 0b01 011 100 Write data to sensor with length 2^3 25 -> 0x20 | 0x05 : 5 bytes of tuples to follow 10 -> Mode 1, Value 0 00 -> Mode 0, Value 0 50, 51, 52 -> Mode 5, Value 0, 1, 2

Sensor should reply back with the same message to confirm: { 5C 25 00 10 00 50 51 52 00 C5 }

After that, the sensor should start sending Mode 0 data messages that contain data from all 3 modes.

Joseph

ysard commented 2 years ago

Thanks for your quick answer, I spent some time on the subject to develop a proof of concept on a dedicated branch: https://github.com/ysard/MyOwnBricks/tree/spike_color_sensor The only thing missing is the complete initialization sequence. Could you please post somewhere the full init sequence to complete this code?

Source: https://github.com/ysard/MyOwnBricks/blob/spike_color_sensor/src/ColorSensor.h Sketch example: https://github.com/ysard/MyOwnBricks/blob/spike_color_sensor/examples/spike_color_sensor/spike_color_sensor.ino


However I admit I'm confused about the INFO_MODE_COMBOS message because pybricks does not use it.

https://github.com/pybricks/pybricks-micropython/blob/81d342d2a6714159fdb123e07e74b5b070cf6300/lib/pbio/src/uartdev.c#L481-L494

=> data->info->mode_combos is never reused in the project except in a test file.

As you mentioned, the packet { 5C 25 00 10 00 50 51 52 00 C5 } has header 0x5C:

I took the opportunity to upload a reference containing the meaning of all the protocol headers here.

PyBricks also does not take into account the reception of CMD_WRITE commands addressed to it and I couldn't find any code that could send this packet to a device. https://github.com/pybricks/pybricks-micropython/blob/81d342d2a6714159fdb123e07e74b5b070cf6300/lib/pbio/src/uartdev.c#L364-L372

In short there is little documentation about INFO_MODE_COMBOS. Although the example given in the doc is for the LEGO BOOST Color and Distance sensor, this packet doesn't seem to serve any purpose in Pybricks, and I never bothered with it myself.


If I understand correctly the CMD_WRITE command sent from the hub to the sensor allows to redefine the default mode data (mode 0 by default) sent in response to a NACK from the hub?

Thus,

Mode 5, Value 0, 1, 2

mode 5 being the RGB_I mode, this implies the exchange of RGB values, that is 3 * int16.

PS: I don't know what the 4th int16 of this mode corresponds to. Definition: PBIO_IODEV_MODE_PUP_COLOR_SENSOR__RGB_I = 5, // read 4x int16_t (Source)

Also, mode 1 being REFLT on 1 byte, and mode 0 being COLOR on 1 byte, I guess you send an array of 8 bytes in total to each NACK sent by the hub ?


I agree with your interpretation of { 4C 20 00 93 }, even if I don't understand the choice of a packet of 4 bytes by the hub with a "00 padding" where the doc cites a similar 3 bytes packet { 0x44, 0x17, 0xac } for the LEGO EV3 Gyro sensor... https://github.com/pybricks/technical-info/blob/master/uart-protocol.md#cmd_write

I note that the "0x5C" packet uses also an unknown 0x00 at the index 2...