diff --git a/posadmesh-tools/flasher/README.md b/posadmesh-tools/flasher/README.md
index bb9a39a2..97de073f 100644
--- a/posadmesh-tools/flasher/README.md
+++ b/posadmesh-tools/flasher/README.md
@@ -85,6 +85,16 @@ nginx -t && systemctl reload nginx
- определение чипа при подключении, журнал операций, прогресс, кнопка перезагрузки;
- для nRF52 (`.uf2`, `.zip`) — скачивание и инструкция: копирование на UF2-диск или DFU через nRF Connect.
+## Грабли
+
+- **`A.charCodeAt is not a function` при прошивке ESP32.** esptool-js 0.5.x ожидает содержимое
+ файла как «двоичную строку»: внутри `writeFlash` вызывается `bstrToUi8(data)`, то есть
+ `data.charCodeAt(i)`. Если передать `Uint8Array`, запись падает. В `app.js` байты приводит
+ `bytesToBinaryString()` (кусками по 32 КБ, чтобы не упереться в лимит аргументов).
+- **Браузер держит старый `app.js`.** Для `location /flasher/` в nginx стоит
+ `Cache-Control: no-cache`, а подключение в `index.html` идёт с версией (`app.js?v=...`).
+ При правке скрипта версию нужно поднять, иначе браузер возьмёт старый файл из кэша.
+
## Ограничения
- прошивка из браузера возможна только для ESP32: nRF52 требует UF2-диска или DFU;
diff --git a/posadmesh-tools/flasher/app.js b/posadmesh-tools/flasher/app.js
index 6a2f80d2..4bf42345 100644
--- a/posadmesh-tools/flasher/app.js
+++ b/posadmesh-tools/flasher/app.js
@@ -213,13 +213,30 @@ async function connect() {
}
}
+// esptool-js (0.5.x) ожидает содержимое прошивки как «двоичную строку» (binary string):
+// внутри writeFlash вызывается bstrToUi8(data), то есть data.charCodeAt(i). Если передать
+// Uint8Array, падает с "data.charCodeAt is not a function" — поэтому конвертируем сами.
+function bytesToBinaryString(bytes) {
+ let out = '';
+ const CHUNK = 0x8000; // чтобы String.fromCharCode.apply не упёрся в лимит аргументов
+ for (let i = 0; i < bytes.length; i += CHUNK) {
+ out += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
+ }
+ return out;
+}
+
async function fetchFirmware(file) {
log('Загрузка ' + file.name + ' (' + human(file.size) + ')…');
const res = await fetch(file.url, { cache: 'no-store' });
if (!res.ok) throw new Error('не удалось скачать файл: HTTP ' + res.status);
const buf = await res.arrayBuffer();
- log('Получено ' + human(buf.byteLength));
- return new Uint8Array(buf);
+ const bytes = new Uint8Array(buf);
+ log('Получено ' + human(bytes.length) + ', подготовка данных…');
+ const data = bytesToBinaryString(bytes);
+ if (data.length !== bytes.length) {
+ throw new Error(`данные повреждены при подготовке: ${data.length} вместо ${bytes.length} байт`);
+ }
+ return data;
}
async function flash() {
diff --git a/posadmesh-tools/flasher/index.html b/posadmesh-tools/flasher/index.html
index 7b642bc5..c5e946f7 100644
--- a/posadmesh-tools/flasher/index.html
+++ b/posadmesh-tools/flasher/index.html
@@ -112,6 +112,6 @@
-
+