@mikrojs/firmware 0.6.0-pr-70.gd2fdc0d → 0.6.0

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/chips.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "chips": ["esp32", "esp32c3", "esp32c6", "esp32s3"]
2
+ "chips": ["esp32", "esp32c3", "esp32c5", "esp32c6", "esp32s3"]
3
3
  }
@@ -132,7 +132,7 @@ include("${MIK_BYTECODE_CMAKE}")
132
132
 
133
133
  # Force linker to include self-registering native modules
134
134
  set(_MIK_MODULES cbor pin i2c spi http wifi rtc nvs_kv sntp sleep neopixel pwm uart)
135
- set(_MIK_BYTECODE_MODULES cbor env result schema fs http/helpers http/request i2c kv/nvs kv/rtc kv/shared neopixel observable observable/operators pin pwm reader sleep spi sntp stdio stream sys test uart udp wifi)
135
+ set(_MIK_BYTECODE_MODULES cbor env result schema fs http/helpers http/request i2c kv/nvs kv/rtc kv/shared neopixel observable observable/lazy observable/operators pin pwm reader sleep spi sntp stdio stream sys test uart udp wifi)
136
136
  if(CONFIG_BT_ENABLED)
137
137
  list(APPEND _MIK_MODULES ble)
138
138
  list(APPEND _MIK_BYTECODE_MODULES ble)
@@ -3,7 +3,6 @@
3
3
 
4
4
  #include "esp_crt_bundle.h"
5
5
  #include "esp_event.h"
6
- #include "esp_heap_caps.h"
7
6
  #include "esp_http_client.h"
8
7
  #include "esp_log.h"
9
8
  #include "esp_netif.h"
@@ -212,24 +211,18 @@ static void mik__http_task(void* arg) {
212
211
  }
213
212
 
214
213
  {
215
- /* Diagnostic: internal-SRAM contiguous block right before the mbedTLS
216
- * handshake. mbedTLS needs ~16 KB twice (in/out content buffers); when
217
- * `internal_largest` drops near or below that, HTTPS connects fail with
218
- * ESP_ERR_HTTP_CONNECT (0x7002). Tracking this until the JS-heap-on-
219
- * PSRAM work lands; see .claude/plans/internal-sram-budget.md. */
220
- printf("[probe] pre-open: internal_free=%u internal_largest=%u total_free=%u\n",
221
- (unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
222
- (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
223
- (unsigned)heap_caps_get_free_size(MALLOC_CAP_8BIT));
224
214
  esp_err_t err = esp_http_client_open(client, args->req.body_len);
225
215
  if (err != ESP_OK) {
226
- printf("[probe] esp_http_client_open failed: %s (0x%x) "
227
- "internal_free=%u internal_largest=%u\n",
228
- esp_err_to_name(err), (unsigned)err,
229
- (unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
230
- (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
231
- snprintf(error_buf, sizeof(error_buf), "fetch failed: could not connect to %s",
232
- args->req.url);
216
+ int sock_errno = esp_http_client_get_errno(client);
217
+ int tls_code = 0;
218
+ int tls_flags = 0;
219
+ esp_err_t last_tls_err =
220
+ esp_http_client_get_and_clear_last_tls_error(client, &tls_code, &tls_flags);
221
+ snprintf(error_buf, sizeof(error_buf),
222
+ "fetch failed: could not connect to %s (%s, errno=%d, "
223
+ "esp_tls=%s, mbedtls=-0x%04x, flags=0x%x)",
224
+ args->req.url, esp_err_to_name(err), sock_errno,
225
+ esp_err_to_name(last_tls_err), tls_code, tls_flags);
233
226
  have_error = true;
234
227
  goto cleanup;
235
228
  }
@@ -9,6 +9,7 @@
9
9
  #include "freertos/FreeRTOS.h"
10
10
  #include "freertos/queue.h"
11
11
  #include "nvs_flash.h"
12
+ #include "mikrojs/platform.h"
12
13
  #include "private.h"
13
14
  #include "utils.h"
14
15
 
@@ -251,7 +252,29 @@ static void mik__wifi_event_handler(void* arg, esp_event_base_t event_base, int3
251
252
  }
252
253
  }
253
254
 
254
- esp_err_t mik__wifi_ensure_initialized() {
255
+ /* Pick the DHCP hostname for the STA netif: configured `wifi.hostname` from
256
+ * mikro.config.json takes priority; otherwise default to `mikrojs-<device-id>`.
257
+ * Returns true if a hostname was applied. */
258
+ static bool mik__wifi_apply_hostname(JSContext* ctx) {
259
+ if (!s_sta_netif) return false;
260
+ char buf[64];
261
+ const char* hostname = nullptr;
262
+ if (ctx) {
263
+ MIKRuntime* mik_rt = MIK_GetRuntime(ctx);
264
+ if (mik_rt && mik_rt->config.wifi_hostname[0] != '\0') {
265
+ hostname = mik_rt->config.wifi_hostname;
266
+ }
267
+ }
268
+ if (!hostname) {
269
+ const char* dev_id = MIK_GetPlatform()->get_device_id();
270
+ if (!dev_id || !dev_id[0]) return false;
271
+ snprintf(buf, sizeof(buf), "mikrojs-%s", dev_id);
272
+ hostname = buf;
273
+ }
274
+ return esp_netif_set_hostname(s_sta_netif, hostname) == ESP_OK;
275
+ }
276
+
277
+ esp_err_t mik__wifi_ensure_initialized(JSContext* ctx) {
255
278
  if (s_wifi_initialized) return ESP_OK;
256
279
 
257
280
  esp_err_t err = nvs_flash_init();
@@ -272,6 +295,7 @@ esp_err_t mik__wifi_ensure_initialized() {
272
295
  * asserting in esp_netif_create_default_wifi_sta. */
273
296
  if (!s_sta_netif) {
274
297
  s_sta_netif = esp_netif_create_default_wifi_sta();
298
+ if (s_sta_netif) mik__wifi_apply_hostname(ctx);
275
299
  }
276
300
  if (!s_sta_netif) return ESP_FAIL;
277
301
 
@@ -310,8 +334,8 @@ fail_after_init:
310
334
 
311
335
  /* Start the WiFi radio. Deferred from init so the radio is not active
312
336
  * until connect() or scan() is actually called. */
313
- static esp_err_t mik__wifi_ensure_started() {
314
- esp_err_t err = mik__wifi_ensure_initialized();
337
+ static esp_err_t mik__wifi_ensure_started(JSContext* ctx) {
338
+ esp_err_t err = mik__wifi_ensure_initialized(ctx);
315
339
  if (err != ESP_OK) return err;
316
340
  if (s_wifi_started) return ESP_OK;
317
341
 
@@ -374,7 +398,7 @@ static JSClassDef mik_wifi_classdef = {
374
398
  };
375
399
 
376
400
  static JSValue mik__wifi_constructor(JSContext* ctx, JSValue new_target, int argc, JSValue* argv) {
377
- esp_err_t err = mik__wifi_ensure_initialized();
401
+ esp_err_t err = mik__wifi_ensure_initialized(ctx);
378
402
  if (err != ESP_OK) {
379
403
  return JS_ThrowInternalError(ctx, "WiFi init failed: %s", esp_err_to_name(err));
380
404
  }
@@ -386,7 +410,7 @@ static JSValue mik__wifi_connect(JSContext* ctx, JSValue this_val, int argc, JSV
386
410
  return mik__result_err_tag(ctx, "CountryNotSet");
387
411
  }
388
412
 
389
- esp_err_t start_err = mik__wifi_ensure_started();
413
+ esp_err_t start_err = mik__wifi_ensure_started(ctx);
390
414
  if (start_err != ESP_OK) {
391
415
  return mik__result_err_named(ctx, "StartFailed",
392
416
  "Failed to start WiFi radio: %s", esp_err_to_name(start_err));
@@ -478,7 +502,7 @@ static JSValue mik__wifi_scan(JSContext* ctx, JSValue this_val, int argc, JSValu
478
502
  return mik__result_err_tag(ctx, "CountryNotSet");
479
503
  }
480
504
 
481
- esp_err_t start_err = mik__wifi_ensure_started();
505
+ esp_err_t start_err = mik__wifi_ensure_started(ctx);
482
506
  if (start_err != ESP_OK) {
483
507
  return mik__result_err_named(ctx, "StartFailed",
484
508
  "Failed to start WiFi radio: %s", esp_err_to_name(start_err));
@@ -631,22 +655,6 @@ static JSValue mik__wifi_get_hostname(JSContext* ctx, JSValue this_val, int argc
631
655
  return JS_NewString(ctx, hostname);
632
656
  }
633
657
 
634
- static JSValue mik__wifi_set_hostname(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
635
- if (!s_sta_netif) {
636
- return mik__result_err_tag(ctx, "NotInitialized");
637
- }
638
- const char* hostname = JS_ToCString(ctx, argv[0]);
639
- if (!hostname) return JS_EXCEPTION;
640
-
641
- esp_err_t err = esp_netif_set_hostname(s_sta_netif, hostname);
642
- JS_FreeCString(ctx, hostname);
643
- if (err != ESP_OK) {
644
- return mik__result_err_named(ctx, "SetFailed",
645
- "Failed to set hostname: %s", esp_err_to_name(err));
646
- }
647
- return mik__result_ok_void(ctx);
648
- }
649
-
650
658
  static JSValue mik__wifi_get_ip_config(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
651
659
  if (!s_sta_netif) {
652
660
  return JS_UNDEFINED;
@@ -746,7 +754,7 @@ static JSValue mik__wifi_ap_start(JSContext* ctx, JSValue this_val, int argc, JS
746
754
  return mik__result_err_tag(ctx, "CountryNotSet");
747
755
  }
748
756
 
749
- esp_err_t err = mik__wifi_ensure_started();
757
+ esp_err_t err = mik__wifi_ensure_started(ctx);
750
758
  if (err != ESP_OK) {
751
759
  return mik__result_err_named(ctx, "StartFailed",
752
760
  "Failed to start WiFi radio: %s", esp_err_to_name(err));
@@ -952,26 +960,11 @@ static JSValue mik__wifi_get_country(JSContext* ctx, JSValue this_val, int argc,
952
960
  return JS_NewStringLen(ctx, cc, 2);
953
961
  }
954
962
 
955
- static JSValue mik__wifi_set_country(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
956
- const char* cc = JS_ToCString(ctx, argv[0]);
957
- if (!cc) return JS_EXCEPTION;
958
-
959
- esp_err_t err = esp_wifi_set_country_code(cc, true);
960
- JS_FreeCString(ctx, cc);
961
-
962
- if (err != ESP_OK) {
963
- return mik__result_err_named(ctx, "SetFailed",
964
- "Failed to set WiFi country code: %s", esp_err_to_name(err));
965
- }
966
- s_country_configured = true;
967
- return mik__result_ok_void(ctx);
968
- }
969
-
970
963
  /* ── TX power ──────────────────────────────────────────────────────── */
971
964
 
972
965
  static JSValue mik__wifi_get_tx_power(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
973
966
  /* esp_wifi_get_max_tx_power requires the radio to be started. */
974
- esp_err_t start_err = mik__wifi_ensure_started();
967
+ esp_err_t start_err = mik__wifi_ensure_started(ctx);
975
968
  if (start_err != ESP_OK) {
976
969
  return mik__result_err_named(ctx, "GetFailed",
977
970
  "Failed to get TX power: %s", esp_err_to_name(start_err));
@@ -988,7 +981,7 @@ static JSValue mik__wifi_get_tx_power(JSContext* ctx, JSValue this_val, int argc
988
981
  static JSValue mik__wifi_set_tx_power(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
989
982
  double dbm;
990
983
  if (JS_ToFloat64(ctx, &dbm, argv[0])) return JS_EXCEPTION;
991
- esp_err_t start_err = mik__wifi_ensure_started();
984
+ esp_err_t start_err = mik__wifi_ensure_started(ctx);
992
985
  if (start_err != ESP_OK) {
993
986
  return mik__result_err_named(ctx, "SetFailed",
994
987
  "Failed to set TX power: %s", esp_err_to_name(start_err));
@@ -1097,7 +1090,6 @@ static const JSCFunctionListEntry mik__wifi_proto_funcs[] = {
1097
1090
  MIK_CFUNC_DEF("off", 2, mik__wifi_off),
1098
1091
  MIK_CFUNC_DEF("mac", 0, mik__wifi_mac),
1099
1092
  MIK_CFUNC_DEF("getHostname", 0, mik__wifi_get_hostname),
1100
- MIK_CFUNC_DEF("setHostname", 1, mik__wifi_set_hostname),
1101
1093
  MIK_CFUNC_DEF("getIpConfig", 0, mik__wifi_get_ip_config),
1102
1094
  MIK_CFUNC_DEF("setIpConfig", 1, mik__wifi_set_ip_config),
1103
1095
  MIK_CFUNC_DEF("apStart", 1, mik__wifi_ap_start),
@@ -1108,7 +1100,6 @@ static const JSCFunctionListEntry mik__wifi_proto_funcs[] = {
1108
1100
  MIK_CFUNC_DEF("getPowerSave", 0, mik__wifi_get_power_save),
1109
1101
  MIK_CFUNC_DEF("setPowerSave", 1, mik__wifi_set_power_save),
1110
1102
  MIK_CFUNC_DEF("getCountry", 0, mik__wifi_get_country),
1111
- MIK_CFUNC_DEF("setCountry", 1, mik__wifi_set_country),
1112
1103
  MIK_CFUNC_DEF("getTxPower", 0, mik__wifi_get_tx_power),
1113
1104
  MIK_CFUNC_DEF("setTxPower", 1, mik__wifi_set_tx_power),
1114
1105
  MIK_CFUNC_DEF("getRssiThreshold", 0, mik__wifi_get_rssi_threshold),
@@ -28,11 +28,15 @@ static void setup() {
28
28
  vTaskDelay(pdMS_TO_TICKS(100));
29
29
  rt = MIK_NewRuntime();
30
30
  ctx = MIK_GetJSContext(rt);
31
+ /* Country is config-only — populate it directly so wifi ops that gate on
32
+ * mik__wifi_try_auto_country can proceed. */
33
+ MIKConfig cfg;
34
+ MIK_DefaultConfig(&cfg);
35
+ snprintf(cfg.wifi_country, sizeof(cfg.wifi_country), "US");
36
+ MIK_SetConfig(rt, &cfg);
31
37
  /* WiFi module is self-registered and lazily initialized.
32
- * The import below triggers init + loop consumer registration.
33
- * Then drain any stale events from previous tests. */
34
- const char* init_code =
35
- "import { Wifi } from \"native:wifi\"; new Wifi().setCountry(\"US\");";
38
+ * The import below triggers init + loop consumer registration. */
39
+ const char* init_code = "import { Wifi } from \"native:wifi\"; new Wifi();";
36
40
  JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test-setup", init_code, strlen(init_code));
37
41
  if (!JS_IsException(ret)) {
38
42
  JS_FreeValue(ctx, ret);
@@ -332,14 +336,22 @@ TEST_CASE("Wifi mac returns a valid MAC address format", "[wifi]") {
332
336
  teardown();
333
337
  }
334
338
 
335
- TEST_CASE("Wifi hostname get/set round-trips", "[wifi]") {
336
- setup();
339
+ TEST_CASE("Wifi hostname reflects configured wifi.hostname", "[wifi]") {
340
+ /* Custom setup with wifi.hostname populated. Mirrors setup() but injects
341
+ * a hostname before the lazy netif init runs. */
342
+ esp_wifi_disconnect();
343
+ vTaskDelay(pdMS_TO_TICKS(100));
344
+ rt = MIK_NewRuntime();
345
+ ctx = MIK_GetJSContext(rt);
346
+ MIKConfig cfg;
347
+ MIK_DefaultConfig(&cfg);
348
+ snprintf(cfg.wifi_country, sizeof(cfg.wifi_country), "US");
349
+ snprintf(cfg.wifi_hostname, sizeof(cfg.wifi_hostname), "test-device");
350
+ MIK_SetConfig(rt, &cfg);
337
351
 
338
352
  JSValue ret = eval_module(R"(
339
353
  import { Wifi } from "native:wifi";
340
- const wifi = new Wifi();
341
- wifi.setHostname("test-device");
342
- globalThis.__hostname = wifi.getHostname();
354
+ globalThis.__hostname = new Wifi().getHostname();
343
355
  )");
344
356
  TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
345
357
 
@@ -446,13 +458,15 @@ TEST_CASE("Wifi power save get/set round-trips", "[wifi]") {
446
458
  teardown();
447
459
  }
448
460
 
449
- TEST_CASE("Wifi country get/set round-trips", "[wifi]") {
461
+ TEST_CASE("Wifi country reflects configured wifi.country", "[wifi]") {
450
462
  setup();
451
463
 
464
+ /* setup() populated wifi_country = "US"; trigger a wifi op so
465
+ * mik__wifi_try_auto_country applies it, then read back via getCountry. */
452
466
  JSValue ret = eval_module(R"(
453
467
  import { Wifi } from "native:wifi";
454
468
  const wifi = new Wifi();
455
- wifi.setCountry("US");
469
+ wifi.scan();
456
470
  globalThis.__cc = wifi.getCountry();
457
471
  )");
458
472
  TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/firmware",
3
- "version": "0.6.0-pr-70.gd2fdc0d",
3
+ "version": "0.6.0",
4
4
  "description": "Mikro.js ESP32 firmware: ESP-IDF component, build tools, and project template",
5
5
  "keywords": [
6
6
  "esp-idf",
@@ -51,8 +51,8 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "esbuild": "^0.28.0",
54
- "@mikrojs/native": "0.6.0-pr-70.gd2fdc0d",
55
- "@mikrojs/quickjs": "0.6.0-pr-70.gd2fdc0d"
54
+ "@mikrojs/native": "0.6.0",
55
+ "@mikrojs/quickjs": "0.6.0"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=24.0.0"
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ {
2
+ "write_flash_args" : [ "--flash-mode", "dio",
3
+ "--flash-size", "4MB",
4
+ "--flash-freq", "80m" ],
5
+ "flash_settings" : {
6
+ "flash_mode": "dio",
7
+ "flash_size": "4MB",
8
+ "flash_freq": "80m"
9
+ },
10
+ "flash_files" : {
11
+ "0x2000" : "bootloader/bootloader.bin",
12
+ "0x8000" : "partition_table/partition-table.bin",
13
+ "0x10000" : "mikrojs.bin"
14
+ },
15
+ "bootloader" : { "offset" : "0x2000", "file" : "bootloader/bootloader.bin", "encrypted" : "false" },
16
+ "partition-table" : { "offset" : "0x8000", "file" : "partition_table/partition-table.bin", "encrypted" : "false" },
17
+ "app" : { "offset" : "0x10000", "file" : "mikrojs.bin", "encrypted" : "false" },
18
+ "extra_esptool_args" : {
19
+ "after" : "hard-reset",
20
+ "before" : "default-reset",
21
+ "stub" : true,
22
+ "chip" : "esp32c5"
23
+ }
24
+ }
Binary file
Binary file
Binary file
@@ -93,7 +93,10 @@ CONFIG_VFS_SUPPORT_SELECT=n
93
93
 
94
94
  # --- Assertions & error strings ---
95
95
  CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
96
- CONFIG_ESP_ERR_TO_NAME_LOOKUP=n
96
+ # Keep esp_err_to_name() readable. Costs ~2 KB flash; without this, runtime
97
+ # errors surface as "UNKNOWN ERROR" instead of e.g. ESP_ERR_NO_MEM, which
98
+ # makes diagnosing on-device failures unnecessarily painful.
99
+ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
97
100
  CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=0
98
101
 
99
102
  # --- lwIP ---
@@ -0,0 +1,15 @@
1
+ # ESP32-C5: same console story as C6 (UART primary, USB-Serial/JTAG
2
+ # secondary). See sdkconfig.defaults.esp32c6 for the reasoning around
3
+ # avoiding ESP-IDF's USB-JTAG VFS at boot. mikrojs installs the
4
+ # USB-Serial/JTAG driver itself for REPL/TLV transport; auto-detection
5
+ # latches to whichever interface receives the first byte.
6
+ CONFIG_ESP_CONSOLE_UART_DEFAULT=y
7
+ CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n
8
+ CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y
9
+
10
+ # Enable PSRAM. Quad SPI is the only supported mode on ESP32-C5.
11
+ # IGNORE_NOTFOUND so the same firmware boots on bare C5 modules too,
12
+ # falling back to internal SRAM only.
13
+ CONFIG_SPIRAM=y
14
+ CONFIG_SPIRAM_USE_MALLOC=y
15
+ CONFIG_SPIRAM_IGNORE_NOTFOUND=y