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":{}}
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.
Then upon changing the programming slot, it sets the mode to download. Presumably, the other mode is "streaming".
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.
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.
Finally the code is executed, and later terminated.
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.
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=0one seems to contain the motor rotation an sensor values seen in the hub. Wild guess ism=2is battery levels?voltage, percent, plugged? Obviouslym=1is the device status triggered bytrigger_current_state.m=4seems to be hub orientation.m=9is 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_programcommand, 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_packagecommand, which contains a base64 encoded version of my program and thetransferid. I will have to take another trace to see what happens with longer programs. I bet you have to do something with thenext_ptrin 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.