Skip to content

Document JSON protocol? #2

@pepijndevos

Description

@pepijndevos

I noticed that you can get a Micropython shell over the USB TTY, but was curious how the app reads the motors and uploads sensors and such. So I went ahead and captured a trace of the USB data. I have not completely figured it out, but it does not seem too complicated, so I thought I'd share. I snipped out all the repeated updates from the hub.

{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 2, 2, 2]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 3, 0]], [7, -878, 427], [2, 1, -2], [-172, 0, 64], "", 0]}
{"i":"XZ2s","m":"get_hub_info","p":{}}
{"i":"XZ2s","r":{"firmware": {"checksum": "b0c335b", "version": [1, 0, 6, 34]}, "runtime": {"version": [2, 1, 4, 13]}, "variant": "1", "extra_files": "641473ba;4a2158b7;adfa1928;271c3044;389d9425;88796775;598a22e8;dd777413;793aadf0;2468ccaf;883c31f8;34cab5cb;9aa3664c;5bf00379;dccbf26c;5639f8c7;cd0d4f0e;542212c6;ecb2ed13;d13ad105;eedee3f7;3ab4af94;2d756977;3c1eb8b7;fab2bbf2;24fa3ac5;93e65f44;1292b727;30f86d13;045bf290;b0e0f641;e8e2d751;220cec0c;127eb15d;125991bf;fb9dc13e;47abd59d;36cc5af6;86d05ed7;e0155d41;fd2fa533;449c00db;62a2614f;4c56fc14;dfc8ad1a;65e6ecf8;ccdf069e;804a7d41;3fc01cf1;3a92a82a;521716f1;97699d1a;0c7a1872;3dc6439e;87718197;d7e51168;0b8f6dce;ba979273;bc6142a7;a4842285"}}
{"m":2,"p":[8.384, 100, true]}
{"i":"rHv7","m":"trigger_current_state","p":{}}
{"m":2,"p":[8.384, 100, true]}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 2, 3, 2]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 4, 0]], [10, -876, 428], [2, 2, -2], [-172, 0, 64], "", 0]}
{"i":"DZ0h","m":"program_modechange","p":{"mode":"download"}}
{"m":1,"p":{"storage": {"available": 28464, "total": 31744, "pct": 11.3327, "unit": "kb", "free": 28464}, "slots": {"4": {"name": "VGltZSB0byBjZWxlYnJhdGU=", "id": 33453, "project_id": "i0eGDC42vNhf", "modified": 1619018480572, "type": "scratch", "created": 1619017975508, "size": 8150}, "0": {"name": "UHJvamVjdCA0", "id": 31645, "project_id": "79YYK4zIFeG_", "modified": 1619115802137, "type": "python", "created": 1619115789833, "size": 380}, "2": {"name": "UHJvamVjdCA0", "id": 11624, "project_id": "znqYK8VOs1AX", "modified": 1619116345548, "type": "python", "created": 1619116335998, "size": 380}}}}
{"m":4,"p":"rightside"}
{"m":9,"p":["TEVHTyBIdWI=", "A8:E2:C1:9B:99:CF"]}
{"m":12,"p":[null, false]}
{"i":"rHv7","r":{}}
{"i":"DZ0h","r":{}}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 3, 4, 4]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 3, 0]], [8, -877, 424], [1, 1, -2], [-172, 0, 64], "", 0]}
{"m":2,"p":[8.386, 100, true]}
{"i":"HWTe","m":"program_modechange","p":{"mode":"download"}}
{"i":"HWTe","r":{}}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 4, 4, 4]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 4, 0]], [7, -877, 426], [2, 2, -2], [-172, 0, 64], "", 0]}
{"m":2,"p":[8.381, 100, true]}
{"i":"7duO","m":"program_modechange","p":{"mode":"download"}}
{"i":"7duO","r":{}}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 1, 2, 2]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 3, 0]], [8, -879, 429], [2, 2, -3], [-171, 0, 64], "", 0]}
{"i":"_MaC","m":"start_write_program","p":{"meta":{"created":1619116708630,"modified":1619116717319,"project_id":"G2NKxiLwOFuY","name":"UHJvamVjdCA0","type":"python"},"size":380,"slotid":5}}
{"i":"_MaC","r":{"blocksize": 512, "transferid": "41475"}}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 4, 4, 4]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 3, 0]], [6, -875, 427], [2, 0, -2], [-170, 0, 64], "", 0]}
{"m":2,"p":[8.384, 100, true]}
{"i":"grax","m":"write_package","p":{"data":"ZnJvbSBtaW5kc3Rvcm1
zIGltcG9ydCBNU0h1YiwgTW90b3IsIE1vdG9yUGFpciwgQ29sb3JTZW5zb3IsIER
pc3RhbmNlU2Vuc29yLCBBcHAKZnJvbSBtaW5kc3Rvcm1zLmNvbnRyb2wgaW1wb3J
0IHdhaXRfZm9yX3NlY29uZHMsIHdhaXRfdW50aWwsIFRpbWVyCmZyb20gbWluZHN
0b3Jtcy5vcGVyYXRvciBpbXBvcnQgZ3JlYXRlcl90aGFuLCBncmVhdGVyX3RoYW5
fb3JfZXF1YWxfdG8sIGxlc3NfdGhhbiwgbGVzc190aGFuX29yX2VxdWFsX3RvLCB
lcXVhbF90bywgbm90X2VxdWFsX3RvCmltcG9ydCBtYXRoCgoKIyBDcmVhdGUgeW9
1ciBvYmplY3RzIGhlcmUuCmh1YiA9IE1TSHViKCkKCgojIFdyaXRlIHlvdXIgcHJ
vZ3JhbSBoZXJlLgpodWIuc3BlYWtlci5iZWVwKCk=","transferid":"41475"}
}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 4, 4, 4]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 3, 0]], [8, -875, 427], [2, 2, -2], [-170, 0, 64], "", 0]}
{"i":"grax","r":{"next_ptr": null}}
{"i":"BfCl","m":"program_execute","p":{"slotid":5}}
{"m":1,"p":{"storage": {"available": 28460, "total": 31744, "pct": 11.3453, "unit": "kb", "free": 28460}, "slots": {"5": {"name": "UHJvamVjdCA0", "id": 57859, "project_id": "G2NKxiLwOFuY", "modified": 1619116717319, "type": "python", "created": 1619116708630, "size": 380}, "4": {"name": "VGltZSB0byBjZWxlYnJhdGU=", "id": 33453, "project_id": "i0eGDC42vNhf", "modified": 1619018480572, "type": "scratch", "created": 1619017975508, "size": 8150}, "0": {"name": "UHJvamVjdCA0", "id": 31645, "project_id": "79YYK4zIFeG_", "modified": 1619115802137, "type": "python", "created": 1619115789833, "size": 380}, "2": {"name": "UHJvamVjdCA0", "id": 11624, "project_id": "znqYK8VOs1AX", "modified": 1619116345548, "type": "python", "created": 1619116335998, "size": 380}}}}
{"m":12,"p":[null, false]}
{"i":"BfCl","r":null}
{"m":0,"p":[[75, [0, 2, -152, 0]], [75, [0, 6, -13, 0]], [61, [0, null, 2, 3, 2]], [0, []], [75, [0, 2, -50, 0]], [75, [0, 0, 4, 0]], [7, -876, 429], [2, 6, -3], [-170, 0, 64], "", 0]}
{"i":"9zLI","m":"program_terminate","p":{}}
{"i":"9zLI","r":{}}

My understanding is the following:

The hub spams JSON updates where "m" specifies the message and "p" contains data. The most frequent m=0 one seems to contain the motor rotation an sensor values seen in the hub. Wild guess is m=2 is battery levels? voltage, percent, plugged? Obviously m=1 is the device status triggered by trigger_current_state. m=4 seems to be hub orientation. m=9 is hostname (LEGO Hub in base64) and mac address.

The commands from the host seem to be of the form {"i":"random ID","m":"method","p":{parameters}}, to which the hub replies with {"i":"matching ID","r":{return data}}.

Upon starting the software, it executes

{"i":"XZ2s","m":"get_hub_info","p":{}}
{"i":"rHv7","m":"trigger_current_state","p":{}}

Then upon changing the programming slot, it sets the mode to download. Presumably, the other mode is "streaming".

{"i":"HWTe","m":"program_modechange","p":{"mode":"download"}}

Now here comes the interesting part. Uploading a Python program. First it starts by sending the start_write_program command, to which the hub responds with the blocksize and transferid.

{"i":"_MaC","m":"start_write_program","p":{"meta":{"created":1619116708630,"modified":1619116717319,"project_id":"G2NKxiLwOFuY","name":"UHJvamVjdCA0","type":"python"},"size":380,"slotid":5}}
{"i":"_MaC","r":{"blocksize": 512, "transferid": "41475"}}

In this case the program is only 380 bytes long, so only one block follows in the write_package command, which contains a base64 encoded version of my program and the transferid. I will have to take another trace to see what happens with longer programs. I bet you have to do something with the next_ptr in the next block.

{"i":"grax","m":"write_package","p":{"data":"ZnJvbSBtaW5kc3Rvcm1
zIGltcG9ydCBNU0h1YiwgTW90b3IsIE1vdG9yUGFpciwgQ29sb3JTZW5zb3IsIER
pc3RhbmNlU2Vuc29yLCBBcHAKZnJvbSBtaW5kc3Rvcm1zLmNvbnRyb2wgaW1wb3J
0IHdhaXRfZm9yX3NlY29uZHMsIHdhaXRfdW50aWwsIFRpbWVyCmZyb20gbWluZHN
0b3Jtcy5vcGVyYXRvciBpbXBvcnQgZ3JlYXRlcl90aGFuLCBncmVhdGVyX3RoYW5
fb3JfZXF1YWxfdG8sIGxlc3NfdGhhbiwgbGVzc190aGFuX29yX2VxdWFsX3RvLCB
lcXVhbF90bywgbm90X2VxdWFsX3RvCmltcG9ydCBtYXRoCgoKIyBDcmVhdGUgeW9
1ciBvYmplY3RzIGhlcmUuCmh1YiA9IE1TSHViKCkKCgojIFdyaXRlIHlvdXIgcHJ
vZ3JhbSBoZXJlLgpodWIuc3BlYWtlci5iZWVwKCk=","transferid":"41475"}
}
{"i":"grax","r":{"next_ptr": null}}

Finally the code is executed, and later terminated.

{"i":"BfCl","m":"program_execute","p":{"slotid":5}}
{"i":"BfCl","r":null}
{"i":"9zLI","m":"program_terminate","p":{}}
{"i":"9zLI","r":{}}

I'd like to experiment more with this, and write a small utility for uploading code and monitoring sensors without having to boot up a Windows VM.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions