@mikrojs/firmware 0.0.7
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/LICENSE +21 -0
- package/README.md +30 -0
- package/bin/idf.py +7 -0
- package/chips.json +3 -0
- package/cmake.js +9 -0
- package/components/mikrojs/CMakeLists.txt +187 -0
- package/components/mikrojs/Kconfig +55 -0
- package/components/mikrojs/idf_component.yml +6 -0
- package/components/mikrojs/include/mem.h +3 -0
- package/components/mikrojs/include/mik_color.h +3 -0
- package/components/mikrojs/include/mik_http_internal.h +77 -0
- package/components/mikrojs/include/mikrojs.h +5 -0
- package/components/mikrojs/include/mikrojs_esp32.h +65 -0
- package/components/mikrojs/include/private.h +10 -0
- package/components/mikrojs/include/utils.h +3 -0
- package/components/mikrojs/mik_ble.cpp +1588 -0
- package/components/mikrojs/mik_ble_c_shim.c +61 -0
- package/components/mikrojs/mik_ble_c_shim.h +37 -0
- package/components/mikrojs/mik_config.cpp +167 -0
- package/components/mikrojs/mik_deploy.cpp +584 -0
- package/components/mikrojs/mik_http.cpp +916 -0
- package/components/mikrojs/mik_i2c.cpp +364 -0
- package/components/mikrojs/mik_main.cpp +542 -0
- package/components/mikrojs/mik_neopixel.cpp +437 -0
- package/components/mikrojs/mik_nvs_kv.cpp +219 -0
- package/components/mikrojs/mik_pin.cpp +195 -0
- package/components/mikrojs/mik_pwm.cpp +525 -0
- package/components/mikrojs/mik_recovery.cpp +86 -0
- package/components/mikrojs/mik_rtc.cpp +305 -0
- package/components/mikrojs/mik_serial_io.cpp +362 -0
- package/components/mikrojs/mik_sleep.cpp +226 -0
- package/components/mikrojs/mik_sntp.cpp +275 -0
- package/components/mikrojs/mik_spi.cpp +330 -0
- package/components/mikrojs/mik_uart.cpp +497 -0
- package/components/mikrojs/mik_wifi.cpp +1434 -0
- package/components/mikrojs/platform_esp32.cpp +192 -0
- package/components/mikrojs/test/CMakeLists.txt +32 -0
- package/components/mikrojs/test/abort_test.cpp +254 -0
- package/components/mikrojs/test/ble_test.cpp +714 -0
- package/components/mikrojs/test/fs_js_test.cpp +458 -0
- package/components/mikrojs/test/fs_pub_test.cpp +312 -0
- package/components/mikrojs/test/http_test.cpp +475 -0
- package/components/mikrojs/test/i2c_test.cpp +138 -0
- package/components/mikrojs/test/modules_extended_test.cpp +137 -0
- package/components/mikrojs/test/modules_test.cpp +131 -0
- package/components/mikrojs/test/pins_test.cpp +47 -0
- package/components/mikrojs/test/pwm_test.cpp +166 -0
- package/components/mikrojs/test/repl_protocol_test.cpp +405 -0
- package/components/mikrojs/test/rtc_test.cpp +331 -0
- package/components/mikrojs/test/runtime_test.cpp +89 -0
- package/components/mikrojs/test/sleep_test.cpp +222 -0
- package/components/mikrojs/test/sntp_test.cpp +249 -0
- package/components/mikrojs/test/stdio_test.cpp +449 -0
- package/components/mikrojs/test/sys_test.cpp +165 -0
- package/components/mikrojs/test/text_encoding_test.cpp +224 -0
- package/components/mikrojs/test/timers_js_test.cpp +244 -0
- package/components/mikrojs/test/timers_test.cpp +79 -0
- package/components/mikrojs/test/wifi_test.cpp +599 -0
- package/default-app/main/CMakeLists.txt +3 -0
- package/default-app/main/main.cpp +5 -0
- package/discover.js +77 -0
- package/index.d.ts +7 -0
- package/index.js +20 -0
- package/package.json +61 -0
- package/partitions.csv +5 -0
- package/prebuilds/esp32/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32/flasher_args.json +24 -0
- package/prebuilds/esp32/mikrojs.bin +0 -0
- package/prebuilds/esp32/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32c3/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c3/flasher_args.json +24 -0
- package/prebuilds/esp32c3/mikrojs.bin +0 -0
- package/prebuilds/esp32c3/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32c6/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32c6/flasher_args.json +24 -0
- package/prebuilds/esp32c6/mikrojs.bin +0 -0
- package/prebuilds/esp32c6/partition_table/partition-table.bin +0 -0
- package/prebuilds/esp32s3/bootloader/bootloader.bin +0 -0
- package/prebuilds/esp32s3/flasher_args.json +24 -0
- package/prebuilds/esp32s3/mikrojs.bin +0 -0
- package/prebuilds/esp32s3/partition_table/partition-table.bin +0 -0
- package/project.cmake +101 -0
- package/resolve.js +54 -0
- package/sdkconfig.defaults +127 -0
- package/sdkconfig.defaults.esp32 +8 -0
- package/sdkconfig.defaults.esp32c3 +15 -0
- package/sdkconfig.defaults.esp32c6 +26 -0
- package/sdkconfig.defaults.esp32s3 +22 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
#include "esp_event.h"
|
|
2
|
+
#include "esp_mac.h"
|
|
3
|
+
#include "esp_wifi.h"
|
|
4
|
+
#include "freertos/FreeRTOS.h"
|
|
5
|
+
#include "freertos/task.h"
|
|
6
|
+
#include "mikrojs.h"
|
|
7
|
+
#include "private.h"
|
|
8
|
+
#include "quickjs.h"
|
|
9
|
+
#include "unity.h"
|
|
10
|
+
|
|
11
|
+
/* WiFi status codes — must match MIKWifiStatus in mik_wifi.cpp */
|
|
12
|
+
#define WIFI_STATUS_IDLE 0
|
|
13
|
+
#define WIFI_STATUS_NO_SSID_AVAIL 1
|
|
14
|
+
#define WIFI_STATUS_CONNECTED 3
|
|
15
|
+
#define WIFI_STATUS_CONNECT_FAILED 4
|
|
16
|
+
#define WIFI_STATUS_CONNECTION_LOST 5
|
|
17
|
+
#define WIFI_STATUS_DISCONNECTED 6
|
|
18
|
+
|
|
19
|
+
static MIKRuntime* rt;
|
|
20
|
+
static JSContext* ctx;
|
|
21
|
+
|
|
22
|
+
/* WiFi consume is needed to drain stale events between tests */
|
|
23
|
+
extern void mik__wifi_consume(JSContext* ctx);
|
|
24
|
+
|
|
25
|
+
static void setup() {
|
|
26
|
+
// Ensure WiFi is idle before each test
|
|
27
|
+
esp_wifi_disconnect();
|
|
28
|
+
vTaskDelay(pdMS_TO_TICKS(100));
|
|
29
|
+
rt = MIK_NewRuntime();
|
|
30
|
+
ctx = MIK_GetJSContext(rt);
|
|
31
|
+
/* 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\");";
|
|
36
|
+
JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test-setup", init_code, strlen(init_code));
|
|
37
|
+
if (!JS_IsException(ret)) {
|
|
38
|
+
JS_FreeValue(ctx, ret);
|
|
39
|
+
mik__execute_jobs(ctx);
|
|
40
|
+
}
|
|
41
|
+
// Drain any stale events from previous tests
|
|
42
|
+
mik__wifi_consume(ctx);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static void teardown() {
|
|
46
|
+
// Disconnect WiFi to avoid leaking "connecting" state into next test
|
|
47
|
+
esp_wifi_disconnect();
|
|
48
|
+
vTaskDelay(pdMS_TO_TICKS(100));
|
|
49
|
+
// Drain events before destroying to prevent dangling promise refs
|
|
50
|
+
mik__wifi_consume(ctx);
|
|
51
|
+
MIK_FreeRuntime(rt);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static JSValue eval_module(const char* code) {
|
|
55
|
+
JSValue ret = MIK_EvalModuleContent(ctx, "mikrojs/test", code, strlen(code));
|
|
56
|
+
if (!JS_IsException(ret)) {
|
|
57
|
+
JS_FreeValue(ctx, ret);
|
|
58
|
+
mik__execute_jobs(ctx);
|
|
59
|
+
}
|
|
60
|
+
return ret;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static int32_t get_wifi_status() {
|
|
64
|
+
JSValue ret = eval_module("globalThis.__s = globalThis.__wifi.status();");
|
|
65
|
+
if (JS_IsException(ret)) return -1;
|
|
66
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
67
|
+
JSValue v = JS_GetPropertyStr(ctx, global, "__s");
|
|
68
|
+
int32_t s;
|
|
69
|
+
JS_ToInt32(ctx, &s, v);
|
|
70
|
+
JS_FreeValue(ctx, v);
|
|
71
|
+
JS_FreeValue(ctx, global);
|
|
72
|
+
return s;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ── Module structure tests ───────────────────────────────────────── */
|
|
76
|
+
|
|
77
|
+
TEST_CASE("native:wifi exports Wifi with expected methods", "[wifi]") {
|
|
78
|
+
setup();
|
|
79
|
+
|
|
80
|
+
JSValue ret = eval_module(R"(
|
|
81
|
+
import { Wifi } from "native:wifi";
|
|
82
|
+
globalThis.__isFunc = typeof Wifi === "function";
|
|
83
|
+
const wifi = new Wifi();
|
|
84
|
+
globalThis.__isObj = typeof wifi === "object" && wifi !== null;
|
|
85
|
+
globalThis.__methods = ["connect","disconnect","rssi","ip","status","scan","on","off"]
|
|
86
|
+
.every(m => typeof wifi[m] === "function");
|
|
87
|
+
)");
|
|
88
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
89
|
+
|
|
90
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
91
|
+
|
|
92
|
+
JSValue isFunc = JS_GetPropertyStr(ctx, global, "__isFunc");
|
|
93
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, isFunc), "Wifi should be a function");
|
|
94
|
+
JS_FreeValue(ctx, isFunc);
|
|
95
|
+
|
|
96
|
+
JSValue isObj = JS_GetPropertyStr(ctx, global, "__isObj");
|
|
97
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, isObj), "new Wifi() should return an object");
|
|
98
|
+
JS_FreeValue(ctx, isObj);
|
|
99
|
+
|
|
100
|
+
JSValue methods = JS_GetPropertyStr(ctx, global, "__methods");
|
|
101
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, methods), "All expected methods should exist");
|
|
102
|
+
JS_FreeValue(ctx, methods);
|
|
103
|
+
|
|
104
|
+
JS_FreeValue(ctx, global);
|
|
105
|
+
teardown();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
TEST_CASE("Wifi ip returns 0.0.0.0 and rssi returns 0 when not connected", "[wifi]") {
|
|
109
|
+
setup();
|
|
110
|
+
|
|
111
|
+
JSValue ret = eval_module(R"(
|
|
112
|
+
import { Wifi } from "native:wifi";
|
|
113
|
+
const wifi = new Wifi();
|
|
114
|
+
globalThis.__ip = wifi.ip();
|
|
115
|
+
globalThis.__rssi = wifi.rssi();
|
|
116
|
+
globalThis.__statusType = typeof wifi.status();
|
|
117
|
+
)");
|
|
118
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
119
|
+
|
|
120
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
121
|
+
|
|
122
|
+
JSValue ipVal = JS_GetPropertyStr(ctx, global, "__ip");
|
|
123
|
+
const char* ipStr = JS_ToCString(ctx, ipVal);
|
|
124
|
+
TEST_ASSERT_EQUAL_STRING("0.0.0.0", ipStr);
|
|
125
|
+
JS_FreeCString(ctx, ipStr);
|
|
126
|
+
JS_FreeValue(ctx, ipVal);
|
|
127
|
+
|
|
128
|
+
int32_t rssi;
|
|
129
|
+
JSValue rssiVal = JS_GetPropertyStr(ctx, global, "__rssi");
|
|
130
|
+
JS_ToInt32(ctx, &rssi, rssiVal);
|
|
131
|
+
JS_FreeValue(ctx, rssiVal);
|
|
132
|
+
TEST_ASSERT_EQUAL_INT32(0, rssi);
|
|
133
|
+
|
|
134
|
+
JSValue stType = JS_GetPropertyStr(ctx, global, "__statusType");
|
|
135
|
+
const char* typeStr = JS_ToCString(ctx, stType);
|
|
136
|
+
TEST_ASSERT_EQUAL_STRING("number", typeStr);
|
|
137
|
+
JS_FreeCString(ctx, typeStr);
|
|
138
|
+
JS_FreeValue(ctx, stType);
|
|
139
|
+
|
|
140
|
+
JS_FreeValue(ctx, global);
|
|
141
|
+
teardown();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ── Connect returns a promise ─────────────────────────────────────── */
|
|
145
|
+
|
|
146
|
+
TEST_CASE("Wifi connect returns a promise", "[wifi]") {
|
|
147
|
+
setup();
|
|
148
|
+
|
|
149
|
+
// Only test that connect() returns a Promise — don't actually wait for connection.
|
|
150
|
+
// We call disconnect() right after to avoid leaving WiFi in "connecting" state.
|
|
151
|
+
JSValue ret = eval_module(R"(
|
|
152
|
+
import { Wifi } from "native:wifi";
|
|
153
|
+
const wifi = new Wifi();
|
|
154
|
+
const result = wifi.connect("test", "pass");
|
|
155
|
+
globalThis.__isPromise = result.ok && result.value instanceof Promise;
|
|
156
|
+
wifi.disconnect();
|
|
157
|
+
)");
|
|
158
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
159
|
+
|
|
160
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
161
|
+
JSValue isPromise = JS_GetPropertyStr(ctx, global, "__isPromise");
|
|
162
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, isPromise), "connect() should return a Promise");
|
|
163
|
+
JS_FreeValue(ctx, isPromise);
|
|
164
|
+
JS_FreeValue(ctx, global);
|
|
165
|
+
teardown();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ── Scan returns a promise ────────────────────────────────────────── */
|
|
169
|
+
|
|
170
|
+
TEST_CASE("Wifi scan returns a promise", "[wifi]") {
|
|
171
|
+
setup();
|
|
172
|
+
|
|
173
|
+
JSValue ret = eval_module(R"(
|
|
174
|
+
import { Wifi } from "native:wifi";
|
|
175
|
+
const wifi = new Wifi();
|
|
176
|
+
const result = wifi.scan();
|
|
177
|
+
globalThis.__isPromise = result.ok && result.value instanceof Promise;
|
|
178
|
+
)");
|
|
179
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
180
|
+
|
|
181
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
182
|
+
JSValue isPromise = JS_GetPropertyStr(ctx, global, "__isPromise");
|
|
183
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, isPromise), "scan() should return a Promise");
|
|
184
|
+
JS_FreeValue(ctx, isPromise);
|
|
185
|
+
JS_FreeValue(ctx, global);
|
|
186
|
+
teardown();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ── Event-driven state machine tests (mock via esp_event_post) ──── */
|
|
190
|
+
|
|
191
|
+
TEST_CASE("WiFi status reflects events through connect-disconnect cycle", "[wifi]") {
|
|
192
|
+
setup();
|
|
193
|
+
|
|
194
|
+
JSValue ret = eval_module(R"(
|
|
195
|
+
import { Wifi } from "native:wifi";
|
|
196
|
+
globalThis.__wifi = new Wifi();
|
|
197
|
+
)");
|
|
198
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
199
|
+
|
|
200
|
+
// Connected
|
|
201
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, nullptr, 0, portMAX_DELAY);
|
|
202
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
203
|
+
mik__wifi_consume(ctx);
|
|
204
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_CONNECTED, get_wifi_status());
|
|
205
|
+
|
|
206
|
+
// Disconnect: NO_AP_FOUND → NO_SSID_AVAIL
|
|
207
|
+
wifi_event_sta_disconnected_t disc = {};
|
|
208
|
+
disc.reason = WIFI_REASON_NO_AP_FOUND;
|
|
209
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
210
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
211
|
+
mik__wifi_consume(ctx);
|
|
212
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_NO_SSID_AVAIL, get_wifi_status());
|
|
213
|
+
|
|
214
|
+
// Disconnect: AUTH_FAIL → CONNECT_FAILED
|
|
215
|
+
disc.reason = WIFI_REASON_AUTH_FAIL;
|
|
216
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
217
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
218
|
+
mik__wifi_consume(ctx);
|
|
219
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_CONNECT_FAILED, get_wifi_status());
|
|
220
|
+
|
|
221
|
+
// Disconnect: BEACON_TIMEOUT → CONNECTION_LOST
|
|
222
|
+
disc.reason = WIFI_REASON_BEACON_TIMEOUT;
|
|
223
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
224
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
225
|
+
mik__wifi_consume(ctx);
|
|
226
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_CONNECTION_LOST, get_wifi_status());
|
|
227
|
+
|
|
228
|
+
// Disconnect: generic → DISCONNECTED
|
|
229
|
+
disc.reason = WIFI_REASON_UNSPECIFIED;
|
|
230
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
231
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
232
|
+
mik__wifi_consume(ctx);
|
|
233
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_DISCONNECTED, get_wifi_status());
|
|
234
|
+
|
|
235
|
+
// Reconnect
|
|
236
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, nullptr, 0, portMAX_DELAY);
|
|
237
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
238
|
+
mik__wifi_consume(ctx);
|
|
239
|
+
TEST_ASSERT_EQUAL_INT32(WIFI_STATUS_CONNECTED, get_wifi_status());
|
|
240
|
+
|
|
241
|
+
teardown();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* ── Event listener tests ──────────────────────────────────────────── */
|
|
245
|
+
|
|
246
|
+
TEST_CASE("Wifi on/off registers and removes event listeners", "[wifi]") {
|
|
247
|
+
setup();
|
|
248
|
+
|
|
249
|
+
JSValue ret = eval_module(R"(
|
|
250
|
+
import { Wifi } from "native:wifi";
|
|
251
|
+
const wifi = new Wifi();
|
|
252
|
+
globalThis.__disconnectCount = 0;
|
|
253
|
+
function onDisconnect(reason) {
|
|
254
|
+
globalThis.__disconnectCount++;
|
|
255
|
+
globalThis.__lastReason = reason;
|
|
256
|
+
}
|
|
257
|
+
wifi.on("disconnect", onDisconnect);
|
|
258
|
+
globalThis.__onDisconnect = onDisconnect;
|
|
259
|
+
globalThis.__wifi = wifi;
|
|
260
|
+
)");
|
|
261
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
262
|
+
|
|
263
|
+
// Fire a disconnect event
|
|
264
|
+
wifi_event_sta_disconnected_t disc = {};
|
|
265
|
+
disc.reason = WIFI_REASON_AUTH_FAIL;
|
|
266
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
267
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
268
|
+
mik__wifi_consume(ctx);
|
|
269
|
+
mik__execute_jobs(ctx);
|
|
270
|
+
|
|
271
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
272
|
+
JSValue countVal = JS_GetPropertyStr(ctx, global, "__disconnectCount");
|
|
273
|
+
int32_t count;
|
|
274
|
+
JS_ToInt32(ctx, &count, countVal);
|
|
275
|
+
JS_FreeValue(ctx, countVal);
|
|
276
|
+
TEST_ASSERT_EQUAL_INT32(1, count);
|
|
277
|
+
|
|
278
|
+
JSValue reasonVal = JS_GetPropertyStr(ctx, global, "__lastReason");
|
|
279
|
+
const char* reason = JS_ToCString(ctx, reasonVal);
|
|
280
|
+
TEST_ASSERT_EQUAL_STRING("auth-failed", reason);
|
|
281
|
+
JS_FreeCString(ctx, reason);
|
|
282
|
+
JS_FreeValue(ctx, reasonVal);
|
|
283
|
+
|
|
284
|
+
// Remove listener and verify it stops receiving events
|
|
285
|
+
eval_module(R"(
|
|
286
|
+
globalThis.__wifi.off("disconnect", globalThis.__onDisconnect);
|
|
287
|
+
)");
|
|
288
|
+
|
|
289
|
+
disc.reason = WIFI_REASON_BEACON_TIMEOUT;
|
|
290
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disc, sizeof(disc), portMAX_DELAY);
|
|
291
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
292
|
+
mik__wifi_consume(ctx);
|
|
293
|
+
mik__execute_jobs(ctx);
|
|
294
|
+
|
|
295
|
+
countVal = JS_GetPropertyStr(ctx, global, "__disconnectCount");
|
|
296
|
+
JS_ToInt32(ctx, &count, countVal);
|
|
297
|
+
JS_FreeValue(ctx, countVal);
|
|
298
|
+
TEST_ASSERT_EQUAL_INT32(1, count); // still 1, listener was removed
|
|
299
|
+
|
|
300
|
+
JS_FreeValue(ctx, global);
|
|
301
|
+
teardown();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* ── Phase 4: Network configuration tests ────────────────────────── */
|
|
305
|
+
|
|
306
|
+
TEST_CASE("Wifi mac returns a valid MAC address format", "[wifi]") {
|
|
307
|
+
setup();
|
|
308
|
+
|
|
309
|
+
JSValue ret = eval_module(R"(
|
|
310
|
+
import { Wifi } from "native:wifi";
|
|
311
|
+
const wifi = new Wifi();
|
|
312
|
+
const result = wifi.mac();
|
|
313
|
+
globalThis.__mac = result.ok ? result.value : "";
|
|
314
|
+
)");
|
|
315
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
316
|
+
|
|
317
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
318
|
+
JSValue macVal = JS_GetPropertyStr(ctx, global, "__mac");
|
|
319
|
+
const char* mac = JS_ToCString(ctx, macVal);
|
|
320
|
+
TEST_ASSERT_NOT_NULL_MESSAGE(mac, "mac() should return a string");
|
|
321
|
+
// MAC format: XX:XX:XX:XX:XX:XX (17 chars)
|
|
322
|
+
TEST_ASSERT_EQUAL_INT(17, strlen(mac));
|
|
323
|
+
// Check colons at expected positions
|
|
324
|
+
TEST_ASSERT_EQUAL_CHAR(':', mac[2]);
|
|
325
|
+
TEST_ASSERT_EQUAL_CHAR(':', mac[5]);
|
|
326
|
+
TEST_ASSERT_EQUAL_CHAR(':', mac[8]);
|
|
327
|
+
TEST_ASSERT_EQUAL_CHAR(':', mac[11]);
|
|
328
|
+
TEST_ASSERT_EQUAL_CHAR(':', mac[14]);
|
|
329
|
+
JS_FreeCString(ctx, mac);
|
|
330
|
+
JS_FreeValue(ctx, macVal);
|
|
331
|
+
JS_FreeValue(ctx, global);
|
|
332
|
+
teardown();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
TEST_CASE("Wifi hostname get/set round-trips", "[wifi]") {
|
|
336
|
+
setup();
|
|
337
|
+
|
|
338
|
+
JSValue ret = eval_module(R"(
|
|
339
|
+
import { Wifi } from "native:wifi";
|
|
340
|
+
const wifi = new Wifi();
|
|
341
|
+
wifi.setHostname("test-device");
|
|
342
|
+
globalThis.__hostname = wifi.getHostname();
|
|
343
|
+
)");
|
|
344
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
345
|
+
|
|
346
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
347
|
+
JSValue hnVal = JS_GetPropertyStr(ctx, global, "__hostname");
|
|
348
|
+
const char* hn = JS_ToCString(ctx, hnVal);
|
|
349
|
+
TEST_ASSERT_EQUAL_STRING("test-device", hn);
|
|
350
|
+
JS_FreeCString(ctx, hn);
|
|
351
|
+
JS_FreeValue(ctx, hnVal);
|
|
352
|
+
JS_FreeValue(ctx, global);
|
|
353
|
+
teardown();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
TEST_CASE("Wifi getIpConfig returns object with expected keys", "[wifi]") {
|
|
357
|
+
setup();
|
|
358
|
+
|
|
359
|
+
JSValue ret = eval_module(R"(
|
|
360
|
+
import { Wifi } from "native:wifi";
|
|
361
|
+
const wifi = new Wifi();
|
|
362
|
+
const result = wifi.getIpConfig();
|
|
363
|
+
const cfg = result.ok ? result.value : undefined;
|
|
364
|
+
globalThis.__hasIp = cfg !== undefined && "ip" in cfg;
|
|
365
|
+
globalThis.__hasNetmask = cfg !== undefined && "netmask" in cfg;
|
|
366
|
+
globalThis.__hasGateway = cfg !== undefined && "gateway" in cfg;
|
|
367
|
+
globalThis.__hasDns = cfg !== undefined && "dns" in cfg;
|
|
368
|
+
)");
|
|
369
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
370
|
+
|
|
371
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
372
|
+
|
|
373
|
+
JSValue hasIp = JS_GetPropertyStr(ctx, global, "__hasIp");
|
|
374
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasIp), "IpConfig should have 'ip' key");
|
|
375
|
+
JS_FreeValue(ctx, hasIp);
|
|
376
|
+
|
|
377
|
+
JSValue hasNetmask = JS_GetPropertyStr(ctx, global, "__hasNetmask");
|
|
378
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasNetmask), "IpConfig should have 'netmask' key");
|
|
379
|
+
JS_FreeValue(ctx, hasNetmask);
|
|
380
|
+
|
|
381
|
+
JSValue hasGateway = JS_GetPropertyStr(ctx, global, "__hasGateway");
|
|
382
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasGateway), "IpConfig should have 'gateway' key");
|
|
383
|
+
JS_FreeValue(ctx, hasGateway);
|
|
384
|
+
|
|
385
|
+
JSValue hasDns = JS_GetPropertyStr(ctx, global, "__hasDns");
|
|
386
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasDns), "IpConfig should have 'dns' key");
|
|
387
|
+
JS_FreeValue(ctx, hasDns);
|
|
388
|
+
|
|
389
|
+
JS_FreeValue(ctx, global);
|
|
390
|
+
teardown();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* ── Phase 5: AP mode tests ──────────────────────────────────────── */
|
|
394
|
+
|
|
395
|
+
TEST_CASE("Wifi AP methods exist and return expected types", "[wifi]") {
|
|
396
|
+
setup();
|
|
397
|
+
|
|
398
|
+
JSValue ret = eval_module(R"(
|
|
399
|
+
import { Wifi } from "native:wifi";
|
|
400
|
+
const wifi = new Wifi();
|
|
401
|
+
globalThis.__apMethods = ["apStart","apStop","apIsActive","apIp","apStations"]
|
|
402
|
+
.every(m => typeof wifi[m] === "function");
|
|
403
|
+
globalThis.__apInactive = wifi.apIsActive() === false;
|
|
404
|
+
globalThis.__apStationsArray = Array.isArray(wifi.apStations());
|
|
405
|
+
)");
|
|
406
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
407
|
+
|
|
408
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
409
|
+
|
|
410
|
+
JSValue apMethods = JS_GetPropertyStr(ctx, global, "__apMethods");
|
|
411
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, apMethods), "All AP methods should exist");
|
|
412
|
+
JS_FreeValue(ctx, apMethods);
|
|
413
|
+
|
|
414
|
+
JSValue apInactive = JS_GetPropertyStr(ctx, global, "__apInactive");
|
|
415
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, apInactive), "AP should be inactive initially");
|
|
416
|
+
JS_FreeValue(ctx, apInactive);
|
|
417
|
+
|
|
418
|
+
JSValue apStations = JS_GetPropertyStr(ctx, global, "__apStationsArray");
|
|
419
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, apStations), "apStations() should return an array");
|
|
420
|
+
JS_FreeValue(ctx, apStations);
|
|
421
|
+
|
|
422
|
+
JS_FreeValue(ctx, global);
|
|
423
|
+
teardown();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* ── Phase 6: Power management & misc tests ──────────────────────── */
|
|
427
|
+
|
|
428
|
+
TEST_CASE("Wifi power save get/set round-trips", "[wifi]") {
|
|
429
|
+
setup();
|
|
430
|
+
|
|
431
|
+
JSValue ret = eval_module(R"(
|
|
432
|
+
import { Wifi } from "native:wifi";
|
|
433
|
+
const wifi = new Wifi();
|
|
434
|
+
wifi.setPowerSave("min");
|
|
435
|
+
globalThis.__ps = wifi.getPowerSave();
|
|
436
|
+
)");
|
|
437
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
438
|
+
|
|
439
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
440
|
+
JSValue psVal = JS_GetPropertyStr(ctx, global, "__ps");
|
|
441
|
+
const char* ps = JS_ToCString(ctx, psVal);
|
|
442
|
+
TEST_ASSERT_EQUAL_STRING("min", ps);
|
|
443
|
+
JS_FreeCString(ctx, ps);
|
|
444
|
+
JS_FreeValue(ctx, psVal);
|
|
445
|
+
JS_FreeValue(ctx, global);
|
|
446
|
+
teardown();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
TEST_CASE("Wifi country get/set round-trips", "[wifi]") {
|
|
450
|
+
setup();
|
|
451
|
+
|
|
452
|
+
JSValue ret = eval_module(R"(
|
|
453
|
+
import { Wifi } from "native:wifi";
|
|
454
|
+
const wifi = new Wifi();
|
|
455
|
+
wifi.setCountry("US");
|
|
456
|
+
globalThis.__cc = wifi.getCountry();
|
|
457
|
+
)");
|
|
458
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
459
|
+
|
|
460
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
461
|
+
JSValue ccVal = JS_GetPropertyStr(ctx, global, "__cc");
|
|
462
|
+
const char* cc = JS_ToCString(ctx, ccVal);
|
|
463
|
+
TEST_ASSERT_EQUAL_STRING("US", cc);
|
|
464
|
+
JS_FreeCString(ctx, cc);
|
|
465
|
+
JS_FreeValue(ctx, ccVal);
|
|
466
|
+
JS_FreeValue(ctx, global);
|
|
467
|
+
teardown();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* ── Scan with filter options ─────────────────────────────────────── */
|
|
471
|
+
|
|
472
|
+
TEST_CASE("Wifi scan accepts filter options", "[wifi]") {
|
|
473
|
+
setup();
|
|
474
|
+
|
|
475
|
+
JSValue ret = eval_module(R"(
|
|
476
|
+
import { Wifi } from "native:wifi";
|
|
477
|
+
const wifi = new Wifi();
|
|
478
|
+
const result = wifi.scan({channel: 1, passive: true});
|
|
479
|
+
globalThis.__isPromise = result.ok && result.value instanceof Promise;
|
|
480
|
+
)");
|
|
481
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
482
|
+
|
|
483
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
484
|
+
JSValue isPromise = JS_GetPropertyStr(ctx, global, "__isPromise");
|
|
485
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, isPromise), "scan({...}) should return a Promise");
|
|
486
|
+
JS_FreeValue(ctx, isPromise);
|
|
487
|
+
JS_FreeValue(ctx, global);
|
|
488
|
+
teardown();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* ── TX power get/set ─────────────────────────────────────────────── */
|
|
492
|
+
|
|
493
|
+
TEST_CASE("Wifi txPower get/set round-trips", "[wifi]") {
|
|
494
|
+
setup();
|
|
495
|
+
|
|
496
|
+
JSValue ret = eval_module(R"(
|
|
497
|
+
import { Wifi } from "native:wifi";
|
|
498
|
+
const wifi = new Wifi();
|
|
499
|
+
wifi.setTxPower(8);
|
|
500
|
+
const result = wifi.getTxPower();
|
|
501
|
+
globalThis.__txPower = result.ok ? result.value : 0;
|
|
502
|
+
)");
|
|
503
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
504
|
+
|
|
505
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
506
|
+
JSValue txVal = JS_GetPropertyStr(ctx, global, "__txPower");
|
|
507
|
+
double txPower;
|
|
508
|
+
JS_ToFloat64(ctx, &txPower, txVal);
|
|
509
|
+
JS_FreeValue(ctx, txVal);
|
|
510
|
+
// ESP-IDF may clamp; just verify it's a reasonable positive number
|
|
511
|
+
TEST_ASSERT_TRUE_MESSAGE(txPower > 0, "TX power should be positive");
|
|
512
|
+
JS_FreeValue(ctx, global);
|
|
513
|
+
teardown();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/* ── RSSI threshold ───────────────────────────────────────────────── */
|
|
517
|
+
|
|
518
|
+
TEST_CASE("Wifi RSSI threshold get/set and event listener", "[wifi]") {
|
|
519
|
+
setup();
|
|
520
|
+
|
|
521
|
+
JSValue ret = eval_module(R"(
|
|
522
|
+
import { Wifi } from "native:wifi";
|
|
523
|
+
const wifi = new Wifi();
|
|
524
|
+
wifi.setRssiThreshold(-70);
|
|
525
|
+
globalThis.__threshold = wifi.getRssiThreshold();
|
|
526
|
+
globalThis.__rssiLowCount = 0;
|
|
527
|
+
wifi.on("rssi-low", (rssi) => {
|
|
528
|
+
globalThis.__rssiLowCount++;
|
|
529
|
+
globalThis.__lastRssi = rssi;
|
|
530
|
+
});
|
|
531
|
+
globalThis.__wifi = wifi;
|
|
532
|
+
)");
|
|
533
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
534
|
+
|
|
535
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
536
|
+
JSValue threshVal = JS_GetPropertyStr(ctx, global, "__threshold");
|
|
537
|
+
int32_t threshold;
|
|
538
|
+
JS_ToInt32(ctx, &threshold, threshVal);
|
|
539
|
+
JS_FreeValue(ctx, threshVal);
|
|
540
|
+
TEST_ASSERT_EQUAL_INT32(-70, threshold);
|
|
541
|
+
|
|
542
|
+
// Fire a mock RSSI low event
|
|
543
|
+
wifi_event_bss_rssi_low_t rssi_evt = {};
|
|
544
|
+
rssi_evt.rssi = -75;
|
|
545
|
+
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &rssi_evt, sizeof(rssi_evt),
|
|
546
|
+
portMAX_DELAY);
|
|
547
|
+
vTaskDelay(pdMS_TO_TICKS(50));
|
|
548
|
+
mik__wifi_consume(ctx);
|
|
549
|
+
mik__execute_jobs(ctx);
|
|
550
|
+
|
|
551
|
+
JSValue countVal = JS_GetPropertyStr(ctx, global, "__rssiLowCount");
|
|
552
|
+
int32_t count;
|
|
553
|
+
JS_ToInt32(ctx, &count, countVal);
|
|
554
|
+
JS_FreeValue(ctx, countVal);
|
|
555
|
+
TEST_ASSERT_EQUAL_INT32(1, count);
|
|
556
|
+
|
|
557
|
+
JSValue rssiVal = JS_GetPropertyStr(ctx, global, "__lastRssi");
|
|
558
|
+
int32_t rssi;
|
|
559
|
+
JS_ToInt32(ctx, &rssi, rssiVal);
|
|
560
|
+
JS_FreeValue(ctx, rssiVal);
|
|
561
|
+
TEST_ASSERT_EQUAL_INT32(-75, rssi);
|
|
562
|
+
|
|
563
|
+
JS_FreeValue(ctx, global);
|
|
564
|
+
teardown();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* ── AP deauthStation and inactiveTimeout methods exist ───────────── */
|
|
568
|
+
|
|
569
|
+
TEST_CASE("Wifi AP extra methods exist", "[wifi]") {
|
|
570
|
+
setup();
|
|
571
|
+
|
|
572
|
+
JSValue ret = eval_module(R"(
|
|
573
|
+
import { Wifi } from "native:wifi";
|
|
574
|
+
const wifi = new Wifi();
|
|
575
|
+
globalThis.__hasDeauth = typeof wifi.apDeauthStation === "function";
|
|
576
|
+
globalThis.__hasGetTimeout = typeof wifi.apGetInactiveTimeout === "function";
|
|
577
|
+
globalThis.__hasSetTimeout = typeof wifi.apSetInactiveTimeout === "function";
|
|
578
|
+
)");
|
|
579
|
+
TEST_ASSERT_FALSE_MESSAGE(JS_IsException(ret), "Module eval should not throw");
|
|
580
|
+
|
|
581
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
582
|
+
|
|
583
|
+
JSValue hasDeauth = JS_GetPropertyStr(ctx, global, "__hasDeauth");
|
|
584
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasDeauth), "apDeauthStation should be a function");
|
|
585
|
+
JS_FreeValue(ctx, hasDeauth);
|
|
586
|
+
|
|
587
|
+
JSValue hasGetTimeout = JS_GetPropertyStr(ctx, global, "__hasGetTimeout");
|
|
588
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasGetTimeout),
|
|
589
|
+
"apGetInactiveTimeout should be a function");
|
|
590
|
+
JS_FreeValue(ctx, hasGetTimeout);
|
|
591
|
+
|
|
592
|
+
JSValue hasSetTimeout = JS_GetPropertyStr(ctx, global, "__hasSetTimeout");
|
|
593
|
+
TEST_ASSERT_TRUE_MESSAGE(JS_ToBool(ctx, hasSetTimeout),
|
|
594
|
+
"apSetInactiveTimeout should be a function");
|
|
595
|
+
JS_FreeValue(ctx, hasSetTimeout);
|
|
596
|
+
|
|
597
|
+
JS_FreeValue(ctx, global);
|
|
598
|
+
teardown();
|
|
599
|
+
}
|
package/discover.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover ESP-IDF components and board sdkconfig defaults from
|
|
3
|
+
* a firmware project's dependencies.
|
|
4
|
+
*
|
|
5
|
+
* Can be used as a module (import {discover} from '@mikrojs/firmware/discover')
|
|
6
|
+
* or run directly via node (reads cwd).
|
|
7
|
+
*/
|
|
8
|
+
import {readFileSync, existsSync} from 'node:fs'
|
|
9
|
+
import {createRequire} from 'node:module'
|
|
10
|
+
import {dirname, join, resolve} from 'node:path'
|
|
11
|
+
|
|
12
|
+
/** Find a package's directory by walking up node_modules from startDir */
|
|
13
|
+
function findPackageDir(name, startDir) {
|
|
14
|
+
let dir = startDir
|
|
15
|
+
while (true) {
|
|
16
|
+
const candidate = join(dir, 'node_modules', name)
|
|
17
|
+
if (existsSync(join(candidate, 'package.json'))) return candidate
|
|
18
|
+
const parent = dirname(dir)
|
|
19
|
+
if (parent === dir) return null
|
|
20
|
+
dir = parent
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Scan dependencies in the given project directory for ESP-IDF components
|
|
26
|
+
* and board sdkconfig defaults.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} projectDir - Directory containing package.json
|
|
29
|
+
* @returns {{components: string, sdkconfigs: string}} Semicolon-separated paths
|
|
30
|
+
*/
|
|
31
|
+
export function discover(projectDir) {
|
|
32
|
+
const require = createRequire(join(projectDir, 'package.json'))
|
|
33
|
+
const pkg = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'))
|
|
34
|
+
const dependencies = pkg.dependencies ?? {}
|
|
35
|
+
const board = process.env.MIKROJS_BOARD ?? ''
|
|
36
|
+
const components = []
|
|
37
|
+
const sdkconfigs = []
|
|
38
|
+
|
|
39
|
+
for (const dep of Object.keys(dependencies)) {
|
|
40
|
+
if (dep === '@mikrojs/native' || dep === '@mikrojs/quickjs' || dep === '@mikrojs/firmware')
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
const depDir = findPackageDir(dep, projectDir)
|
|
44
|
+
if (!depDir) continue
|
|
45
|
+
|
|
46
|
+
// Check for cmake component exports (cmake.js files use CJS)
|
|
47
|
+
let cmake
|
|
48
|
+
try {
|
|
49
|
+
cmake = require(dep + '/cmake')
|
|
50
|
+
} catch {
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (cmake.componentPath) components.push(cmake.componentPath)
|
|
55
|
+
if (Array.isArray(cmake.componentPaths)) components.push(...cmake.componentPaths)
|
|
56
|
+
|
|
57
|
+
// Read board-specific sdkconfig from mikrojs.boards manifest
|
|
58
|
+
try {
|
|
59
|
+
const depPkg = JSON.parse(readFileSync(join(depDir, 'package.json'), 'utf8'))
|
|
60
|
+
if (depPkg.mikrojs?.boards) {
|
|
61
|
+
for (const [subpath, config] of Object.entries(depPkg.mikrojs.boards)) {
|
|
62
|
+
const boardName = subpath.startsWith('./') ? subpath.slice(2) : subpath
|
|
63
|
+
if ((!board || board === boardName) && config.sdkconfig) {
|
|
64
|
+
sdkconfigs.push(resolve(depDir, config.sdkconfig))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// No board manifest
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
components: components.join(';'),
|
|
75
|
+
sdkconfigs: sdkconfigs.join(';'),
|
|
76
|
+
}
|
|
77
|
+
}
|
package/index.d.ts
ADDED