@mikrojs/firmware 0.20.0 → 0.20.2-next.20260907205905

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.
@@ -41,6 +41,17 @@ if(CONFIG_BT_ENABLED)
41
41
  list(APPEND _MIK_BLE_SRCS "mik_ble.cpp" "mik_ble_c_shim.c")
42
42
  endif()
43
43
 
44
+ # The WiFi driver and the `wifi` native module only compile under
45
+ # CONFIG_MIKROJS_WIFI. Nothing else references esp_wifi, so leaving
46
+ # mik_wifi.cpp out is what drops net80211, pp and wpa_supplicant from the
47
+ # link (about 20 KB of static RAM). `esp_wifi` stays in REQUIRES
48
+ # unconditionally: IDF expands REQUIRES before sdkconfig is loaded, so a
49
+ # CONFIG_-gated entry would always be empty there.
50
+ set(_MIK_WIFI_SRCS "")
51
+ if(CONFIG_MIKROJS_WIFI)
52
+ list(APPEND _MIK_WIFI_SRCS "mik_wifi.cpp")
53
+ endif()
54
+
44
55
  # mik_ota.cpp includes <miniz.h> for tinfl (gunzip). On the esp32c6 the
45
56
  # implementation is in the chip mask ROM and the header comes from the
46
57
  # always-available `esp_rom` common dependency, so no REQUIRES entry is needed.
@@ -118,7 +129,7 @@ idf_component_register(
118
129
  "mik_spi.cpp"
119
130
  "mik_sntp.cpp"
120
131
  "mik_uart.cpp"
121
- "mik_wifi.cpp"
132
+ ${_MIK_WIFI_SRCS}
122
133
  )
123
134
 
124
135
  # QuickJS headers as SYSTEM includes (suppresses warnings)
@@ -195,8 +206,11 @@ endif()
195
206
  include("${MIK_BYTECODE_CMAKE}")
196
207
 
197
208
  # Force linker to include self-registering native modules
198
- set(_MIK_MODULES cbor pin i2c i2s spi http http_server wifi rtc nvs_kv sntp sleep neopixel pwm uart ota)
209
+ set(_MIK_MODULES cbor pin i2c i2s spi http http_server rtc nvs_kv sntp sleep neopixel pwm uart ota)
199
210
  list(APPEND _MIK_MODULES ota_client)
211
+ if(CONFIG_MIKROJS_WIFI)
212
+ list(APPEND _MIK_MODULES wifi)
213
+ endif()
200
214
  set(_MIK_BYTECODE_MODULES abort cbor env result schema fs http/server i2c i2s kv/nvs kv/rtc kv/shared module neopixel observable observable/lazy observable/operators ota ota/client ota/config pin pwm reader sleep spi sntp stdio stream sys test uart udp watchdog)
201
215
  if(CONFIG_BT_ENABLED)
202
216
  list(APPEND _MIK_MODULES ble)
@@ -22,6 +22,32 @@ menu "mikrojs console"
22
22
 
23
23
  endmenu
24
24
 
25
+ menu "mikrojs WiFi"
26
+
27
+ config MIKROJS_WIFI
28
+ bool "Compile the WiFi driver and the mikro/wifi module"
29
+ default y
30
+ depends on SOC_WIFI_SUPPORTED
31
+ help
32
+ When enabled, the firmware links the ESP-IDF WiFi driver
33
+ (net80211, pp, wpa_supplicant) and provides the `mikro/wifi`
34
+ module. The driver takes about 20 KB of internal RAM at boot
35
+ whether or not the radio is ever started.
36
+
37
+ Disable for apps that do all their networking over another
38
+ link (a cellular modem on a UART, for instance) to get that
39
+ RAM back. `import 'mikro/wifi'` then throws ReferenceError:
40
+ Native module 'native:mikro/wifi' is not available, the boot
41
+ banner lists no WiFi radio and BoardInfo.features omits
42
+ "wifi". HTTP and SNTP still compile; they need a network
43
+ interface from elsewhere.
44
+
45
+ IDF's own CONFIG_ESP_WIFI_ENABLED cannot do this: it has no
46
+ prompt on WiFi-capable chips, so setting it to n in
47
+ sdkconfig.defaults is silently reverted to y.
48
+
49
+ endmenu
50
+
25
51
  menu "mikrojs runtime memory"
26
52
 
27
53
  config MIKROJS_QUICKJS_HEAP_PSRAM
@@ -320,12 +320,14 @@ void MIK_Main(void) {
320
320
  * the low-level radio driver is linked, even when no higher-level
321
321
  * stack is compiled in. WiFi and BLE each require both silicon support
322
322
  * and a corresponding `mikrojs/wifi`/`mikrojs/ble` JS module — we gate
323
- * on the CONFIG_* flags as a proxy. 802.15.4 is deliberately omitted
324
- * until a `mikrojs/zigbee` or `mikrojs/thread` module ships. */
323
+ * on the flags that compile those modules in: CONFIG_MIKROJS_WIFI (the
324
+ * component's own switch, since IDF forces CONFIG_ESP_WIFI_ENABLED on
325
+ * for capable silicon) and CONFIG_BT_ENABLED. 802.15.4 is deliberately
326
+ * omitted until a `mikrojs/zigbee` or `mikrojs/thread` module ships. */
325
327
  esp_chip_info_t chip_info;
326
328
  esp_chip_info(&chip_info);
327
329
 
328
- #if CONFIG_ESP_WIFI_ENABLED
330
+ #if CONFIG_MIKROJS_WIFI
329
331
  bool has_wifi = (chip_info.features & CHIP_FEATURE_WIFI_BGN) != 0;
330
332
  #else
331
333
  bool has_wifi = false;
@@ -744,24 +746,19 @@ void MIK_Main(void) {
744
746
  if (safe_mode) {
745
747
  ESP_LOGW(TAG, "Safe mode: skipping entry point %s", app_config.entry_point);
746
748
  } else {
747
- /* Catch async fatals (unhandled rejections) during an OTA trial. */
749
+ /* Catch fatals during an OTA trial: MIK_RunEntry hands a synchronous
750
+ * throw to this handler itself, the loop's rejection flush hands it
751
+ * unhandled rejections. Both note the failure and arm the panic
752
+ * restart so reconcile reverts on the next (clean-looking) boot. */
748
753
  MIK_SetErrorHandler(mik_rt, ota_trial_error_handler, nullptr);
749
- char entry_err[160] = {0};
750
- int rc = MIK_RunEntryErr(mik_rt, app_config.entry_point, entry_err, sizeof(entry_err));
754
+ int rc = MIK_RunEntry(mik_rt, app_config.entry_point);
751
755
  if (rc == -EINVAL) {
752
756
  ESP_LOGW(TAG, "No entry point configured (no \"main\" field in package.json)");
753
757
  } else if (rc == -ENOENT) {
754
758
  ESP_LOGW(TAG, "Entry point not found: %s", app_config.entry_point);
755
759
  } else if (rc == -EFAULT) {
760
+ /* Already reported to the console by the runtime. */
756
761
  ESP_LOGE(TAG, "Failed to evaluate %s", app_config.entry_point);
757
- /* A synchronous top-level throw doesn't reach the rejection handler
758
- * above. If this is a trial build, flag it and arm the panic restart
759
- * so reconcile reverts on the next (clean-looking) boot. */
760
- if (mik__ota_in_trial()) {
761
- mik__ota_note_trial_failure(entry_err[0] ? entry_err
762
- : "entry failed to evaluate");
763
- MIK_Stop(mik_rt);
764
- }
765
762
  }
766
763
  }
767
764
  }
@@ -75,6 +75,10 @@ struct MIKOtaClientState {
75
75
  * only copy left after ota.reconcile() ran. settle() marks it delivered. */
76
76
  bool has_last_install = false;
77
77
  MIKOtaDiagnostic last_install = {};
78
+ /* Set by report() when the stored decline made it into the body, so settle()
79
+ * clears only a record the registry has seen. A read that failed leaves the
80
+ * record for the next round. */
81
+ bool decline_reported = false;
78
82
  };
79
83
 
80
84
  int mik__ota_client_slot = -1;
@@ -350,7 +354,8 @@ JSValue kv_string_or_undefined(JSContext* ctx, const char* key) {
350
354
  MIKOtaClientState* state = state_of(ctx);
351
355
  if (!state || !state->env || !state->env->kv_get_str) return JS_UNDEFINED;
352
356
  char buf[256] = {};
353
- if (!state->env->kv_get_str(state->env->opaque, key, buf, sizeof(buf)) || buf[0] == '\0') {
357
+ if (state->env->kv_get_str(state->env->opaque, key, buf, sizeof(buf)) != MIK_OTA_KV_OK ||
358
+ buf[0] == '\0') {
354
359
  return JS_UNDEFINED;
355
360
  }
356
361
  return JS_NewString(ctx, buf);
@@ -911,7 +916,8 @@ JSValue ota_report(JSContext* ctx, JSValue, int, JSValue*) {
911
916
  * decline(). Without it a registry cannot tell a device still working
912
917
  * through a download from one that has stopped. */
913
918
  MIKOtaDeclineRecord declined;
914
- if (mikrojs::MIKOtaStore(env).GetDecline(&declined)) {
919
+ state->decline_reported = mikrojs::MIKOtaStore(env).GetDecline(&declined);
920
+ if (state->decline_reported) {
915
921
  JSValue last = JS_NewObject(ctx);
916
922
  JS_SetPropertyStr(ctx, last, "checksum", JS_NewString(ctx, declined.checksum));
917
923
  JS_SetPropertyStr(ctx, last, "reason", JS_NewString(ctx, declined.reason));
@@ -960,10 +966,13 @@ JSValue ota_settle(JSContext* ctx, JSValue, int argc, JSValue* argv) {
960
966
  /* Confirm before the deliveries, as the built-in does: it must settle the
961
967
  * document held BEFORE this round, never the one about to be armed. */
962
968
  mikrojs::mik__ota_policy_confirm(state->env);
963
- /* The round completed, so the cached install report and the stored decline
964
- * record were delivered. */
969
+ /* The round completed, so the cached install report was delivered, and so
970
+ * was the stored decline record if report() carried it. */
965
971
  state->has_last_install = false;
966
- mikrojs::MIKOtaStore(state->env).ClearDecline();
972
+ if (state->decline_reported) {
973
+ mikrojs::MIKOtaStore(state->env).ClearDecline();
974
+ state->decline_reported = false;
975
+ }
967
976
 
968
977
  bool renamed = false;
969
978
  if (usable) {
@@ -105,11 +105,13 @@ bool env_kv_set_blob(void*, const char* key, const uint8_t* data, size_t len) {
105
105
  return kv_write_blob(key, data, len);
106
106
  }
107
107
 
108
- bool env_kv_get_str(void*, const char* key, char* out, size_t max_len) {
108
+ MIKOtaKvStatus env_kv_get_str(void*, const char* key, char* out, size_t max_len) {
109
109
  uint8_t buf[320];
110
110
  size_t len = sizeof(buf);
111
- if (kv_read_blob(key, buf, &len) != MIK_OTA_KV_OK) return false;
112
- return mik__kv_decode_str(buf, len, out, max_len);
111
+ MIKOtaKvStatus status = kv_read_blob(key, buf, &len);
112
+ if (status != MIK_OTA_KV_OK) return status;
113
+ /* Stored bytes that will not decode are corruption, not absence. */
114
+ return mik__kv_decode_str(buf, len, out, max_len) ? MIK_OTA_KV_OK : MIK_OTA_KV_ERROR;
113
115
  }
114
116
 
115
117
  bool env_kv_set_str(void*, const char* key, const char* value) {
@@ -119,11 +121,12 @@ bool env_kv_set_str(void*, const char* key, const char* value) {
119
121
  return kv_write_blob(key, buf, needed);
120
122
  }
121
123
 
122
- bool env_kv_get_i32(void*, const char* key, int32_t* out) {
124
+ MIKOtaKvStatus env_kv_get_i32(void*, const char* key, int32_t* out) {
123
125
  uint8_t buf[16];
124
126
  size_t len = sizeof(buf);
125
- if (kv_read_blob(key, buf, &len) != MIK_OTA_KV_OK) return false;
126
- return mik__kv_decode_i32(buf, len, out);
127
+ MIKOtaKvStatus status = kv_read_blob(key, buf, &len);
128
+ if (status != MIK_OTA_KV_OK) return status;
129
+ return mik__kv_decode_i32(buf, len, out) ? MIK_OTA_KV_OK : MIK_OTA_KV_ERROR;
127
130
  }
128
131
 
129
132
  bool env_kv_set_i32(void*, const char* key, int32_t value) {
@@ -1,5 +1,7 @@
1
1
  # ble_test.cpp references mik__ble_consume which is only defined when
2
- # NimBLE (CONFIG_BT_ENABLED) is available. Exclude it for BT-less builds.
2
+ # NimBLE (CONFIG_BT_ENABLED) is available, and wifi_test.cpp references
3
+ # mik__wifi_consume which needs CONFIG_MIKROJS_WIFI. Exclude each for
4
+ # builds without its stack.
3
5
  set(_MIK_TEST_SRCS
4
6
  abort_test.cpp
5
7
  fs_js_test.cpp
@@ -25,11 +27,13 @@ set(_MIK_TEST_SRCS
25
27
  timers_test.cpp
26
28
  udp_test.cpp
27
29
  watchdog_test.cpp
28
- wifi_test.cpp
29
30
  )
30
31
  if(CONFIG_BT_ENABLED)
31
32
  list(APPEND _MIK_TEST_SRCS ble_test.cpp)
32
33
  endif()
34
+ if(CONFIG_MIKROJS_WIFI)
35
+ list(APPEND _MIK_TEST_SRCS wifi_test.cpp)
36
+ endif()
33
37
 
34
38
  idf_component_register(SRCS ${_MIK_TEST_SRCS}
35
39
  INCLUDE_DIRS "."
@@ -0,0 +1,2 @@
1
+ .build/
2
+ .cache/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/firmware",
3
- "version": "0.20.0",
3
+ "version": "0.20.2-next.20260907205905",
4
4
  "description": "Mikro.js ESP32 firmware: ESP-IDF component, build tools, and project template",
5
5
  "keywords": [
6
6
  "esp-idf",
@@ -46,9 +46,9 @@
46
46
  "./package.json": "./package.json"
47
47
  },
48
48
  "dependencies": {
49
- "esbuild": "^0.28.0",
50
- "@mikrojs/native": "0.20.0",
51
- "@mikrojs/quickjs": "0.20.0"
49
+ "@mikrojs/native": "0.20.2-next.20260907205905+8b6f502",
50
+ "@mikrojs/quickjs": "0.20.2-next.20260907205905+8b6f502",
51
+ "esbuild": "^0.28.2"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=24.0.0"
Binary file
Binary file
Binary file
Binary file
Binary file