Skip to content

Commit d4725fd

Browse files
Send micro:bit 1 device id to micro:bit 2 (#211)
This is used to determine the frequency and included in the radio packets to further disambiguate. Updated hexes make the corresponding change. Co-authored-by: Carlos Pereira Atencio <[email protected]>
1 parent 339f86c commit d4725fd

File tree

13 files changed

+30876
-47148
lines changed

13 files changed

+30876
-47148
lines changed

public/firmware/local-sensors-v0.1.0.hex renamed to public/firmware/local-sensors-v0.2.0.hex

Lines changed: 7634 additions & 7616 deletions
Large diffs are not rendered by default.

public/firmware/radio-bridge-v0.1.0.hex

Lines changed: 0 additions & 16507 deletions
This file was deleted.

public/firmware/radio-remote-v0.1.0-dev.hex renamed to public/firmware/radio-bridge-v0.2.0.hex

Lines changed: 7645 additions & 7702 deletions
Large diffs are not rendered by default.

public/firmware/radio-remote-v0.1.0.hex renamed to public/firmware/radio-remote-v0.2.0-dev.hex

Lines changed: 7719 additions & 7694 deletions
Large diffs are not rendered by default.

public/firmware/radio-local.hex renamed to public/firmware/radio-remote-v0.2.0.hex

Lines changed: 7712 additions & 7519 deletions
Large diffs are not rendered by default.

src/__tests__/microbit-usb-conection.test.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@ describe('Microbit USB connection tests', () => {
2626
});
2727
});
2828

29-
test('Can connect read serial', async () => {
30-
const mockUsbDevice = new MockUSBDevice().withSerialNumber('123test').build();
31-
const connection = new TestableMicrobitUSB(mockUsbDevice);
32-
expect(connection.getSerialNumber()).toBe('123test');
33-
});
34-
3529
test('Serial number 9900 should be a version 1', async () => {
3630
const mockUsbDevice = new MockUSBDevice().withSerialNumber('9900serno').build();
3731
const connection = new TestableMicrobitUSB(mockUsbDevice);

src/__tests__/serialProtocol.test.ts

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -184,30 +184,22 @@ describe('processResponseMessage', () => {
184184
it('processes valid Handshake responses', () => {
185185
const message1 = 'R[0]HS[1]';
186186
const message2 = 'R[1122aabb]HS[255]';
187-
const message3 = 'R[FFFFFFFF]HS[-1000]';
188187

189188
const got1 = processResponseMessage(message1);
190189
const got2 = processResponseMessage(message2);
191-
const got3 = processResponseMessage(message3);
192190

193191
expect(got1).toEqual({
194192
message: message1,
195193
messageId: 0,
196-
cmdType: 'HS',
194+
type: 'HS',
197195
value: 1,
198196
});
199197
expect(got2).toEqual({
200198
message: message2,
201199
messageId: 0x1122aabb,
202-
cmdType: 'HS',
200+
type: 'HS',
203201
value: 255,
204202
});
205-
expect(got3).toEqual({
206-
message: message3,
207-
messageId: 0xffffffff,
208-
cmdType: 'HS',
209-
value: -1000,
210-
});
211203
});
212204

213205
it('processes valid Radio Frequency response', () => {
@@ -218,11 +210,24 @@ describe('processResponseMessage', () => {
218210
expect(got).toEqual({
219211
message: message,
220212
messageId: 0x1234,
221-
cmdType: 'RF',
213+
type: 'RF',
222214
value: 42,
223215
});
224216
});
225217

218+
it('processes valid Remote micro:bit ID response', () => {
219+
const message = 'R[1234]RMBID[4294967295]';
220+
221+
const got = processResponseMessage(message);
222+
223+
expect(got).toEqual({
224+
message: message,
225+
messageId: 0x1234,
226+
type: 'RMBID',
227+
value: 4294967295,
228+
});
229+
});
230+
226231
it('processes valid Software Versions responses', () => {
227232
const message1 = 'R[1234]SWVER[0.0.0]';
228233
const message2 = 'R[1234]SWVER[99.99.99]';
@@ -235,19 +240,19 @@ describe('processResponseMessage', () => {
235240
expect(got1).toEqual({
236241
message: message1,
237242
messageId: 0x1234,
238-
cmdType: 'SWVER',
243+
type: 'SWVER',
239244
value: '0.0.0',
240245
});
241246
expect(got2).toEqual({
242247
message: message2,
243248
messageId: 0x1234,
244-
cmdType: 'SWVER',
249+
type: 'SWVER',
245250
value: '99.99.99',
246251
});
247252
expect(got3).toEqual({
248253
message: message3,
249254
messageId: 0x1234,
250-
cmdType: 'SWVER',
255+
type: 'SWVER',
251256
value: '1.2.3',
252257
});
253258
});
@@ -262,13 +267,13 @@ describe('processResponseMessage', () => {
262267
expect(got1).toEqual({
263268
message: message1,
264269
messageId: 0x1234,
265-
cmdType: 'HWVER',
270+
type: 'HWVER',
266271
value: 0,
267272
});
268273
expect(got2).toEqual({
269274
message: message2,
270275
messageId: 0x1234,
271-
cmdType: 'HWVER',
276+
type: 'HWVER',
272277
value: 9999,
273278
});
274279
});
@@ -281,7 +286,7 @@ describe('processResponseMessage', () => {
281286
expect(got).toEqual({
282287
message: message,
283288
messageId: 0x1234,
284-
cmdType: 'ZSTART',
289+
type: 'ZSTART',
285290
value: '',
286291
});
287292
});
@@ -294,11 +299,24 @@ describe('processResponseMessage', () => {
294299
expect(got).toEqual({
295300
message: message,
296301
messageId: 0x1234,
297-
cmdType: 'STOP',
302+
type: 'STOP',
298303
value: '',
299304
});
300305
});
301306

307+
it('processes valid Error response', () => {
308+
const message = 'R[1234]ERROR[1]';
309+
310+
const got = processResponseMessage(message);
311+
312+
expect(got).toEqual({
313+
message: message,
314+
messageId: 0x1234,
315+
type: 'ERROR',
316+
value: 1,
317+
});
318+
});
319+
302320
it('throws away messages that are not a response', () => {
303321
// First a valid response to stablish a baseline
304322
expect(processResponseMessage('R[0]STOP[]')).not.toBeUndefined();
@@ -326,8 +344,10 @@ describe('processResponseMessage', () => {
326344
it('throws away response messages with invalid number values', () => {
327345
// First valid messages to stablish a baseline
328346
expect(processResponseMessage('R[0]RF[10000]')).not.toBeUndefined();
329-
expect(processResponseMessage('R[0]RF[-1]')).not.toBeUndefined();
347+
expect(processResponseMessage('R[0]RMBID[4294967295]')).not.toBeUndefined();
330348
// Now invalid values
349+
expect(processResponseMessage('R[0]RMBID[4294967296]')).toBeUndefined();
350+
expect(processResponseMessage('R[0]RF[-1]')).toBeUndefined();
331351
expect(processResponseMessage('R[0]RF[1-2]')).toBeUndefined();
332352
expect(processResponseMessage('R[0]RF[1,2]')).toBeUndefined();
333353
expect(processResponseMessage('R[0]RF[1F]')).toBeUndefined();

src/components/connection-prompt/ConnectDialogContainer.svelte

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,41 @@
55
-->
66

77
<script lang="ts">
8-
import StandardDialog from '../dialogs/StandardDialog.svelte';
9-
import StartRadioDialog from './radio/StartRadioDialog.svelte';
10-
import StartBluetoothDialog from './bluetooth/StartBluetoothDialog.svelte';
11-
import ConnectCableDialog from './bluetooth/ConnectCableDialog.svelte';
12-
import SelectMicrobitDialogUsb from './usb/SelectMicrobitDialogUsb.svelte';
13-
import ConnectBatteryDialog from './bluetooth/ConnectBatteryDialog.svelte';
14-
import BluetoothConnectDialog from './bluetooth/BluetoothConnectDialog.svelte';
15-
import DownloadingDialog from './usb/DownloadingDialog.svelte';
16-
import ManualInstallTutorial from './usb/manual/ManualInstallTutorial.svelte';
17-
import {
18-
ConnectDialogStates,
19-
connectionDialogState,
20-
} from '../../script/stores/connectDialogStore';
21-
import { btPatternInput } from '../../script/stores/connectionStore';
22-
import MBSpecs from '../../script/microbit-interfacing/MBSpecs';
23-
import BrokenFirmwareDetected from './usb/BrokenFirmwareDetected.svelte';
24-
import BluetoothConnectingDialog from './bluetooth/BluetoothConnectingDialog.svelte';
25-
import SelectMicrobitDialogBluetooth from './bluetooth/SelectMicrobitDialogBluetooth.svelte';
26-
import MicrobitWearingInstructionDialog from './MicrobitWearingInstructionDialog.svelte';
27-
import WebUsbTryAgain, { USBTryAgainType } from './WebUsbTryAgain.svelte';
288
import { onDestroy, onMount } from 'svelte';
29-
import { get, Unsubscriber } from 'svelte/store';
30-
import { compatibility, state } from '../../script/stores/uiStore';
9+
import { Unsubscriber, get } from 'svelte/store';
3110
import { isDevMode } from '../../script/environment';
3211
import { flags } from '../../script/flags';
33-
import ConnectingMicrobits from './radio/ConnectingMicrobits.svelte';
12+
import MBSpecs from '../../script/microbit-interfacing/MBSpecs';
13+
import MicrobitUSB from '../../script/microbit-interfacing/MicrobitUSB';
3414
import Microbits, {
3515
FlashStage,
3616
HexType,
3717
} from '../../script/microbit-interfacing/Microbits';
38-
import MicrobitUSB from '../../script/microbit-interfacing/MicrobitUSB';
18+
import {
19+
ConnectDialogStates,
20+
connectionDialogState,
21+
} from '../../script/stores/connectDialogStore';
22+
import {
23+
btPatternInput,
24+
radioBridgeRemoteDeviceId,
25+
} from '../../script/stores/connectionStore';
26+
import { compatibility, state } from '../../script/stores/uiStore';
27+
import StandardDialog from '../dialogs/StandardDialog.svelte';
28+
import MicrobitWearingInstructionDialog from './MicrobitWearingInstructionDialog.svelte';
3929
import WebBluetoothTryAgain from './WebBluetoothTryAgain.svelte';
30+
import WebUsbTryAgain, { USBTryAgainType } from './WebUsbTryAgain.svelte';
31+
import BluetoothConnectDialog from './bluetooth/BluetoothConnectDialog.svelte';
32+
import BluetoothConnectingDialog from './bluetooth/BluetoothConnectingDialog.svelte';
33+
import ConnectBatteryDialog from './bluetooth/ConnectBatteryDialog.svelte';
34+
import ConnectCableDialog from './bluetooth/ConnectCableDialog.svelte';
35+
import SelectMicrobitDialogBluetooth from './bluetooth/SelectMicrobitDialogBluetooth.svelte';
36+
import StartBluetoothDialog from './bluetooth/StartBluetoothDialog.svelte';
37+
import ConnectingMicrobits from './radio/ConnectingMicrobits.svelte';
38+
import StartRadioDialog from './radio/StartRadioDialog.svelte';
39+
import BrokenFirmwareDetected from './usb/BrokenFirmwareDetected.svelte';
40+
import DownloadingDialog from './usb/DownloadingDialog.svelte';
41+
import SelectMicrobitDialogUsb from './usb/SelectMicrobitDialogUsb.svelte';
42+
import ManualInstallTutorial from './usb/manual/ManualInstallTutorial.svelte';
4043
4144
const { bluetooth, usb } = get(compatibility);
4245
let endOfFlow = false;
@@ -113,7 +116,7 @@
113116
114117
async function flashMicrobit(usb: MicrobitUSB): Promise<void> {
115118
try {
116-
const name = await usb.getFriendlyName();
119+
const deviceId = await usb.getDeviceId();
117120
const hexForStage = stageToHex(flashStage);
118121
await usb.flashHex(hexForStage, progress => {
119122
// Flash hex
@@ -125,11 +128,20 @@
125128
}
126129
flashProgress = progress;
127130
});
128-
// Finished flashing successfully
131+
132+
// Store radio/bluetooth details. Radio is essential to pass to micro:bit 2.
133+
// Bluetooth saves the user from entering the pattern.
134+
if (flashStage === 'bluetooth') {
135+
$btPatternInput = MBSpecs.Utility.nameToPattern(
136+
MBSpecs.Utility.serialNumberToName(deviceId),
137+
);
138+
}
139+
if (flashStage === 'radio-remote') {
140+
$radioBridgeRemoteDeviceId = deviceId;
141+
}
142+
143+
// Next UI state:
129144
if (flashStage === 'bluetooth' || flashStage === 'radio-remote') {
130-
if (flashStage === 'bluetooth') {
131-
$btPatternInput = MBSpecs.Utility.nameToPattern(name);
132-
}
133145
$connectionDialogState.connectionState = ConnectDialogStates.CONNECT_BATTERY;
134146
} else if (flashStage === 'radio-bridge') {
135147
onConnectingSerial(usb);
@@ -157,7 +169,10 @@
157169
158170
async function onConnectingSerial(usb: MicrobitUSB): Promise<void> {
159171
$connectionDialogState.connectionState = ConnectDialogStates.CONNECTING_MICROBITS;
160-
await Microbits.assignSerialInput(usb);
172+
if ($radioBridgeRemoteDeviceId === -1) {
173+
throw new Error('Radio bridge device id not set');
174+
}
175+
await Microbits.assignSerialInput(usb, $radioBridgeRemoteDeviceId);
161176
endFlow();
162177
}
163178

src/script/microbit-interfacing/MicrobitSerial.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,14 @@ export class MicrobitSerial implements MicrobitConnection {
2929
// To avoid concurrent connect attempts
3030
private isConnecting: boolean = false;
3131

32-
// TODO: The radio frequency should be randomly generated once per session.
33-
// If we want a session to be restored (e.g. from local storage) and
34-
// the previously flashed micro:bits to continue working without
35-
// reflashing we need to store and retrieve this value somehow.
36-
// FIXME: Setting this to the hex files default value for now, as we need
37-
// to configure the radio frequency for both micro:bits after they
38-
// are flashed, not just the radio bridge.
39-
private sessionRadioFrequency = 42;
4032
private connectionCheckIntervalId: ReturnType<typeof setInterval> | undefined;
4133
private lastReceivedMessageTimestamp: number | undefined;
4234
private isReconnect: boolean = false;
4335

44-
constructor(private usb: MicrobitUSB) {}
36+
constructor(
37+
private usb: MicrobitUSB,
38+
private remoteDeviceId: number,
39+
) {}
4540

4641
async connect(...states: DeviceRequestStates[]): Promise<void> {
4742
logMessage('Serial connect', states);
@@ -55,7 +50,7 @@ export class MicrobitSerial implements MicrobitConnection {
5550
let onPeriodicMessageRecieved: (() => void) | undefined;
5651

5752
const handleError = (e: unknown) => {
58-
console.error(e);
53+
logError('Serial error', e);
5954
void this.disconnectInternal(false, 'bridge');
6055
};
6156
const processMessage = (data: string) => {
@@ -122,14 +117,15 @@ export class MicrobitSerial implements MicrobitConnection {
122117
}, 1000);
123118
}
124119

125-
// Set the radio frequency to a value unique to this session
126-
const radioFreqCommand = protocol.generateCmdRadioFrequency(
127-
this.sessionRadioFrequency,
128-
);
129-
const radioFreqResponse = await this.sendCmdWaitResponse(radioFreqCommand);
130-
if (radioFreqResponse.value !== this.sessionRadioFrequency) {
120+
logMessage(`Serial: using remote device id ${this.remoteDeviceId}`);
121+
const remoteMbIdCommand = protocol.generateCmdRemoteMbId(this.remoteDeviceId);
122+
const remoteMbIdResponse = await this.sendCmdWaitResponse(remoteMbIdCommand);
123+
if (
124+
remoteMbIdResponse.type === protocol.ResponseTypes.Error ||
125+
remoteMbIdResponse.value !== this.remoteDeviceId
126+
) {
131127
throw new Error(
132-
`Failed to set radio frequency. Expected ${this.sessionRadioFrequency}, got ${radioFreqResponse.value}`,
128+
`Failed to set remote micro:bit ID. Expected ${this.remoteDeviceId}, got ${remoteMbIdResponse.value}`,
133129
);
134130
}
135131

@@ -140,15 +136,21 @@ export class MicrobitSerial implements MicrobitConnection {
140136
accelerometer: true,
141137
buttons: true,
142138
});
143-
await this.usb.serialWrite(startCmd.message);
144139
const periodicMessagePromise = new Promise<void>((resolve, reject) => {
145140
onPeriodicMessageRecieved = resolve;
146141
setTimeout(() => {
147142
onPeriodicMessageRecieved = undefined;
148143
reject(new Error('Failed to receive data from remote micro:bit'));
149144
}, 500);
150145
});
151-
await this.sendCmdWaitResponse(startCmd);
146+
147+
const startCmdResponse = await this.sendCmdWaitResponse(startCmd);
148+
if (startCmdResponse.type === protocol.ResponseTypes.Error) {
149+
throw new Error(
150+
`Failed to start streaming sensors data. Error response received: ${startCmdResponse.message}`,
151+
);
152+
}
153+
152154
if (this.isReconnect) {
153155
await periodicMessagePromise;
154156
} else {
@@ -246,6 +248,7 @@ export class MicrobitSerial implements MicrobitConnection {
246248
// As a workaround we can spam the micro:bit with handshake messages until
247249
// enough responses have been queued in the buffer to fill it and the data
248250
// starts to flow.
251+
logMessage('Serial handshake');
249252
const handshakeResult = await new Promise<protocol.MessageResponse>(
250253
async (resolve, reject) => {
251254
const attempts = 20;
@@ -284,9 +287,10 @@ export class MicrobitSerial implements MicrobitConnection {
284287
export const startSerialConnection = async (
285288
usb: MicrobitUSB,
286289
requestState: DeviceRequestStates,
290+
remoteDeviceId: number,
287291
): Promise<MicrobitSerial | undefined> => {
288292
try {
289-
const serial = new MicrobitSerial(usb);
293+
const serial = new MicrobitSerial(usb, remoteDeviceId);
290294
await serial.connect(requestState);
291295
return serial;
292296
} catch (e) {

0 commit comments

Comments
 (0)