@@ -8,12 +8,15 @@ const debug = Debug("pybrickshub");
88
99
1010/**
11- * The PybricksHub is emitted if the discovered device is hub with pybricks firmware.
11+ * The PybricksHub is emitted if the discovered device is a hub with Pybricks firmware installed.
12+ * To flash your hub with Pybricks firmware, follow the instructions from https://pybricks.com.
13+ * The class supports hubs with Pybricks version 3.2.0 or newer.
1214 * @class PybricksHub
1315 * @extends BaseHub
1416 */
1517export class PybricksHub extends BaseHub {
16- private _checkSumCallback : ( ( buffer : Buffer ) => any ) | undefined ;
18+ private _maxCharSize : number = 100 ; // overwritten by value from capabilities characteristic
19+ private _maxUserProgramSize : number = 16000 ; // overwritten by value from capabilities characteristic
1720
1821 public static IsPybricksHub ( peripheral : Peripheral ) {
1922 return (
@@ -39,15 +42,20 @@ export class PybricksHub extends BaseHub {
3942 debug ( "Connect completed" ) ;
4043 this . emit ( "connect" ) ;
4144 resolve ( ) ;
45+ this . _bleDevice . readFromCharacteristic ( Consts . BLECharacteristic . PYBRICKS_CAPABILITIES , ( err , data ) => {
46+ if ( data ) {
47+ this . _maxCharSize = data . readUInt16LE ( 0 ) ;
48+ this . _maxUserProgramSize = data . readUInt32LE ( 6 ) ;
49+ debug ( "Recieved capabilities " , data , " maxCharSize: " , this . _maxCharSize , " maxUserProgramSize: " , this . _maxUserProgramSize ) ;
50+ }
51+ } ) ;
4252 this . _bleDevice . subscribeToCharacteristic ( Consts . BLECharacteristic . PYBRICKS_NUS_TX , this . _parseMessage . bind ( this ) ) ;
53+ this . _bleDevice . subscribeToCharacteristic ( Consts . BLECharacteristic . PYBRICKS_COMMAND_EVENT , ( data ) => debug ( "Recieved command event " , data ) ) ;
4354 } ) ;
4455 }
4556
4657 private _parseMessage ( data ?: Buffer ) {
4758 debug ( "Received Message (PYBRICKS_NUS_TX)" , data ) ;
48- if ( this . _checkSumCallback && data ) {
49- return this . _checkSumCallback ( data ) ;
50- }
5159 this . emit ( "recieve" , data ) ;
5260 }
5361
@@ -56,38 +64,50 @@ export class PybricksHub extends BaseHub {
5664 return this . _bleDevice . writeToCharacteristic ( uuid , message ) ;
5765 }
5866
59- public startUserProgram ( pythonCode : string ) {
67+ public uploadUserProgram ( pythonCode : string ) {
6068 debug ( "Compiling Python User Program" , pythonCode ) ;
61- return compile ( "UserProgram .py" , pythonCode ) . then ( async ( result ) => {
69+ return compile ( 'userProgram .py' , pythonCode ) . then ( async ( result ) => {
6270 if ( result . mpy ) {
63- debug ( "Uploading Python User Program" , result . mpy ) ;
64- const programLength = Buffer . alloc ( 4 ) ;
65- programLength . writeUint32LE ( result . mpy . byteLength ) ;
66- const checkSumPromise = new Promise < boolean > ( ( resolve ) => {
67- const checkSum = programLength . reduce ( ( a , b ) => a ^ b ) ;
68- this . _checkSumCallback = ( data ) => resolve ( data [ 0 ] === checkSum ) ;
69- } ) ;
70- this . send ( programLength , Consts . BLECharacteristic . PYBRICKS_NUS_RX ) ;
71- await checkSumPromise ;
72- const chunkSize = 100 ;
73- for ( let i = 0 ; i < result . mpy . byteLength ; i += chunkSize ) {
74- const chunk = result . mpy . slice ( i , i + chunkSize ) ;
75- const checkSumPromise = new Promise < boolean > ( ( resolve ) => {
76- const checkSum = chunk . reduce ( ( a , b ) => a ^ b ) ;
77- this . _checkSumCallback = ( data ) => resolve ( data [ 0 ] === checkSum ) ;
78- } ) ;
79- this . send ( Buffer . from ( chunk ) , Consts . BLECharacteristic . PYBRICKS_NUS_RX ) ;
80- await checkSumPromise ;
71+ const multiFileBlob = Buffer . concat ( [ Buffer . from ( [ 0 , 0 , 0 , 0 ] ) , Buffer . from ( '__main__\0' ) , result . mpy ] ) ;
72+ multiFileBlob . writeUInt32LE ( result . mpy . length ) ;
73+ if ( multiFileBlob . length > this . _maxUserProgramSize ) {
74+ throw new Error ( `User program size ${ multiFileBlob . length } larger than maximum ${ this . _maxUserProgramSize } ` ) ;
8175 }
82- this . _checkSumCallback = undefined ;
76+ debug ( "Uploading Python User Program" , multiFileBlob ) ;
77+ await this . writeUserProgramMeta ( 0 ) ;
78+ const chunkSize = this . _maxCharSize - 5 ;
79+ for ( let i = 0 ; i < multiFileBlob . length ; i += chunkSize ) {
80+ const chunk = multiFileBlob . slice ( i , i + chunkSize ) ;
81+ await this . writeUserRam ( i , Buffer . from ( chunk ) ) ;
82+ }
83+ await this . writeUserProgramMeta ( multiFileBlob . length ) ;
8384 debug ( "Finished uploading" ) ;
8485 }
8586 else throw new Error ( `Compiling Python User Program failed: ${ result . err } ` ) ;
8687 } ) ;
8788 }
8889
8990 public stopUserProgram ( ) {
90- return this . send ( Buffer . from ( [ 0 ] ) , Consts . BLECharacteristic . PYBRICKS_CONTROL ) ;
91+ debug ( "Stopping Python User Program" ) ;
92+ return this . send ( Buffer . from ( [ 0 ] ) , Consts . BLECharacteristic . PYBRICKS_COMMAND_EVENT ) ;
93+ }
94+
95+ public startUserProgram ( ) {
96+ debug ( "Starting Python User Program" ) ;
97+ return this . send ( Buffer . from ( [ 1 ] ) , Consts . BLECharacteristic . PYBRICKS_COMMAND_EVENT ) ;
98+ }
99+
100+ private writeUserProgramMeta ( programLength : number ) {
101+ const message = Buffer . alloc ( 5 ) ;
102+ message [ 0 ] = 3 ;
103+ message . writeUint32LE ( programLength , 1 ) ;
104+ return this . send ( message , Consts . BLECharacteristic . PYBRICKS_COMMAND_EVENT ) ;
105+ }
106+
107+ private writeUserRam ( offset : number , payload : Buffer ) {
108+ const message = Buffer . concat ( [ Buffer . from ( [ 4 , 0 , 0 , 0 , 0 ] ) , payload ] ) ;
109+ message . writeUint32LE ( offset , 1 ) ;
110+ return this . send ( message , Consts . BLECharacteristic . PYBRICKS_COMMAND_EVENT ) ;
91111 }
92112}
93113
0 commit comments