@mikrojs/firmware 0.21.0 → 0.21.1-next.20260927212749
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MikroFirmwareConfig.cmake +15 -0
- package/README.md +5 -5
- package/bin/mikro-fw.js +7 -0
- package/components/mikrojs/CMakeLists.txt +110 -87
- package/components/mikrojs/mik_gpio.cpp +1 -1
- package/components/mikrojs/mik_i2c.cpp +4 -4
- package/components/mikrojs/mik_i2s.cpp +5 -5
- package/components/mikrojs/mik_main.cpp +2 -5
- package/components/mikrojs/mik_neopixel.cpp +1 -1
- package/components/mikrojs/mik_nvs_kv.cpp +5 -5
- package/components/mikrojs/mik_ota_env.cpp +2 -3
- package/components/mikrojs/mik_pwm.cpp +1 -1
- package/components/mikrojs/mik_rtc.cpp +16 -12
- package/components/mikrojs/mik_spi.cpp +5 -5
- package/components/mikrojs/mik_uart.cpp +8 -8
- package/components/mikrojs/mik_wifi.cpp +1 -3
- package/components/mikrojs/test/nvs_kv_test.cpp +21 -0
- package/components/mikrojs/test/rtc_test.cpp +74 -3
- package/dist/boards.d.ts +184 -0
- package/dist/boards.d.ts.map +1 -0
- package/dist/boards.js +532 -0
- package/dist/boards.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +57 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs.d.ts +16 -0
- package/dist/inputs.d.ts.map +1 -0
- package/dist/inputs.js +97 -0
- package/dist/inputs.js.map +1 -0
- package/dist/manifest.d.ts +44 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +164 -0
- package/dist/manifest.js.map +1 -0
- package/package.json +37 -16
- package/project.cmake +72 -52
- package/resolve.cmake +36 -0
- package/chips.json +0 -3
- package/cmake.js +0 -9
- package/discover.js +0 -82
- package/index.d.ts +0 -9
- package/index.js +0 -41
- package/prebuilds/esp32-generic/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32-generic/firmware.json +0 -1
- package/prebuilds/esp32-generic/flasher_args.json +0 -24
- package/prebuilds/esp32-generic/mikrojs.bin +0 -0
- package/prebuilds/esp32-generic/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32c3-generic/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c3-generic/firmware.json +0 -1
- package/prebuilds/esp32c3-generic/flasher_args.json +0 -24
- package/prebuilds/esp32c3-generic/mikrojs.bin +0 -0
- package/prebuilds/esp32c3-generic/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32c5-generic/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c5-generic/firmware.json +0 -1
- package/prebuilds/esp32c5-generic/flasher_args.json +0 -24
- package/prebuilds/esp32c5-generic/mikrojs.bin +0 -0
- package/prebuilds/esp32c5-generic/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32c6-generic/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c6-generic/firmware.json +0 -1
- package/prebuilds/esp32c6-generic/flasher_args.json +0 -24
- package/prebuilds/esp32c6-generic/mikrojs.bin +0 -0
- package/prebuilds/esp32c6-generic/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32s3-generic/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32s3-generic/firmware.json +0 -1
- package/prebuilds/esp32s3-generic/flasher_args.json +0 -24
- package/prebuilds/esp32s3-generic/mikrojs.bin +0 -0
- package/prebuilds/esp32s3-generic/partition_table/partition-table.bin +0 -0
- package/resolve.js +0 -64
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# find_package(MikroFirmware COMPONENTS esp32 NO_DEFAULT_PATH); `mikro idf` sets MikroFirmware_DIR.
|
|
2
|
+
# Without a component the package is only found (esp32/test uses its files itself).
|
|
3
|
+
|
|
4
|
+
foreach(_mik_family IN LISTS MikroFirmware_FIND_COMPONENTS)
|
|
5
|
+
if(NOT _mik_family STREQUAL "esp32")
|
|
6
|
+
set(MikroFirmware_FOUND FALSE)
|
|
7
|
+
set(MikroFirmware_NOT_FOUND_MESSAGE
|
|
8
|
+
"@mikrojs/firmware has no component \"${_mik_family}\". It builds for: esp32")
|
|
9
|
+
return()
|
|
10
|
+
endif()
|
|
11
|
+
endforeach()
|
|
12
|
+
|
|
13
|
+
if("esp32" IN_LIST MikroFirmware_FIND_COMPONENTS)
|
|
14
|
+
include("${CMAKE_CURRENT_LIST_DIR}/project.cmake")
|
|
15
|
+
endif()
|
package/README.md
CHANGED
|
@@ -4,18 +4,18 @@ ESP32 firmware package for Mikro.js. Provides the ESP-IDF integration, build sys
|
|
|
4
4
|
|
|
5
5
|
## What's included
|
|
6
6
|
|
|
7
|
-
- **`project.cmake`** - CMake
|
|
7
|
+
- **`MikroFirmwareConfig.cmake`** and **`project.cmake`** - the CMake package that integrates with ESP-IDF. Turns the native modules that the project lists into ESP-IDF components, merges SDK config, and applies partition table defaults.
|
|
8
8
|
- **`components/mikrojs/`** - ESP-IDF component that compiles the Mikro.js runtime, QuickJS engine, and platform-specific C modules.
|
|
9
9
|
- **Default configs** - `sdkconfig.defaults` and `partitions.csv` for common setups.
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
A custom firmware project depends on this package and
|
|
13
|
+
A custom firmware project depends on this package and finds it with `find_package(MikroFirmware REQUIRED COMPONENTS esp32 NO_DEFAULT_PATH)`. `mikro idf` resolves the package from the project through its `cmake` export condition and passes its folder to CMake (see the docs below for the CMakeLists boilerplate):
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
my-firmware/
|
|
17
|
-
├── package.json # depends on @mikrojs/firmware
|
|
18
|
-
├── CMakeLists.txt #
|
|
17
|
+
├── package.json # depends on @mikrojs/firmware and mikro
|
|
18
|
+
├── CMakeLists.txt # find_package(MikroFirmware ...)
|
|
19
19
|
└── main/ # optional: omit it and the package's default main
|
|
20
20
|
├── CMakeLists.txt # (which calls MIK_Main()) is used automatically
|
|
21
21
|
└── main.cpp
|
|
@@ -28,4 +28,4 @@ See the [Custom Firmware](https://mikrojs.dev/develop/custom-firmware) docs for
|
|
|
28
28
|
- Node.js >= 24
|
|
29
29
|
- ESP-IDF >= 6.1 (installed via [EIM](https://docs.espressif.com/projects/idf-im-ui/en/latest/))
|
|
30
30
|
|
|
31
|
-
To build,
|
|
31
|
+
To build, run `pn mikro idf build` in the project. `mikro idf` passes its arguments to `idf.py`, through EIM when ESP-IDF is not active in the shell, and tells CMake where this package is. Plain `idf.py` does not.
|
package/bin/mikro-fw.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// In the Mikro.js repository, run the TypeScript source, so a firmware build
|
|
3
|
+
// never runs a stale dist/. A published package has only dist/.
|
|
4
|
+
import {existsSync} from 'node:fs'
|
|
5
|
+
|
|
6
|
+
const src = new URL('../src/cli.ts', import.meta.url)
|
|
7
|
+
await import(existsSync(src) ? src.href : '../dist/cli.js')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ──
|
|
1
|
+
# ── Package paths ────────────────────────────────────────────────────
|
|
2
2
|
# MIKROJS_PROJECT_DIR is the directory containing package.json and node_modules.
|
|
3
3
|
# Defaults to CMAKE_SOURCE_DIR which works for both:
|
|
4
4
|
# - Monorepo: CMAKE_SOURCE_DIR is esp32/ (has package.json with workspace deps)
|
|
@@ -7,28 +7,18 @@ if(NOT DEFINED MIKROJS_PROJECT_DIR)
|
|
|
7
7
|
set(MIKROJS_PROJECT_DIR "${CMAKE_SOURCE_DIR}")
|
|
8
8
|
endif()
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
# Parse JSON output
|
|
24
|
-
string(JSON MIK_INCLUDE_DIR GET "${_NATIVE_JSON}" "include")
|
|
25
|
-
string(JSON MIK_SRC_DIR GET "${_NATIVE_JSON}" "src")
|
|
26
|
-
string(JSON MIK_RUNTIME_DIR GET "${_NATIVE_JSON}" "runtime")
|
|
27
|
-
string(JSON MIK_SCRIPTS_DIR GET "${_NATIVE_JSON}" "scripts")
|
|
28
|
-
string(JSON MIK_BYTECODE_CMAKE GET "${_NATIVE_JSON}" "bytecodeCmake")
|
|
29
|
-
|
|
30
|
-
# Include quickjs.cmake for QUICKJS_SOURCES, QUICKJS_INCLUDE_DIR, etc.
|
|
31
|
-
include("${QUICKJS_CMAKE_PATH}")
|
|
10
|
+
# The MIK_* paths come from resolve.cmake, which project.cmake includes.
|
|
11
|
+
# ESP-IDF first runs this file in a separate script-mode pass that only
|
|
12
|
+
# collects REQUIRES, where they are unset. That pass reads no SRCS, so only
|
|
13
|
+
# the include below needs a guard; REQUIRES must never depend on them.
|
|
14
|
+
if(MIK_SRC_DIR)
|
|
15
|
+
# Include quickjs.cmake for QUICKJS_SOURCES, QUICKJS_INCLUDE_DIR, etc.
|
|
16
|
+
include("${MIK_QUICKJS_CMAKE}")
|
|
17
|
+
elseif(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
|
18
|
+
message(FATAL_ERROR
|
|
19
|
+
"mikrojs: the @mikrojs/native paths are not set. Include @mikrojs/firmware's "
|
|
20
|
+
"project.cmake in the project's CMakeLists.txt, before project().")
|
|
21
|
+
endif()
|
|
32
22
|
|
|
33
23
|
# nanocbor lives inside the native package
|
|
34
24
|
set(MIK_NANOCBOR_DIR "${MIK_SRC_DIR}/../deps/nanocbor")
|
|
@@ -155,15 +145,15 @@ set_source_files_properties(${QUICKJS_SOURCES} PROPERTIES
|
|
|
155
145
|
COMPILE_OPTIONS "-Wno-unused-but-set-variable")
|
|
156
146
|
|
|
157
147
|
# ── Board and version metadata ──────────────────────────────────────
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
148
|
+
file(READ "${CMAKE_CURRENT_LIST_DIR}/../../package.json" _MIK_PACKAGE)
|
|
149
|
+
string(JSON _MIK_VERSION GET "${_MIK_PACKAGE}" version)
|
|
150
|
+
# The version and the build date change between builds, so only the files that
|
|
151
|
+
# read them get them: the rest of the component neither recompiles nor misses ccache.
|
|
152
|
+
set_property(SOURCE "${MIK_SRC_DIR}/mik_repl.cpp" "${MIK_SRC_DIR}/mik_sys.cpp" "mik_main.cpp"
|
|
153
|
+
"mik_ota_env.cpp" APPEND PROPERTY COMPILE_DEFINITIONS "MIK_FW_VERSION=\"${_MIK_VERSION}\"")
|
|
164
154
|
# Core runtime owns the bare `native:mikro/*` namespace. MIK_PACKAGE_NAME makes
|
|
165
155
|
# MIK_REGISTER_MODULE enforce that every native module here is named
|
|
166
|
-
# "native:mikro/<module>";
|
|
156
|
+
# "native:mikro/<module>"; a package's native module sets its package name.
|
|
167
157
|
target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_PACKAGE_NAME=\"mikro\"")
|
|
168
158
|
# The version is read at configure time, so configure must re-run when the
|
|
169
159
|
# firmware package version changes — otherwise an incremental idf.py build
|
|
@@ -171,24 +161,53 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_PACKAGE_NAME=\"mikro\""
|
|
|
171
161
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
172
162
|
"${CMAKE_CURRENT_LIST_DIR}/../../package.json")
|
|
173
163
|
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
164
|
+
# Board name and description: MIKROJS_BOARD_NAME and MIKROJS_BOARD_DESCRIPTION
|
|
165
|
+
# (resolved by project.cmake), else the name and description in the nearest
|
|
166
|
+
# package.json at or above the project (the app's, for a firmware folder inside
|
|
167
|
+
# an app). The name is compiled in and reported as sys.board.name, and as
|
|
168
|
+
# `board` and `fw` in MSG_READY. The CLI only auto-flashes its bundled image over
|
|
169
|
+
# a device whose `fw` matches that image's name, so other firmware is never
|
|
170
|
+
# replaced by accident. Without a name, `fw` is omitted and the board is
|
|
171
|
+
# <chip>-generic.
|
|
172
|
+
set(_MIK_NAME "${_MIK_BOARD_NAME}")
|
|
173
|
+
set(_MIK_DESCRIPTION "${_MIK_BOARD_DESCRIPTION}")
|
|
174
|
+
set(_MIK_FW_DIR "${CMAKE_SOURCE_DIR}")
|
|
175
|
+
while(NOT EXISTS "${_MIK_FW_DIR}/package.json")
|
|
176
|
+
get_filename_component(_MIK_FW_PARENT "${_MIK_FW_DIR}" DIRECTORY)
|
|
177
|
+
if(_MIK_FW_PARENT STREQUAL _MIK_FW_DIR)
|
|
178
|
+
break()
|
|
179
|
+
endif()
|
|
180
|
+
set(_MIK_FW_DIR "${_MIK_FW_PARENT}")
|
|
181
|
+
endwhile()
|
|
182
|
+
if(EXISTS "${_MIK_FW_DIR}/package.json")
|
|
183
|
+
file(READ "${_MIK_FW_DIR}/package.json" _MIK_PROJECT_PACKAGE)
|
|
184
|
+
foreach(_field IN ITEMS name description)
|
|
185
|
+
string(TOUPPER "${_field}" _FIELD)
|
|
186
|
+
if(_MIK_${_FIELD} STREQUAL "")
|
|
187
|
+
string(JSON _MIK_${_FIELD} ERROR_VARIABLE _MIK_FIELD_ERROR
|
|
188
|
+
GET "${_MIK_PROJECT_PACKAGE}" ${_field})
|
|
189
|
+
if(_MIK_FIELD_ERROR)
|
|
190
|
+
set(_MIK_${_FIELD} "")
|
|
191
|
+
endif()
|
|
192
|
+
endif()
|
|
193
|
+
endforeach()
|
|
190
194
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
191
|
-
"${
|
|
195
|
+
"${_MIK_FW_DIR}/package.json")
|
|
196
|
+
endif()
|
|
197
|
+
# STREQUAL, not if(_MIK_NAME): a project named "0", "off", or "false" would read
|
|
198
|
+
# as a false constant and silently lose its name.
|
|
199
|
+
if(NOT _MIK_NAME STREQUAL "")
|
|
200
|
+
# The device keeps the name in a 64-byte buffer, and a registry rejects a
|
|
201
|
+
# check-in whose board name has another form.
|
|
202
|
+
string(LENGTH "${_MIK_NAME}" _MIK_NAME_LENGTH)
|
|
203
|
+
if(_MIK_NAME_LENGTH GREATER 63 OR NOT _MIK_NAME MATCHES
|
|
204
|
+
"^(@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*(/[a-z0-9]([a-z0-9.-]*[a-z0-9])?)?$")
|
|
205
|
+
message(FATAL_ERROR
|
|
206
|
+
"mikrojs: the board name is \"${_MIK_NAME}\". A board name has at most 63 "
|
|
207
|
+
"characters and the form of a package name, optionally followed by /<board> "
|
|
208
|
+
"(lowercase letters, digits, '.' and '-'). Set MIKROJS_BOARD_NAME.")
|
|
209
|
+
endif()
|
|
210
|
+
target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_BOARD_NAME=\"${_MIK_NAME}\"")
|
|
192
211
|
endif()
|
|
193
212
|
|
|
194
213
|
# UTC build timestamp. `string(TIMESTAMP ... UTC)` honors SOURCE_DATE_EPOCH
|
|
@@ -196,21 +215,24 @@ endif()
|
|
|
196
215
|
# build via its own reconfigure step, so in practice it's close to
|
|
197
216
|
# build-fresh on ESP-IDF).
|
|
198
217
|
string(TIMESTAMP _MIK_BUILD_DATE_UTC "%Y-%m-%dT%H:%M:%SZ" UTC)
|
|
199
|
-
|
|
218
|
+
set_property(SOURCE "${MIK_SRC_DIR}/mik_sys.cpp" APPEND PROPERTY COMPILE_DEFINITIONS
|
|
219
|
+
"MIK_BUILD_DATE_UTC=\"${_MIK_BUILD_DATE_UTC}\"")
|
|
200
220
|
|
|
201
|
-
#
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
message(FATAL_ERROR
|
|
208
|
-
"mikrojs: MIKROJS_BOARD is \"$ENV{MIKROJS_BOARD}\". A board name has at most 47 "
|
|
209
|
-
"characters: lowercase letters, digits, '.' and '-', and it starts and ends with a "
|
|
210
|
-
"letter or digit.")
|
|
211
|
-
endif()
|
|
212
|
-
target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_BOARD_NAME=\"$ENV{MIKROJS_BOARD}\"")
|
|
221
|
+
# firmware.json next to flasher_args.json: what the image is, for the tools
|
|
222
|
+
# that read it without a device (mikro flash, mikro fw pack and build).
|
|
223
|
+
# Written below, once the feature list is known.
|
|
224
|
+
set(_MIK_FW_JSON "{")
|
|
225
|
+
if(NOT _MIK_NAME STREQUAL "")
|
|
226
|
+
string(APPEND _MIK_FW_JSON "\"name\": \"${_MIK_NAME}\", ")
|
|
213
227
|
endif()
|
|
228
|
+
if(NOT _MIK_DESCRIPTION STREQUAL "")
|
|
229
|
+
# Free text: escape it for JSON, on one line.
|
|
230
|
+
string(REPLACE "\\" "\\\\" _MIK_DESCRIPTION "${_MIK_DESCRIPTION}")
|
|
231
|
+
string(REPLACE "\"" "\\\"" _MIK_DESCRIPTION "${_MIK_DESCRIPTION}")
|
|
232
|
+
string(REGEX REPLACE "[\r\n\t]+" " " _MIK_DESCRIPTION "${_MIK_DESCRIPTION}")
|
|
233
|
+
string(APPEND _MIK_FW_JSON "\"description\": \"${_MIK_DESCRIPTION}\", ")
|
|
234
|
+
endif()
|
|
235
|
+
string(APPEND _MIK_FW_JSON "\"chip\": \"${IDF_TARGET}\", \"version\": \"${_MIK_VERSION}\", ")
|
|
214
236
|
|
|
215
237
|
# ── Module lists derived from runtime/modules.json ───────────────────
|
|
216
238
|
# Feature gating for the derived lists: ble-gated modules compile only when
|
|
@@ -227,37 +249,40 @@ else()
|
|
|
227
249
|
set(_MIK_WIFI_FLAG "--wifi=off")
|
|
228
250
|
endif()
|
|
229
251
|
|
|
230
|
-
# Firmware feature list (e.g. "wifi,i2s,ble"), reported in MSG_READY so the
|
|
231
|
-
# host knows which feature-gated builtins this firmware carries.
|
|
232
252
|
execute_process(
|
|
233
|
-
COMMAND node "${MIK_SCRIPTS_DIR}/modules-list.js"
|
|
234
|
-
OUTPUT_VARIABLE
|
|
253
|
+
COMMAND node "${MIK_SCRIPTS_DIR}/modules-list.js" ${_MIK_BLE_FLAG} ${_MIK_WIFI_FLAG}
|
|
254
|
+
OUTPUT_VARIABLE _MIK_LISTS
|
|
235
255
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
236
|
-
RESULT_VARIABLE
|
|
256
|
+
RESULT_VARIABLE _MIK_LISTS_RESULT
|
|
237
257
|
)
|
|
238
|
-
if(NOT
|
|
239
|
-
message(FATAL_ERROR "modules-list.js
|
|
258
|
+
if(NOT _MIK_LISTS_RESULT EQUAL 0)
|
|
259
|
+
message(FATAL_ERROR "modules-list.js failed")
|
|
240
260
|
endif()
|
|
261
|
+
string(JSON _MIK_FEATURES GET "${_MIK_LISTS}" features)
|
|
262
|
+
string(JSON _MIK_MODULES GET "${_MIK_LISTS}" native)
|
|
263
|
+
string(JSON _MIK_BYTECODE_MODULES GET "${_MIK_LISTS}" bytecode)
|
|
264
|
+
|
|
265
|
+
# The features in firmware.json too, so the tools can tell a board's images
|
|
266
|
+
# apart and match the one a device runs.
|
|
267
|
+
set(_MIK_FEATURES_JSON "")
|
|
268
|
+
foreach(_mik_feature IN LISTS _MIK_FEATURES)
|
|
269
|
+
if(NOT _MIK_FEATURES_JSON STREQUAL "")
|
|
270
|
+
string(APPEND _MIK_FEATURES_JSON ", ")
|
|
271
|
+
endif()
|
|
272
|
+
string(APPEND _MIK_FEATURES_JSON "\"${_mik_feature}\"")
|
|
273
|
+
endforeach()
|
|
274
|
+
string(APPEND _MIK_FW_JSON "\"features\": [${_MIK_FEATURES_JSON}]}\n")
|
|
275
|
+
file(WRITE "${CMAKE_BINARY_DIR}/firmware.json" "${_MIK_FW_JSON}")
|
|
276
|
+
|
|
277
|
+
# Firmware feature list (e.g. "wifi,i2s,ble"), reported in MSG_READY so the
|
|
278
|
+
# host knows which feature-gated builtins this firmware carries.
|
|
241
279
|
string(REPLACE ";" "," _MIK_FEATURES "${_MIK_FEATURES}")
|
|
242
280
|
target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_FW_FEATURES=\"${_MIK_FEATURES}\"")
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
RESULT_VARIABLE _MIK_MODULES_RESULT
|
|
249
|
-
)
|
|
250
|
-
if(NOT _MIK_MODULES_RESULT EQUAL 0)
|
|
251
|
-
message(FATAL_ERROR "modules-list.js native failed")
|
|
252
|
-
endif()
|
|
253
|
-
execute_process(
|
|
254
|
-
COMMAND node "${MIK_SCRIPTS_DIR}/modules-list.js" bytecode ${_MIK_BLE_FLAG} ${_MIK_WIFI_FLAG}
|
|
255
|
-
OUTPUT_VARIABLE _MIK_BYTECODE_MODULES
|
|
256
|
-
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
257
|
-
RESULT_VARIABLE _MIK_BYTECODE_RESULT
|
|
258
|
-
)
|
|
259
|
-
if(NOT _MIK_BYTECODE_RESULT EQUAL 0)
|
|
260
|
-
message(FATAL_ERROR "modules-list.js bytecode failed")
|
|
281
|
+
# Without radios there is no WiFi, lwIP or TLS demand to hold heap back for, so
|
|
282
|
+
# the default memReserved drops from 64 KB to 16 KB (mik_app_config.cpp). The
|
|
283
|
+
# CLI's defaultMemReserved follows the same rule.
|
|
284
|
+
if(NOT CONFIG_BT_ENABLED AND NOT CONFIG_MIKROJS_WIFI)
|
|
285
|
+
target_compile_definitions(${COMPONENT_LIB} PRIVATE "MIK_DEFAULT_MEM_RESERVED=16384")
|
|
261
286
|
endif()
|
|
262
287
|
# The lists are read at configure time, so configure must re-run when the
|
|
263
288
|
# table or the script changes.
|
|
@@ -273,8 +298,6 @@ mikrojs_force_include_modules(${_MIK_MODULES})
|
|
|
273
298
|
mikrojs_generate_bytecode(
|
|
274
299
|
RUNTIME_DIR "${MIK_RUNTIME_DIR}"
|
|
275
300
|
MODULES ${_MIK_BYTECODE_MODULES}
|
|
276
|
-
MODULE_PREFIX "mikro"
|
|
277
|
-
SYMBOL_PREFIX "mikro"
|
|
278
301
|
TARGET gen_bytecode
|
|
279
302
|
WORKING_DIRECTORY "${MIKROJS_PROJECT_DIR}"
|
|
280
303
|
)
|
|
@@ -93,7 +93,7 @@ static MIKGpioState* mik__gpio_get(JSContext* ctx, JSValueConst this_val, JSClas
|
|
|
93
93
|
|
|
94
94
|
/* Claims `gpio` for the handle kind, or returns the GpioInUse Result. */
|
|
95
95
|
static JSValue mik__gpio_claim(JSContext* ctx, int gpio, MIKGpioKind kind) {
|
|
96
|
-
return
|
|
96
|
+
return MIK_ClaimGpios(ctx, &gpio, 1, s_owner_names[kind]);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/* Converts a JS value to a pin level. Returns false unless it is the number 0 or 1. */
|
|
@@ -60,7 +60,7 @@ static void mik__i2c_release(MIKI2CState* s) {
|
|
|
60
60
|
i2c_del_master_bus(s->bus);
|
|
61
61
|
s->bus = nullptr;
|
|
62
62
|
const int gpios[] = {s->sda, s->scl};
|
|
63
|
-
|
|
63
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2c");
|
|
64
64
|
s->active = false;
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -129,7 +129,7 @@ static JSValue js_i2c(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
129
129
|
JSValue invalid = mik__gpio_check(ctx, checks, countof(checks));
|
|
130
130
|
if (!JS_IsUndefined(invalid)) return invalid;
|
|
131
131
|
const int gpios[] = {sda, scl};
|
|
132
|
-
JSValue claim_failed =
|
|
132
|
+
JSValue claim_failed = MIK_ClaimGpios(ctx, gpios, countof(gpios), "I2c");
|
|
133
133
|
if (!JS_IsUndefined(claim_failed)) return claim_failed;
|
|
134
134
|
|
|
135
135
|
i2c_master_bus_config_t bus_cfg = {};
|
|
@@ -143,7 +143,7 @@ static JSValue js_i2c(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
143
143
|
i2c_master_bus_handle_t bus = nullptr;
|
|
144
144
|
esp_err_t err = i2c_new_master_bus(&bus_cfg, &bus);
|
|
145
145
|
if (err != ESP_OK) {
|
|
146
|
-
|
|
146
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2c");
|
|
147
147
|
return mik__result_err_named(ctx, "BusInitFailed", "failed to initialize I2C bus %d: %s",
|
|
148
148
|
(int)port, esp_err_to_name(err));
|
|
149
149
|
}
|
|
@@ -151,7 +151,7 @@ static JSValue js_i2c(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
151
151
|
auto* s = static_cast<MIKI2CState*>(calloc(1, sizeof(MIKI2CState)));
|
|
152
152
|
if (!s) {
|
|
153
153
|
i2c_del_master_bus(bus);
|
|
154
|
-
|
|
154
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2c");
|
|
155
155
|
return JS_ThrowOutOfMemory(ctx);
|
|
156
156
|
}
|
|
157
157
|
s->bus = bus;
|
|
@@ -193,7 +193,7 @@ static void mik__i2s_finalizer(JSRuntime* rt, JSValue val) {
|
|
|
193
193
|
i2s_del_channel(s->rx_chan);
|
|
194
194
|
}
|
|
195
195
|
const int gpios[] = MIK__I2S_GPIOS(s);
|
|
196
|
-
|
|
196
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2s");
|
|
197
197
|
}
|
|
198
198
|
for (int i = 0; i < s->tx_count; i++) {
|
|
199
199
|
MIKI2sTxChunk* c = &s->tx_queue[(s->tx_head + i) % MIK_I2S_TX_QUEUE_DEPTH];
|
|
@@ -362,7 +362,7 @@ static JSValue js_i2s(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
362
362
|
s->din = din;
|
|
363
363
|
|
|
364
364
|
const int gpios[] = MIK__I2S_GPIOS(s);
|
|
365
|
-
JSValue claim_failed =
|
|
365
|
+
JSValue claim_failed = MIK_ClaimGpios(ctx, gpios, countof(gpios), "I2s");
|
|
366
366
|
if (!JS_IsUndefined(claim_failed)) {
|
|
367
367
|
free(s);
|
|
368
368
|
return claim_failed;
|
|
@@ -374,7 +374,7 @@ static JSValue js_i2s(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
374
374
|
if (err == ESP_OK && !mik__i2s_track(ctx, s)) err = ESP_ERR_NO_MEM;
|
|
375
375
|
if (err != ESP_OK) {
|
|
376
376
|
mik__i2s_del_channels(s);
|
|
377
|
-
|
|
377
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2s");
|
|
378
378
|
free(s);
|
|
379
379
|
return mik__result_err_named(ctx, "ChannelInitFailed", "I2S init failed: %s",
|
|
380
380
|
esp_err_to_name(err));
|
|
@@ -385,7 +385,7 @@ static JSValue js_i2s(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
385
385
|
if (JS_IsException(obj)) {
|
|
386
386
|
mik__i2s_untrack(ctx, s);
|
|
387
387
|
mik__i2s_del_channels(s);
|
|
388
|
-
|
|
388
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2s");
|
|
389
389
|
free(s);
|
|
390
390
|
return obj;
|
|
391
391
|
}
|
|
@@ -421,7 +421,7 @@ static JSValue js_i2s_end(JSContext* ctx, JSValue this_val, int argc, JSValue* a
|
|
|
421
421
|
mik__i2s_untrack(ctx, s);
|
|
422
422
|
mik__i2s_del_channels(s);
|
|
423
423
|
const int gpios[] = MIK__I2S_GPIOS(s);
|
|
424
|
-
|
|
424
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "I2s");
|
|
425
425
|
s->active = false;
|
|
426
426
|
MIK_DropHandle(ctx, this_val);
|
|
427
427
|
return JS_UNDEFINED;
|
|
@@ -565,8 +565,7 @@ void MIK_Main(void) {
|
|
|
565
565
|
* (USB-Serial/JTAG). `printf` goes to newlib stdio, which
|
|
566
566
|
* ESP-IDF routes to UART0 and is invisible over USB. */
|
|
567
567
|
const char* core_word = chip_info.cores == 1 ? "core" : "cores";
|
|
568
|
-
const
|
|
569
|
-
const char* dev_id = plat->get_device_id ? plat->get_device_id() : nullptr;
|
|
568
|
+
const char* dev_id = MIK_GetPlatform()->get_device_id();
|
|
570
569
|
char banner[160];
|
|
571
570
|
int off = 0;
|
|
572
571
|
#ifdef MIK_FW_VERSION
|
|
@@ -577,9 +576,7 @@ void MIK_Main(void) {
|
|
|
577
576
|
JS_GetVersion(), CONFIG_IDF_TARGET);
|
|
578
577
|
#endif
|
|
579
578
|
/* Stable per-board device id (chip MAC as Crockford Base32). */
|
|
580
|
-
|
|
581
|
-
off += snprintf(banner + off, sizeof(banner) - off, " (%s)", dev_id);
|
|
582
|
-
}
|
|
579
|
+
off += snprintf(banner + off, sizeof(banner) - off, " (%s)", dev_id);
|
|
583
580
|
off += snprintf(banner + off, sizeof(banner) - off, ", %d %s", chip_info.cores, core_word);
|
|
584
581
|
if (has_wifi || has_bt || has_ble) {
|
|
585
582
|
off += snprintf(banner + off, sizeof(banner) - off, ", ");
|
|
@@ -213,7 +213,7 @@ static JSValue js_neopixel(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
213
213
|
JSValue invalid = mik__gpio_check(ctx, &check, 1);
|
|
214
214
|
if (!JS_IsUndefined(invalid)) return invalid;
|
|
215
215
|
const int gpios[] = {gpio};
|
|
216
|
-
JSValue claim_failed =
|
|
216
|
+
JSValue claim_failed = MIK_ClaimGpios(ctx, gpios, 1, "NeoPixel");
|
|
217
217
|
if (!JS_IsUndefined(claim_failed)) return claim_failed;
|
|
218
218
|
|
|
219
219
|
int bytes_per_led = rgbw ? 4 : 3;
|
|
@@ -186,14 +186,14 @@ static JSValue mik__nvs_kv_remove(JSContext* ctx, JSValue this_val, int argc, JS
|
|
|
186
186
|
esp_err_t err = nvs_erase_key(handle, key);
|
|
187
187
|
JS_FreeCString(ctx, key);
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
/* A missing key is already removed, not a failure. */
|
|
190
|
+
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
|
190
191
|
nvs_close(handle);
|
|
191
|
-
return JS_NewBool(ctx,
|
|
192
|
+
return JS_NewBool(ctx, true);
|
|
192
193
|
}
|
|
193
|
-
|
|
194
|
-
nvs_commit(handle);
|
|
194
|
+
if (err == ESP_OK) err = nvs_commit(handle);
|
|
195
195
|
nvs_close(handle);
|
|
196
|
-
return JS_NewBool(ctx,
|
|
196
|
+
return JS_NewBool(ctx, err == ESP_OK);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
static JSValue mik__nvs_kv_clear(JSContext* ctx, JSValue this_val, int argc, JSValue* argv,
|
|
@@ -339,9 +339,8 @@ bool env_read_app_version(void* opaque, char* out, size_t out_len) {
|
|
|
339
339
|
const MIKOtaEnv* mik__ota_env_for(MIKRuntime* rt, int bytecode_version) {
|
|
340
340
|
g_state.rt = rt;
|
|
341
341
|
g_state.bytecode_version = bytecode_version;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
snprintf(g_state.device_id, sizeof(g_state.device_id), "%s", id ? id : "");
|
|
342
|
+
snprintf(g_state.device_id, sizeof(g_state.device_id), "%s",
|
|
343
|
+
MIK_GetPlatform()->get_device_id());
|
|
345
344
|
|
|
346
345
|
MIKOtaEnv& env = g_state.env;
|
|
347
346
|
env = {};
|
|
@@ -232,7 +232,7 @@ static JSValue js_pwm(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
232
232
|
invalid = mik__gpio_check(ctx, &check, 1);
|
|
233
233
|
if (!JS_IsUndefined(invalid)) return invalid;
|
|
234
234
|
const int gpios[] = {gpio};
|
|
235
|
-
JSValue claim_failed =
|
|
235
|
+
JSValue claim_failed = MIK_ClaimGpios(ctx, gpios, 1, "Pwm");
|
|
236
236
|
if (!JS_IsUndefined(claim_failed)) return claim_failed;
|
|
237
237
|
|
|
238
238
|
int ch = mik__pwm_alloc_channel();
|
|
@@ -157,26 +157,29 @@ static JSValue mik__rtc_set(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
157
157
|
auto* hdr = reinterpret_cast<RtcHeader*>(s_rtc_store);
|
|
158
158
|
uint8_t* data = mik__rtc_data();
|
|
159
159
|
|
|
160
|
-
/*
|
|
160
|
+
/* Check space before changing anything, counting the bytes an existing entry would free, so
|
|
161
|
+
* a failed set keeps the old value and a valid CRC */
|
|
161
162
|
uint8_t* next = nullptr;
|
|
162
163
|
uint8_t* found = mik__rtc_find(key, static_cast<uint8_t>(key_len), &next);
|
|
164
|
+
size_t old_size = (found && next) ? static_cast<size_t>(next - found) : 0;
|
|
165
|
+
size_t used = hdr->data_len - old_size;
|
|
166
|
+
size_t needed = mik__rtc_entry_size(static_cast<uint8_t>(key_len), static_cast<uint16_t>(val_len));
|
|
167
|
+
if (used + needed > MIK_RTC_DATA_CAPACITY) {
|
|
168
|
+
JS_FreeCString(ctx, key);
|
|
169
|
+
return mik__result_err(ctx, MIK_ERR_KV_STORAGE_FULL, 0,
|
|
170
|
+
"RTC memory full (need %zu bytes, %zu available)", needed,
|
|
171
|
+
MIK_RTC_DATA_CAPACITY - used);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Remove existing entry if present */
|
|
163
175
|
if (found && next) {
|
|
164
176
|
uint8_t* end = data + hdr->data_len;
|
|
165
177
|
size_t tail = end - next;
|
|
166
178
|
memmove(found, next, tail);
|
|
167
|
-
hdr->data_len -= static_cast<uint16_t>(
|
|
179
|
+
hdr->data_len -= static_cast<uint16_t>(old_size);
|
|
168
180
|
hdr->entry_count--;
|
|
169
181
|
}
|
|
170
182
|
|
|
171
|
-
/* Check space */
|
|
172
|
-
size_t needed = mik__rtc_entry_size(static_cast<uint8_t>(key_len), static_cast<uint16_t>(val_len));
|
|
173
|
-
if (hdr->data_len + needed > MIK_RTC_DATA_CAPACITY) {
|
|
174
|
-
JS_FreeCString(ctx, key);
|
|
175
|
-
return mik__result_err(ctx, MIK_ERR_KV_STORAGE_FULL, 0,
|
|
176
|
-
"RTC memory full (need %zu bytes, %zu available)", needed,
|
|
177
|
-
MIK_RTC_DATA_CAPACITY - hdr->data_len);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
183
|
/* Append new entry: encode CBOR directly into the RTC store */
|
|
181
184
|
uint8_t* write_pos = data + hdr->data_len;
|
|
182
185
|
write_pos[0] = static_cast<uint8_t>(key_len);
|
|
@@ -250,7 +253,8 @@ static JSValue mik__rtc_remove(JSContext* ctx, JSValue this_val, int argc, JSVal
|
|
|
250
253
|
uint8_t* found = mik__rtc_find(key, static_cast<uint8_t>(key_len), &next);
|
|
251
254
|
JS_FreeCString(ctx, key);
|
|
252
255
|
|
|
253
|
-
|
|
256
|
+
/* A missing key is already removed, not a failure. */
|
|
257
|
+
if (!found || !next) return JS_NewBool(ctx, true);
|
|
254
258
|
|
|
255
259
|
uint8_t* end = data + hdr->data_len;
|
|
256
260
|
size_t tail = end - next;
|
|
@@ -34,7 +34,7 @@ static void mik__spi_release(MIKSPIState* s) {
|
|
|
34
34
|
s->device = nullptr;
|
|
35
35
|
spi_bus_free(s->host);
|
|
36
36
|
const int gpios[] = {s->clk, s->mosi, s->miso, s->cs};
|
|
37
|
-
|
|
37
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "Spi");
|
|
38
38
|
s->active = false;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -95,7 +95,7 @@ static JSValue js_spi(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
95
95
|
JSValue invalid = mik__gpio_check(ctx, checks, countof(checks));
|
|
96
96
|
if (!JS_IsUndefined(invalid)) return invalid;
|
|
97
97
|
const int gpios[] = {clk, mosi, miso, cs};
|
|
98
|
-
JSValue claim_failed =
|
|
98
|
+
JSValue claim_failed = MIK_ClaimGpios(ctx, gpios, countof(gpios), "Spi");
|
|
99
99
|
if (!JS_IsUndefined(claim_failed)) return claim_failed;
|
|
100
100
|
|
|
101
101
|
/* SPI2_HOST is 1, SPI3_HOST is 2, etc. — host maps directly */
|
|
@@ -110,7 +110,7 @@ static JSValue js_spi(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
110
110
|
|
|
111
111
|
esp_err_t err = spi_bus_initialize(host_id, &bus_cfg, SPI_DMA_CH_AUTO);
|
|
112
112
|
if (err != ESP_OK) {
|
|
113
|
-
|
|
113
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "Spi");
|
|
114
114
|
return mik__result_err_named(ctx, "BusInitFailed", "SPI bus init failed: %s",
|
|
115
115
|
esp_err_to_name(err));
|
|
116
116
|
}
|
|
@@ -129,7 +129,7 @@ static JSValue js_spi(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
129
129
|
err = spi_bus_add_device(host_id, &dev_cfg, &device);
|
|
130
130
|
if (err != ESP_OK) {
|
|
131
131
|
spi_bus_free(host_id);
|
|
132
|
-
|
|
132
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "Spi");
|
|
133
133
|
return mik__result_err_named(ctx, "AddDeviceFailed", "failed to add SPI device: %s",
|
|
134
134
|
esp_err_to_name(err));
|
|
135
135
|
}
|
|
@@ -138,7 +138,7 @@ static JSValue js_spi(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
138
138
|
if (!s) {
|
|
139
139
|
spi_bus_remove_device(device);
|
|
140
140
|
spi_bus_free(host_id);
|
|
141
|
-
|
|
141
|
+
MIK_ReleaseGpios(gpios, countof(gpios), "Spi");
|
|
142
142
|
return JS_ThrowOutOfMemory(ctx);
|
|
143
143
|
}
|
|
144
144
|
s->device = device;
|