@@ -1138,6 +1138,7 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
11381138 function * sendCommand (
11391139 command : number ,
11401140 payload ?: Uint8Array ,
1141+ options ?: { timeoutms ?: number } ,
11411142 ) : SagaGenerator < [ DataView | undefined , Error | undefined ] > {
11421143 // We need to start listing for reply before sending command in order
11431144 // to avoid race conditions.
@@ -1163,9 +1164,11 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
11631164 return [ undefined , sendError ] ;
11641165 }
11651166
1167+ const timeoutms = options ?. timeoutms ?? 5000 ;
1168+
11661169 const { reply, timeout } = yield * race ( {
11671170 reply : take ( replyChannel ) ,
1168- timeout : delay ( 5000 ) ,
1171+ timeout : delay ( timeoutms ) ,
11691172 } ) ;
11701173
11711174 replyChannel . close ( ) ;
@@ -1209,63 +1212,89 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
12091212 // FIXME: should be called much earlier.
12101213 yield * put ( didStart ( ) ) ;
12111214
1212- const sectorSize = 64 * 1024 ; // flash memory sector size
12131215 const maxPayloadSize = 1018 ; // maximum payload size for EV3 commands
12141216
1215- for ( let i = 0 ; i < action . firmware . byteLength ; i += sectorSize ) {
1216- const sectorData = action . firmware . slice ( i , i + sectorSize ) ;
1217- assert ( sectorData . byteLength <= sectorSize , 'sector data too large' ) ;
1217+ const erasePayload = new DataView ( new ArrayBuffer ( 8 ) ) ;
1218+ erasePayload . setUint32 ( 0 , 0 , true ) ; // start address
1219+ erasePayload . setUint32 ( 4 , action . firmware . byteLength , true ) ; // size
1220+ console . debug ( `Erasing bytes [0x0, ${ hex ( action . firmware . byteLength , 0 ) } )` ) ;
12181221
1219- const erasePayload = new DataView ( new ArrayBuffer ( 8 ) ) ;
1220- erasePayload . setUint32 ( 0 , i , true ) ;
1221- erasePayload . setUint32 ( 4 , sectorData . byteLength , true ) ;
1222- const [ , eraseError ] = yield * sendCommand (
1223- 0xf0 ,
1224- new Uint8Array ( erasePayload . buffer ) ,
1222+ yield * put (
1223+ alertsShowAlert (
1224+ 'firmware' ,
1225+ 'flashProgress' ,
1226+ {
1227+ action : 'erase' ,
1228+ progress : undefined ,
1229+ } ,
1230+ firmwareBleProgressToastId ,
1231+ true ,
1232+ ) ,
1233+ ) ;
1234+
1235+ // Measured erase rate is approximately .25 kB/ms. This was on a powerful
1236+ // computer so it may be that flashing from something like a raspberry pi
1237+ // would take longer. We'll set a timeout three times as long as would have
1238+ // taken at the measured rate.
1239+ const eraseTimeoutMs = ( action . firmware . byteLength / 256 ) * 1000 * 3 ;
1240+ const startTime = Date . now ( ) ;
1241+ const [ , eraseError ] = yield * sendCommand (
1242+ 0xf0 ,
1243+ new Uint8Array ( erasePayload . buffer ) ,
1244+ { timeoutms : eraseTimeoutMs } ,
1245+ ) ;
1246+ console . debug (
1247+ `EV3 erase took ${ Date . now ( ) - startTime } ms for ${
1248+ action . firmware . byteLength
1249+ } bytes, timeout was ${ eraseTimeoutMs } ms`,
1250+ ) ;
1251+
1252+ if ( eraseError ) {
1253+ yield * put (
1254+ alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1255+ error : eraseError ,
1256+ } ) ,
12251257 ) ;
1258+ // FIXME: should have a better error reason
1259+ yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , eraseError ) ) ;
1260+ yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
1261+ yield * cleanup ( ) ;
1262+ return ;
1263+ }
12261264
1227- if ( eraseError ) {
1265+ // If we don't write an exact multiple of the sector size, the flash process
1266+ // will hang on the last write we send.
1267+ const firmware = action . firmware ;
1268+ for ( let i = 0 ; i < firmware . byteLength ; i += maxPayloadSize ) {
1269+ const payload = firmware . slice ( i , i + maxPayloadSize ) ;
1270+ console . debug (
1271+ `Programming bytes [${ hex ( i , 0 ) } , ${ hex ( i + maxPayloadSize , 0 ) } )` ,
1272+ ) ;
1273+
1274+ const [ , sendError ] = yield * sendCommand ( 0xf2 , new Uint8Array ( payload ) ) ;
1275+ if ( sendError ) {
12281276 yield * put (
12291277 alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1230- error : eraseError ,
1278+ error : sendError ,
12311279 } ) ,
12321280 ) ;
12331281 // FIXME: should have a better error reason
1234- yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , eraseError ) ) ;
1282+ yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , sendError ) ) ;
12351283 yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
12361284 yield * cleanup ( ) ;
12371285 return ;
12381286 }
12391287
1240- for ( let j = 0 ; j < sectorData . byteLength ; j += maxPayloadSize ) {
1241- const payload = sectorData . slice ( j , j + maxPayloadSize ) ;
1242-
1243- const [ , sendError ] = yield * sendCommand ( 0xf2 , new Uint8Array ( payload ) ) ;
1244- if ( sendError ) {
1245- yield * put (
1246- alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1247- error : sendError ,
1248- } ) ,
1249- ) ;
1250- // FIXME: should have a better error reason
1251- yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , sendError ) ) ;
1252- yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
1253- yield * cleanup ( ) ;
1254- return ;
1255- }
1256- }
1257-
1258- yield * put (
1259- didProgress ( ( i + sectorData . byteLength ) / action . firmware . byteLength ) ,
1260- ) ;
1288+ const progress = ( i + payload . byteLength ) / firmware . byteLength ;
1289+ yield * put ( didProgress ( progress ) ) ;
12611290
12621291 yield * put (
12631292 alertsShowAlert (
12641293 'firmware' ,
12651294 'flashProgress' ,
12661295 {
12671296 action : 'flash' ,
1268- progress : ( i + sectorData . byteLength ) / action . firmware . byteLength ,
1297+ progress : progress ,
12691298 } ,
12701299 firmwareBleProgressToastId ,
12711300 true ,
0 commit comments