Skip to content

Commit 61a623d

Browse files
committed
firmware: Firmware must be sector size multiple.
If the EV3 firmware size is not a multiple of the sector size, the flashing process will hang. Raise an error if this happens.
1 parent 6548390 commit 61a623d

File tree

6 files changed

+30
-4
lines changed

6 files changed

+30
-4
lines changed

src/firmware/actions.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2020-2025 The Pybricks Authors
2+
// Copyright (c) 2020-2026 The Pybricks Authors
33

44
import { FirmwareReaderError, HubType } from '@pybricks/firmware';
55
import { createAction } from '../actions';
@@ -47,6 +47,8 @@ export enum FailToFinishReasonType {
4747
FailedToCompile = 'flashFirmware.failToFinish.reason.failedToCompile',
4848
/** The combined firmware-base.bin and main.mpy are too big. */
4949
FirmwareSize = 'flashFirmware.failToFinish.reason.firmwareSize',
50+
/** The firmware's start or end is not aligned to the sector boundary. */
51+
FirmwareAlignment = 'flashFirmware.failToFinish.reason.firmwareAlignment',
5052
/** An unexpected error occurred. */
5153
Unknown = 'flashFirmware.failToFinish.reason.unknown',
5254
}
@@ -94,6 +96,9 @@ export type FailToFinishReasonBadMetadata =
9496
export type FailToFinishReasonFirmwareSize =
9597
Reason<FailToFinishReasonType.FirmwareSize>;
9698

99+
export type FailToFinishReasonFirmwareAlignment =
100+
Reason<FailToFinishReasonType.FirmwareAlignment>;
101+
97102
export type FailToFinishReasonFailedToCompile =
98103
Reason<FailToFinishReasonType.FailedToCompile>;
99104

@@ -113,6 +118,7 @@ export type FailToFinishReason =
113118
| FailToFinishReasonZipError
114119
| FailToFinishReasonBadMetadata
115120
| FailToFinishReasonFirmwareSize
121+
| FailToFinishReasonFirmwareAlignment
116122
| FailToFinishReasonFailedToCompile
117123
| FailToFinishReasonUnknown;
118124

src/firmware/sagas.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,20 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
12411241
// FIXME: should be called much earlier.
12421242
yield* put(didStart());
12431243

1244+
console.debug(`Firmware size: ${action.firmware.byteLength} bytes`);
1245+
1246+
// Apparently, erasing a span of the flash creates some sort of record in
1247+
// the EV3, and we can only write within a given erase span. Writes that
1248+
// cross the boundary will hang. To avoid this, we erase the whole firmware
1249+
// range at once.
1250+
const sectorSize = 64 * 1024; // flash memory sector size
1251+
if (action.firmware.byteLength % sectorSize !== 0) {
1252+
yield* put(didFailToFinish(FailToFinishReasonType.FirmwareAlignment));
1253+
yield* put(firmwareDidFailToFlashEV3());
1254+
yield* cleanup();
1255+
return;
1256+
}
1257+
12441258
const maxPayloadSize = 1018; // maximum payload size for EV3 commands
12451259

12461260
const erasePayload = new DataView(new ArrayBuffer(8));

src/notifications/i18n.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2020-2022 The Pybricks Authors
2+
// Copyright (c) 2020-2025 The Pybricks Authors
33
//
44
// Notification translation keys.
55

@@ -35,6 +35,7 @@ export enum I18nId {
3535
FlashFirmwareBadMetadata = 'flashFirmware.badMetadata',
3636
FlashFirmwareCompileError = 'flashFirmware.compileError',
3737
FlashFirmwareSizeTooBig = 'flashFirmware.sizeTooBig',
38+
FlashFirmwareAlignment = 'flashFirmware.alignment',
3839
FlashFirmwareUnexpectedError = 'flashFirmware.unexpectedError',
3940
ServiceWorkerUpdateMessage = 'serviceWorker.update.message',
4041
ServiceWorkerUpdateAction = 'serviceWorker.update.action',

src/notifications/sagas.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2021-2023 The Pybricks Authors
2+
// Copyright (c) 2021-2025 The Pybricks Authors
33

44
import type { ToastOptions, Toaster } from '@blueprintjs/core';
55
import { FirmwareReaderError, FirmwareReaderErrorCode } from '@pybricks/firmware';
@@ -95,6 +95,7 @@ test.each([
9595
),
9696
didFailToFinish(FailToFinishReasonType.FailedToCompile),
9797
didFailToFinish(FailToFinishReasonType.FirmwareSize),
98+
didFailToFinish(FailToFinishReasonType.FirmwareAlignment),
9899
didFailToFinish(FailToFinishReasonType.Unknown, new Error('test error')),
99100
appDidCheckForUpdate(false),
100101
fileStorageDidFailToInitialize(new Error('test error')),

src/notifications/sagas.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2020-2023 The Pybricks Authors
2+
// Copyright (c) 2020-2026 The Pybricks Authors
33

44
// Saga for managing notifications (toasts)
55

@@ -228,6 +228,9 @@ function* showFlashFirmwareError(
228228
case FailToFinishReasonType.FirmwareSize:
229229
yield* showSingleton(Level.Error, I18nId.FlashFirmwareSizeTooBig);
230230
break;
231+
case FailToFinishReasonType.FirmwareAlignment:
232+
yield* showSingleton(Level.Error, I18nId.FlashFirmwareAlignment);
233+
break;
231234
case FailToFinishReasonType.Unknown:
232235
yield* showUnexpectedError(
233236
I18nId.FlashFirmwareUnexpectedError,

src/notifications/translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"badMetadata": "The firmware.metadata.json file contains missing or invalid entries. Fix it then try again.",
3434
"compileError": "The included main.py file could not be compiled. Fix it then try again.",
3535
"sizeTooBig": "The combined firmware and main.py are too big to fit in the flash memory.",
36+
"alignment": "The firmware's start or end is not aligned to the sector boundary.",
3637
"unexpectedError": "Unexpected error while trying to install firmware: {errorMessage}"
3738
},
3839
"mpy": {

0 commit comments

Comments
 (0)