Skip to content

Commit 9cd1a76

Browse files
committed
ev3: Fix issues with flashing (some?) EV3s.
Flash the EV3 all in one go rather than sector-by-sector. This appears to resolve issues we had with flashing (some?) EV3s, and mirrors what we do in pybricksdev.
1 parent 4516cc8 commit 9cd1a76

File tree

1 file changed

+82
-40
lines changed

1 file changed

+82
-40
lines changed

src/firmware/sagas.ts

Lines changed: 82 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,73 +1219,115 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
12191219

12201220
defined(version);
12211221

1222-
console.debug(
1223-
`EV3 bootloader version: ${version.getUint32(
1224-
0,
1225-
true,
1226-
)}, HW version: ${version.getUint32(4, true)}`,
1227-
);
1222+
// For reasons that we do not currently understand, some EV3s do not return
1223+
// anything to our GetVersion command. We don't actually use the version
1224+
// for anything so we will just ignore this error.
1225+
try {
1226+
console.debug(
1227+
`EV3 bootloader version: ${version.getUint32(
1228+
0,
1229+
true,
1230+
)}, HW version: ${version.getUint32(4, true)}`,
1231+
);
1232+
} catch (err) {
1233+
console.error(`Failed to parse ev3 version response: ${ensureError(err)}`);
1234+
}
12281235

12291236
// FIXME: should be called much earlier.
12301237
yield* put(didStart());
12311238

12321239
const sectorSize = 64 * 1024; // flash memory sector size
12331240
const maxPayloadSize = 1018; // maximum payload size for EV3 commands
12341241

1235-
for (let i = 0; i < action.firmware.byteLength; i += sectorSize) {
1236-
const sectorData = action.firmware.slice(i, i + sectorSize);
1237-
assert(sectorData.byteLength <= sectorSize, 'sector data too large');
1242+
console.info(`Firmware size: ${action.firmware.byteLength} bytes`);
1243+
1244+
// Apparently, erasing a span of the flash creates some sort of record in
1245+
// the EV3, and we can only write within a given erase span. Writes that
1246+
// cross the boundary will hang. To avoid this, we erase the whole firmware
1247+
// range at once.
1248+
const numSectors = Math.ceil(action.firmware.byteLength / sectorSize);
1249+
const erasePayload = new DataView(new ArrayBuffer(8));
1250+
erasePayload.setUint32(0, 0, true); // start address
1251+
erasePayload.setUint32(4, numSectors * sectorSize, true); // size
1252+
console.debug(
1253+
`Erasing bytes [0x${(0).toString(16)}, 0x${(numSectors * sectorSize).toString(
1254+
16,
1255+
)})`,
1256+
);
1257+
1258+
yield* put(
1259+
alertsShowAlert(
1260+
'firmware',
1261+
'flashProgress',
1262+
{
1263+
action: 'erase',
1264+
progress: undefined,
1265+
},
1266+
firmwareBleProgressToastId,
1267+
true,
1268+
),
1269+
);
1270+
1271+
const [, eraseError] = yield* sendCommand(
1272+
0xf0,
1273+
new Uint8Array(erasePayload.buffer),
1274+
);
1275+
1276+
if (eraseError) {
1277+
yield* put(
1278+
alertsShowAlert('alerts', 'unexpectedError', {
1279+
error: eraseError,
1280+
}),
1281+
);
1282+
// FIXME: should have a better error reason
1283+
yield* put(didFailToFinish(FailToFinishReasonType.Unknown, eraseError));
1284+
yield* put(firmwareDidFailToFlashEV3());
1285+
yield* cleanup();
1286+
return;
1287+
}
1288+
1289+
// If we don't write an exact multiple of the sector size, the flash process
1290+
// will hang on the last write we send.
1291+
let firmware = action.firmware;
1292+
if (firmware.byteLength !== numSectors * sectorSize) {
1293+
const paddedFirmware = new Uint8Array(numSectors * sectorSize);
1294+
paddedFirmware.set(new Uint8Array(firmware), 0);
1295+
firmware = paddedFirmware.buffer;
1296+
}
12381297

1239-
const erasePayload = new DataView(new ArrayBuffer(8));
1240-
erasePayload.setUint32(0, i, true);
1241-
erasePayload.setUint32(4, sectorData.byteLength, true);
1242-
const [, eraseError] = yield* sendCommand(
1243-
0xf0,
1244-
new Uint8Array(erasePayload.buffer),
1298+
for (let i = 0; i < firmware.byteLength; i += maxPayloadSize) {
1299+
const writeLength = Math.min(maxPayloadSize, firmware.byteLength - i);
1300+
const payload = firmware.slice(i, i + writeLength);
1301+
console.debug(
1302+
`Programming bytes [0x${i.toString(16)}, 0x${(i + writeLength).toString(
1303+
16,
1304+
)})`,
12451305
);
12461306

1247-
if (eraseError) {
1307+
const [, sendError] = yield* sendCommand(0xf2, new Uint8Array(payload));
1308+
if (sendError) {
12481309
yield* put(
12491310
alertsShowAlert('alerts', 'unexpectedError', {
1250-
error: eraseError,
1311+
error: sendError,
12511312
}),
12521313
);
12531314
// FIXME: should have a better error reason
1254-
yield* put(didFailToFinish(FailToFinishReasonType.Unknown, eraseError));
1315+
yield* put(didFailToFinish(FailToFinishReasonType.Unknown, sendError));
12551316
yield* put(firmwareDidFailToFlashEV3());
12561317
yield* cleanup();
12571318
return;
12581319
}
12591320

1260-
for (let j = 0; j < sectorData.byteLength; j += maxPayloadSize) {
1261-
const payload = sectorData.slice(j, j + maxPayloadSize);
1262-
1263-
const [, sendError] = yield* sendCommand(0xf2, new Uint8Array(payload));
1264-
if (sendError) {
1265-
yield* put(
1266-
alertsShowAlert('alerts', 'unexpectedError', {
1267-
error: sendError,
1268-
}),
1269-
);
1270-
// FIXME: should have a better error reason
1271-
yield* put(didFailToFinish(FailToFinishReasonType.Unknown, sendError));
1272-
yield* put(firmwareDidFailToFlashEV3());
1273-
yield* cleanup();
1274-
return;
1275-
}
1276-
}
1277-
1278-
yield* put(
1279-
didProgress((i + sectorData.byteLength) / action.firmware.byteLength),
1280-
);
1321+
const progress = (i + writeLength) / firmware.byteLength;
1322+
yield* put(didProgress(progress));
12811323

12821324
yield* put(
12831325
alertsShowAlert(
12841326
'firmware',
12851327
'flashProgress',
12861328
{
12871329
action: 'flash',
1288-
progress: (i + sectorData.byteLength) / action.firmware.byteLength,
1330+
progress: progress,
12891331
},
12901332
firmwareBleProgressToastId,
12911333
true,

0 commit comments

Comments
 (0)