-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEspUpdater.js
129 lines (115 loc) · 3.7 KB
/
EspUpdater.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
export default class ESPUpdater {
constructor(port, logger) {
this.port = port;
this.logger = logger;
this.reader = null;
this.writer = null;
this.inputBuffer = [];
this.flashWriteSize = 4096; // Default for ESP
}
async connect() {
this.reader = this.port.readable.getReader();
this.writer = this.port.writable.getWriter();
this.logger.log("Connected to ESP device.");
}
async disconnect() {
this.reader.releaseLock();
this.writer.releaseLock();
await this.port.close();
this.logger.log("Disconnected from ESP device.");
}
async initialize() {
await this.hardReset(true);
await this.sync();
}
async sync() {
for (let i = 0; i < 5; i++) {
this.inputBuffer.length = 0;
await this.sendCommand(ESP_SYNC, SYNC_PACKET);
try {
let [_reply, data] = await this.getResponse(ESP_SYNC);
if (data[0] === 0 && data[1] === 0) {
this.logger.log("Device synchronized.");
return true;
}
} catch (e) {
this.logger.log("Sync attempt failed.");
}
}
throw new Error("Failed to synchronize with the ESP.");
}
async flashFirmware(binaryData, updateProgress) {
await this.flashBegin(binaryData.byteLength);
let position = 0;
let seq = 0;
while (position < binaryData.byteLength) {
const chunk = binaryData.slice(position, position + this.flashWriteSize);
await this.flashBlock(chunk, seq);
position += chunk.byteLength;
seq++;
updateProgress(position, binaryData.byteLength);
}
await this.flashFinish();
}
async flashBegin(size) {
const numBlocks = Math.ceil(size / this.flashWriteSize);
const buffer = pack("<IIII", size, numBlocks, this.flashWriteSize, 0);
await this.sendCommand(ESP_FLASH_BEGIN, buffer);
this.logger.log("Flash prepared.");
}
async flashBlock(data, seq) {
const checksum = this.calculateChecksum(data);
const buffer = pack("<IIII", data.byteLength, seq, 0, 0).concat([...data]);
await this.sendCommand(ESP_FLASH_DATA, buffer, checksum);
}
async flashFinish() {
const buffer = pack("<I", 1);
await this.sendCommand(ESP_FLASH_END, buffer);
this.logger.log("Flashing complete.");
}
calculateChecksum(data) {
let checksum = 0xEF; // Initial value for ESP checksum
for (const byte of data) {
checksum ^= byte;
}
return checksum;
}
async sendCommand(opcode, buffer, checksum = 0) {
const packet = slipEncode([...pack("<BBHI", 0x00, opcode, buffer.length, checksum), ...buffer]);
await this.writer.write(new Uint8Array(packet));
}
async getResponse(opcode) {
const packet = await this.readPacket();
const [resp, opRet, _lenRet, val] = unpack("<BBHI", packet.slice(0, 8));
if (resp !== 1 || opRet !== opcode) {
throw new Error(`Invalid response for command ${opcode}`);
}
return [val, packet.slice(8)];
}
async readPacket() {
let packet = [];
let inEscape = false;
while (true) {
const { value, done } = await this.reader.read();
if (done) throw new Error("Connection closed.");
for (const byte of value) {
if (inEscape) {
packet.push(byte === 0xdc ? 0xc0 : byte === 0xdd ? 0xdb : byte);
inEscape = false;
} else if (byte === 0xdb) {
inEscape = true;
} else if (byte === 0xc0) {
return packet;
} else {
packet.push(byte);
}
}
}
}
async hardReset(bootloader = false) {
await this.port.setSignals({ dataTerminalReady: !bootloader, requestToSend: bootloader });
await sleep(100);
await this.port.setSignals({ dataTerminalReady: false, requestToSend: false });
await sleep(1000);
}
}