Skip to content

Conversation

@jaguilar
Copy link

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.

Fixes pybricks/support#2375.

@laurensvalk
Copy link
Member

laurensvalk commented Dec 20, 2025

Fixes pybricks/support#2375.

This looks like a useful change, but it's a fix for a different problem 😄 In pybricks/support#2375 it never gets to flashing. It fails right away on getting the bad response to the get version command.

EDIT: I'll collect some notes in the issue thread instead.

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.
);

const firmware = new Uint8Array(firmwareBase.length + 4);
const [checksumFunc, checksumSize] = (function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const [checksumFunc, checksumSize] = (function () {
const [checksumFunc, checksumExtraLength] = (function () {

As to not confuse with metadata['checkum-size'].

})();

if (checksum === undefined) {
if (checksumFunc === undefined) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would make sense to move this to fail as early as possible (line 366).

);

const firmware = new Uint8Array(firmwareBase.length + 4);
const [checksumFunc, checksumSize] = (function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const [checksumFunc, checksumSize] = (function () {
const [checksumFunc, checksumSize] = (() => {

This code base typically uses lambda syntax.

true,
)}, HW version: ${version.getUint32(4, true)}`,
);
// For reasons that we do not currently understand, some EV3s do not return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we fail the get the version correctly, then can't we expect other responses to fail?

Seems like this needs more investigation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact the other responses do not fail. I agree it warrants further investigation. Perhaps a new issue, though, since the method of flashing the firmware does not depend on the version?

for (let i = 0; i < action.firmware.byteLength; i += sectorSize) {
const sectorData = action.firmware.slice(i, i + sectorSize);
assert(sectorData.byteLength <= sectorSize, 'sector data too large');
console.info(`Firmware size: ${action.firmware.byteLength} bytes`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.info(`Firmware size: ${action.firmware.byteLength} bytes`);
console.debug(`Firmware size: ${action.firmware.byteLength} bytes`);

Don't really need to spam the logs.

erasePayload.setUint32(0, 0, true); // start address
erasePayload.setUint32(4, numSectors * sectorSize, true); // size
console.debug(
`Erasing bytes [0x${(0).toString(16)}, 0x${(numSectors * sectorSize).toString(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would also be useful to log the number of sectors that we calculated.

erasePayload.setUint32(0, 0, true); // start address
erasePayload.setUint32(4, numSectors * sectorSize, true); // size
console.debug(
`Erasing bytes [0x${(0).toString(16)}, 0x${(numSectors * sectorSize).toString(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a - 1 on the end address?

erasePayload.setUint32(0, 0, true); // start address
erasePayload.setUint32(4, numSectors * sectorSize, true); // size
console.debug(
`Erasing bytes [0x${(0).toString(16)}, 0x${(numSectors * sectorSize).toString(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, we also have a util function to add leading zeros in hex numbers.

Comment on lines +1246 to +1247
// Apparently, erasing a span of the flash creates some sort of record in
// the EV3, and we can only write within a given erase span. Writes that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though that is what we were doing before. Erase one sector - write the entire one sector - erase the next sector - write one sector - and so on...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There has been quite a bit of testing and discussion about this already. Erasing the required space in one go was found to be more reliable. It's also what we've been testing with Pybricksdev.

Copy link
Author

@jaguilar jaguilar Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Particularly here and here.

// will hang on the last write we send.
const firmware = action.firmware;
for (let i = 0; i < firmware.byteLength; i += maxPayloadSize) {
const writeLength = Math.min(maxPayloadSize, firmware.byteLength - i);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this is redundant since slice doesn't pad thing but rather returns a partial if i + maxPayloadSize exceeds the firmware size.

@dlech
Copy link
Member

dlech commented Dec 23, 2025

It seems like this is fixing at least 3 different things. So would be better to split it up into 3 commits.

  1. Fix not adding extra padding for non-existent checksum.
  2. Change to erase all then flash all instead erase/flash one sector at a time.
  3. Verify that the firmware blob size is exactly aligned to sector size.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] EV3 firmware installation sends bad status values

3 participants