@mikrojs/firmware 0.8.0-pr-115.g87a99f9 → 0.8.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/components/mikrojs/include/mikrojs_esp32.h +10 -0
- package/components/mikrojs/mik_serial_io.cpp +111 -25
- package/components/mikrojs/mik_sleep.cpp +257 -147
- package/components/mikrojs/test/sleep_test.cpp +13 -85
- package/package.json +3 -3
- package/prebuilds/esp32/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32/mikrojs.bin +0 -0
- package/prebuilds/esp32c3/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c3/mikrojs.bin +0 -0
- package/prebuilds/esp32c5/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c5/mikrojs.bin +0 -0
- package/prebuilds/esp32c6/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c6/mikrojs.bin +0 -0
- package/prebuilds/esp32s3/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32s3/mikrojs.bin +0 -0
|
@@ -64,6 +64,16 @@ void mik_logfile_resume(void);
|
|
|
64
64
|
/* Serial binary I/O (mik_serial_io.cpp) */
|
|
65
65
|
void mik__serial_binary_begin_no_echo(void);
|
|
66
66
|
|
|
67
|
+
/* Detach the USB Serial/JTAG peripheral from the host (uninstalls the
|
|
68
|
+
* driver and disables the PHY pad) so the CLI sees a clean disconnect
|
|
69
|
+
* before light sleep. No-op when the active console isn't USJ. */
|
|
70
|
+
void mik__serial_io_detach_usb(void);
|
|
71
|
+
|
|
72
|
+
/* Re-attach the USB Serial/JTAG peripheral after a prior detach. The
|
|
73
|
+
* host re-enumerates the device. No-op when the active console isn't
|
|
74
|
+
* USJ. */
|
|
75
|
+
void mik__serial_io_attach_usb(void);
|
|
76
|
+
|
|
67
77
|
/* Shared NVS helpers (mik_serial_io.cpp) */
|
|
68
78
|
extern const char* MIK__NVS_NS_ENV; /* env var values */
|
|
69
79
|
extern const char* MIK__NVS_NS_SEC; /* secret-flag markers (u8) */
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
19
19
|
#include "driver/usb_serial_jtag.h"
|
|
20
|
+
#include "esp_rom_sys.h"
|
|
21
|
+
#include "hal/usb_serial_jtag_ll.h"
|
|
20
22
|
/* mikrojs owns the USB-Serial/JTAG peripheral directly. Leaving
|
|
21
23
|
* CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y would have ESP-IDF register a
|
|
22
24
|
* VFS and write to the TX FIFO from stdio, racing our driver ISR. */
|
|
@@ -74,35 +76,41 @@ static enum { CONSOLE_UART, CONSOLE_USB_SERIAL_JTAG } s_console =
|
|
|
74
76
|
static bool s_usj_installed = false;
|
|
75
77
|
#endif
|
|
76
78
|
|
|
77
|
-
void mik__console_init(void) {
|
|
78
79
|
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
80
|
+
/* Install USB-Serial/JTAG via the public driver API only. No VFS
|
|
81
|
+
* registration, no `fileno(stdin)`, no newlib integration — that's
|
|
82
|
+
* the whole point of this path.
|
|
83
|
+
*
|
|
84
|
+
* Both rings are sized to fit the largest single TLV frame plus a
|
|
85
|
+
* little headroom for the next frame's header to land alongside it.
|
|
86
|
+
*
|
|
87
|
+
* RX (host → device): the largest single command frame is a
|
|
88
|
+
* CMD_DEPLOY_PUT_CHUNK (≤ 2 KB body + 5 B header). Deploy PUTs no
|
|
89
|
+
* longer carry the file body in one frame — they're streamed as
|
|
90
|
+
* begin + N chunks with per-chunk MSG_OK pacing — so this size is
|
|
91
|
+
* a function of the chunk size, not of the largest deployable file.
|
|
92
|
+
* 4 KB gives ~2× headroom over a single chunk so the next command
|
|
93
|
+
* can be queued behind it.
|
|
94
|
+
*
|
|
95
|
+
* TX (device → host): bumped from the IDF default (256 B) to 2 KB
|
|
96
|
+
* so MSG_READY resends during the boot-handshake window don't fill
|
|
97
|
+
* the ring before the CLI's TTY starts draining. At 256 B the ring
|
|
98
|
+
* fills after ~3 MSG_READY sends, then subsequent writes (including
|
|
99
|
+
* the MSG_OK response to the first CMD_RUNTIME_PAUSE) stall long
|
|
100
|
+
* enough to blow past the CLI's 10 s pause timeout and force a
|
|
101
|
+
* restart-and-retry on every `mikro dev`. 2 KB is still small
|
|
102
|
+
* enough not to meaningfully dent RAM for display-heavy apps. */
|
|
103
|
+
static bool mik__usj_install_with_default_config(void) {
|
|
102
104
|
usb_serial_jtag_driver_config_t usj_cfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
|
|
103
105
|
usj_cfg.rx_buffer_size = 4096;
|
|
104
106
|
usj_cfg.tx_buffer_size = 2048;
|
|
105
|
-
|
|
107
|
+
return usb_serial_jtag_driver_install(&usj_cfg) == ESP_OK;
|
|
108
|
+
}
|
|
109
|
+
#endif
|
|
110
|
+
|
|
111
|
+
void mik__console_init(void) {
|
|
112
|
+
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
113
|
+
s_usj_installed = mik__usj_install_with_default_config();
|
|
106
114
|
#endif
|
|
107
115
|
|
|
108
116
|
#if MIK_CONSOLE_HAS_UART
|
|
@@ -228,6 +236,84 @@ void mik__serial_binary_begin_no_echo(void) {
|
|
|
228
236
|
* so `\n` passes through unchanged on both USJ and UART paths. */
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
/* ── USB detach/attach for light sleep ─────────────────────────────
|
|
240
|
+
*
|
|
241
|
+
* Light sleep on USJ-console chips (C3, C6, S3, …) powers down the
|
|
242
|
+
* digital peripheral domain but leaves the USB device enumerated on
|
|
243
|
+
* the host. node-serialport doesn't see a close, so the CLI can't
|
|
244
|
+
* tell the difference between "device is busy" and "device is asleep".
|
|
245
|
+
*
|
|
246
|
+
* To make the disconnect visible we uninstall the driver and pull
|
|
247
|
+
* down the D+ pad before sleeping. On wake we restore the pad and
|
|
248
|
+
* re-install the driver. The host re-enumerates, the CLI's existing
|
|
249
|
+
* disconnect → reconnect path handles the rest. */
|
|
250
|
+
|
|
251
|
+
void mik__serial_io_detach_usb(void) {
|
|
252
|
+
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
253
|
+
if (s_console != CONSOLE_USB_SERIAL_JTAG) return;
|
|
254
|
+
if (!s_usj_installed) return;
|
|
255
|
+
|
|
256
|
+
/* USJ has no public flush API. A short delay covers TX-ring drain
|
|
257
|
+
* for the typical log-line-sized writes we send before sleeping. */
|
|
258
|
+
vTaskDelay(pdMS_TO_TICKS(20));
|
|
259
|
+
|
|
260
|
+
usb_serial_jtag_driver_uninstall();
|
|
261
|
+
s_usj_installed = false;
|
|
262
|
+
|
|
263
|
+
/* Force a host-visible disconnect by overriding D+/D- pulls to
|
|
264
|
+
* present SE0 on the bus. `usb_serial_jtag_ll_phy_enable_pad(false)`
|
|
265
|
+
* (which ESP-IDF's own sleep prep uses) only gates the internal pad
|
|
266
|
+
* routing for leakage — it does NOT release the host-visible 1.5 k
|
|
267
|
+
* D+ pullup, so the host still sees the device enumerated. The
|
|
268
|
+
* pull-override is the only mechanism in ESP-IDF v6.0.1 that
|
|
269
|
+
* actually triggers a re-enumerate. C6 has no `_SUPPORT_LIGHT_SLEEP`
|
|
270
|
+
* cap for USJ (IDF-6395 in soc_caps.h), so this is the supported
|
|
271
|
+
* path.
|
|
272
|
+
* Refs:
|
|
273
|
+
* components/hal/include/hal/usb_serial_jtag_ll.h
|
|
274
|
+
* docs.espressif.com/projects/esp-iot-solution/.../usb_serial_jtag.html
|
|
275
|
+
*/
|
|
276
|
+
usb_serial_jtag_pull_override_vals_t off = {
|
|
277
|
+
.dp_pu = 0,
|
|
278
|
+
.dm_pu = 0,
|
|
279
|
+
.dp_pd = 1,
|
|
280
|
+
.dm_pd = 1,
|
|
281
|
+
};
|
|
282
|
+
usb_serial_jtag_ll_phy_enable_pull_override(&off);
|
|
283
|
+
/* >2.5 ms of SE0 so the host registers a disconnect rather than a
|
|
284
|
+
* USB suspend. 5 ms gives plenty of margin without meaningfully
|
|
285
|
+
* delaying sleep entry. */
|
|
286
|
+
esp_rom_delay_us(5000);
|
|
287
|
+
#endif
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
void mik__serial_io_attach_usb(void) {
|
|
291
|
+
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
292
|
+
if (s_console != CONSOLE_USB_SERIAL_JTAG) return;
|
|
293
|
+
if (s_usj_installed) return;
|
|
294
|
+
|
|
295
|
+
/* `disable_pull_override` only flips `pad_pull_override` back to 0;
|
|
296
|
+
* the `dp_pullup`/`dp_pulldown` register fields still hold the
|
|
297
|
+
* disconnect values from detach (D+ pullup off, D+ pulldown on),
|
|
298
|
+
* so the host sees no connect signal until the peripheral autopilots
|
|
299
|
+
* — which only happens reliably once the driver is active. Drive
|
|
300
|
+
* the override to an explicit "connect" state (D+ pullup on,
|
|
301
|
+
* everything else off) first so the host sees J-state immediately,
|
|
302
|
+
* install the driver while the override holds it, then release. */
|
|
303
|
+
usb_serial_jtag_pull_override_vals_t connect = {
|
|
304
|
+
.dp_pu = 1,
|
|
305
|
+
.dm_pu = 0,
|
|
306
|
+
.dp_pd = 0,
|
|
307
|
+
.dm_pd = 0,
|
|
308
|
+
};
|
|
309
|
+
usb_serial_jtag_ll_phy_enable_pull_override(&connect);
|
|
310
|
+
|
|
311
|
+
s_usj_installed = mik__usj_install_with_default_config();
|
|
312
|
+
|
|
313
|
+
usb_serial_jtag_ll_phy_disable_pull_override();
|
|
314
|
+
#endif
|
|
315
|
+
}
|
|
316
|
+
|
|
231
317
|
/* ── NVS helpers ─────────────────────────────────────────────────── */
|
|
232
318
|
|
|
233
319
|
const char* MIK__NVS_NS_ENV = "mik.env";
|
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
#include "driver/gpio.h"
|
|
2
|
+
#include "driver/rtc_io.h"
|
|
2
3
|
#include "esp_sleep.h"
|
|
3
4
|
#include "soc/soc_caps.h"
|
|
4
5
|
|
|
5
6
|
#include "mikrojs/private.h"
|
|
6
7
|
#include "mikrojs/utils.h"
|
|
8
|
+
#include "mikrojs_esp32.h"
|
|
9
|
+
|
|
10
|
+
/* For deep-sleep wakeup, the chip's digital pad config (set by `pinMode`)
|
|
11
|
+
* doesn't persist — only the RTC-IO pad does. Without an explicit RTC
|
|
12
|
+
* pull, EXT0/EXT1 pins float and trigger spurious wakes. Configure the
|
|
13
|
+
* RTC pull to the opposite of the wake direction so the pin idles in
|
|
14
|
+
* the non-trigger state and a button press flips it. */
|
|
15
|
+
static void mik__rtc_pull_for_wake(int pin, bool wake_on_high) {
|
|
16
|
+
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
|
|
17
|
+
if (!rtc_gpio_is_valid_gpio(static_cast<gpio_num_t>(pin))) return;
|
|
18
|
+
gpio_num_t g = static_cast<gpio_num_t>(pin);
|
|
19
|
+
rtc_gpio_pullup_dis(g);
|
|
20
|
+
rtc_gpio_pulldown_dis(g);
|
|
21
|
+
if (wake_on_high) {
|
|
22
|
+
rtc_gpio_pulldown_en(g);
|
|
23
|
+
} else {
|
|
24
|
+
rtc_gpio_pullup_en(g);
|
|
25
|
+
}
|
|
26
|
+
#else
|
|
27
|
+
(void)pin;
|
|
28
|
+
(void)wake_on_high;
|
|
29
|
+
#endif
|
|
30
|
+
}
|
|
7
31
|
|
|
8
32
|
static const char* mik__wakeup_cause_str(uint32_t causes) {
|
|
9
33
|
if (causes & BIT(ESP_SLEEP_WAKEUP_TIMER)) return "timer";
|
|
@@ -15,17 +39,221 @@ static const char* mik__wakeup_cause_str(uint32_t causes) {
|
|
|
15
39
|
return "undefined";
|
|
16
40
|
}
|
|
17
41
|
|
|
42
|
+
/* ── Wakeup source configuration ───────────────────────────────────── */
|
|
43
|
+
|
|
44
|
+
static bool mik__configure_timer(JSContext* ctx, JSValue sources) {
|
|
45
|
+
JSValue v = JS_GetPropertyStr(ctx, sources, "timer");
|
|
46
|
+
bool ok = true;
|
|
47
|
+
if (!JS_IsUndefined(v)) {
|
|
48
|
+
double ms;
|
|
49
|
+
if (JS_ToFloat64(ctx, &ms, v)) {
|
|
50
|
+
ok = false;
|
|
51
|
+
} else if (ms < 0) {
|
|
52
|
+
JS_ThrowRangeError(ctx, "timer must be >= 0");
|
|
53
|
+
ok = false;
|
|
54
|
+
} else {
|
|
55
|
+
uint64_t us = static_cast<uint64_t>(ms * 1000.0);
|
|
56
|
+
esp_err_t err = esp_sleep_enable_timer_wakeup(us);
|
|
57
|
+
if (err != ESP_OK) {
|
|
58
|
+
JS_ThrowInternalError(ctx, "timer wakeup failed: %s", esp_err_to_name(err));
|
|
59
|
+
ok = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
JS_FreeValue(ctx, v);
|
|
64
|
+
return ok;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static bool mik__read_level(JSContext* ctx, JSValue obj, const char* key, int* out_level) {
|
|
68
|
+
JSValue v = JS_GetPropertyStr(ctx, obj, key);
|
|
69
|
+
const char* s = JS_ToCString(ctx, v);
|
|
70
|
+
JS_FreeValue(ctx, v);
|
|
71
|
+
if (!s) return false;
|
|
72
|
+
bool ok = true;
|
|
73
|
+
if (strcmp(s, "high") == 0)
|
|
74
|
+
*out_level = 1;
|
|
75
|
+
else if (strcmp(s, "low") == 0)
|
|
76
|
+
*out_level = 0;
|
|
77
|
+
else {
|
|
78
|
+
JS_ThrowRangeError(ctx, "%s must be 'high' or 'low'", key);
|
|
79
|
+
ok = false;
|
|
80
|
+
}
|
|
81
|
+
JS_FreeCString(ctx, s);
|
|
82
|
+
return ok;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static bool mik__configure_gpio(JSContext* ctx, JSValue sources) {
|
|
86
|
+
JSValue gpio = JS_GetPropertyStr(ctx, sources, "gpio");
|
|
87
|
+
if (JS_IsUndefined(gpio)) {
|
|
88
|
+
JS_FreeValue(ctx, gpio);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
JSValue pin_val = JS_GetPropertyStr(ctx, gpio, "pin");
|
|
93
|
+
int32_t pin;
|
|
94
|
+
bool ok = !JS_ToInt32(ctx, &pin, pin_val);
|
|
95
|
+
JS_FreeValue(ctx, pin_val);
|
|
96
|
+
|
|
97
|
+
int level = 0;
|
|
98
|
+
if (ok) ok = mik__read_level(ctx, gpio, "level", &level);
|
|
99
|
+
JS_FreeValue(ctx, gpio);
|
|
100
|
+
if (!ok) return false;
|
|
101
|
+
|
|
102
|
+
gpio_int_type_t intr = (level == 1) ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
|
|
103
|
+
esp_err_t err = gpio_wakeup_enable(static_cast<gpio_num_t>(pin), intr);
|
|
104
|
+
if (err != ESP_OK) {
|
|
105
|
+
JS_ThrowInternalError(ctx, "GPIO wakeup on pin %d failed: %s", pin, esp_err_to_name(err));
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
err = esp_sleep_enable_gpio_wakeup();
|
|
109
|
+
if (err != ESP_OK) {
|
|
110
|
+
JS_ThrowInternalError(ctx, "GPIO wakeup source failed: %s", esp_err_to_name(err));
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static bool mik__configure_ext0(JSContext* ctx, JSValue sources) {
|
|
117
|
+
JSValue ext0 = JS_GetPropertyStr(ctx, sources, "ext0");
|
|
118
|
+
if (JS_IsUndefined(ext0)) {
|
|
119
|
+
JS_FreeValue(ctx, ext0);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
#if SOC_PM_SUPPORT_EXT0_WAKEUP
|
|
123
|
+
JSValue pin_val = JS_GetPropertyStr(ctx, ext0, "pin");
|
|
124
|
+
int32_t pin;
|
|
125
|
+
bool ok = !JS_ToInt32(ctx, &pin, pin_val);
|
|
126
|
+
JS_FreeValue(ctx, pin_val);
|
|
127
|
+
|
|
128
|
+
int level = 0;
|
|
129
|
+
if (ok) ok = mik__read_level(ctx, ext0, "level", &level);
|
|
130
|
+
JS_FreeValue(ctx, ext0);
|
|
131
|
+
if (!ok) return false;
|
|
132
|
+
|
|
133
|
+
mik__rtc_pull_for_wake(pin, level == 1);
|
|
134
|
+
esp_err_t err = esp_sleep_enable_ext0_wakeup(static_cast<gpio_num_t>(pin), level);
|
|
135
|
+
if (err != ESP_OK) {
|
|
136
|
+
JS_ThrowInternalError(ctx, "EXT0 wakeup on pin %d failed: %s", pin, esp_err_to_name(err));
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
#else
|
|
141
|
+
JS_FreeValue(ctx, ext0);
|
|
142
|
+
JS_ThrowInternalError(ctx, "EXT0 wakeup is not supported on this chip");
|
|
143
|
+
return false;
|
|
144
|
+
#endif
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static bool mik__configure_ext1(JSContext* ctx, JSValue sources) {
|
|
148
|
+
JSValue ext1 = JS_GetPropertyStr(ctx, sources, "ext1");
|
|
149
|
+
if (JS_IsUndefined(ext1)) {
|
|
150
|
+
JS_FreeValue(ctx, ext1);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
|
154
|
+
JSValue pins_val = JS_GetPropertyStr(ctx, ext1, "pins");
|
|
155
|
+
JSValue len_val = JS_GetPropertyStr(ctx, pins_val, "length");
|
|
156
|
+
uint32_t pin_count;
|
|
157
|
+
bool ok = !JS_ToUint32(ctx, &pin_count, len_val);
|
|
158
|
+
JS_FreeValue(ctx, len_val);
|
|
159
|
+
|
|
160
|
+
uint64_t pin_mask = 0;
|
|
161
|
+
for (uint32_t i = 0; ok && i < pin_count; i++) {
|
|
162
|
+
JSValue pin_val = JS_GetPropertyUint32(ctx, pins_val, i);
|
|
163
|
+
int32_t pin;
|
|
164
|
+
if (JS_ToInt32(ctx, &pin, pin_val))
|
|
165
|
+
ok = false;
|
|
166
|
+
else
|
|
167
|
+
pin_mask |= (1ULL << pin);
|
|
168
|
+
JS_FreeValue(ctx, pin_val);
|
|
169
|
+
}
|
|
170
|
+
JS_FreeValue(ctx, pins_val);
|
|
171
|
+
|
|
172
|
+
int mode_value = 0;
|
|
173
|
+
if (ok) {
|
|
174
|
+
JSValue mode_val = JS_GetPropertyStr(ctx, ext1, "mode");
|
|
175
|
+
const char* mode_str = JS_ToCString(ctx, mode_val);
|
|
176
|
+
JS_FreeValue(ctx, mode_val);
|
|
177
|
+
if (!mode_str) {
|
|
178
|
+
ok = false;
|
|
179
|
+
} else {
|
|
180
|
+
if (strcmp(mode_str, "any-low") == 0) {
|
|
181
|
+
// ESP32's EXT1 only ships ALL_LOW (wake when every selected pin
|
|
182
|
+
// is low); newer chips ship ANY_LOW. The two are equivalent for
|
|
183
|
+
// a single-pin mask, so we accept that case on ESP32 and route
|
|
184
|
+
// it through ALL_LOW. Multi-pin "any-low" can't be honored on
|
|
185
|
+
// ESP32 — the hardware always requires every pin to be low —
|
|
186
|
+
// so we reject it loudly rather than silently changing the
|
|
187
|
+
// wake condition.
|
|
188
|
+
#if CONFIG_IDF_TARGET_ESP32
|
|
189
|
+
if (pin_count > 1) {
|
|
190
|
+
JS_ThrowTypeError(
|
|
191
|
+
ctx,
|
|
192
|
+
"'any-low' with multiple pins is not supported on ESP32: "
|
|
193
|
+
"hardware can only wake when all selected pins are low. "
|
|
194
|
+
"Use one pin per ext1 source, or use 'any-high' mode.");
|
|
195
|
+
ok = false;
|
|
196
|
+
} else {
|
|
197
|
+
mode_value = ESP_EXT1_WAKEUP_ALL_LOW;
|
|
198
|
+
}
|
|
199
|
+
#else
|
|
200
|
+
mode_value = ESP_EXT1_WAKEUP_ANY_LOW;
|
|
201
|
+
#endif
|
|
202
|
+
} else if (strcmp(mode_str, "any-high") == 0) {
|
|
203
|
+
mode_value = ESP_EXT1_WAKEUP_ANY_HIGH;
|
|
204
|
+
} else {
|
|
205
|
+
JS_ThrowRangeError(ctx, "ext1.mode must be 'any-low' or 'any-high'");
|
|
206
|
+
ok = false;
|
|
207
|
+
}
|
|
208
|
+
JS_FreeCString(ctx, mode_str);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
JS_FreeValue(ctx, ext1);
|
|
212
|
+
if (!ok) return false;
|
|
213
|
+
|
|
214
|
+
const bool wake_on_high = mode_value == ESP_EXT1_WAKEUP_ANY_HIGH;
|
|
215
|
+
for (int pin = 0; pin < 64; pin++) {
|
|
216
|
+
if (pin_mask & (1ULL << pin)) {
|
|
217
|
+
mik__rtc_pull_for_wake(pin, wake_on_high);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
esp_err_t err = esp_sleep_enable_ext1_wakeup(
|
|
221
|
+
pin_mask, static_cast<esp_sleep_ext1_wakeup_mode_t>(mode_value));
|
|
222
|
+
if (err != ESP_OK) {
|
|
223
|
+
JS_ThrowInternalError(ctx, "EXT1 wakeup failed: %s", esp_err_to_name(err));
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
#else
|
|
228
|
+
JS_FreeValue(ctx, ext1);
|
|
229
|
+
JS_ThrowInternalError(ctx, "EXT1 wakeup is not supported on this chip");
|
|
230
|
+
return false;
|
|
231
|
+
#endif
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Clears any previously-configured sources, then applies the ones in
|
|
235
|
+
* `sources`. Throws and returns false on any error. */
|
|
236
|
+
static bool mik__configure_wakeup_sources(JSContext* ctx, JSValue sources) {
|
|
237
|
+
if (!JS_IsObject(sources)) {
|
|
238
|
+
JS_ThrowTypeError(ctx, "wakeup sources must be an object");
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
|
|
242
|
+
if (!mik__configure_timer(ctx, sources)) return false;
|
|
243
|
+
if (!mik__configure_gpio(ctx, sources)) return false;
|
|
244
|
+
if (!mik__configure_ext0(ctx, sources)) return false;
|
|
245
|
+
if (!mik__configure_ext1(ctx, sources)) return false;
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
18
249
|
/* ── native:sleep JS module ─────────────────────────────────────────── */
|
|
19
250
|
|
|
20
251
|
static JSValue mik__sleep_deep(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
21
|
-
|
|
22
|
-
if (JS_ToInt64(ctx, &ms, argv[0])) return JS_EXCEPTION;
|
|
252
|
+
if (!mik__configure_wakeup_sources(ctx, argv[0])) return JS_EXCEPTION;
|
|
23
253
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return JS_ThrowInternalError(ctx, "timer wakeup failed: %s", esp_err_to_name(err));
|
|
28
|
-
}
|
|
254
|
+
/* Flush + close the file log so the buffered line buffer and stdio
|
|
255
|
+
* buffer make it to flash; deep sleep reboots the chip otherwise. */
|
|
256
|
+
mik_logfile_close();
|
|
29
257
|
|
|
30
258
|
/* Note: we intentionally do NOT call MIK_FreeRuntime() here. We are inside
|
|
31
259
|
* a JS function call, so the runtime still has live GC objects on the call
|
|
@@ -37,23 +265,19 @@ static JSValue mik__sleep_deep(JSContext* ctx, JSValue this_val, int argc, JSVal
|
|
|
37
265
|
}
|
|
38
266
|
|
|
39
267
|
static JSValue mik__sleep_light(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
40
|
-
|
|
41
|
-
if (JS_ToInt64(ctx, &ms, argv[0])) return JS_EXCEPTION;
|
|
42
|
-
|
|
43
|
-
if (ms > 0) {
|
|
44
|
-
esp_err_t err = esp_sleep_enable_timer_wakeup(static_cast<uint64_t>(ms) * 1000);
|
|
45
|
-
if (err != ESP_OK)
|
|
46
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
47
|
-
"failed to enable timer wakeup: %s",
|
|
48
|
-
esp_err_to_name(err));
|
|
49
|
-
}
|
|
268
|
+
if (!mik__configure_wakeup_sources(ctx, argv[0])) return JS_EXCEPTION;
|
|
50
269
|
|
|
270
|
+
/* Tear down USB before sleeping so the host sees a clean unplug,
|
|
271
|
+
* not a silent enumerated-but-asleep device. This lets `mikro dev`
|
|
272
|
+
* trigger its existing reconnect path. Restored after wake. */
|
|
273
|
+
mik__serial_io_detach_usb();
|
|
51
274
|
esp_err_t err = esp_light_sleep_start();
|
|
275
|
+
mik__serial_io_attach_usb();
|
|
276
|
+
|
|
52
277
|
if (err != ESP_OK)
|
|
53
|
-
return
|
|
54
|
-
"light sleep failed: %s", esp_err_to_name(err));
|
|
278
|
+
return JS_ThrowInternalError(ctx, "light sleep failed: %s", esp_err_to_name(err));
|
|
55
279
|
|
|
56
|
-
return
|
|
280
|
+
return JS_UNDEFINED;
|
|
57
281
|
}
|
|
58
282
|
|
|
59
283
|
static JSValue mik__sleep_get_wakeup_cause(JSContext* ctx, JSValue this_val, int argc,
|
|
@@ -62,126 +286,24 @@ static JSValue mik__sleep_get_wakeup_cause(JSContext* ctx, JSValue this_val, int
|
|
|
62
286
|
return JS_NewString(ctx, mik__wakeup_cause_str(causes));
|
|
63
287
|
}
|
|
64
288
|
|
|
65
|
-
static JSValue
|
|
66
|
-
JSValue* argv) {
|
|
67
|
-
int64_t us;
|
|
68
|
-
if (JS_ToInt64(ctx, &us, argv[0])) return JS_EXCEPTION;
|
|
69
|
-
|
|
70
|
-
esp_err_t err = esp_sleep_enable_timer_wakeup(static_cast<uint64_t>(us));
|
|
71
|
-
if (err != ESP_OK)
|
|
72
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
73
|
-
"failed to enable timer wakeup: %s", esp_err_to_name(err));
|
|
74
|
-
|
|
75
|
-
return mik__result_ok_void(ctx);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
static JSValue mik__sleep_enable_gpio_wakeup(JSContext* ctx, JSValue this_val, int argc,
|
|
289
|
+
static JSValue mik__sleep_can_wake_from_ext0(JSContext* ctx, JSValue this_val, int argc,
|
|
79
290
|
JSValue* argv) {
|
|
80
|
-
int32_t pin, level;
|
|
81
|
-
if (JS_ToInt32(ctx, &pin, argv[0])) return JS_EXCEPTION;
|
|
82
|
-
if (JS_ToInt32(ctx, &level, argv[1])) return JS_EXCEPTION;
|
|
83
|
-
|
|
84
|
-
esp_err_t err =
|
|
85
|
-
gpio_wakeup_enable(static_cast<gpio_num_t>(pin), static_cast<gpio_int_type_t>(level));
|
|
86
|
-
if (err != ESP_OK)
|
|
87
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
88
|
-
"failed to enable GPIO wakeup on pin %d: %s", pin,
|
|
89
|
-
esp_err_to_name(err));
|
|
90
|
-
|
|
91
|
-
err = esp_sleep_enable_gpio_wakeup();
|
|
92
|
-
if (err != ESP_OK)
|
|
93
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
94
|
-
"failed to enable GPIO wakeup source: %s",
|
|
95
|
-
esp_err_to_name(err));
|
|
96
|
-
|
|
97
|
-
return mik__result_ok_void(ctx);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/* EXT0 / EXT1 wakeup is chip-specific (not on ESP32-C3, C6, H2). We still
|
|
101
|
-
* register the JS exports unconditionally so `mikrojs/sleep`'s static
|
|
102
|
-
* re-exports resolve on every target; the function bodies return a
|
|
103
|
-
* `WakeupConfigFailed` error on chips where the capability is missing. */
|
|
104
|
-
static JSValue mik__sleep_enable_ext0_wakeup(JSContext* ctx, JSValue this_val, int argc,
|
|
105
|
-
JSValue* argv) {
|
|
106
291
|
#if SOC_PM_SUPPORT_EXT0_WAKEUP
|
|
107
|
-
|
|
108
|
-
if (JS_ToInt32(ctx, &pin, argv[0])) return JS_EXCEPTION;
|
|
109
|
-
if (JS_ToInt32(ctx, &level, argv[1])) return JS_EXCEPTION;
|
|
110
|
-
|
|
111
|
-
esp_err_t err = esp_sleep_enable_ext0_wakeup(static_cast<gpio_num_t>(pin), level);
|
|
112
|
-
if (err != ESP_OK)
|
|
113
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
114
|
-
"failed to enable EXT0 wakeup on pin %d: %s", pin,
|
|
115
|
-
esp_err_to_name(err));
|
|
116
|
-
|
|
117
|
-
return mik__result_ok_void(ctx);
|
|
292
|
+
return JS_TRUE;
|
|
118
293
|
#else
|
|
119
|
-
return
|
|
120
|
-
"EXT0 wakeup is not supported on this chip");
|
|
294
|
+
return JS_FALSE;
|
|
121
295
|
#endif
|
|
122
296
|
}
|
|
123
297
|
|
|
124
|
-
static JSValue
|
|
125
|
-
|
|
298
|
+
static JSValue mik__sleep_can_wake_from_ext1(JSContext* ctx, JSValue this_val, int argc,
|
|
299
|
+
JSValue* argv) {
|
|
126
300
|
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
|
127
|
-
|
|
128
|
-
int32_t mode;
|
|
129
|
-
if (JS_ToInt64(ctx, &pin_mask, argv[0])) return JS_EXCEPTION;
|
|
130
|
-
if (JS_ToInt32(ctx, &mode, argv[1])) return JS_EXCEPTION;
|
|
131
|
-
|
|
132
|
-
esp_err_t err = esp_sleep_enable_ext1_wakeup(static_cast<uint64_t>(pin_mask),
|
|
133
|
-
static_cast<esp_sleep_ext1_wakeup_mode_t>(mode));
|
|
134
|
-
if (err != ESP_OK)
|
|
135
|
-
return mik__result_err_named(ctx, "WakeupConfigFailed",
|
|
136
|
-
"failed to enable EXT1 wakeup: %s", esp_err_to_name(err));
|
|
137
|
-
|
|
138
|
-
return mik__result_ok_void(ctx);
|
|
301
|
+
return JS_TRUE;
|
|
139
302
|
#else
|
|
140
|
-
return
|
|
141
|
-
"EXT1 wakeup is not supported on this chip");
|
|
303
|
+
return JS_FALSE;
|
|
142
304
|
#endif
|
|
143
305
|
}
|
|
144
306
|
|
|
145
|
-
static JSValue mik__sleep_disable_wakeup_source(JSContext* ctx, JSValue this_val, int argc,
|
|
146
|
-
JSValue* argv) {
|
|
147
|
-
esp_sleep_source_t source = ESP_SLEEP_WAKEUP_ALL;
|
|
148
|
-
|
|
149
|
-
if (argc > 0 && !JS_IsUndefined(argv[0])) {
|
|
150
|
-
const char* str = JS_ToCString(ctx, argv[0]);
|
|
151
|
-
if (!str) return JS_EXCEPTION;
|
|
152
|
-
|
|
153
|
-
bool valid = true;
|
|
154
|
-
if (strcmp(str, "timer") == 0)
|
|
155
|
-
source = ESP_SLEEP_WAKEUP_TIMER;
|
|
156
|
-
else if (strcmp(str, "ext0") == 0)
|
|
157
|
-
source = ESP_SLEEP_WAKEUP_EXT0;
|
|
158
|
-
else if (strcmp(str, "ext1") == 0)
|
|
159
|
-
source = ESP_SLEEP_WAKEUP_EXT1;
|
|
160
|
-
else if (strcmp(str, "gpio") == 0)
|
|
161
|
-
source = ESP_SLEEP_WAKEUP_GPIO;
|
|
162
|
-
else if (strcmp(str, "touchpad") == 0)
|
|
163
|
-
source = ESP_SLEEP_WAKEUP_TOUCHPAD;
|
|
164
|
-
else if (strcmp(str, "ulp") == 0)
|
|
165
|
-
source = ESP_SLEEP_WAKEUP_ULP;
|
|
166
|
-
else
|
|
167
|
-
valid = false;
|
|
168
|
-
|
|
169
|
-
if (!valid) {
|
|
170
|
-
JSValue err = JS_ThrowRangeError(ctx, "unknown wakeup source: %s", str);
|
|
171
|
-
JS_FreeCString(ctx, str);
|
|
172
|
-
return err;
|
|
173
|
-
}
|
|
174
|
-
JS_FreeCString(ctx, str);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
esp_err_t err = esp_sleep_disable_wakeup_source(source);
|
|
178
|
-
if (err != ESP_OK)
|
|
179
|
-
return mik__result_err_named(ctx, "DisableWakeupFailed",
|
|
180
|
-
"failed to disable wakeup source: %s", esp_err_to_name(err));
|
|
181
|
-
|
|
182
|
-
return mik__result_ok_void(ctx);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
307
|
/* ── Module init ─────────────────────────────────────────────────── */
|
|
186
308
|
|
|
187
309
|
static int mik__sleep_module_init(JSContext* ctx, JSModuleDef* m) {
|
|
@@ -192,20 +314,11 @@ static int mik__sleep_module_init(JSContext* ctx, JSModuleDef* m) {
|
|
|
192
314
|
JS_SetModuleExport(ctx, m, "getWakeupCause",
|
|
193
315
|
JS_NewCFunction(ctx, mik__sleep_get_wakeup_cause, "getWakeupCause", 0));
|
|
194
316
|
JS_SetModuleExport(
|
|
195
|
-
ctx, m, "
|
|
196
|
-
JS_NewCFunction(ctx,
|
|
197
|
-
JS_SetModuleExport(
|
|
198
|
-
ctx, m, "enableGpioWakeup",
|
|
199
|
-
JS_NewCFunction(ctx, mik__sleep_enable_gpio_wakeup, "enableGpioWakeup", 2));
|
|
200
|
-
JS_SetModuleExport(
|
|
201
|
-
ctx, m, "enableExt0Wakeup",
|
|
202
|
-
JS_NewCFunction(ctx, mik__sleep_enable_ext0_wakeup, "enableExt0Wakeup", 2));
|
|
203
|
-
JS_SetModuleExport(
|
|
204
|
-
ctx, m, "enableExt1Wakeup",
|
|
205
|
-
JS_NewCFunction(ctx, mik__sleep_enable_ext1_wakeup, "enableExt1Wakeup", 2));
|
|
317
|
+
ctx, m, "canWakeFromExt0",
|
|
318
|
+
JS_NewCFunction(ctx, mik__sleep_can_wake_from_ext0, "canWakeFromExt0", 0));
|
|
206
319
|
JS_SetModuleExport(
|
|
207
|
-
ctx, m, "
|
|
208
|
-
JS_NewCFunction(ctx,
|
|
320
|
+
ctx, m, "canWakeFromExt1",
|
|
321
|
+
JS_NewCFunction(ctx, mik__sleep_can_wake_from_ext1, "canWakeFromExt1", 0));
|
|
209
322
|
return 0;
|
|
210
323
|
}
|
|
211
324
|
|
|
@@ -215,11 +328,8 @@ static JSModuleDef* mik__sleep_init(JSContext* ctx) {
|
|
|
215
328
|
JS_AddModuleExport(ctx, m, "deepSleep");
|
|
216
329
|
JS_AddModuleExport(ctx, m, "lightSleep");
|
|
217
330
|
JS_AddModuleExport(ctx, m, "getWakeupCause");
|
|
218
|
-
JS_AddModuleExport(ctx, m, "
|
|
219
|
-
JS_AddModuleExport(ctx, m, "
|
|
220
|
-
JS_AddModuleExport(ctx, m, "enableExt0Wakeup");
|
|
221
|
-
JS_AddModuleExport(ctx, m, "enableExt1Wakeup");
|
|
222
|
-
JS_AddModuleExport(ctx, m, "disableWakeupSource");
|
|
331
|
+
JS_AddModuleExport(ctx, m, "canWakeFromExt0");
|
|
332
|
+
JS_AddModuleExport(ctx, m, "canWakeFromExt1");
|
|
223
333
|
return m;
|
|
224
334
|
}
|
|
225
335
|
|
|
@@ -31,26 +31,17 @@ TEST_CASE("native:sleep exports expected functions", "[modules]") {
|
|
|
31
31
|
setup();
|
|
32
32
|
|
|
33
33
|
JSValue ret = eval_module(R"(
|
|
34
|
-
import {
|
|
35
|
-
deepSleep, lightSleep, getWakeupCause,
|
|
36
|
-
enableTimerWakeup, enableGpioWakeup,
|
|
37
|
-
disableWakeupSource
|
|
38
|
-
} from "native:sleep";
|
|
34
|
+
import { deepSleep, lightSleep, getWakeupCause } from "native:sleep";
|
|
39
35
|
globalThis.__deepSleep = typeof deepSleep === "function";
|
|
40
36
|
globalThis.__lightSleep = typeof lightSleep === "function";
|
|
41
37
|
globalThis.__getWakeupCause = typeof getWakeupCause === "function";
|
|
42
|
-
globalThis.__enableTimerWakeup = typeof enableTimerWakeup === "function";
|
|
43
|
-
globalThis.__enableGpioWakeup = typeof enableGpioWakeup === "function";
|
|
44
|
-
globalThis.__disableWakeupSource = typeof disableWakeupSource === "function";
|
|
45
38
|
)");
|
|
46
39
|
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
47
40
|
|
|
48
41
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
49
42
|
|
|
50
|
-
const char* names[] = {"__deepSleep",
|
|
51
|
-
|
|
52
|
-
"__disableWakeupSource"};
|
|
53
|
-
for (int i = 0; i < 6; i++) {
|
|
43
|
+
const char* names[] = {"__deepSleep", "__lightSleep", "__getWakeupCause"};
|
|
44
|
+
for (int i = 0; i < 3; i++) {
|
|
54
45
|
JSValue v = JS_GetPropertyStr(ctx, global, names[i]);
|
|
55
46
|
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, v), names[i]);
|
|
56
47
|
JS_FreeValue(ctx, v);
|
|
@@ -92,77 +83,15 @@ TEST_CASE("native:sleep getWakeupCause returns a string", "[modules]") {
|
|
|
92
83
|
teardown();
|
|
93
84
|
}
|
|
94
85
|
|
|
95
|
-
/* ──
|
|
86
|
+
/* ── invalid gpio.level throws RangeError ────────────────────────── */
|
|
96
87
|
|
|
97
|
-
TEST_CASE("native:sleep
|
|
88
|
+
TEST_CASE("native:sleep lightSleep throws RangeError on invalid level", "[modules]") {
|
|
98
89
|
setup();
|
|
99
90
|
|
|
100
91
|
JSValue ret = eval_module(R"(
|
|
101
|
-
import {
|
|
102
|
-
enableTimerWakeup(5000000);
|
|
103
|
-
globalThis.__ok = true;
|
|
104
|
-
)");
|
|
105
|
-
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
106
|
-
|
|
107
|
-
JSValue global = JS_GetGlobalObject(ctx);
|
|
108
|
-
JSValue ok = JS_GetPropertyStr(ctx, global, "__ok");
|
|
109
|
-
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, ok), "enableTimerWakeup should succeed");
|
|
110
|
-
JS_FreeValue(ctx, ok);
|
|
111
|
-
JS_FreeValue(ctx, global);
|
|
112
|
-
teardown();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/* ── disableWakeupSource works ───────────────────────────────────── */
|
|
116
|
-
|
|
117
|
-
TEST_CASE("native:sleep disableWakeupSource clears all sources", "[modules]") {
|
|
118
|
-
setup();
|
|
119
|
-
|
|
120
|
-
JSValue ret = eval_module(R"(
|
|
121
|
-
import { enableTimerWakeup, disableWakeupSource } from "native:sleep";
|
|
122
|
-
enableTimerWakeup(1000000);
|
|
123
|
-
disableWakeupSource();
|
|
124
|
-
globalThis.__ok = true;
|
|
125
|
-
)");
|
|
126
|
-
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
127
|
-
|
|
128
|
-
JSValue global = JS_GetGlobalObject(ctx);
|
|
129
|
-
JSValue ok = JS_GetPropertyStr(ctx, global, "__ok");
|
|
130
|
-
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, ok), "disableWakeupSource should succeed");
|
|
131
|
-
JS_FreeValue(ctx, ok);
|
|
132
|
-
JS_FreeValue(ctx, global);
|
|
133
|
-
teardown();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* ── disableWakeupSource with specific source ────────────────────── */
|
|
137
|
-
|
|
138
|
-
TEST_CASE("native:sleep disableWakeupSource with 'timer'", "[modules]") {
|
|
139
|
-
setup();
|
|
140
|
-
|
|
141
|
-
JSValue ret = eval_module(R"(
|
|
142
|
-
import { enableTimerWakeup, disableWakeupSource } from "native:sleep";
|
|
143
|
-
enableTimerWakeup(1000000);
|
|
144
|
-
disableWakeupSource("timer");
|
|
145
|
-
globalThis.__ok = true;
|
|
146
|
-
)");
|
|
147
|
-
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
148
|
-
|
|
149
|
-
JSValue global = JS_GetGlobalObject(ctx);
|
|
150
|
-
JSValue ok = JS_GetPropertyStr(ctx, global, "__ok");
|
|
151
|
-
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, ok), "disableWakeupSource('timer') should succeed");
|
|
152
|
-
JS_FreeValue(ctx, ok);
|
|
153
|
-
JS_FreeValue(ctx, global);
|
|
154
|
-
teardown();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/* ── disableWakeupSource throws on invalid source ────────────────── */
|
|
158
|
-
|
|
159
|
-
TEST_CASE("native:sleep disableWakeupSource throws on invalid source", "[modules]") {
|
|
160
|
-
setup();
|
|
161
|
-
|
|
162
|
-
JSValue ret = eval_module(R"(
|
|
163
|
-
import { disableWakeupSource } from "native:sleep";
|
|
92
|
+
import { lightSleep } from "native:sleep";
|
|
164
93
|
try {
|
|
165
|
-
|
|
94
|
+
lightSleep({gpio: {pin: 0, level: "bogus"}});
|
|
166
95
|
globalThis.__threw = false;
|
|
167
96
|
} catch (e) {
|
|
168
97
|
globalThis.__threw = true;
|
|
@@ -174,8 +103,7 @@ TEST_CASE("native:sleep disableWakeupSource throws on invalid source", "[modules
|
|
|
174
103
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
175
104
|
|
|
176
105
|
JSValue threw = JS_GetPropertyStr(ctx, global, "__threw");
|
|
177
|
-
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, threw),
|
|
178
|
-
"disableWakeupSource should throw on invalid source");
|
|
106
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, threw), "lightSleep should throw on invalid level");
|
|
179
107
|
JS_FreeValue(ctx, threw);
|
|
180
108
|
|
|
181
109
|
JSValue isRange = JS_GetPropertyStr(ctx, global, "__isRangeError");
|
|
@@ -190,19 +118,19 @@ TEST_CASE("native:sleep disableWakeupSource throws on invalid source", "[modules
|
|
|
190
118
|
/* Light sleep disconnects UART console on targets without USB Serial/JTAG,
|
|
191
119
|
causing the test monitor to stall. Only run on chips that have USB
|
|
192
120
|
Serial/JTAG which keeps the connection alive through light sleep. */
|
|
193
|
-
TEST_CASE("native:sleep lightSleep with
|
|
121
|
+
TEST_CASE("native:sleep lightSleep with 1us timer returns or rejects gracefully", "[modules]") {
|
|
194
122
|
#if !SOC_USB_SERIAL_JTAG_SUPPORTED
|
|
195
|
-
TEST_IGNORE_MESSAGE("light sleep disconnects UART console
|
|
123
|
+
TEST_IGNORE_MESSAGE("light sleep disconnects UART console, skipped on non-USB targets");
|
|
196
124
|
#endif
|
|
197
125
|
setup();
|
|
198
126
|
|
|
199
127
|
JSValue ret = eval_module(R"(
|
|
200
128
|
import { lightSleep } from "native:sleep";
|
|
201
129
|
try {
|
|
202
|
-
lightSleep(1);
|
|
130
|
+
lightSleep({timer: 1});
|
|
203
131
|
globalThis.__result = "ok";
|
|
204
132
|
} catch (e) {
|
|
205
|
-
|
|
133
|
+
/* Light sleep may throw on boards with USB JTAG or other constraints */
|
|
206
134
|
globalThis.__result = "caught:" + e.message;
|
|
207
135
|
}
|
|
208
136
|
)");
|
|
@@ -212,7 +140,7 @@ TEST_CASE("native:sleep lightSleep with 1ms returns or rejects gracefully", "[mo
|
|
|
212
140
|
JSValue result = JS_GetPropertyStr(ctx, global, "__result");
|
|
213
141
|
const char* str = JS_ToCString(ctx, result);
|
|
214
142
|
TEST_ASSERT_NOT_NULL_MESSAGE(str, "result should be set");
|
|
215
|
-
/* Either "ok" (sleep succeeded) or "caught:..." (threw a catchable error)
|
|
143
|
+
/* Either "ok" (sleep succeeded) or "caught:..." (threw a catchable error) */
|
|
216
144
|
TEST_ASSERT_TRUE_MESSAGE(strncmp(str, "ok", 2) == 0 || strncmp(str, "caught:", 7) == 0,
|
|
217
145
|
"lightSleep should either succeed or throw a catchable error");
|
|
218
146
|
JS_FreeCString(ctx, str);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikrojs/firmware",
|
|
3
|
-
"version": "0.8.0
|
|
3
|
+
"version": "0.8.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/
|
|
55
|
-
"@mikrojs/
|
|
54
|
+
"@mikrojs/quickjs": "0.8.0",
|
|
55
|
+
"@mikrojs/native": "0.8.0"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=24.0.0"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|