Skip to content

Commit 85b144d

Browse files
author
Stefania
authored
Merge pull request #151 from arduino/firmware-updater
update firmware of wifi101 and wifiNINA to latest stable version
2 parents 2d91260 + dbdcdfa commit 85b144d

File tree

4 files changed

+273
-1
lines changed

4 files changed

+273
-1
lines changed

demo/app.jsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import React from 'react';
2222
import Daemon from '../src';
23+
import FirmwareUpdater from '../src/firmware-updater';
2324

2425
import { HEX } from './serial_mirror';
2526
import V2 from './v2/v2.jsx';
@@ -33,6 +34,7 @@ const scrollToBottom = (target) => {
3334
};
3435

3536
const daemon = new Daemon('https://builder.arduino.cc/v3/boards', chromeExtensionID);
37+
const firmwareUpdater = new FirmwareUpdater(daemon);
3638

3739
const handleUpload = () => {
3840
const target = {
@@ -50,6 +52,22 @@ const handleBootloaderMode = (e, port) => {
5052
daemon.setBootloaderMode(port);
5153
};
5254

55+
const handleUpdateFirmware = (e, boardId, port, wifiModule) => {
56+
e.preventDefault();
57+
if (![firmwareUpdater.updateStatusEnum.NOPE, firmwareUpdater.updateStatusEnum.DONE, firmwareUpdater.updateStatusEnum.ERROR].includes(firmwareUpdater.updating.getValue().status)) {
58+
return;
59+
}
60+
firmwareUpdater.updateFirmware(boardId, port, wifiModule);
61+
firmwareUpdater.updatingDone.subscribe(() => {
62+
console.log('Firmware updated successfully!');
63+
});
64+
65+
firmwareUpdater.updatingError.subscribe(update => {
66+
console.log('Something went wrong when trying to update the firmware');
67+
console.error(update.err);
68+
});
69+
};
70+
5371
const handleDownloadTool = e => {
5472
e.preventDefault();
5573
const toolname = document.getElementById('toolname');
@@ -179,6 +197,10 @@ class App extends React.Component {
179197
close
180198
</a> - <a href="#" onClick={(e) => handleBootloaderMode(e, device.Name)}>
181199
bootloader mode
200+
</a> - <a href="#" onClick={(e) => handleUpdateFirmware(e, 'mkrwifi1010', device.Name, 'wifiNina')}>
201+
Wifi NINA update firmware
202+
</a> - <a href="#" onClick={(e) => handleUpdateFirmware(e, 'mkr1000', device.Name, 'wifi101')}>
203+
Wifi 101 update firmware
182204
</a>
183205
</li>);
184206

src/firmware-updater.js

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import {BehaviorSubject} from 'rxjs';
2+
3+
import {
4+
takeUntil,
5+
filter,
6+
first
7+
} from 'rxjs/operators';
8+
9+
const versionsMap = {
10+
wifi101: {
11+
latestVersion: 'WINC1500 19.5.4',
12+
loaderPath: null,
13+
latestVersionPath: null
14+
},
15+
wifiNina: {
16+
latestVersion: 'NINA 1.2.2',
17+
loaderPath: null,
18+
latestVersionPath: null
19+
}
20+
};
21+
22+
/* The status of the Firmware Updater Tool */
23+
const FWUToolStatusEnum = Object.freeze({
24+
NOPE: 'NOPE',
25+
OK: 'OK',
26+
CHECKING: 'CHECKING',
27+
ERROR: 'ERROR DOWNLOADING TOOL'
28+
});
29+
30+
/* The signatures needed to run the commands to use the Firmware Updater Tool */
31+
const signaturesEnum = Object.freeze({
32+
GET_FIRMWARE_INFO: 'aceffd98d331df0daa5bb3308bb49a95767d77e7a1557c07a0ec544d2f41c3ec67269f01ce9a63e01f3b43e087ab8eb22b7f1d34135b6686e8ce27d4b5dc083ec8e6149df11880d32486448a71280ef3128efccbd45a84dbd7990a9420a65ee86b3822edba3554fa8e6ca11aec12d4dd99ad072285b98bfdf7b2b64f677da50feb8bddef25a36f52d7605078487d8a5d7cbdc84bfa65d510cee97b46baefea149139a9a6ed4b545346040536e33d850e6ad84c83fe605f677e2ca77439de3fa42350ce504ad9a49cf62c6751d4c2a284500d2c628cd52cd73b4c3e7ef08ae823eb8941383f9c6ff0686da532369d3b266ded8fdd33cca1a128068a4795920f25',
33+
UPLOAD_FIRMWARE_BOSSAC: 'b2137435f74601a0b1b090ca1592b14199c1c79c87cecac4bb168456b570f17b635f438f5cd80649a101ab27085394e4a0e92f41a2b1b65932789eee8c3ebf39633a503a8a53d3291944afe10ff88f6ea06b0fd7fd7de6d70b302df3a92091a2abe2691ad1aeee051e4bb69f6afa3bd41e643855769347dca018804ae97bcb1331df796fd8921a11b333b45bebe430d323ddd151a907e8fb0e875a45d093cdb4332ec3dbd72a50341b538e058b3f25d4a528bea514b96b8e0701032d64232b7141c62f2231352f4197a1011292a4c3e900c133824e148f3703ea8374873d9097578146819e62685f1a8cbc442dd435ea603136c86ecf028df39fd10a8b3499c6',
34+
UPLOAD_FIRMWARE_AVRDUDE: '68ed3d9abdc7c77d01223d09c7ae55b08b8ff94f2a42d21a672effb4bdeeb12b10177e831805b3037d9f8d38e8eeeb8327b6c4691731a2a146cfd12398e5a12596e097160ae8d84fc488650ee57439f7fcd83f27d01e9834de555ceec7dc6951b3be5e5a3507752dce8e4ba1f6f9e6494162d537009db899882b9c4fca0868ad446b82fdeabf93bc30c5a39f8fe9c25e799842a10f4171e2896a0e19667b34258f06494663a4a102bcf9fd61d1ff8ebec18204bbd1a66de3e0b53e257ce521b41999dc82428539086ae9a4a5ea7112d87c05a2782cdace0e6576b162294f9ba47c658cc40999ba31be8a129689a703f6c4182055a600b6e41d450fad2896180d'
35+
});
36+
37+
38+
export default class FirmwareUpdater {
39+
constructor(Daemon) {
40+
this.updateStatusEnum = Object.freeze({
41+
NOPE: 'NOPE',
42+
STARTED: 'STARTED',
43+
GETTING_INFO: 'GETTING_INFO',
44+
GOT_INFO: 'GOT_INFO',
45+
UPLOADING: 'UPLOADING',
46+
DONE: 'DONE',
47+
ERROR: 'ERROR'
48+
});
49+
50+
this.Daemon = Daemon;
51+
this.Daemon.downloadingDone.subscribe(() => {
52+
this.FWUToolStatus = FWUToolStatusEnum.OK;
53+
});
54+
55+
this.FWUToolStatus = FWUToolStatusEnum.NOPE;
56+
this.updating = new BehaviorSubject({ status: this.updateStatusEnum.NOPE });
57+
58+
this.updatingDone = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.DONE))
59+
.pipe(first())
60+
.pipe(takeUntil(this.updating.pipe(filter(update => update.status === this.updateStatusEnum.ERROR))));
61+
62+
this.updatingError = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.ERROR))
63+
.pipe(first())
64+
.pipe(takeUntil(this.updatingDone));
65+
66+
this.gotFWInfo = this.updating.pipe(filter(update => update.status === this.updateStatusEnum.GOT_INFO))
67+
.pipe(first())
68+
.pipe(takeUntil(this.updatingDone))
69+
.pipe(takeUntil(this.updatingError));
70+
}
71+
72+
getFirmwareInfo(boardId, port, wifiModule) {
73+
this.updating.next({ status: this.updateStatusEnum.GETTING_INFO });
74+
let versionsList = [];
75+
let firmwareInfoMessagesSubscription;
76+
77+
const handleFirmwareInfoMessage = message => {
78+
switch (message.ProgrammerStatus) {
79+
case 'Starting':
80+
break;
81+
case 'Busy':
82+
if (message.Msg.indexOf('Flashing with command:') >= 0) {
83+
return;
84+
}
85+
const versions = JSON.parse(message.Msg);
86+
Object.keys(versions).forEach(v => {
87+
if (versions[v][0].IsLoader) {
88+
versionsMap[wifiModule].loaderPath = versions[v][0].Path;
89+
}
90+
else {
91+
versionsList = [...versionsList, ...versions[v]];
92+
}
93+
});
94+
const latestVersion = versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion)
95+
versionsMap[wifiModule].latestVersionPath = latestVersion.Path;
96+
break;
97+
case 'Error':
98+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Couldn't get firmware info: ${message}` })
99+
firmwareInfoMessagesSubscription.unsubscribe();
100+
break;
101+
case 'Done':
102+
firmwareInfoMessagesSubscription.unsubscribe();
103+
this.updating.next({ status: this.updateStatusEnum.GOT_INFO, versionsList })
104+
break;
105+
default:
106+
break;
107+
}
108+
}
109+
110+
if (this.FWUToolStatus !== FWUToolStatusEnum.OK) {
111+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't get firmware info: couldn't find firmware updater tool` });
112+
return;
113+
}
114+
115+
firmwareInfoMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
116+
if (message.ProgrammerStatus) {
117+
handleFirmwareInfoMessage(message);
118+
}
119+
});
120+
const data = {
121+
board: boardId,
122+
port,
123+
commandline: `"{runtime.tools.fwupdater.path}/updater" -get_available_for {network.password}`,
124+
signature: signaturesEnum.GET_FIRMWARE_INFO,
125+
extra: {
126+
auth: {
127+
password: boardId
128+
}
129+
},
130+
filename: 'ListFirmwareVersionsInfo.bin'
131+
};
132+
133+
return fetch(`${this.Daemon.pluginURL}/upload`, {
134+
method: 'POST',
135+
headers: {
136+
'Content-Type': 'text/plain; charset=utf-8'
137+
},
138+
body: JSON.stringify(data)
139+
}).then(response => {
140+
if (!response.ok) {
141+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Error fetching ${this.Daemon.pluginURL}/upload` });
142+
return;
143+
}
144+
}).catch(reason => {
145+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Coudln't list firmware versions info.` });
146+
return ;
147+
});
148+
}
149+
150+
/*
151+
wifiModule can be either 'wifiNina' or 'wifi101'
152+
*/
153+
updateFirmware(boardId, port, wifiModule) {
154+
this.updating.next({ status: this.updateStatusEnum.STARTED });
155+
if (!port) {
156+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: no port selected.` });
157+
return;
158+
}
159+
this.gotFWInfo.subscribe(state => {
160+
if (!versionsMap[wifiModule] && !versionsMap[wifiModule].latestVersion) {
161+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: couldn't find module '${wifiModule}'` });
162+
return;
163+
}
164+
const latestVersion = state.versionsList.find(version => version.Name === versionsMap[wifiModule].latestVersion);
165+
if (!latestVersion) {
166+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: couldn't find version '${versionsMap[wifiModule].latestVersion}' for module '${versionsMap[wifiModule]}'` });
167+
return;
168+
}
169+
170+
let updateFirmwareMessagesSubscription;
171+
172+
const handleFirmwareUpdateMessage = message => {
173+
switch (message.ProgrammerStatus) {
174+
case 'Error':
175+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: ${message.Msg}`});
176+
updateFirmwareMessagesSubscription.unsubscribe();
177+
break;
178+
case 'Done':
179+
this.updating.next({ status: this.updateStatusEnum.DONE});
180+
updateFirmwareMessagesSubscription.unsubscribe();
181+
break;
182+
default:
183+
break;
184+
}
185+
}
186+
187+
updateFirmwareMessagesSubscription = this.Daemon.appMessages.subscribe(message => {
188+
if (message.ProgrammerStatus) {
189+
handleFirmwareUpdateMessage(message);
190+
}
191+
});
192+
193+
let addresses = '';
194+
const rootCertificates = [{
195+
domain: 'arduino.cc',
196+
port: 443
197+
}];
198+
199+
rootCertificates.forEach(address => {
200+
if (address.domain && address.port) {
201+
addresses += `-address ${address.domain}:${address.port} `;
202+
}
203+
});
204+
205+
const isUsingAvrdude = boardId === 'uno2018';
206+
const programmer = isUsingAvrdude ? '{runtime.tools.avrdude}/bin/avrdude' : '{runtime.tools.bossac}/bossac';
207+
208+
const loaderPath = versionsMap[wifiModule].loaderPath;
209+
if (!loaderPath) {
210+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: invalid loaderPath` });
211+
return;
212+
}
213+
214+
const data = {
215+
board: boardId,
216+
port,
217+
commandline: `"{runtime.tools.fwupdater.path}/updater" -flasher {network.password} -firmware {upload.verbose} -port {serial.port} -restore_binary "{build.path}/{build.project_name}.bin" -programmer ${programmer}`,
218+
hex: '',
219+
extra: {
220+
auth: {
221+
password: loaderPath
222+
},
223+
verbose: true,
224+
params_verbose: `${versionsMap[wifiModule].latestVersionPath} ${addresses}` // eslint-disable-line camelcase
225+
},
226+
signature: isUsingAvrdude ? signaturesEnum.UPLOAD_FIRMWARE_AVRDUDE : signaturesEnum.UPLOAD_FIRMWARE_BOSSAC,
227+
filename: 'CheckFirmwareVersion.bin'
228+
};
229+
230+
fetch(`${this.Daemon.pluginURL}/upload`, {
231+
method: 'POST',
232+
headers: {
233+
'Content-Type': 'text/plain; charset=utf-8'
234+
},
235+
body: JSON.stringify(data)
236+
}).then(response => {
237+
if (!response.ok) {
238+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: error fetching ${this.Daemon.pluginURL}/upload` });
239+
return;
240+
}
241+
}).catch(reason => {
242+
this.updating.next({ status: this.updateStatusEnum.ERROR, err: `Can't update Firmware: ${reason}` });
243+
return;
244+
});
245+
});
246+
this.getFirmwareInfo(boardId, port, wifiModule);
247+
}
248+
}

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
import SocketDaemon from './socket-daemon';
2222
import ChromeOsDaemon from './chrome-app-daemon';
23-
23+
import FirmwareUpdater from './firmware-updater';
2424

2525
const Daemon = window.navigator.userAgent.indexOf(' CrOS ') !== -1 ? ChromeOsDaemon : SocketDaemon;
2626

2727
export default Daemon;
28+
export {FirmwareUpdater};

src/socket-daemon.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ export default class SocketDaemon extends Daemon {
209209
if (!driversRequested) {
210210
this.downloadTool('windows-drivers', 'latest', 'arduino');
211211
this.downloadTool('bossac', '1.7.0', 'arduino');
212+
this.downloadTool('fwupdater', 'latest', 'arduino');
212213
driversRequested = false;
213214
}
214215

0 commit comments

Comments
 (0)