@@ -8,12 +8,15 @@ const debug = Debug("pybrickshub");
8
8
9
9
10
10
/**
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.
12
14
* @class PybricksHub
13
15
* @extends BaseHub
14
16
*/
15
17
export 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
17
20
18
21
public static IsPybricksHub ( peripheral : Peripheral ) {
19
22
return (
@@ -39,15 +42,20 @@ export class PybricksHub extends BaseHub {
39
42
debug ( "Connect completed" ) ;
40
43
this . emit ( "connect" ) ;
41
44
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
+ } ) ;
42
52
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 ) ) ;
43
54
} ) ;
44
55
}
45
56
46
57
private _parseMessage ( data ?: Buffer ) {
47
58
debug ( "Received Message (PYBRICKS_NUS_TX)" , data ) ;
48
- if ( this . _checkSumCallback && data ) {
49
- return this . _checkSumCallback ( data ) ;
50
- }
51
59
this . emit ( "recieve" , data ) ;
52
60
}
53
61
@@ -56,38 +64,50 @@ export class PybricksHub extends BaseHub {
56
64
return this . _bleDevice . writeToCharacteristic ( uuid , message ) ;
57
65
}
58
66
59
- public startUserProgram ( pythonCode : string ) {
67
+ public uploadUserProgram ( pythonCode : string ) {
60
68
debug ( "Compiling Python User Program" , pythonCode ) ;
61
- return compile ( "UserProgram .py" , pythonCode ) . then ( async ( result ) => {
69
+ return compile ( 'userProgram .py' , pythonCode ) . then ( async ( result ) => {
62
70
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 } ` ) ;
81
75
}
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 ) ;
83
84
debug ( "Finished uploading" ) ;
84
85
}
85
86
else throw new Error ( `Compiling Python User Program failed: ${ result . err } ` ) ;
86
87
} ) ;
87
88
}
88
89
89
90
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 ) ;
91
111
}
92
112
}
93
113
0 commit comments