From 250984e04cb5eb2f49748f420ebf3b189b383355 Mon Sep 17 00:00:00 2001 From: mrfrase3 Date: Thu, 12 Aug 2021 23:23:11 +0800 Subject: [PATCH 1/5] esp uploady boi --- buildspec.yml | 1 + package.json | 2 + public/stubs/esp32.json | 1 + public/stubs/esp32c3.json | 1 + public/stubs/esp32h2.json | 1 + public/stubs/esp32s2.json | 1 + public/stubs/esp32s3.json | 1 + public/stubs/esp8266.json | 1 + src/components/files/editor.vue | 2 +- src/components/libs/libs-table.vue | 8 +- src/components/terminal/terminal.vue | 4 + src/plugins/compile-server.js | 22 +- src/plugins/uploader/avrgirl.js | 2 +- src/plugins/uploader/esptool/ESPLoader.js | 1603 +++++++++++++++++++++ src/plugins/uploader/esptool/index.js | 71 + src/plugins/uploader/esptool/webserial.js | 191 +++ src/plugins/uploader/index.js | 12 +- yarn.lock | 10 + 18 files changed, 1918 insertions(+), 16 deletions(-) create mode 100644 public/stubs/esp32.json create mode 100644 public/stubs/esp32c3.json create mode 100644 public/stubs/esp32h2.json create mode 100644 public/stubs/esp32s2.json create mode 100644 public/stubs/esp32s3.json create mode 100644 public/stubs/esp8266.json create mode 100644 src/plugins/uploader/esptool/ESPLoader.js create mode 100644 src/plugins/uploader/esptool/index.js create mode 100644 src/plugins/uploader/esptool/webserial.js diff --git a/buildspec.yml b/buildspec.yml index cd62a67..ddd306d 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -10,6 +10,7 @@ phases: build: commands: - yarn build + - echo '["https://compile.duino.app"]' > dist/servers.json artifacts: files: - '**/*' diff --git a/package.json b/package.json index a7a2e51..926100c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "avrgirl-arduino": "https://github.com/duinoapp/avrgirl-arduino.git#browser-port-forwarding", "core-js": "^3.6.5", "country-code-emoji": "^2.2.0", + "crypto-js": "^4.0.0", "dayjs": "^1.8.28", "debounce-promise": "^3.1.2", "feathers-localstorage": "^5.1.1", @@ -26,6 +27,7 @@ "intel-hex": "^0.1.2", "lodash": "^4.17.15", "monaco-editor-webpack-plugin": "<=1.8.2", + "pako": "^2.0.3", "register-service-worker": "^1.7.1", "roboto-fontface": "*", "sass": "^1.26.7", diff --git a/public/stubs/esp32.json b/public/stubs/esp32.json new file mode 100644 index 0000000..4e48a85 --- /dev/null +++ b/public/stubs/esp32.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1074520064, "entry": 1074521516, "data": "CMD8Pw==", "data_start": 1073605544} \ No newline at end of file diff --git a/public/stubs/esp32c3.json b/public/stubs/esp32c3.json new file mode 100644 index 0000000..80dad8c --- /dev/null +++ b/public/stubs/esp32c3.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1077411840, "entry": 1077413488, "data": "DEDIPw==", "data_start": 1070164904} \ No newline at end of file diff --git a/public/stubs/esp32h2.json b/public/stubs/esp32h2.json new file mode 100644 index 0000000..67375e8 --- /dev/null +++ b/public/stubs/esp32h2.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1077411840, "entry": 1077413328, "data": "DEDIPw==", "data_start": 1070164904} \ No newline at end of file diff --git a/public/stubs/esp32s2.json b/public/stubs/esp32s2.json new file mode 100644 index 0000000..74a8576 --- /dev/null +++ b/public/stubs/esp32s2.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1073905664, "entry": 1073907540, "data": "CAD9Pw==", "data_start": 1073622004} \ No newline at end of file diff --git a/public/stubs/esp32s3.json b/public/stubs/esp32s3.json new file mode 100644 index 0000000..ba7d1ee --- /dev/null +++ b/public/stubs/esp32s3.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1077379072, "entry": 1077381116, "data": "CADKPw==", "data_start": 1070279592} \ No newline at end of file diff --git a/public/stubs/esp8266.json b/public/stubs/esp8266.json new file mode 100644 index 0000000..6ae7a2e --- /dev/null +++ b/public/stubs/esp8266.json @@ -0,0 +1 @@ +{"text": "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", "text_start": 1074843648, "entry": 1074843652, "data": "CIH+PwUFBAACAwcAAwMLALnXEEDv1xBAHdgQQLrYEEBo5xBAHtkQQHTZEEDA2RBAaOcQQILaEED/2hBAwNsQQGjnEEBo5xBAWNwQQGjnEEA33xBAAOAQQDvgEEBo5xBAaOcQQNfgEEBo5xBAv+EQQGXiEECj4xBAY+QQQDTlEEBo5xBAaOcQQGjnEEBo5xBAYuYQQGjnEEBX5xBAkN0QQI/YEECm5RBAq9oQQPzZEEBo5xBA7OYQQDHnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQCLaEEBf2hBAvuUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", "data_start": 1073720488} \ No newline at end of file diff --git a/src/components/files/editor.vue b/src/components/files/editor.vue index f06239c..7e36ee2 100644 --- a/src/components/files/editor.vue +++ b/src/components/files/editor.vue @@ -56,6 +56,6 @@ export default { diff --git a/src/components/libs/libs-table.vue b/src/components/libs/libs-table.vue index 1c2db82..66327dd 100644 --- a/src/components/libs/libs-table.vue +++ b/src/components/libs/libs-table.vue @@ -133,9 +133,11 @@ export default { const res = await this.$compiler.librariesSearch( this.search, itemsPerPage, - page - 1, - sortBy[0], - sortDesc[0], + { + skip: page - 1, + sortBy: sortBy[0], + sortDesc: sortDesc[0], + }, ); this.items = res.data; this.total = res.total; diff --git a/src/components/terminal/terminal.vue b/src/components/terminal/terminal.vue index ec8e145..a5ce640 100644 --- a/src/components/terminal/terminal.vue +++ b/src/components/terminal/terminal.vue @@ -39,6 +39,7 @@ export default { this.$terminal.write(val.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n')); }, clear() { + this.$terminal.write('\r\n'); this.$terminal.clear(); }, focus() { @@ -50,6 +51,9 @@ export default { fit() { this.$terminal.fit(); }, + cols() { + return this.$terminal.cols; + }, }, }; diff --git a/src/plugins/compile-server.js b/src/plugins/compile-server.js index c6a7ef9..72f72ec 100644 --- a/src/plugins/compile-server.js +++ b/src/plugins/compile-server.js @@ -148,10 +148,14 @@ class CompileServer extends EventEmitter { console.log('loading finished'); } - async librariesSearch(search, limit = 10, skip = 0, sortBy = 'name', sortDesc = false) { + async librariesSearch(search, limit = 10, { + skip = 0, sortBy = 'name', sortDesc = false, exact = false, + } = {}) { await this.initPromise; const e = encodeURIComponent; - const query = `?search=${e(search ?? '')}&limit=${limit}&skip=${skip}&sortBy=${e(sortBy)}&sortDesc=${sortDesc}`; + const query = `?search=${ + e(search ?? '') + }&limit=${limit}&skip=${skip}&sortBy=${e(sortBy)}&sortDesc=${sortDesc}&exact=${exact}`; const res = await this.serverReq(`info/libraries${query}`); return res || { limit, skip, total: 0, data: [], @@ -180,8 +184,8 @@ class CompileServer extends EventEmitter { async _getLibs({ libraries }) { if (!libraries?.length) return []; - const search = libraries.map(({ name }) => name.replaceAll(' ', '.')).join(' '); - const { data } = await this.librariesSearch(search, libraries.length); + const search = libraries.map(({ name }) => name.replaceAll(' ', '.')).join(','); + const { data } = await this.librariesSearch(search, libraries.length, { exact: true }); return libraries.map((lib) => ({ ...lib, url: data @@ -236,8 +240,11 @@ class CompileServer extends EventEmitter { throw new Error(res.error); } this.emit('console.log', res.log); + if (res.log.includes('In function \'spiTransferBytesNL\':')) { + this.emit('console.log', '> Please note that the above "-Wincompatible-pointer-types" warning is only a warning.\r\n'); + } this.emit('console.progress', { percent: 1, message: 'Done!' }); - return res.hex; + return res; // this.disconnect(); // return res.hex; } @@ -252,11 +259,12 @@ class CompileServer extends EventEmitter { } const flags = this._getFlags(); try { - const hex = await this.compile(1, false); + const res = await this.compile(1, false); + if (!res.hex && !res.files) throw new Error('Failed to compile code'); this.emit('console.progress', { percent: 0.5, message: 'Uploading code...' }); // eslint-disable-next-line no-console // console.log(this.Vue.$serial); - await this.Vue.$uploader.upload(hex, { ...flags }); + await this.Vue.$uploader.upload(res, { ...flags }); this.emit('console.progress', { percent: 1.0, message: 'Done!' }); } catch (err) { // eslint-disable-next-line no-console diff --git a/src/plugins/uploader/avrgirl.js b/src/plugins/uploader/avrgirl.js index 0b45c01..cb0087d 100644 --- a/src/plugins/uploader/avrgirl.js +++ b/src/plugins/uploader/avrgirl.js @@ -11,7 +11,7 @@ const getConfig = (board) => { const isValid = (board) => true || !!getConfig(board)?.protocol; -const upload = (hex, board, serial, config) => new Promise((resolve, reject) => { +const upload = ({ hex }, board, serial, config) => new Promise((resolve, reject) => { const avrgirl = new Avrgirl({ board: getConfig(board), serialPort: serial, diff --git a/src/plugins/uploader/esptool/ESPLoader.js b/src/plugins/uploader/esptool/ESPLoader.js new file mode 100644 index 0000000..518f98a --- /dev/null +++ b/src/plugins/uploader/esptool/ESPLoader.js @@ -0,0 +1,1603 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-throw-literal */ +/* eslint-disable prefer-destructuring */ +/* eslint-disable no-plusplus */ +/* eslint-disable class-methods-use-this */ +/* eslint-disable no-bitwise */ +/* eslint-disable camelcase */ +/* eslint-disable max-classes-per-file */ +import pako from 'pako'; +import CryptoJS from 'crypto-js'; + +const stubCache = {}; + +class ESP8266ROM { + static CHIP_NAME = 'ESP8266'; + + static IS_STUB = true; + + static CHIP_DETECT_MAGIC_VALUE = 0xfff0c101; + + static FLASH_WRITE_SIZE = 0x400; + + // OTP ROM addresses + static ESP_OTP_MAC0 = 0x3ff00050 + + static ESP_OTP_MAC1 = 0x3ff00054 + + static ESP_OTP_MAC3 = 0x3ff0005c + + static SPI_REG_BASE = 0x60000200 + + static SPI_USR_OFFS = 0x1c + + static SPI_USR1_OFFS = 0x20 + + static SPI_USR2_OFFS = 0x24 + + static SPI_MOSI_DLEN_OFFS = null + + static SPI_MISO_DLEN_OFFS = null + + static SPI_W0_OFFS = 0x40 + + static UART_CLKDIV_REG = 0x60000014 + + static XTAL_CLK_DIVIDER = 2 + + static FLASH_SIZES = { + '512KB': 0x00, + '256KB': 0x10, + '1MB': 0x20, + '2MB': 0x30, + '4MB': 0x40, + '2MB-c1': 0x50, + '4MB-c1': 0x60, + '8MB': 0x80, + '16MB': 0x90, + } + + static BOOTLOADER_FLASH_OFFSET = 0 + + static MEMORY_MAP = [[0x3FF00000, 0x3FF00010, 'DPORT'], + [0x3FFE8000, 0x40000000, 'DRAM'], + [0x40100000, 0x40108000, 'IRAM'], + [0x40201010, 0x402E1010, 'IROM']] + + static get_efuses = async (loader) => { + // Return the 128 bits of ESP8266 efuse as a single integer + const result = (await loader.read_reg({ addr: 0x3ff0005c }) << 96) + | (await loader.read_reg({ addr: 0x3ff00058 }) << 64) + | (await loader.read_reg({ addr: 0x3ff00054 }) << 32) + | await loader.read_reg({ addr: 0x3ff00050 }); + return result; + } + + static _get_flash_size = (efuses) => { + // rX_Y = EFUSE_DATA_OUTX[Y] + const r0_4 = (efuses & (1 << 4)) !== 0; + const r3_25 = (efuses & (1 << 121)) !== 0; + const r3_26 = (efuses & (1 << 122)) !== 0; + const r3_27 = (efuses & (1 << 123)) !== 0; + + if (r0_4 && !r3_25) { + if (!r3_27 && !r3_26) { + return 1; + } if (!r3_27 && r3_26) { + return 2; + } + } + if (!r0_4 && r3_25) { + if (!r3_27 && !r3_26) { + return 2; + } if (!r3_27 && r3_26) { + return 4; + } + } + return -1; + } + + static get_chip_description = async (loader) => { + const efuses = await this.get_efuses(loader); + const is_8285 = (efuses & (((1 << 4) | 1) << 80)) !== 0; // One or the other efuse bit is set for ESP8285 + if (is_8285) { + const flash_size = this._get_flash_size(efuses); + const max_temp = (efuses & (1 << 5)) !== 0; // This efuse bit identifies the max flash temperature + const chip_name = { + 1: max_temp ? 'ESP8285H08' : 'ESP8285N08', + 2: max_temp ? 'ESP8285H16' : 'ESP8285N16', + }[flash_size] || 'ESP8285'; + return chip_name; + } + return 'ESP8266EX'; + } + + static get_chip_features = async (loader) => { + const features = ['WiFi']; + if (await this.get_chip_description(loader) === 'ESP8285') { + features.push('Embedded Flash'); + } + return features; + } + + static flash_spi_attach = async (loader, hspi_arg) => { + if (this.IS_STUB) { + await super.flash_spi_attach(loader, hspi_arg); + } else { + // ESP8266 ROM has no flash_spi_attach command in serial protocol, + // but flash_begin will do it + await loader.flash_begin(0, 0); + } + } + + static flash_set_parameters = async (loader, size) => { + // not implemented in ROM, but OK to silently skip for ROM + if (this.IS_STUB) { + await super.flash_set_parameters(loader, size); + } + } + + static chip_id = async (loader) => { + // Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function + const id0 = await loader.read_reg({ addr: this.ESP_OTP_MAC0 }); + const id1 = await loader.read_reg({ addr: this.ESP_OTP_MAC1 }); + return (id0 >> 24) | ((id1 & 0xffffff) << 8); + } + + static read_mac = async (loader) => { + // Read MAC from OTP ROM + const mac0 = await loader.read_reg({ addr: this.ESP_OTP_MAC0 }); + const mac1 = await loader.read_reg({ addr: this.ESP_OTP_MAC1 }); + const mac3 = await loader.read_reg({ addr: this.ESP_OTP_MAC3 }); + let oui; + if (mac3 !== 0) { + oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff); + } else if (((mac1 >> 16) & 0xff) === 0) { + oui = (0x18, 0xfe, 0x34); + } else if (((mac1 >> 16) & 0xff) === 1) { + oui = (0xac, 0xd0, 0x74); + } else { + throw ('Unknown OUI'); + } + return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff); + } + + static get_erase_size = (offset, size) => size + + // eslint-disable-next-line no-unused-vars + static get_crystal_freq = async (loader) => 40 +} + +class ESP32ROM { + static CHIP_NAME = 'ESP32'; + + static IS_STUB = true; + + static IMAGE_CHIP_ID = 0; + + static CHIP_DETECT_MAGIC_VALUE = 0x00f01d83; + + static EFUSE_RD_REG_BASE = 0x3ff5a000; + + static DR_REG_SYSCON_BASE = 0x3ff66000; + + static UART_CLKDIV_REG = 0x3ff40014; + + static UART_CLKDIV_MASK = 0xFFFFF; + + static UART_DATE_REG_ADDR = 0x60000078; + + static XTAL_CLK_DIVIDER= 1; + + static FLASH_WRITE_SIZE = 0x400; + + static BOOTLOADER_FLASH_OFFSET = 0x1000; + + static FLASH_SIZES = { + '1MB': 0x00, '2MB': 0x10, '4MB': 0x20, '8MB': 0x30, '16MB': 0x40, + }; + + static SPI_REG_BASE = 0x3ff42000; + + static SPI_USR_OFFS = 0x1c; + + static SPI_USR1_OFFS = 0x20; + + static SPI_USR2_OFFS = 0x24; + + static SPI_W0_OFFS = 0x80; + + static SPI_MOSI_DLEN_OFFS = 0x28; + + static SPI_MISO_DLEN_OFFS = 0x2c; + + static read_efuse = async (loader, offset) => { + const addr = this.EFUSE_RD_REG_BASE + (4 * offset); + // console.log(`Read efuse ${addr}`); + return loader.read_reg({ addr }); + } + + static get_pkg_version = async (loader) => { + const word3 = await this.read_efuse(loader, 3); + let pkg_version = (word3 >> 9) & 0x07; + pkg_version += ((word3 >> 2) & 0x1) << 3; + return pkg_version; + } + + static get_chip_revision = async (loader) => { + const word3 = await this.read_efuse(loader, 3); + const word5 = await this.read_efuse(loader, 5); + const apb_ctl_date = await loader.read_reg({ addr: this.DR_REG_SYSCON_BASE + 0x7C }); + + const rev_bit0 = (word3 >> 15) & 0x1; + const rev_bit1 = (word5 >> 20) & 0x1; + const rev_bit2 = (apb_ctl_date >> 31) & 0x1; + if (rev_bit0 !== 0) { + if (rev_bit1 !== 0) { + if (rev_bit2 !== 0) { + return 3; + } + return 2; + } + return 1; + } + return 0; + } + + static get_chip_description = async (loader) => { + const chip_desc = ['ESP32-D0WDQ6', 'ESP32-D0WD', 'ESP32-D2WD', '', 'ESP32-U4WDH', 'ESP32-PICO-D4', 'ESP32-PICO-V3-02']; + let chip_name = ''; + const pkg_version = await this.get_pkg_version(loader); + const chip_revision = await this.get_chip_revision(loader); + const rev3 = (chip_revision === 3); + const single_core = await this.read_efuse(loader, 3) & (1 << 0); + + if (single_core !== 0) { + chip_desc[0] = 'ESP32-S0WDQ6'; + chip_desc[1] = 'ESP32-S0WD'; + } + if (rev3) { + chip_desc[5] = 'ESP32-PICO-V3'; + } + if (pkg_version >= 0 && pkg_version <= 6) { + chip_name = chip_desc[pkg_version]; + } else { + chip_name = 'Unknown ESP32'; + } + + if (rev3 && (pkg_version === 0 || pkg_version === 1)) { + chip_name += '-V3'; + } + return `${chip_name} (revision ${chip_revision})`; + } + + static get_chip_features = async (loader) => { + const features = ['Wi-Fi']; + const word3 = await this.read_efuse(loader, 3); + + const chip_ver_dis_bt = word3 & (1 << 1); + if (chip_ver_dis_bt === 0) { + features.push(' BT'); + } + + const chip_ver_dis_app_cpu = word3 & (1 << 0); + if (chip_ver_dis_app_cpu !== 0) { + features.push(' Single Core'); + } else { + features.push(' Dual Core'); + } + + const chip_cpu_freq_rated = word3 & (1 << 13); + if (chip_cpu_freq_rated !== 0) { + const chip_cpu_freq_low = word3 & (1 << 12); + if (chip_cpu_freq_low !== 0) { + features.push(' 160MHz'); + } else { + features.push(' 240MHz'); + } + } + + const pkg_version = await this.get_pkg_version(loader); + if ([2, 4, 5, 6].includes(pkg_version)) { + features.push(' Embedded Flash'); + } + + if (pkg_version === 6) { + features.push(' Embedded PSRAM'); + } + + const word4 = await this.read_efuse(loader, 4); + const adc_vref = (word4 >> 8) & 0x1F; + if (adc_vref !== 0) { + features.push(' VRef calibration in efuse'); + } + + const blk3_part_res = (word3 >> 14) & 0x1; + if (blk3_part_res !== 0) { + features.push(' BLK3 partially reserved'); + } + + const word6 = await this.read_efuse(loader, 6); + const coding_scheme = word6 & 0x3; + const coding_scheme_arr = ['None', '3/4', 'Repeat (UNSUPPORTED)', 'Invalid']; + features.push(` Coding Scheme ${coding_scheme_arr[coding_scheme]}`); + + return features; + } + + static get_crystal_freq = async (loader) => { + const uart_div = await loader.read_reg({ addr: this.UART_CLKDIV_REG }) & this.UART_CLKDIV_MASK; + const ets_xtal = (loader.transport.baudrate * uart_div) / 1000000 / this.XTAL_CLK_DIVIDER; + let norm_xtal; + if (ets_xtal > 33) { + norm_xtal = 40; + } else { + norm_xtal = 26; + } + if (Math.abs(norm_xtal - ets_xtal) > 1) { + loader.log('WARNING: Unsupported crystal in use'); + } + return norm_xtal; + } + + static _d2h(d) { + const h = (+d).toString(16); + return h.length === 1 ? `0${h}` : h; + } + + static read_mac = async (loader) => { + let mac0 = await this.read_efuse(loader, 1); + mac0 >>>= 0; + let mac1 = await this.read_efuse(loader, 2); + mac1 >>>= 0; + const mac = new Uint8Array(6); + mac[0] = (mac1 >> 8) & 0xff; + mac[1] = mac1 & 0xff; + mac[2] = (mac0 >> 24) & 0xff; + mac[3] = (mac0 >> 16) & 0xff; + mac[4] = (mac0 >> 8) & 0xff; + mac[5] = mac0 & 0xff; + + return (`${ + this._d2h(mac[0]) + }:${ + this._d2h(mac[1]) + }:${ + this._d2h(mac[2]) + }:${ + this._d2h(mac[3]) + }:${ + this._d2h(mac[4]) + }:${ + this._d2h(mac[5]) + }`); + } + + static get_erase_size = (offset, size) => size +} + +class ESP32S2ROM { + static CHIP_NAME = 'ESP32-S2'; + + static IS_STUB = true; + + static IMAGE_CHIP_ID = 2; + + static CHIP_DETECT_MAGIC_VALUE = 0x000007c6; + + static MAC_EFUSE_REG = 0x3f41A044; + + static EFUSE_BASE = 0x3f41A000; + + static UART_CLKDIV_REG = 0x3f400014; + + static UART_CLKDIV_MASK = 0xFFFFF; + + static UART_DATE_REG_ADDR = 0x60000078; + + static FLASH_WRITE_SIZE = 0x400; + + static BOOTLOADER_FLASH_OFFSET = 0x1000; + + static FLASH_SIZES = { + '1MB': 0x00, '2MB': 0x10, '4MB': 0x20, '8MB': 0x30, '16MB': 0x40, + }; + + static SPI_REG_BASE = 0x3f402000; + + static SPI_USR_OFFS = 0x18; + + static SPI_USR1_OFFS = 0x1c; + + static SPI_USR2_OFFS = 0x20; + + static SPI_W0_OFFS = 0x58; + + static SPI_MOSI_DLEN_OFFS = 0x24; + + static SPI_MISO_DLEN_OFFS = 0x28; + + static get_pkg_version = async (loader) => { + const num_word = 3; + const block1_addr = this.EFUSE_BASE + 0x044; + const addr = block1_addr + (4 * num_word); + const word3 = await loader.read_reg({ addr }); + const pkg_version = (word3 >> 21) & 0x0F; + return pkg_version; + } + + static get_chip_description = async (loader) => { + const chip_desc = ['ESP32-S2', 'ESP32-S2FH16', 'ESP32-S2FH32']; + const pkg_ver = await this.get_pkg_version(loader); + if (pkg_ver >= 0 && pkg_ver <= 2) { + return chip_desc[pkg_ver]; + } + return 'unknown ESP32-S2'; + } + + static get_chip_features = async (loader) => { + const features = ['Wi-Fi']; + const pkg_ver = await this.get_pkg_version(loader); + if (pkg_ver === 1) { + features.push('Embedded 2MB Flash'); + } else if (pkg_ver === 2) { + features.push('Embedded 4MB Flash'); + } + const num_word = 4; + const block2_addr = this.EFUSE_BASE + 0x05C; + const addr = block2_addr + (4 * num_word); + const word4 = await loader.read_reg({ addr }); + const block2_ver = (word4 >> 4) & 0x07; + + if (block2_ver === 1) { + features.push('ADC and temperature sensor calibration in BLK2 of efuse'); + } + return features; + } + + // eslint-disable-next-line no-unused-vars + static get_crystal_freq = async (loader) => 40 + + static _d2h(d) { + const h = (+d).toString(16); + return h.length === 1 ? `0${h}` : h; + } + + static read_mac = async (loader) => { + let mac0 = await loader.read_reg({ addr: this.MAC_EFUSE_REG }); + mac0 >>>= 0; + let mac1 = await loader.read_reg({ addr: this.MAC_EFUSE_REG + 4 }); + mac1 = (mac1 >>> 0) & 0x0000ffff; + const mac = new Uint8Array(6); + mac[0] = (mac1 >> 8) & 0xff; + mac[1] = mac1 & 0xff; + mac[2] = (mac0 >> 24) & 0xff; + mac[3] = (mac0 >> 16) & 0xff; + mac[4] = (mac0 >> 8) & 0xff; + mac[5] = mac0 & 0xff; + + return (`${ + this._d2h(mac[0]) + }:${ + this._d2h(mac[1]) + }:${ + this._d2h(mac[2]) + }:${ + this._d2h(mac[3]) + }:${ + this._d2h(mac[4]) + }:${ + this._d2h(mac[5]) + }`); + } + + static get_erase_size = (offset, size) => size +} + +class ESP32S3BETA2ROM { + static CHIP_NAME = 'ESP32-S3'; + + static IMAGE_CHIP_ID = 4; + + static CHIP_DETECT_MAGIC_VALUE = 0xeb004136; + + // eslint-disable-next-line no-unused-vars + static get_pkg_version = async (loader) => { + } + + // eslint-disable-next-line no-unused-vars + static get_chip_revision = async (loader) => { + } + + // eslint-disable-next-line no-unused-vars + static get_chip_description = async (loader) => { + } + + // eslint-disable-next-line no-unused-vars + static get_chip_features = async (loader) => { + } + + // eslint-disable-next-line no-unused-vars + static get_crystal_freq = async (loader) => { + } + + // eslint-disable-next-line no-unused-vars + static read_mac = async (loader) => { + } +} + +class ESP32C3ROM { + static CHIP_NAME = 'ESP32-C3'; + + static IS_STUB = true; + + static IMAGE_CHIP_ID = 5; + + static CHIP_DETECT_MAGIC_VALUE = 0x6921506f; + + static EFUSE_BASE = 0x60008800; + + static MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; + + static UART_CLKDIV_REG = 0x3ff40014; + + static UART_CLKDIV_MASK = 0xFFFFF; + + static UART_DATE_REG_ADDR = 0x6000007C; + + static FLASH_WRITE_SIZE = 0x400; + + static BOOTLOADER_FLASH_OFFSET = 0x1000; + + static FLASH_SIZES = { + '1MB': 0x00, '2MB': 0x10, '4MB': 0x20, '8MB': 0x30, '16MB': 0x40, + }; + + static SPI_REG_BASE = 0x60002000; + + static SPI_USR_OFFS = 0x18; + + static SPI_USR1_OFFS = 0x1C; + + static SPI_USR2_OFFS = 0x20; + + static SPI_MOSI_DLEN_OFFS = 0x24; + + static SPI_MISO_DLEN_OFFS = 0x28; + + static SPI_W0_OFFS = 0x58; + + static get_pkg_version = async (loader) => { + const num_word = 3; + const block1_addr = this.EFUSE_BASE + 0x044; + const addr = block1_addr + (4 * num_word); + const word3 = await loader.read_reg({ addr }); + const pkg_version = (word3 >> 21) & 0x0F; + return pkg_version; + } + + static get_chip_revision = async (loader) => { + const block1_addr = this.EFUSE_BASE + 0x044; + const num_word = 3; + const pos = 18; + const addr = block1_addr + (4 * num_word); + const ret = (await loader.read_reg({ addr }) & (0x7 << pos)) >> pos; + return ret; + } + + static get_chip_description = async (loader) => { + let desc; + const pkg_ver = await this.get_pkg_version(loader); + if (pkg_ver === 0) { + desc = 'ESP32-C3'; + } else { + desc = 'unknown ESP32-C3'; + } + const chip_rev = await this.get_chip_revision(loader); + desc += ` (revision ${chip_rev})`; + return desc; + } + + // eslint-disable-next-line no-unused-vars + static get_chip_features = async (loader) => ['Wi-Fi'] + + // eslint-disable-next-line no-unused-vars + static get_crystal_freq = async (loader) => 40 + + static _d2h(d) { + const h = (+d).toString(16); + return h.length === 1 ? `0${h}` : h; + } + + static read_mac = async (loader) => { + let mac0 = await loader.read_reg({ addr: this.MAC_EFUSE_REG }); + mac0 >>>= 0; + let mac1 = await loader.read_reg({ addr: this.MAC_EFUSE_REG + 4 }); + mac1 = (mac1 >>> 0) & 0x0000ffff; + const mac = new Uint8Array(6); + mac[0] = (mac1 >> 8) & 0xff; + mac[1] = mac1 & 0xff; + mac[2] = (mac0 >> 24) & 0xff; + mac[3] = (mac0 >> 16) & 0xff; + mac[4] = (mac0 >> 8) & 0xff; + mac[5] = mac0 & 0xff; + + return (`${ + this._d2h(mac[0]) + }:${ + this._d2h(mac[1]) + }:${ + this._d2h(mac[2]) + }:${ + this._d2h(mac[3]) + }:${ + this._d2h(mac[4]) + }:${ + this._d2h(mac[5]) + }`); + } + + static get_erase_size = (offset, size) => size +} + +export default class ESPLoader { + ESP_RAM_BLOCK = 0x1800; + + ESP_FLASH_BEGIN = 0x02; + + ESP_FLASH_DATA = 0x03; + + ESP_FLASH_END = 0x04; + + ESP_MEM_BEGIN = 0x05; + + ESP_MEM_END = 0x06; + + ESP_MEM_DATA = 0x07; + + ESP_WRITE_REG = 0x09; + + ESP_FLASH_DEFL_BEGIN = 0x10; + + ESP_FLASH_DEFL_DATA = 0x11; + + ESP_FLASH_DEFL_END = 0x12; + + ESP_SPI_FLASH_MD5 = 0x13; + + ESP_READ_REG = 0x0A; + + ESP_SPI_ATTACH = 0x0D; + + // Only Stub supported commands + ESP_ERASE_FLASH = 0xD0; + + ESP_ERASE_REGION = 0xD1; + + ESP_IMAGE_MAGIC = 0xe9; + + ESP_CHECKSUM_MAGIC = 0xef; + + ERASE_REGION_TIMEOUT_PER_MB = 30000; + + ERASE_WRITE_TIMEOUT_PER_MB = 40000; + + MD5_TIMEOUT_PER_MB = 8000; + + CHIP_ERASE_TIMEOUT = 120000; + + MAX_TIMEOUT = this.CHIP_ERASE_TIMEOUT * 2; + + CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000; + + DETECTED_FLASH_SIZES = { + 0x12: '256KB', 0x13: '512KB', 0x14: '1MB', 0x15: '2MB', 0x16: '4MB', 0x17: '8MB', 0x18: '16MB', + }; + + constructor(transport, terminal) { + this.transport = transport; + this.terminal = terminal; + this.IS_STUB = false; + this.chip = null; + + // if (terminal) { + // this.terminal.clear(); + // } + + this.log('esptool.js v0.1-dev'); + this.log(`Serial port ${this.transport.get_info()}`); + } + + _sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + log(str) { + if (this.transport) { + this.terminal.log(str); + } else { + // eslint-disable-next-line no-console + console.log(str); + } + } + + write_char(str) { + if (this.transport) { + this.terminal.write(str); + } else { + // eslint-disable-next-line no-console + console.log(str); + } + } + + _short_to_bytearray(i) { + return [i & 0xff, (i >> 8) & 0xff]; + } + + _int_to_bytearray(i) { + return [i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, (i >> 24) & 0xff]; + } + + _bytearray_to_short(i, j) { + return (new Uint16Array([(i | (j >> 8))]))[0]; + } + + _bytearray_to_int(i, j, k, l) { + return (new Uint32Array([(i | (j << 8) | (k << 16) | (l << 24))]))[0]; + } + + _appendBuffer(buffer1, buffer2) { + const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); + tmp.set(new Uint8Array(buffer1), 0); + tmp.set(new Uint8Array(buffer2), buffer1.byteLength); + return tmp.buffer; + } + + _appendArray(arr1, arr2) { + const c = new Uint8Array(arr1.length + arr2.length); + c.set(arr1, 0); + c.set(arr2, arr1.length); + return c; + } + + async _loadStub() { + const stubName = this.chip.CHIP_NAME.replaceAll('-', '').toLowerCase(); + if (stubCache[stubName]) { + return stubCache[stubName]; + } + const stub = await fetch(`/stubs/${stubName}.json`).then((res) => res.json()); + + stub.data = Buffer.from(stub.data, 'base64'); + stub.text = Buffer.from(stub.text, 'base64'); + + stubCache[stubName] = stub; + return stub; + } + + ui8ToBstr(u8Array) { + let i; + const len = u8Array.length; + let b_str = ''; + for (i = 0; i < len; i++) { + b_str += String.fromCharCode(u8Array[i]); + } + return b_str; + } + + bstrToUi8(bStr) { + const len = bStr.length; + const u8_array = new Uint8Array(len); + for (let i = 0; i < len; i++) { + u8_array[i] = bStr.charCodeAt(i); + } + return u8_array; + } + + flush_input = async () => { + try { + await this.transport.read({ timeout: 200 }); + } catch (e) { + Math.random(e); + } + } + + command = async ({ + op = null, data = [], chk = 0, wait_response = true, timeout = 3000, min_data = 12, + } = {}) => { + // console.log("command "+ op + " " + wait_response + " " + timeout); + if (op != null) { + const pkt = new Uint8Array(8 + data.length); + pkt[0] = 0x00; + pkt[1] = op; + pkt[2] = this._short_to_bytearray(data.length)[0]; + pkt[3] = this._short_to_bytearray(data.length)[1]; + pkt[4] = this._int_to_bytearray(chk)[0]; + pkt[5] = this._int_to_bytearray(chk)[1]; + pkt[6] = this._int_to_bytearray(chk)[2]; + pkt[7] = this._int_to_bytearray(chk)[3]; + + let i; + for (i = 0; i < data.length; i++) { + pkt[8 + i] = data[i]; + } + // console.log("Command " + pkt); + await this.transport.write(pkt); + } + + if (wait_response) { + try { + const p = await this.transport.read({ timeout, min_data }); + // console.log(this.transport.slip_reader_enabled, p); + // const resp = p[0]; + const op_ret = p[1]; + // const len_ret = this._bytearray_to_short(p[2], p[3]); + const val = this._bytearray_to_int(p[4], p[5], p[6], p[7]); + // eslint-disable-next-line no-console + // console.log(`Resp ${resp} ${op_ret} ${op} ${len_ret} ${val} ${p}`); + const datum = p.slice(8); + // eslint-disable-next-line eqeqeq + if (op == null || op_ret == op) { + return [val, datum]; + } + throw ('invalid response'); + } catch (e) { + if (e === 'timeout') { + throw (e); + } + } + } + return []; + } + + read_reg = async ({ addr, timeout = 3000 } = {}) => { + // console.log(`read reg ${addr} ${timeout}`); + const pkt = this._int_to_bytearray(addr); + const val = await this.command({ op: this.ESP_READ_REG, data: pkt, timeout }); + // console.log('Read reg resp', val); + return val[0]; + } + + write_reg = async ({ + addr, value, mask = 0xFFFFFFFF, delay_us = 0, delay_after_us = 0, + } = {}) => { + let pkt = this._appendArray(this._int_to_bytearray(addr), this._int_to_bytearray(value)); + pkt = this._appendArray(pkt, this._int_to_bytearray(mask)); + pkt = this._appendArray(pkt, this._int_to_bytearray(delay_us)); + + if (delay_after_us > 0) { + pkt = this._appendArray(pkt, this._int_to_bytearray(this.chip.UART_DATE_REG_ADDR)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(delay_after_us)); + } + + await this.check_command({ op_description: 'write target memory', op: this.ESP_WRITE_REG, data: pkt }); + } + + sync = async () => { + // console.log('Sync'); + const cmd = new Uint8Array(36); + let i; + cmd[0] = 0x07; + cmd[1] = 0x07; + cmd[2] = 0x12; + cmd[3] = 0x20; + for (i = 0; i < 32; i++) { + cmd[4 + i] = 0x55; + } + + try { + const resp = await this.command({ op: 0x08, data: cmd, timeout: 100 }); + this.syncStubDetected = resp[0] === 0; + return resp; + } catch (e) { + // eslint-disable-next-line no-console + console.log(`Sync err ${e}`); + throw (e); + } + } + + _connect_attempt = async ({ mode = 'default_reset', esp32r0_delay = false } = {}) => { + // console.log(`_connect_attempt ${esp32r0_delay}`); + if (mode !== 'no_reset') { + await this.transport.setDTR(false); + await this.transport.setRTS(true); + await this._sleep(100); + if (esp32r0_delay) { + // await this._sleep(1200); + await this._sleep(2000); + } + await this.transport.setDTR(true); + await this.transport.setRTS(false); + if (esp32r0_delay) { + // await this._sleep(400); + } + await this._sleep(50); + await this.transport.setDTR(false); + } + let i = 0; + // eslint-disable-next-line no-constant-condition + while (1) { + try { + const res = await this.transport.read({ timeout: 1000 }); + i += res.length; + // console.log("Len = " + res.length); + // var str = new TextDecoder().decode(res); + // this.log(str); + } catch (e) { + if (e === 'timeout') { + break; + } + } + await this._sleep(50); + } + this.transport.slip_reader_enabled = true; + i = 7; + while (i--) { + try { + await this.sync(); + return 'success'; + } catch (error) { + if (error === 'timeout') { + if (esp32r0_delay) { + this.write_char('_'); + } else { + this.write_char('.'); + } + } + } + await this._sleep(50); + } + return 'error'; + } + + // eslint-disable-next-line no-unused-vars + async connect({ mode = 'default_reset', attempts = 7, detecting = false } = {}) { + let i; + let resp; + this.write_char('Connecting...'); + await this.transport.connect(); + for (i = 0; i < attempts; i++) { + resp = await this._connect_attempt({ esp32r0_delay: false }); + if (resp === 'success') { + break; + } + resp = await this._connect_attempt({ esp32r0_delay: true }); + if (resp === 'success') { + break; + } + } + if (resp !== 'success') { + this.log('Failed to connect with the device'); + return 'error'; + } + this.write_char('\n'); + this.write_char('\r'); + await this._sleep(100); + await this.flush_input(); + + if (!detecting) { + const chip_magic_value = await this.read_reg({ addr: 0x40001000 }); + // eslint-disable-next-line no-console + // console.log(`Chip Magic ${chip_magic_value}`); + const chips = [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32C3ROM]; + this.chip = chips.find((cls) => chip_magic_value === cls.CHIP_DETECT_MAGIC_VALUE); + // console.log('chip', this.chip); + } + return null; + } + + detect_chip = async () => { + await this.connect(); + this.write_char('Detecting chip type... '); + if (this.chip != null) { + this.log(this.chip.CHIP_NAME); + } + } + + check_command = async ({ + // eslint-disable-next-line no-unused-vars + op_description = '', op = null, data = [], chk = 0, timeout = 3000, min_data, + } = {}) => { + // console.log(`check_command ${op}`); + const resp = await this.command({ + op, data, chk, timeout, min_data, + }); + if (resp[1].length > 4) { + return resp[1]; + } + return resp[0]; + } + + mem_begin = async (size, blocks, blocksize, offset) => { + /* XXX: Add check to ensure that STUB is not getting overwritten */ + // console.log(`mem_begin ${size} ${blocks} ${blocksize} ${offset}`); + let pkt = this._appendArray(this._int_to_bytearray(size), this._int_to_bytearray(blocks)); + pkt = this._appendArray(pkt, this._int_to_bytearray(blocksize)); + pkt = this._appendArray(pkt, this._int_to_bytearray(offset)); + await this.check_command({ op_description: 'write to target RAM', op: this.ESP_MEM_BEGIN, data: pkt }); + } + + checksum = (data) => { + let i; + let chk = 0xEF; + + for (i = 0; i < data.length; i++) { + chk ^= data[i]; + } + return chk; + } + + mem_block = async (buffer, seq) => { + let pkt = this._appendArray(this._int_to_bytearray(buffer.length), this._int_to_bytearray(seq)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, buffer); + const checksum = this.checksum(buffer); + await this.check_command({ + op_description: 'write to target RAM', op: this.ESP_MEM_DATA, data: pkt, chk: checksum, + }); + } + + mem_finish = async (entrypoint) => { + const is_entry = (entrypoint === 0) ? 1 : 0; + const pkt = this._appendArray(this._int_to_bytearray(is_entry), this._int_to_bytearray(entrypoint)); + return this.check_command({ + op_description: 'leave RAM download mode', op: this.ESP_MEM_END, data: pkt, timeout: 500, min_data: 12, + }); // XXX: handle non-stub with diff timeout + } + + flash_spi_attach = async (hspi_arg) => { + const pkt = this._int_to_bytearray(hspi_arg); + await this.check_command({ op_description: 'configure SPI flash pins', op: this.ESP_SPI_ATTACH, data: pkt }); + } + + timeout_per_mb = (seconds_per_mb, size_bytes) => { + const result = seconds_per_mb * (size_bytes / 1000000); + if (result < 3000) { + return 3000; + } + return result; + } + + flash_begin = async (size, offset) => { + const num_blocks = Math.floor((size + this.FLASH_WRITE_SIZE - 1) / this.FLASH_WRITE_SIZE); + const erase_size = this.chip.get_erase_size(offset, size); + + const d = new Date(); + const t1 = d.getTime(); + + let timeout = 3000; + if (this.IS_STUB === false) { + timeout = this.timeout_per_mb(this.ERASE_REGION_TIMEOUT_PER_MB, size); + } + + // eslint-disable-next-line no-console + // console.log(`flash begin ${erase_size} ${num_blocks} ${this.FLASH_WRITE_SIZE} ${offset} ${size}`); + let pkt = this._appendArray(this._int_to_bytearray(erase_size), this._int_to_bytearray(num_blocks)); + pkt = this._appendArray(pkt, this._int_to_bytearray(this.FLASH_WRITE_SIZE)); + pkt = this._appendArray(pkt, this._int_to_bytearray(offset)); + if (this.IS_STUB === false) { + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); // XXX: Support encrypted + } + + await this.check_command({ + op_description: 'enter Flash download mode', op: this.ESP_FLASH_BEGIN, data: pkt, timeout, + }); + + const t2 = d.getTime(); + if (size !== 0 && this.IS_STUB === false) { + this.log(`Took ${(t2 - t1) / 1000}.${(t2 - t1) % 1000}s to erase flash block`); + } + return num_blocks; + } + + flash_defl_begin = async (size, compsize, offset) => { + const num_blocks = Math.floor((compsize + this.FLASH_WRITE_SIZE - 1) / this.FLASH_WRITE_SIZE); + const erase_blocks = Math.floor((size + this.FLASH_WRITE_SIZE - 1) / this.FLASH_WRITE_SIZE); + + const d = new Date(); + const t1 = d.getTime(); + + let write_size; let + timeout; + if (this.IS_STUB) { + write_size = size; + timeout = 3000; + } else { + write_size = erase_blocks * this.FLASH_WRITE_SIZE; + timeout = this.timeout_per_mb(this.ERASE_REGION_TIMEOUT_PER_MB, write_size); + } + this.log(`Compressed ${size} bytes to ${compsize}...`); + + let pkt = this._appendArray(this._int_to_bytearray(write_size), this._int_to_bytearray(num_blocks)); + pkt = this._appendArray(pkt, this._int_to_bytearray(this.FLASH_WRITE_SIZE)); + pkt = this._appendArray(pkt, this._int_to_bytearray(offset)); + + if ( + (this.chip.CHIP_NAME === 'ESP32-S2' || this.chip.CHIP_NAME === 'ESP32-S3' || this.chip.CHIP_NAME === 'ESP32-C3') + && (this.IS_STUB === false) + ) { + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + } + if (this.chip.CHIP_NAME === 'ESP8266') { + await this.flush_input(); + } + await this.check_command({ + op_description: 'enter compressed flash mode', op: this.ESP_FLASH_DEFL_BEGIN, data: pkt, timeout, + }); + const t2 = d.getTime(); + if (size !== 0 && this.IS_STUB === false) { + this.log(`Took ${(t2 - t1) / 1000}.${(t2 - t1) % 1000}s to erase flash block`); + } + return num_blocks; + } + + flash_block = async (data, seq, timeout) => { + let pkt = this._appendArray(this._int_to_bytearray(data.length), this._int_to_bytearray(seq)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, data); + + const checksum = this.checksum(data); + + await this.check_command({ + op_description: `write to target Flash after seq ${seq}`, op: this.ESP_FLASH_DATA, data: pkt, chk: checksum, timeout, + }); + } + + flash_defl_block = async (data, seq, timeout) => { + let pkt = this._appendArray(this._int_to_bytearray(data.length), this._int_to_bytearray(seq)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, data); + + const checksum = this.checksum(data); + // console.log(`flash_defl_block ${data[0].toString(16)}`, +' ' + data[1].toString(16)); + + await this.check_command({ + op_description: `write compressed data to flash after seq ${seq}`, + op: this.ESP_FLASH_DEFL_DATA, + data: pkt, + chk: checksum, + timeout, + }); + } + + flash_finish = async ({ reboot = false } = {}) => { + const val = reboot ? 0 : 1; + const pkt = this._int_to_bytearray(val); + + await this.check_command({ op_description: 'leave Flash mode', op: this.ESP_FLASH_END, data: pkt }); + } + + flash_defl_finish = async ({ reboot = false } = {}) => { + const val = reboot ? 0 : 1; + const pkt = this._int_to_bytearray(val); + + await this.check_command({ op_description: 'leave compressed flash mode', op: this.ESP_FLASH_DEFL_END, data: pkt }); + } + + run_spiflash_command = async (spiflash_command, data, read_bits) => { + // SPI_USR register flags + const SPI_USR_COMMAND = (1 << 31); + const SPI_USR_MISO = (1 << 28); + const SPI_USR_MOSI = (1 << 27); + + // SPI registers, base address differs ESP32* vs 8266 + const base = this.chip.SPI_REG_BASE; + const SPI_CMD_REG = base + 0x00; + const SPI_USR_REG = base + this.chip.SPI_USR_OFFS; + const SPI_USR1_REG = base + this.chip.SPI_USR1_OFFS; + const SPI_USR2_REG = base + this.chip.SPI_USR2_OFFS; + const SPI_W0_REG = base + this.chip.SPI_W0_OFFS; + + let set_data_lengths; + if (this.chip.SPI_MOSI_DLEN_OFFS != null) { + set_data_lengths = async (mosi_bits, miso_bits) => { + const SPI_MOSI_DLEN_REG = base + this.chip.SPI_MOSI_DLEN_OFFS; + const SPI_MISO_DLEN_REG = base + this.chip.SPI_MISO_DLEN_OFFS; + if (mosi_bits > 0) { + await this.write_reg({ addr: SPI_MOSI_DLEN_REG, value: (mosi_bits - 1) }); + } + if (miso_bits > 0) { + await this.write_reg({ addr: SPI_MISO_DLEN_REG, value: (miso_bits - 1) }); + } + }; + } else { + set_data_lengths = async (mosi_bits, miso_bits) => { + const SPI_DATA_LEN_REG = SPI_USR1_REG; + const SPI_MOSI_BITLEN_S = 17; + const SPI_MISO_BITLEN_S = 8; + const mosi_mask = (mosi_bits === 0) ? 0 : (mosi_bits - 1); + const miso_mask = (miso_bits === 0) ? 0 : (miso_bits - 1); + const val = (miso_mask << SPI_MISO_BITLEN_S) | (mosi_mask << SPI_MOSI_BITLEN_S); + await this.write_reg({ addr: SPI_DATA_LEN_REG, value: val }); + }; + } + + const SPI_CMD_USR = (1 << 18); + const SPI_USR2_COMMAND_LEN_SHIFT = 28; + if (read_bits > 32) { + throw 'Reading more than 32 bits back from a SPI flash operation is unsupported'; + } + if (data.length > 64) { + throw 'Writing more than 64 bytes of data with one SPI command is unsupported'; + } + + const data_bits = data.length * 8; + const old_spi_usr = await this.read_reg({ addr: SPI_USR_REG }); + const old_spi_usr2 = await this.read_reg({ addr: SPI_USR2_REG }); + let flags = SPI_USR_COMMAND; + let i; + if (read_bits > 0) { + flags |= SPI_USR_MISO; + } + if (data_bits > 0) { + flags |= SPI_USR_MOSI; + } + await set_data_lengths(data_bits, read_bits); + await this.write_reg({ addr: SPI_USR_REG, value: flags }); + let val = (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command; + await this.write_reg({ addr: SPI_USR2_REG, value: val }); + if (data_bits === 0) { + await this.write_reg({ addr: SPI_W0_REG, value: 0 }); + } else { + if (data.length % 4 !== 0) { + const padding = new Uint8Array(data.length % 4); + // eslint-disable-next-line no-param-reassign + data = this._appendArray(data, padding); + } + let next_reg = SPI_W0_REG; + for (i = 0; i < data.length - 4; i += 4) { + val = this._bytearray_to_int(data[i], data[i + 1], data[i + 2], data[i + 3]); + await this.write_reg({ addr: next_reg, value: val }); + next_reg += 4; + } + } + await this.write_reg({ addr: SPI_CMD_REG, value: SPI_CMD_USR }); + for (i = 0; i < 10; i++) { + val = await this.read_reg({ addr: SPI_CMD_REG }) & SPI_CMD_USR; + if (val === 0) { + break; + } + } + if (i === 10) { + throw 'SPI command did not complete in time'; + } + const stat = await this.read_reg({ addr: SPI_W0_REG }); + await this.write_reg({ addr: SPI_USR_REG, value: old_spi_usr }); + await this.write_reg({ addr: SPI_USR2_REG, value: old_spi_usr2 }); + return stat; + } + + read_flash_id = async () => { + const SPIFLASH_RDID = 0x9F; + const pkt = new Uint8Array(0); + return this.run_spiflash_command(SPIFLASH_RDID, pkt, 24); + } + + erase_flash = async () => { + this.log('Erasing flash (this may take a while)...'); + let d = new Date(); + const t1 = d.getTime(); + const ret = await this.check_command({ + op_description: 'erase flash', + op: this.ESP_ERASE_FLASH, + timeout: this.CHIP_ERASE_TIMEOUT, + }); + d = new Date(); + const t2 = d.getTime(); + this.log(`Chip erase completed successfully in ${(t2 - t1) / 1000}s`); + return ret; + } + + toHex(buffer) { + return Array.prototype.map.call(buffer, (x) => (`00${x.toString(16)}`).slice(-2)).join(''); + } + + flash_md5sum = async (addr, size) => { + const timeout = this.timeout_per_mb(this.MD5_TIMEOUT_PER_MB, size); + let pkt = this._appendArray(this._int_to_bytearray(addr), this._int_to_bytearray(size)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + pkt = this._appendArray(pkt, this._int_to_bytearray(0)); + + let res = await this.check_command({ + op_description: 'calculate md5sum', op: this.ESP_SPI_FLASH_MD5, data: pkt, timeout, min_data: 26, + }); + if (res.length > 16) { + res = res.slice(0, 16); + } + const strmd5 = this.toHex(res); + return strmd5; + } + + run_stub = async () => { + this.log('Fetching stub...'); + + const stub = await this._loadStub(); + // console.log(stub); + const { + data, text, data_start, text_start, entry, + } = stub; + + this.log('Uploading stub...'); + + let blocks = Math.floor((text.length + this.ESP_RAM_BLOCK - 1) / this.ESP_RAM_BLOCK); + let i; + + await this.mem_begin(text.length, blocks, this.ESP_RAM_BLOCK, text_start); + for (i = 0; i < blocks; i++) { + const from_offs = i * this.ESP_RAM_BLOCK; + let to_offs = from_offs + this.ESP_RAM_BLOCK; + if (to_offs > text.length) to_offs = text.length; + await this.mem_block(text.slice(from_offs, to_offs), i); + } + + blocks = Math.floor((data.length + this.ESP_RAM_BLOCK - 1) / this.ESP_RAM_BLOCK); + await this.mem_begin(data.length, blocks, this.ESP_RAM_BLOCK, data_start); + for (i = 0; i < blocks; i++) { + const from_offs = i * this.ESP_RAM_BLOCK; + let to_offs = from_offs + this.ESP_RAM_BLOCK; + if (to_offs > data.length) to_offs = data.length; + await this.mem_block(data.slice(from_offs, to_offs), i); + } + + this.log('Running stub...'); + let valid = false; + await this.mem_finish(entry); + + if (this.chip.CHIP_NAME === 'ESP8266') { + const [reply] = await this.sync(); + if (reply === 0) valid = true; + } else { + const res = await this.transport.read({ timeout: 1000, min_data: 6 }); + if (res[0] === 79 && res[1] === 72 && res[2] === 65 && res[3] === 73) { + valid = true; + } + } + + if (valid) { + this.log('Stub running...'); + this.IS_STUB = true; + this.FLASH_WRITE_SIZE = 0x4000; + return this.chip; + } + this.log('Failed to start stub. Unexpected response'); + return null; + } + + main_fn = async () => { + await this.detect_chip(); + if (this.chip == null) { + this.log('Error in connecting to board'); + return; + } + + const chip = await this.chip.get_chip_description(this); + this.log(`Chip is ${chip}`); + this.log(`Features: ${await this.chip.get_chip_features(this)}`); + this.log(`Crystal is ${await this.chip.get_crystal_freq(this)}MHz`); + this.log(`MAC: ${await this.chip.read_mac(this)}`); + await this.chip.read_mac(this); + + if (this.chip.IS_STUB) await this.run_stub(); + else this.FLASH_WRITE_SIZE = this.chip.FLASH_WRITE_SIZE || 0x4000; + } + + flash_size_bytes = (flash_size) => { + let flash_size_b = -1; + if (flash_size.indexOf('KB') !== -1) { + flash_size_b = parseInt(flash_size.slice(0, flash_size.indexOf('KB')), 10) * 1024; + } else if (flash_size.indexOf('MB') !== -1) { + flash_size_b = parseInt(flash_size.slice(0, flash_size.indexOf('MB')), 10) * 1024 * 1024; + } + return flash_size_b; + } + + pad_array = (arr, len, fillValue) => Object.assign(new Array(len).fill(fillValue), arr) + + parse_flash_size_arg = (flsz) => { + if (typeof this.chip.FLASH_SIZES[flsz] === 'undefined') { + this.log(`Flash size ${flsz} is not supported by this chip type. Supported sizes: ${this.chip.FLASH_SIZES}`); + throw 'Invalid flash size'; + } + return this.chip.FLASH_SIZES[flsz]; + } + + _update_image_flash_params = (image, address, flash_size, flash_mode, flash_freq) => { + // console.log(`_update_image_flash_params ${flash_size} ${flash_mode} ${flash_freq}`); + if (image.length < 8) { + return image; + } + if (address !== this.chip.BOOTLOADER_FLASH_OFFSET) { + return image; + } + if (flash_size === 'keep' && flash_mode === 'keep' && flash_freq === 'keep') { + // console.log('Not changing the image'); + return image; + } + + const magic = image[0]; + let a_flash_mode = image[2]; + const flash_size_freq = image[3]; + if (magic !== this.ESP_IMAGE_MAGIC) { + this.log(`Warning: Image file at 0x${ + address.toString(16) + } doesn't look like an image file, so not changing any flash settings.`); + return image; + } + + /* XXX: Yet to implement actual image verification */ + + if (flash_mode !== 'keep') { + const flash_modes = { + qio: 0, qout: 1, dio: 2, dout: 3, + }; + a_flash_mode = flash_modes[flash_mode]; + } + let a_flash_freq = flash_size_freq & 0x0F; + if (flash_freq !== 'keep') { + const flash_freqs = { + '40m': 0, '26m': 1, '20m': 2, '80m': 0xf, + }; + a_flash_freq = flash_freqs[flash_freq]; + } + let a_flash_size = flash_size_freq & 0xF0; + if (flash_size !== 'keep') { + a_flash_size = this.parse_flash_size_arg(flash_size); + } + + const flash_params = (a_flash_mode << 8) | (a_flash_freq + a_flash_size); + this.log(`Flash params set to ${flash_params.toString(16)}`); + if (image[2] !== (a_flash_mode << 8)) { + // eslint-disable-next-line no-param-reassign + image[2] = (a_flash_mode << 8); + } + if (image[3] !== (a_flash_freq + a_flash_size)) { + // eslint-disable-next-line no-param-reassign + image[3] = (a_flash_freq + a_flash_size); + } + return image; + } + + write_flash = async ({ + fileArray = [], flash_size = 'keep', flash_mode = 'keep', flash_freq = 'keep', erase_all = false, compress = true, + } = {}) => { + // console.log('EspLoader program'); + if (flash_size !== 'keep') { + const flash_end = this.flash_size_bytes(flash_size); + for (let i = 0; i < fileArray.length; i++) { + if ((fileArray[i].data.length + fileArray[i].address) > flash_end) { + this.log("Specified file doesn't fit in the available flash"); + return; + } + } + } + + if (this.IS_STUB === true && erase_all === true) { + this.erase_flash(); + } + let image; + let address; + for (let i = 0; i < fileArray.length; i++) { + // console.log(`Data Length ${fileArray[i].data.length}`); + // image = this.pad_array(fileArray[i].data, Math.floor((fileArray[i].data.length + 3)/4) * 4, 0xff); + // XXX : handle padding + image = fileArray[i].data; + address = fileArray[i].address; + // console.log(`Image Length ${image.length}`); + if (image.length === 0) { + this.log('Warning: File is empty'); + // eslint-disable-next-line no-continue + continue; + } + image = this._update_image_flash_params(image, address, flash_size, flash_mode, flash_freq); + const calcmd5 = CryptoJS.MD5(CryptoJS.enc.Base64.parse(image.toString('base64'))); + // console.log(`Image MD5 ${calcmd5}`); + const uncsize = image.length; + let blocks; + // console.log(image); + if (compress) { + // const uncimage = this.bstrToUi8(image); + image = pako.deflate(image, { level: 9 }); + // console.log('Compressed image '); + // console.log(image); + blocks = await this.flash_defl_begin(uncsize, image.length, address); + } else { + blocks = await this.flash_begin(uncsize, address); + } + let seq = 0; + let bytes_sent = 0; + // const bytes_written = 0; + + let d = new Date(); + const t1 = d.getTime(); + + let timeout = 5000; + while (image.length > 0) { + // console.log(`Write loop ${address} ${seq} ${blocks}`); + this.write_char(`\rWriting at 0x${ + (address + (seq * this.FLASH_WRITE_SIZE)).toString(16) + }... (${ + Math.floor(100 * ((seq + 1) / blocks)) + }%)`); + let block = image.slice(0, this.FLASH_WRITE_SIZE); + if (compress) { + /* + let block_uncompressed = pako.inflate(block).length; + //let len_uncompressed = block_uncompressed.length; + bytes_written += block_uncompressed; + if (this.timeout_per_mb(this.ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed) > 3000) { + block_timeout = this.timeout_per_mb(this.ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed); + } else { + block_timeout = 3000; + } */ // XXX: Partial block inflate seems to be unsupported in Pako. Hardcoding timeout + const block_timeout = 5000; + if (this.IS_STUB === false) { + timeout = block_timeout; + } + await this.flash_defl_block(block, seq, timeout); + if (this.IS_STUB) { + timeout = block_timeout; + } + } else { + // this.log('Yet to handle Non Compressed writes'); + // block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block)) + if (block.length < this.FLASH_WRITE_SIZE) { + const existingBlock = block.toString('base64'); + block = Buffer.alloc(this.FLASH_WRITE_SIZE, 0xff); + block.write(existingBlock, 'base64'); + } + // if encrypted: + // esp.flash_encrypt_block(block, seq) + // else: + // esp.flash_block(block, seq) + // bytes_written += len(block) + await this.flash_block(block, seq, timeout); + } + bytes_sent += block.length; + image = image.slice(this.FLASH_WRITE_SIZE, image.length); + seq++; + } + if (this.IS_STUB) { + await this.read_reg({ addr: this.CHIP_DETECT_MAGIC_REG_ADDR, timeout }); + } + d = new Date(); + const t = d.getTime() - t1; + this.log(''); + this.log(`Wrote ${uncsize} bytes${ + compress ? ` (${bytes_sent} compressed)` : '' + } at 0x${address.toString(16)} in ${t / 1000} seconds.`); + this._sleep(100); + if (this.IS_STUB || this.chip.CHIP_NAME !== 'ESP8266') { + const res = await this.flash_md5sum(address, uncsize); + if (`${res}` !== `${calcmd5}`) { + this.log(`File md5: ${calcmd5}`); + this.log(`Flash md5: ${res}`); + } else { + this.log('Hash of data verified.'); + } + } + } + this.log('Leaving...'); + + if (this.IS_STUB) { + await this.flash_begin(0, 0); + if (compress) { + await this.flash_defl_finish(); + } else { + await this.flash_finish(); + } + } + } + + flash_id = async () => { + // console.log('flash_id'); + const flashid = await this.read_flash_id(); + this.log(`Manufacturer: ${(flashid & 0xff).toString(16)}`); + const flid_lowbyte = (flashid >> 16) & 0xff; + this.log(`Device: ${((flashid >> 8) & 0xff).toString(16)}${flid_lowbyte.toString(16)}`); + this.log(`Detected flash size: ${this.DETECTED_FLASH_SIZES[flid_lowbyte] || 'Unknown'}`); + } +} diff --git a/src/plugins/uploader/esptool/index.js b/src/plugins/uploader/esptool/index.js new file mode 100644 index 0000000..8fed3f6 --- /dev/null +++ b/src/plugins/uploader/esptool/index.js @@ -0,0 +1,71 @@ +/* eslint-disable camelcase */ +import ESPLoader from './ESPLoader'; +import Transport from './webserial'; + +const asyncTimeout = (timeout) => new Promise((resolve) => setTimeout(() => resolve(timeout), timeout)); +const isValid = (board) => ['esp8266', 'esp32'].includes(board.props?.build?.mcu); + +// eslint-disable-next-line no-unused-vars +const upload = async ({ files, flash_mode, flash_freq }, board, serial, config) => { + const log = (...args) => config.debug(`${args.join(' ')}\r\n`); + const term = { log, debug: log, write: config.debug }; + + const { port } = serial; + const transport = new Transport(port, term); + let espLoader; + + try { + log('> Connecting...'); + espLoader = new ESPLoader(transport, term); + await espLoader.main_fn(); + // await espLoader.flash_id(); + log('> Connected'); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + log('Failed to connect:', typeof err === 'string' ? err : err.message); + try { + await transport.disconnect(); + } catch (err2) { + // eslint-disable-next-line no-console + console.error(err2); + } + return; + } + + try { + if (board.config?.wipe && board.config.wipe !== 'none') { + log('> Erasing device flash...'); + await espLoader.erase_flash(); + log('> Successfully erased device flash'); + } + log('> Writing main data partition, this may take a while...'); + await espLoader.write_flash({ + fileArray: files.map((file) => ({ ...file, data: Buffer.from(file.data, 'base64') })), + flash_size: 'keep', + // flash_freq, + // flash_mode, + // compress: board.props?.build?.mcu !== 'esp8266', + }); + await espLoader.flash_defl_finish({ reboot: true }); + await asyncTimeout(100); + log('> Successfully written data partition'); + log('> Flashing succeeded! Have a nice day! :)'); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + log('Failed to upload:', typeof err === 'string' ? err : err.message); + } + + try { + await transport.disconnect(); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + } +}; + +export default { + isValid, + default: upload, +}; diff --git a/src/plugins/uploader/esptool/webserial.js b/src/plugins/uploader/esptool/webserial.js new file mode 100644 index 0000000..205b33c --- /dev/null +++ b/src/plugins/uploader/esptool/webserial.js @@ -0,0 +1,191 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable class-methods-use-this */ +/* eslint-disable no-continue */ +/* eslint-disable camelcase */ + +export default class Transport { + constructor(device, logger = console) { + this.device = device; + this.slip_reader_enabled = false; + this.logger = logger; + } + + get_info() { + const info = this.device.getInfo(); + return `WebSerial VendorID 0x${info.usbVendorId.toString(16)} ProductID 0x${info.usbProductId.toString(16)}`; + } + + slip_writer(data) { + let count_esc = 0; + let i = 0; + let j = 0; + + for (i = 0; i < data.length; i++) { + if (data[i] === 0xC0 || data[i] === 0xDB) { + count_esc++; + } + } + const out_data = new Uint8Array(2 + count_esc + data.length); + out_data[0] = 0xC0; + j = 1; + for (i = 0; i < data.length; i++, j++) { + if (data[i] === 0xC0) { + out_data[j++] = 0xDB; + out_data[j] = 0xDC; + continue; + } + if (data[i] === 0xDB) { + out_data[j++] = 0xDB; + out_data[j] = 0xDD; + continue; + } + + out_data[j] = data[i]; + } + out_data[j] = 0xC0; + return out_data; + } + + write = async (data) => { + const writer = this.device.writable.getWriter(); + const out_data = this.slip_writer(data); + await writer.write(out_data.buffer); + writer.releaseLock(); + } + + _appendBuffer(buffer1, buffer2) { + const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); + tmp.set(new Uint8Array(buffer1), 0); + tmp.set(new Uint8Array(buffer2), buffer1.byteLength); + return tmp.buffer; + } + + /* this function expects complete packet (hence reader reads for atleast 8 bytes. This function is + * stateless and returns the first wellformed packet only after replacing escape sequence */ + slip_reader(data) { + let i = 0; + let data_start = 0; + let data_end = 0; + let state = 'init'; + while (i < data.length) { + if (state === 'init' && data[i] === 0xC0 && data[i + 1] !== 0xC0) { + data_start = i + 1; + state = 'valid_data'; + i++; + continue; + } + if (state === 'valid_data' && data[i] === 0xC0) { + data_end = i - 1; + state = 'packet_complete'; + break; + } + i++; + } + // console.log(state, data, data_start, data_end); + if (state === 'valid_data' && data[data.length - 1] === 0) { + data_end = data.length - 1; + state = 'packet_complete'; + } + if (state !== 'packet_complete') { + return new Uint8Array(0); + } + + const temp_pkt = new Uint8Array(data_end - data_start + 1); + let j = 0; + for (i = data_start; i <= data_end; i++, j++) { + if (data[i] === 0xDB && data[i + 1] === 0xDC) { + temp_pkt[j] = 0xC0; + i++; + continue; + } + if (data[i] === 0xDB && data[i + 1] === 0xDD) { + temp_pkt[j] = 0xDB; + i++; + continue; + } + temp_pkt[j] = data[i]; + } + const packet = temp_pkt.slice(0, j); /* Remove unused bytes due to escape seq */ + return packet; + } + + read = async ({ timeout = 0, min_data = 12 } = {}) => { + let t; + let packet = null; + let value; + let done; + // this.logger.log(`Read with timeout ${timeout}`); + const reader = this.device.readable.getReader(); + if (timeout > 0) { + t = setTimeout(() => { + reader.cancel(); + reader.releaseLock(); + }, timeout); + } + + do { + this.reader = reader; + // eslint-disable-next-line no-await-in-loop + const o = await reader.read(); + this.reader = null; + value = o.value; + done = o.done; + if (packet == null) { + if (value) packet = value; + } else { + const p = new Uint8Array(this._appendBuffer(packet.buffer, value.buffer)); + packet = p; + } + if (done) { + break; + } + } while (packet.length < min_data); + + if (done) { + console.log('timed out', packet); + // eslint-disable-next-line no-throw-literal + throw ('timeout'); + } else { + if (timeout > 0) { + clearTimeout(t); + } + reader.releaseLock(); + if (this.slip_reader_enabled) { + const val_final = this.slip_reader(packet); + return val_final; + } + return packet; + } + } + + rawRead = async () => { + const reader = this.device.readable.getReader(); + + this.reader = reader; + const o = await reader.read(); + this.reader = null; + reader.releaseLock(); + return o.value; + } + + setRTS = async (state) => { + await this.device.setSignals({ requestToSend: state }); + } + + setDTR = async (state) => { + await this.device.setSignals({ dataTerminalReady: state }); + } + + connect = async () => { + await this.device.open({ baudRate: 115200 }); + this.baudrate = 115200; + } + + disconnect = async () => { + if (this.reader !== null) { + this.reader.cancel(); + this.reader.releaseLock(); + } + await this.device.close(); + } +} diff --git a/src/plugins/uploader/index.js b/src/plugins/uploader/index.js index b899154..6505c9f 100644 --- a/src/plugins/uploader/index.js +++ b/src/plugins/uploader/index.js @@ -3,6 +3,7 @@ import get from 'lodash/get'; import store from '../../store'; // import avrdude from './stk500'; import avrdude from './avrgirl'; +import esptool from './esptool'; const asyncTimeout = (timeout) => new Promise((resolve) => setTimeout(() => resolve(timeout), timeout)); @@ -15,13 +16,15 @@ class Uploader extends EventEmitter { this.Vue = Vue; this.toolMap = { avrdude, + esptool, + esptool_py: esptool, }; } // eslint-disable-next-line class-methods-use-this isSupported(board) { // eslint-disable-line no-unused-vars const tool = get(board, 'props.upload.tool', '').split(':').pop(); - const toolProt = `${tool}.${get(board, 'props.upload.protocol')}`; + const toolProt = `${tool}.${get(board, 'props.upload.protocol', 'default')}`; return !!get(this.toolMap, toolProt) && get(this.toolMap, tool).isValid(board); } @@ -31,11 +34,11 @@ class Uploader extends EventEmitter { return this.waitForClose(serial, count + 1); } - async upload(hex, config) { + async upload(res, config) { const serial = this.Vue.$serial; const existBaud = serial.baud; const [board] = store.getters['boards/find']({ query: { uuid: store.getters.currentBoard } }).data; - const toolProt = `${board?.props?.upload?.tool?.split(':').pop()}.${board?.props?.upload?.protocol}`; + const toolProt = `${board?.props?.upload?.tool?.split(':').pop()}.${board?.props?.upload?.protocol || 'default'}`; const uploader = get(this.toolMap, toolProt); if (!uploader) throw new Error('Board not currently supported'); @@ -43,9 +46,10 @@ class Uploader extends EventEmitter { await serial.setMute(true); await serial.disconnect(); - await uploader(hex, board, serial.serial, { + await uploader(res, board, serial.serial, { ...config, debug: (message) => this.Vue.$compiler.emit('console.log', message), + progress: (message, percent) => this.Vue.$compiler.emit('console.progress', { message, percent: (percent * 0.5) + 0.5 }), }); await this.waitForClose(serial); diff --git a/yarn.lock b/yarn.lock index 979688e..ae29a10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3203,6 +3203,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc" + integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg== + css-color-names@0.0.4, css-color-names@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" @@ -6959,6 +6964,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pako@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.3.tgz#cdf475e31b678565251406de9e759196a0ea7a43" + integrity sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw== + pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" From ec2bfb4d17f08d8514ad83283c6272470dbb4fec Mon Sep 17 00:00:00 2001 From: mrfrase3 Date: Thu, 12 Aug 2021 23:38:07 +0800 Subject: [PATCH 2/5] remove log --- src/plugins/uploader/esptool/webserial.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/uploader/esptool/webserial.js b/src/plugins/uploader/esptool/webserial.js index 205b33c..a5ea78d 100644 --- a/src/plugins/uploader/esptool/webserial.js +++ b/src/plugins/uploader/esptool/webserial.js @@ -142,7 +142,7 @@ export default class Transport { } while (packet.length < min_data); if (done) { - console.log('timed out', packet); + // console.log('timed out', packet); // eslint-disable-next-line no-throw-literal throw ('timeout'); } else { From e151baf5600e49991e3d5c3824fa9e466130e6dd Mon Sep 17 00:00:00 2001 From: mrfrase3 Date: Thu, 12 Aug 2021 23:43:39 +0800 Subject: [PATCH 3/5] 3.3.0 --- package.json | 2 +- service-worker.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 926100c..737e66e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "duinoapp-client", - "version": "3.2.3", + "version": "3.3.0", "author": "Fraser Bullock", "license": "GPL-3.0", "private": true, diff --git a/service-worker.js b/service-worker.js index 0c5e1d3..fbecf91 100644 --- a/service-worker.js +++ b/service-worker.js @@ -3,7 +3,7 @@ workbox.core.setCacheNameDetails({ prefix: 'd4' }); // Do not touch this line -const LATEST_VERSION = '3.2.3'; +const LATEST_VERSION = '3.3.0'; self.addEventListener('activate', (event) => { console.log(`%c ${LATEST_VERSION} `, 'background: #ddd; color: #0000ff'); From e2966b3912063a6b0246df316cc311eeb3a78b38 Mon Sep 17 00:00:00 2001 From: mrfrase3 Date: Sat, 14 Aug 2021 13:26:12 +0800 Subject: [PATCH 4/5] minor fixes --- buildspec.yml | 2 +- public/servers-beta.json | 3 ++ src/components/settings/monitor.vue | 65 +++++++++++++++++++++++++++++ src/plugins/compile-server.js | 16 +++---- src/plugins/serial/base-serial.js | 7 +++- src/store/services/boards.js | 6 ++- src/store/tools.js | 3 ++ src/views/tools/Settings.vue | 5 +++ 8 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 public/servers-beta.json create mode 100644 src/components/settings/monitor.vue diff --git a/buildspec.yml b/buildspec.yml index ddd306d..f9e9693 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -10,7 +10,7 @@ phases: build: commands: - yarn build - - echo '["https://compile.duino.app"]' > dist/servers.json + - cp dist/servers-beta.json dist/servers.json artifacts: files: - '**/*' diff --git a/public/servers-beta.json b/public/servers-beta.json new file mode 100644 index 0000000..5110ddd --- /dev/null +++ b/public/servers-beta.json @@ -0,0 +1,3 @@ +[ + "https://compile-beta.duino.app" +] \ No newline at end of file diff --git a/src/components/settings/monitor.vue b/src/components/settings/monitor.vue new file mode 100644 index 0000000..2a36fcb --- /dev/null +++ b/src/components/settings/monitor.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/plugins/compile-server.js b/src/plugins/compile-server.js index 72f72ec..8f0395a 100644 --- a/src/plugins/compile-server.js +++ b/src/plugins/compile-server.js @@ -167,7 +167,7 @@ class CompileServer extends EventEmitter { const board = store.getters['boards/find']({ query: { uuid: store.getters.currentBoard } }).data[0]; if (!board) return 'arduino:avr:uno'; return Object.keys(board.config) - .filter((i) => !board.config_options + .filter((i) => board.config[i] && !board.config_options .find((c) => c.option === i).values .find((v) => v.value === board.config[i]).isDefault) .reduce((a, i) => `${a}:${i}=${board.config[i]}`, board.fqbn); @@ -202,13 +202,13 @@ class CompileServer extends EventEmitter { .map((f) => ({ content: f.body, name: `${project.ref}/${f.name}` })); this.emit('console.progress', { percent: 0, message: 'Initialising Libraries...' }); const libs = await this._getLibs(project); - if (libs.length) { - await this.serverReq('libraries/cache', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ libs }), - }); - } + // if (libs.length) { + // await this.serverReq('libraries/cache', { + // method: 'POST', + // headers: { 'Content-Type': 'application/json' }, + // body: JSON.stringify({ libs }), + // }); + // } this.emit('console.progress', { percent: 0.25 * mod, message: 'Compiling code...' }); const req = { fqbn: this._getFqbn(), diff --git a/src/plugins/serial/base-serial.js b/src/plugins/serial/base-serial.js index a9f0c3f..9e69500 100644 --- a/src/plugins/serial/base-serial.js +++ b/src/plugins/serial/base-serial.js @@ -1,6 +1,7 @@ /* eslint-disable no-unused-vars */ /* eslint-disable class-methods-use-this */ import EventEmitter from 'events'; +import store from '@/store'; /* Interface should emit the following: @@ -28,7 +29,6 @@ class BaseSerial extends EventEmitter { this.mute = false; this.baud = Number(window.localStorage.currentBaudRate) || 115200; this.lastBaud = 115200; - this.encoding = 'ascii'; this.devices = []; this.currentDevice = null; this.connected = false; @@ -39,6 +39,11 @@ class BaseSerial extends EventEmitter { // console.log('debug', this.DEBUG); } + get encoding() { + const [settings] = store.getters['settings/find']({ query: { key: 'monitor' } }).data; + return settings?.value?.encoding || 'ascii'; + } + install(Vue) { // eslint-disable-next-line no-param-reassign Vue.$serial = this; diff --git a/src/store/services/boards.js b/src/store/services/boards.js index f7b956a..c05d2f2 100644 --- a/src/store/services/boards.js +++ b/src/store/services/boards.js @@ -16,7 +16,11 @@ class Board extends BaseModel { if (!this.config_options) return {}; const config = {}; this.config_options.forEach((con) => { - config[con.option] = (con.values.find((val) => val.selected) || {}).value; + config[con.option] = ( + con.values.find((val) => val.selected) + || con.values.find((val) => val.isDefault) + || {} + ).value; }); return config; }, diff --git a/src/store/tools.js b/src/store/tools.js index 5b40d8b..36a8653 100644 --- a/src/store/tools.js +++ b/src/store/tools.js @@ -41,4 +41,7 @@ export const settingsDefaults = { compiler: { verbose: false, }, + monitor: { + encoding: 'ascii', + }, }; diff --git a/src/views/tools/Settings.vue b/src/views/tools/Settings.vue index 80fd0af..2ec027b 100644 --- a/src/views/tools/Settings.vue +++ b/src/views/tools/Settings.vue @@ -12,17 +12,22 @@ + + + From 07cf32803c4b33192137ad9742d46e92b73c77be Mon Sep 17 00:00:00 2001 From: mrfrase3 Date: Sat, 14 Aug 2021 13:26:25 +0800 Subject: [PATCH 5/5] 3.3.1 --- package.json | 2 +- service-worker.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 737e66e..f734292 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "duinoapp-client", - "version": "3.3.0", + "version": "3.3.1", "author": "Fraser Bullock", "license": "GPL-3.0", "private": true, diff --git a/service-worker.js b/service-worker.js index fbecf91..6c70ce6 100644 --- a/service-worker.js +++ b/service-worker.js @@ -3,7 +3,7 @@ workbox.core.setCacheNameDetails({ prefix: 'd4' }); // Do not touch this line -const LATEST_VERSION = '3.3.0'; +const LATEST_VERSION = '3.3.1'; self.addEventListener('activate', (event) => { console.log(`%c ${LATEST_VERSION} `, 'background: #ddd; color: #0000ff');