Skip to content

Commit b5d7c3b

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-61: Implemented burn bootloader.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
1 parent 525e688 commit b5d7c3b

File tree

6 files changed

+145
-6
lines changed

6 files changed

+145
-6
lines changed

arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/l
117117
import { EditorWidgetFactory } from './theia/editor/editor-widget-factory';
118118
import { OutputWidget as TheiaOutputWidget } from '@theia/output/lib/browser/output-widget';
119119
import { OutputWidget } from './theia/output/output-widget';
120+
import { BurnBootloader } from './contributions/burn-bootloader';
120121

121122
const ElementQueries = require('css-element-queries/src/ElementQueries');
122123

@@ -358,4 +359,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
358359
Contribution.configure(bind, QuitApp);
359360
Contribution.configure(bind, SketchControl);
360361
Contribution.configure(bind, Settings);
362+
Contribution.configure(bind, BurnBootloader);
361363
});
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { inject, injectable } from 'inversify';
2+
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
3+
import { CoreService } from '../../common/protocol';
4+
import { ArduinoMenus } from '../menu/arduino-menus';
5+
import { BoardsDataStore } from '../boards/boards-data-store';
6+
import { MonitorConnection } from '../monitor/monitor-connection';
7+
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
8+
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution';
9+
10+
@injectable()
11+
export class BurnBootloader extends SketchContribution {
12+
13+
@inject(CoreService)
14+
protected readonly coreService: CoreService;
15+
16+
@inject(MonitorConnection)
17+
protected readonly monitorConnection: MonitorConnection;
18+
19+
@inject(BoardsDataStore)
20+
protected readonly boardsDataStore: BoardsDataStore;
21+
22+
@inject(BoardsServiceClientImpl)
23+
protected readonly boardsServiceClientImpl: BoardsServiceClientImpl;
24+
25+
@inject(OutputChannelManager)
26+
protected readonly outputChannelManager: OutputChannelManager;
27+
28+
registerCommands(registry: CommandRegistry): void {
29+
registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, {
30+
execute: () => this.burnBootloader()
31+
});
32+
}
33+
34+
registerMenus(registry: MenuModelRegistry): void {
35+
registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, {
36+
commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id,
37+
label: 'Burn Bootloader',
38+
order: 'z99'
39+
});
40+
}
41+
42+
async burnBootloader(): Promise<void> {
43+
const monitorConfig = this.monitorConnection.monitorConfig;
44+
if (monitorConfig) {
45+
await this.monitorConnection.disconnect();
46+
}
47+
try {
48+
const { boardsConfig } = this.boardsServiceClientImpl;
49+
if (!boardsConfig || !boardsConfig.selectedBoard) {
50+
throw new Error('No boards selected. Please select a board.');
51+
}
52+
if (!boardsConfig.selectedBoard.fqbn) {
53+
throw new Error(`No core is installed for the '${boardsConfig.selectedBoard.name}' board. Please install the core.`);
54+
}
55+
const { selectedPort } = boardsConfig;
56+
if (!selectedPort) {
57+
throw new Error('No ports selected. Please select a port.');
58+
}
59+
60+
const port = selectedPort.address;
61+
const [fqbn, { selectedProgrammer: programmer }] = await Promise.all([
62+
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard.fqbn),
63+
this.boardsDataStore.getData(boardsConfig.selectedBoard.fqbn)
64+
]);
65+
66+
if (!programmer) {
67+
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
68+
}
69+
70+
this.outputChannelManager.getChannel('Arduino: bootloader').clear();
71+
await this.coreService.burnBootloader({
72+
fqbn,
73+
programmer,
74+
port
75+
});
76+
this.messageService.info('Done burning bootloader.', { timeout: 1000 });
77+
} catch (e) {
78+
this.messageService.error(e.toString());
79+
} finally {
80+
if (monitorConfig) {
81+
await this.monitorConnection.connect(monitorConfig);
82+
}
83+
}
84+
}
85+
86+
}
87+
88+
export namespace BurnBootloader {
89+
export namespace Commands {
90+
export const BURN_BOOTLOADER: Command = {
91+
id: 'arduino-burn-bootloader'
92+
};
93+
}
94+
}

arduino-ide-extension/src/browser/contributions/upload-sketch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class UploadSketch extends SketchContribution {
103103
if (usingProgrammer) {
104104
const programmer = selectedProgrammer;
105105
if (!programmer) {
106-
throw new Error('Programmer is not selected. Please select a programmer.');
106+
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
107107
}
108108
let port: undefined | string = undefined;
109109
// If the port is set by the user, we pass it to the CLI as it might be required.

arduino-ide-extension/src/browser/menu/arduino-menus.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export namespace ArduinoMenus {
3030
export const TOOLS = [...MAIN_MENU_BAR, '4_tools'];
3131
// `Auto Format`, `Library Manager...`, `Boards Manager...`
3232
export const TOOLS__MAIN_GROUP = [...TOOLS, '0_main'];
33-
// Core settings, such as `Processor` and `Programmers` for the board.
33+
// Core settings, such as `Processor` and `Programmers` for the board and `Burn Bootloader`
3434
export const TOOLS__BOARD_SETTINGS_GROUP = [...TOOLS, '1_board_settings'];
3535

3636
// Context menu

arduino-ide-extension/src/common/protocol/core-service.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const CoreService = Symbol('CoreService');
1111
export interface CoreService extends JsonRpcServer<CoreServiceClient> {
1212
compile(options: CoreService.Compile.Options): Promise<void>;
1313
upload(options: CoreService.Upload.Options): Promise<void>;
14+
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
1415
}
1516

1617
export namespace CoreService {
@@ -29,4 +30,12 @@ export namespace CoreService {
2930
Compile.Options & Readonly<{ programmer: Programmer, port?: string }>;
3031
}
3132

33+
export namespace Bootloader {
34+
export interface Options {
35+
readonly fqbn: string;
36+
readonly programmer: Programmer;
37+
readonly port: string;
38+
}
39+
}
40+
3241
}

arduino-ide-extension/src/node/core-service-impl.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { BoardsService } from '../common/protocol/boards-service';
66
import { CoreClientProvider } from './core-client-provider';
77
import * as path from 'path';
88
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
9-
import { UploadReq, UploadResp } from './cli-protocol/commands/upload_pb';
9+
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp } from './cli-protocol/commands/upload_pb';
1010

1111
@injectable()
1212
export class CoreServiceImpl implements CoreService {
@@ -113,9 +113,9 @@ export class CoreServiceImpl implements CoreService {
113113

114114
try {
115115
await new Promise<void>((resolve, reject) => {
116-
result.on('data', (cr: UploadResp) => {
117-
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
118-
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
116+
result.on('data', (resp: UploadResp) => {
117+
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
118+
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
119119
});
120120
result.on('error', error => reject(error));
121121
result.on('end', () => resolve());
@@ -127,6 +127,40 @@ export class CoreServiceImpl implements CoreService {
127127
}
128128
}
129129

130+
async burnBootloader(options: CoreService.Bootloader.Options): Promise<void> {
131+
const coreClient = await this.coreClientProvider.client();
132+
if (!coreClient) {
133+
return;
134+
}
135+
const { fqbn, port, programmer } = options;
136+
if (!fqbn) {
137+
throw new Error('The selected board has no FQBN.');
138+
}
139+
if (!port) {
140+
throw new Error('Port must be specified.');
141+
}
142+
const { client, instance } = coreClient;
143+
const req = new BurnBootloaderReq();
144+
req.setFqbn(fqbn);
145+
req.setPort(port);
146+
req.setProgrammer(programmer.id);
147+
req.setInstance(instance);
148+
const result = client.burnBootloader(req);
149+
try {
150+
await new Promise<void>((resolve, reject) => {
151+
result.on('data', (resp: BurnBootloaderResp) => {
152+
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
153+
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
154+
});
155+
result.on('error', error => reject(error));
156+
result.on('end', () => resolve());
157+
});
158+
} catch (e) {
159+
this.toolOutputService.append({ tool: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
160+
throw e;
161+
}
162+
}
163+
130164
setClient(client: CoreServiceClient | undefined): void {
131165
this.client = client;
132166
}

0 commit comments

Comments
 (0)