@@ -47,9 +47,15 @@ UART.ports = ["Web Serial"]; // force only Web Serial to be used
47
47
UART.debug = 3; // show all debug messages
48
48
etc...
49
49
50
+ As of Espruino 2v25 you can also send data packets:
51
+
52
+ UART.getConnection().espruinoSendFile("test.txt","This is a test of sending data to Espruino").then(_=>console.log("Done"))
53
+
50
54
ChangeLog:
51
55
52
56
...
57
+ 1.02: Added better on/emit/removeListener handling
58
+ Add .espruinoSendPacket
53
59
1.01: Add UART.ports to allow available to user to be restricted
54
60
Add configurable baud rate
55
61
Updated modal dialog look (with common fn for selector and modal)
@@ -60,6 +66,9 @@ ChangeLog:
60
66
To do:
61
67
62
68
* write/eval/etc should return promises
69
+ * move 'connection.received' handling into removeListener and add an upper limit (100k?)
70
+ * add a 'line' event for each line of data that's received
71
+ * add espruinoEval method using the new packet system
63
72
64
73
*/
65
74
( function ( root , factory ) {
@@ -105,6 +114,94 @@ To do:
105
114
if ( uart . log ) uart . log ( level , s ) ;
106
115
}
107
116
117
+ /// Base connection class - BLE/Serial add their write/etc on top of this
118
+ class Connection {
119
+ // on/emit work for close/data/open/error/ack/nak events
120
+ on ( evt , cb ) { let e = "on" + evt ; if ( ! this [ e ] ) this [ e ] = [ ] ; this [ e ] . push ( cb ) ; } ; // on only works with a single handler
121
+ emit ( evt , data ) { let e = "on" + evt ; if ( this [ e ] ) this [ e ] . forEach ( fn => fn ( data ) ) ; } ;
122
+ removeListener ( evt , callback ) { let e = "on" + evt ; if ( this [ e ] ) this [ e ] = this [ e ] . filter ( fn => fn != callback ) ; } ;
123
+ isOpen = false ; // is the connection actually open?
124
+ isOpening = true ; // in the process of opening a connection?
125
+ txInProgress = false ; // is transmission in progress?
126
+ parsePackets = false ; // If set we parse the input stream for Espruino packet data transfers
127
+ rxDataHandler ( data ) { // Called when data is received, and passed it on to event listeners
128
+ log ( 3 , "Received " + JSON . stringify ( data ) ) ;
129
+ // TODO: handle XON/XOFF centrally here?
130
+ if ( this . parsePackets ) {
131
+ for ( var i = 0 ; i < data . length ; i ++ ) {
132
+ let ch = data [ i ] ;
133
+ if ( ch == "\x06" ) {
134
+ log ( 3 , "Got ACK" ) ;
135
+ this . emit ( "ack" ) ;
136
+ ch = undefined ;
137
+ } else if ( ch == "\x15" ) {
138
+ log ( 3 , "Got NAK" ) ;
139
+ this . emit ( "nak" ) ;
140
+ ch = undefined ;
141
+ }
142
+ if ( ch === undefined ) { // if we're supposed to remove the char, do it
143
+ console . log ( "before" , data ) ;
144
+ data = data . substring ( 0 , i - 1 ) + data . substring ( i + 1 ) ;
145
+ console . log ( "after" , data ) ;
146
+ i -- ;
147
+ }
148
+ }
149
+ }
150
+ connection . emit ( 'data' , data ) ;
151
+ }
152
+
153
+ /* Send a packet of type "RESPONSE/EVAL/EVENT/FILE_SEND/DATA" to Espruino */
154
+ espruinoSendPacket ( pkType , data ) {
155
+ if ( "string" != typeof data ) throw new Error ( "'data' must be a String" ) ;
156
+ if ( data . length > 0x1FFF ) throw new Error ( "'data' too long" ) ;
157
+ const PKTYPES = {
158
+ RESPONSE : 0 , // Response to an EVAL packet
159
+ EVAL : 0x2000 , // execute and return the result as RESPONSE packet
160
+ EVENT : 0x4000 , // parse as JSON and create `E.on('packet', ...)` event
161
+ FILE_SEND : 0x6000 , // called before DATA, with {fn:"filename",s:123}
162
+ DATA : 0x8000 , // Sent after FILE_SEND with blocks of data for the file
163
+ }
164
+ if ( ! pkType in PKTYPES ) throw new Error ( "'pkType' not one of " + Object . keys ( PKTYPES ) ) ;
165
+ let connection = this ;
166
+ return new Promise ( ( resolve , reject ) => {
167
+ function tidy ( ) {
168
+ connection . removeListener ( "ack" , onACK ) ;
169
+ connection . removeListener ( "nak" , onNAK ) ;
170
+ }
171
+ function onACK ( ok ) {
172
+ tidy ( ) ;
173
+ resolve ( ) ;
174
+ }
175
+ function onNAK ( ok ) {
176
+ tidy ( ) ;
177
+ reject ( ) ;
178
+ }
179
+ connection . parsePackets = true ;
180
+ connection . on ( "ack" , onACK ) ;
181
+ connection . on ( "nak" , onNAK ) ;
182
+ let flags = data . length | PKTYPES [ pkType ] ;
183
+ write ( String . fromCharCode ( /*DLE*/ 16 , /*SOH*/ 1 , ( flags >> 8 ) & 0xFF , flags & 0xFF ) + data ) ;
184
+ } ) ;
185
+ }
186
+ /* Send a file to Espruino using 2v25 packets */
187
+ espruinoSendFile ( filename , data , options ) {
188
+ if ( "string" != typeof data ) throw new Error ( "'data' must be a String" ) ;
189
+ options = options || { } ;
190
+ options . fn = filename ;
191
+ options . s = data . length ;
192
+ let connection = this ;
193
+ return connection . espruinoSendPacket ( "FILE_SEND" , JSON . stringify ( options ) ) . then ( sendData ) ;
194
+ function sendData ( ) {
195
+ if ( data . length == 0 ) return Promise . resolve ( ) ;
196
+ const CHUNK = 512 ;
197
+ let packet = data . substring ( 0 , CHUNK ) ;
198
+ data = data . substring ( CHUNK ) ;
199
+ return connection . espruinoSendPacket ( "DATA" , packet ) . then ( sendData ) ;
200
+ }
201
+ }
202
+ } ;
203
+
204
+ ///
108
205
var endpoints = [ ] ;
109
206
endpoints . push ( {
110
207
name : "Web Bluetooth" ,
@@ -253,9 +350,7 @@ To do:
253
350
}
254
351
}
255
352
}
256
- var str = ab2str ( dataview . buffer ) ;
257
- log ( 3 , "Received " + JSON . stringify ( str ) ) ;
258
- connection . emit ( 'data' , str ) ;
353
+ connection . rxDataHandler ( ab2str ( dataview . buffer ) ) ;
259
354
} ) ;
260
355
return rxCharacteristic . startNotifications ( ) ;
261
356
} ) . then ( function ( ) {
@@ -306,16 +401,14 @@ To do:
306
401
navigator . serial . requestPort ( { } ) . then ( function ( port ) {
307
402
log ( 1 , "Connecting to serial port" ) ;
308
403
serialPort = port ;
309
- return port . open ( { baudRate : baud } ) ;
404
+ return port . open ( { baudRate : uart . baud } ) ;
310
405
} ) . then ( function ( ) {
311
406
function readLoop ( ) {
312
407
var reader = serialPort . readable . getReader ( ) ;
313
408
reader . read ( ) . then ( function ( { value, done } ) {
314
409
reader . releaseLock ( ) ;
315
410
if ( value ) {
316
- var str = ab2str ( value . buffer ) ;
317
- log ( 3 , "Received " + JSON . stringify ( str ) ) ;
318
- connection . emit ( 'data' , str ) ;
411
+ connection . rxDataHandler ( ab2str ( value . buffer ) ) ;
319
412
}
320
413
if ( done ) {
321
414
disconnected ( ) ;
@@ -344,7 +437,9 @@ To do:
344
437
connection . write = function ( data , callback ) {
345
438
var writer = serialPort . writable . getWriter ( ) ;
346
439
// TODO: progress?
440
+ log ( 2 , "Sending " + JSON . stringify ( data ) ) ;
347
441
writer . write ( str2ab ( data ) ) . then ( function ( ) {
442
+ log ( 3 , "Sent" ) ;
348
443
callback ( ) ;
349
444
} ) . catch ( function ( error ) {
350
445
log ( 0 , 'SEND ERROR: ' + error ) ;
@@ -412,13 +507,7 @@ To do:
412
507
// ======================================================================
413
508
var connection ;
414
509
function connect ( callback ) {
415
- var connection = {
416
- on : function ( evt , cb ) { this [ "on" + evt ] = cb ; } ,
417
- emit : function ( evt , data ) { if ( this [ "on" + evt ] ) this [ "on" + evt ] ( data ) ; } ,
418
- isOpen : false ,
419
- isOpening : true ,
420
- txInProgress : false
421
- } ;
510
+ connection = new Connection ( ) ;
422
511
423
512
if ( uart . ports . length == 0 ) {
424
513
console . error ( `UART: No ports in uart.ports` ) ;
@@ -579,7 +668,7 @@ To do:
579
668
// ----------------------------------------------------------
580
669
581
670
var uart = {
582
- version : "1.01 " ,
671
+ version : "1.02 " ,
583
672
/// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all.
584
673
debug : 1 ,
585
674
/// Should we use flow control? Default is true
@@ -636,7 +725,10 @@ To do:
636
725
callback ( ) ;
637
726
}
638
727
} ) ;
639
- }
728
+ } ,
729
+ /* This is the list of 'drivers' for Web Bluetooth/Web Serial. It's possible to add to these
730
+ and also change 'ports' in order to add your own custom endpoints (eg WebSockets) */
731
+ endpoints : endpoints
640
732
} ;
641
733
return uart ;
642
734
} ) ) ;
0 commit comments