@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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/bin/idf.py +7 -0
  4. package/chips.json +3 -0
  5. package/cmake.js +9 -0
  6. package/components/mikrojs/CMakeLists.txt +187 -0
  7. package/components/mikrojs/Kconfig +55 -0
  8. package/components/mikrojs/idf_component.yml +6 -0
  9. package/components/mikrojs/include/mem.h +3 -0
  10. package/components/mikrojs/include/mik_color.h +3 -0
  11. package/components/mikrojs/include/mik_http_internal.h +77 -0
  12. package/components/mikrojs/include/mikrojs.h +5 -0
  13. package/components/mikrojs/include/mikrojs_esp32.h +65 -0
  14. package/components/mikrojs/include/private.h +10 -0
  15. package/components/mikrojs/include/utils.h +3 -0
  16. package/components/mikrojs/mik_ble.cpp +1588 -0
  17. package/components/mikrojs/mik_ble_c_shim.c +61 -0
  18. package/components/mikrojs/mik_ble_c_shim.h +37 -0
  19. package/components/mikrojs/mik_config.cpp +167 -0
  20. package/components/mikrojs/mik_deploy.cpp +584 -0
  21. package/components/mikrojs/mik_http.cpp +916 -0
  22. package/components/mikrojs/mik_i2c.cpp +364 -0
  23. package/components/mikrojs/mik_main.cpp +542 -0
  24. package/components/mikrojs/mik_neopixel.cpp +437 -0
  25. package/components/mikrojs/mik_nvs_kv.cpp +219 -0
  26. package/components/mikrojs/mik_pin.cpp +195 -0
  27. package/components/mikrojs/mik_pwm.cpp +525 -0
  28. package/components/mikrojs/mik_recovery.cpp +86 -0
  29. package/components/mikrojs/mik_rtc.cpp +305 -0
  30. package/components/mikrojs/mik_serial_io.cpp +362 -0
  31. package/components/mikrojs/mik_sleep.cpp +226 -0
  32. package/components/mikrojs/mik_sntp.cpp +275 -0
  33. package/components/mikrojs/mik_spi.cpp +330 -0
  34. package/components/mikrojs/mik_uart.cpp +497 -0
  35. package/components/mikrojs/mik_wifi.cpp +1434 -0
  36. package/components/mikrojs/platform_esp32.cpp +192 -0
  37. package/components/mikrojs/test/CMakeLists.txt +32 -0
  38. package/components/mikrojs/test/abort_test.cpp +254 -0
  39. package/components/mikrojs/test/ble_test.cpp +714 -0
  40. package/components/mikrojs/test/fs_js_test.cpp +458 -0
  41. package/components/mikrojs/test/fs_pub_test.cpp +312 -0
  42. package/components/mikrojs/test/http_test.cpp +475 -0
  43. package/components/mikrojs/test/i2c_test.cpp +138 -0
  44. package/components/mikrojs/test/modules_extended_test.cpp +137 -0
  45. package/components/mikrojs/test/modules_test.cpp +131 -0
  46. package/components/mikrojs/test/pins_test.cpp +47 -0
  47. package/components/mikrojs/test/pwm_test.cpp +166 -0
  48. package/components/mikrojs/test/repl_protocol_test.cpp +405 -0
  49. package/components/mikrojs/test/rtc_test.cpp +331 -0
  50. package/components/mikrojs/test/runtime_test.cpp +89 -0
  51. package/components/mikrojs/test/sleep_test.cpp +222 -0
  52. package/components/mikrojs/test/sntp_test.cpp +249 -0
  53. package/components/mikrojs/test/stdio_test.cpp +449 -0
  54. package/components/mikrojs/test/sys_test.cpp +165 -0
  55. package/components/mikrojs/test/text_encoding_test.cpp +224 -0
  56. package/components/mikrojs/test/timers_js_test.cpp +244 -0
  57. package/components/mikrojs/test/timers_test.cpp +79 -0
  58. package/components/mikrojs/test/wifi_test.cpp +599 -0
  59. package/default-app/main/CMakeLists.txt +3 -0
  60. package/default-app/main/main.cpp +5 -0
  61. package/discover.js +77 -0
  62. package/index.d.ts +7 -0
  63. package/index.js +20 -0
  64. package/package.json +61 -0
  65. package/partitions.csv +5 -0
  66. package/prebuilds/esp32/bootloader/bootloader.bin +0 -0
  67. package/prebuilds/esp32/flasher_args.json +24 -0
  68. package/prebuilds/esp32/mikrojs.bin +0 -0
  69. package/prebuilds/esp32/partition_table/partition-table.bin +0 -0
  70. package/prebuilds/esp32c3/bootloader/bootloader.bin +0 -0
  71. package/prebuilds/esp32c3/flasher_args.json +24 -0
  72. package/prebuilds/esp32c3/mikrojs.bin +0 -0
  73. package/prebuilds/esp32c3/partition_table/partition-table.bin +0 -0
  74. package/prebuilds/esp32c6/bootloader/bootloader.bin +0 -0
  75. package/prebuilds/esp32c6/flasher_args.json +24 -0
  76. package/prebuilds/esp32c6/mikrojs.bin +0 -0
  77. package/prebuilds/esp32c6/partition_table/partition-table.bin +0 -0
  78. package/prebuilds/esp32s3/bootloader/bootloader.bin +0 -0
  79. package/prebuilds/esp32s3/flasher_args.json +24 -0
  80. package/prebuilds/esp32s3/mikrojs.bin +0 -0
  81. package/prebuilds/esp32s3/partition_table/partition-table.bin +0 -0
  82. package/project.cmake +101 -0
  83. package/resolve.js +54 -0
  84. package/sdkconfig.defaults +127 -0
  85. package/sdkconfig.defaults.esp32 +8 -0
  86. package/sdkconfig.defaults.esp32c3 +15 -0
  87. package/sdkconfig.defaults.esp32c6 +26 -0
  88. package/sdkconfig.defaults.esp32s3 +22 -0
@@ -0,0 +1,364 @@
1
+ #include <cstring>
2
+
3
+ #include "driver/i2c_master.h"
4
+ #include "mikrojs/private.h"
5
+ #include "mikrojs/utils.h"
6
+
7
+ #define MIK_I2C_TAG "native:i2c"
8
+ #define MIK_I2C_DEFAULT_FREQ 100000
9
+ #define MIK_I2C_DEFAULT_TIMEOUT_MS 100
10
+ #define MIK_I2C_SCAN_START 0x08
11
+ #define MIK_I2C_SCAN_END 0x77
12
+ #define MIK_I2C_MAX_PENDING_WRITE 256
13
+
14
+ static JSClassID mik_i2c_class_id;
15
+
16
+ typedef struct {
17
+ i2c_master_bus_handle_t bus;
18
+ int32_t port; // 0 or 1
19
+ int32_t sda;
20
+ int32_t scl;
21
+ uint32_t freq;
22
+ int32_t timeout_ms;
23
+ bool begun;
24
+ /* Pending write buffer for stop=false (used with transmit_receive) */
25
+ uint8_t pending_write[MIK_I2C_MAX_PENDING_WRITE];
26
+ size_t pending_write_len;
27
+ uint16_t pending_write_addr;
28
+ bool has_pending_write;
29
+ } MIKI2CState;
30
+
31
+ /* ── Helpers ───────────────────────────────────────────────────────── */
32
+
33
+ static MIKI2CState* mik__i2c_get(JSContext* ctx, JSValue this_val) {
34
+ auto* s = static_cast<MIKI2CState*>(JS_GetOpaque2(ctx, this_val, mik_i2c_class_id));
35
+ return s;
36
+ }
37
+
38
+ static void mik__i2c_clear_pending(MIKI2CState* s) {
39
+ s->has_pending_write = false;
40
+ s->pending_write_len = 0;
41
+ }
42
+
43
+ static esp_err_t mik__i2c_add_device(MIKI2CState* s, uint16_t addr,
44
+ i2c_master_dev_handle_t* out_dev) {
45
+ i2c_device_config_t dev_cfg = {};
46
+ dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
47
+ dev_cfg.device_address = addr;
48
+ dev_cfg.scl_speed_hz = s->freq;
49
+ return i2c_master_bus_add_device(s->bus, &dev_cfg, out_dev);
50
+ }
51
+
52
+ /* ── Finalizer ─────────────────────────────────────────────────────── */
53
+
54
+ static void mik__i2c_finalizer(JSRuntime* rt, JSValue val) {
55
+ auto* s = static_cast<MIKI2CState*>(JS_GetOpaque(val, mik_i2c_class_id));
56
+ if (!s) return;
57
+ if (s->begun) {
58
+ i2c_del_master_bus(s->bus);
59
+ }
60
+ free(s);
61
+ }
62
+
63
+ static JSClassDef mik_i2c_class = {
64
+ .class_name = "I2c",
65
+ .finalizer = mik__i2c_finalizer,
66
+ };
67
+
68
+ /* ── Constructor ───────────────────────────────────────────────────── */
69
+
70
+ static JSValue js_i2c_constructor(JSContext* ctx, JSValue new_target, int argc, JSValue* argv) {
71
+ if (argc < 1) return JS_ThrowTypeError(ctx, "I2c requires busNo argument");
72
+
73
+ int32_t port;
74
+ if (JS_ToInt32(ctx, &port, argv[0])) return JS_EXCEPTION;
75
+ if (port < 0 || port > 1) return JS_ThrowRangeError(ctx, "busNo must be 0 or 1");
76
+
77
+ auto* s = static_cast<MIKI2CState*>(calloc(1, sizeof(MIKI2CState)));
78
+ if (!s) return JS_ThrowOutOfMemory(ctx);
79
+
80
+ s->port = port;
81
+ s->sda = -1;
82
+ s->scl = -1;
83
+ s->freq = MIK_I2C_DEFAULT_FREQ;
84
+ s->timeout_ms = MIK_I2C_DEFAULT_TIMEOUT_MS;
85
+ s->begun = false;
86
+ mik__i2c_clear_pending(s);
87
+
88
+ /* Parse options object */
89
+ if (argc >= 2 && JS_IsObject(argv[1])) {
90
+ JSValue opts = argv[1];
91
+ JSValue v;
92
+
93
+ v = JS_GetPropertyStr(ctx, opts, "sda");
94
+ if (!JS_IsUndefined(v)) {
95
+ if (JS_ToInt32(ctx, &s->sda, v)) {
96
+ JS_FreeValue(ctx, v);
97
+ free(s);
98
+ return JS_EXCEPTION;
99
+ }
100
+ }
101
+ JS_FreeValue(ctx, v);
102
+
103
+ v = JS_GetPropertyStr(ctx, opts, "scl");
104
+ if (!JS_IsUndefined(v)) {
105
+ if (JS_ToInt32(ctx, &s->scl, v)) {
106
+ JS_FreeValue(ctx, v);
107
+ free(s);
108
+ return JS_EXCEPTION;
109
+ }
110
+ }
111
+ JS_FreeValue(ctx, v);
112
+
113
+ v = JS_GetPropertyStr(ctx, opts, "freq");
114
+ if (!JS_IsUndefined(v)) {
115
+ int32_t freq;
116
+ if (JS_ToInt32(ctx, &freq, v)) {
117
+ JS_FreeValue(ctx, v);
118
+ free(s);
119
+ return JS_EXCEPTION;
120
+ }
121
+ s->freq = static_cast<uint32_t>(freq);
122
+ }
123
+ JS_FreeValue(ctx, v);
124
+
125
+ v = JS_GetPropertyStr(ctx, opts, "timeout");
126
+ if (!JS_IsUndefined(v)) {
127
+ if (JS_ToInt32(ctx, &s->timeout_ms, v)) {
128
+ JS_FreeValue(ctx, v);
129
+ free(s);
130
+ return JS_EXCEPTION;
131
+ }
132
+ }
133
+ JS_FreeValue(ctx, v);
134
+ }
135
+
136
+ JSValue obj = JS_NewObjectClass(ctx, mik_i2c_class_id);
137
+ if (JS_IsException(obj)) {
138
+ free(s);
139
+ return obj;
140
+ }
141
+ JS_SetOpaque(obj, s);
142
+ return obj;
143
+ }
144
+
145
+ /* ── Methods ───────────────────────────────────────────────────────── */
146
+
147
+ static JSValue js_i2c_begin(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
148
+ auto* s = mik__i2c_get(ctx, this_val);
149
+ if (!s) return JS_EXCEPTION;
150
+ if (s->begun) return mik__result_ok_void(ctx); // idempotent
151
+
152
+ if (s->sda < 0 || s->scl < 0) return mik__result_err_tag(ctx, "MissingPins");
153
+
154
+ i2c_master_bus_config_t bus_cfg = {};
155
+ bus_cfg.i2c_port = static_cast<i2c_port_num_t>(s->port);
156
+ bus_cfg.sda_io_num = static_cast<gpio_num_t>(s->sda);
157
+ bus_cfg.scl_io_num = static_cast<gpio_num_t>(s->scl);
158
+ bus_cfg.clk_source = I2C_CLK_SRC_DEFAULT;
159
+ bus_cfg.glitch_ignore_cnt = 7;
160
+ bus_cfg.flags.enable_internal_pullup = true;
161
+
162
+ esp_err_t err = i2c_new_master_bus(&bus_cfg, &s->bus);
163
+ if (err != ESP_OK)
164
+ return mik__result_err_named(ctx, "BusInitFailed",
165
+ "failed to initialize I2C bus %d: %s", s->port,
166
+ esp_err_to_name(err));
167
+
168
+ s->begun = true;
169
+ mik__i2c_clear_pending(s);
170
+ return mik__result_ok_void(ctx);
171
+ }
172
+
173
+ static JSValue js_i2c_end(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
174
+ auto* s = mik__i2c_get(ctx, this_val);
175
+ if (!s) return JS_EXCEPTION;
176
+ if (!s->begun) return mik__result_ok_void(ctx); // idempotent
177
+
178
+ mik__i2c_clear_pending(s);
179
+ esp_err_t err = i2c_del_master_bus(s->bus);
180
+ if (err != ESP_OK)
181
+ return mik__result_err_named(ctx, "BusDeinitFailed",
182
+ "failed to deinitialize I2C bus: %s", esp_err_to_name(err));
183
+
184
+ s->bus = nullptr;
185
+ s->begun = false;
186
+ return mik__result_ok_void(ctx);
187
+ }
188
+
189
+ static JSValue js_i2c_write(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
190
+ auto* s = mik__i2c_get(ctx, this_val);
191
+ if (!s) return JS_EXCEPTION;
192
+ if (!s->begun) return mik__result_err_tag(ctx, "NotStarted");
193
+
194
+ int32_t addr;
195
+ if (JS_ToInt32(ctx, &addr, argv[0])) return JS_EXCEPTION;
196
+
197
+ size_t data_len;
198
+ uint8_t* data;
199
+
200
+ /* Try typed array (Uint8Array) first, then raw ArrayBuffer */
201
+ size_t offset, elem_size;
202
+ JSValue ab = JS_GetTypedArrayBuffer(ctx, argv[1], &offset, &data_len, &elem_size);
203
+ if (!JS_IsException(ab)) {
204
+ data = JS_GetArrayBuffer(ctx, &data_len, ab);
205
+ JS_FreeValue(ctx, ab);
206
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 2");
207
+ data += offset;
208
+ } else {
209
+ /* Clear the pending exception from GetTypedArrayBuffer and try raw ArrayBuffer */
210
+ JSValue exc = JS_GetException(ctx);
211
+ JS_FreeValue(ctx, exc);
212
+ data = JS_GetArrayBuffer(ctx, &data_len, argv[1]);
213
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 2");
214
+ }
215
+
216
+ /* Check stop parameter (default: true) */
217
+ bool stop = true;
218
+ if (argc >= 3 && !JS_IsUndefined(argv[2])) {
219
+ stop = JS_ToBool(ctx, argv[2]);
220
+ }
221
+
222
+ if (!stop) {
223
+ /* Buffer write data for later transmit_receive */
224
+ if (data_len > MIK_I2C_MAX_PENDING_WRITE)
225
+ return mik__result_err_tag(ctx, "WriteTooLarge");
226
+ memcpy(s->pending_write, data, data_len);
227
+ s->pending_write_len = data_len;
228
+ s->pending_write_addr = static_cast<uint16_t>(addr);
229
+ s->has_pending_write = true;
230
+ return mik__result_ok_void(ctx);
231
+ }
232
+
233
+ /* Normal write with STOP */
234
+ mik__i2c_clear_pending(s);
235
+
236
+ i2c_master_dev_handle_t dev;
237
+ esp_err_t err = mik__i2c_add_device(s, static_cast<uint16_t>(addr), &dev);
238
+ if (err != ESP_OK)
239
+ return mik__result_err_named(ctx, "AddDeviceFailed",
240
+ "failed to add I2C device 0x%02x: %s", addr,
241
+ esp_err_to_name(err));
242
+
243
+ err = i2c_master_transmit(dev, data, data_len, s->timeout_ms);
244
+ i2c_master_bus_rm_device(dev);
245
+
246
+ if (err != ESP_OK)
247
+ return mik__result_err_named(ctx, "WriteFailed",
248
+ "I2C write to 0x%02x failed: %s", addr,
249
+ esp_err_to_name(err));
250
+
251
+ return mik__result_ok_void(ctx);
252
+ }
253
+
254
+ static JSValue js_i2c_read(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
255
+ auto* s = mik__i2c_get(ctx, this_val);
256
+ if (!s) return JS_EXCEPTION;
257
+ if (!s->begun) return mik__result_err_tag(ctx, "NotStarted");
258
+
259
+ int32_t addr;
260
+ if (JS_ToInt32(ctx, &addr, argv[0])) return JS_EXCEPTION;
261
+
262
+ int32_t bytes;
263
+ if (JS_ToInt32(ctx, &bytes, argv[1])) return JS_EXCEPTION;
264
+ if (bytes <= 0) return JS_ThrowRangeError(ctx, "read length must be > 0");
265
+
266
+ /* Allocate with js_malloc — MIK_NewUint8Array takes ownership and frees via js_free_rt */
267
+ auto* buf = static_cast<uint8_t*>(js_malloc(ctx, bytes));
268
+ if (!buf) return JS_EXCEPTION;
269
+
270
+ i2c_master_dev_handle_t dev;
271
+ esp_err_t err = mik__i2c_add_device(s, static_cast<uint16_t>(addr), &dev);
272
+ if (err != ESP_OK) {
273
+ js_free(ctx, buf);
274
+ return mik__result_err_named(ctx, "AddDeviceFailed",
275
+ "failed to add I2C device 0x%02x: %s", addr,
276
+ esp_err_to_name(err));
277
+ }
278
+
279
+ if (s->has_pending_write && s->pending_write_addr == static_cast<uint16_t>(addr)) {
280
+ /* Combined write-read (ReSTART) via transmit_receive */
281
+ err = i2c_master_transmit_receive(dev, s->pending_write, s->pending_write_len, buf, bytes,
282
+ s->timeout_ms);
283
+ mik__i2c_clear_pending(s);
284
+ } else {
285
+ mik__i2c_clear_pending(s);
286
+ err = i2c_master_receive(dev, buf, bytes, s->timeout_ms);
287
+ }
288
+
289
+ i2c_master_bus_rm_device(dev);
290
+
291
+ if (err != ESP_OK) {
292
+ js_free(ctx, buf);
293
+ return mik__result_err_named(ctx, "ReadFailed",
294
+ "I2C read from 0x%02x failed: %s", addr,
295
+ esp_err_to_name(err));
296
+ }
297
+
298
+ /* MIK_NewUint8Array takes ownership of buf — do NOT free it */
299
+ return mik__result_ok(ctx, MIK_NewUint8Array(ctx, buf, bytes));
300
+ }
301
+
302
+ static JSValue js_i2c_scan(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
303
+ auto* s = mik__i2c_get(ctx, this_val);
304
+ if (!s) return JS_EXCEPTION;
305
+ if (!s->begun) return mik__result_err_tag(ctx, "NotStarted");
306
+
307
+ /* Worst case: all addresses respond */
308
+ uint8_t found[MIK_I2C_SCAN_END - MIK_I2C_SCAN_START + 1];
309
+ int count = 0;
310
+
311
+ for (int addr = MIK_I2C_SCAN_START; addr <= MIK_I2C_SCAN_END; addr++) {
312
+ esp_err_t err = i2c_master_probe(s->bus, static_cast<uint16_t>(addr), s->timeout_ms);
313
+ if (err == ESP_OK) {
314
+ found[count++] = static_cast<uint8_t>(addr);
315
+ }
316
+ }
317
+
318
+ size_t alloc_size = count > 0 ? count : 1;
319
+ auto* result = static_cast<uint8_t*>(js_malloc(ctx, alloc_size));
320
+ if (!result) return JS_EXCEPTION;
321
+ if (count > 0) memcpy(result, found, count);
322
+
323
+ /* MIK_NewUint8Array takes ownership of result — do NOT free it */
324
+ return mik__result_ok(ctx, MIK_NewUint8Array(ctx, result, count));
325
+ }
326
+
327
+ /* ── Prototype ─────────────────────────────────────────────────────── */
328
+
329
+ static const JSCFunctionListEntry mik_i2c_proto_funcs[] = {
330
+ MIK_CFUNC_DEF("begin", 0, js_i2c_begin),
331
+ MIK_CFUNC_DEF("end", 0, js_i2c_end),
332
+ MIK_CFUNC_DEF("write", 3, js_i2c_write),
333
+ MIK_CFUNC_DEF("read", 2, js_i2c_read),
334
+ MIK_CFUNC_DEF("scan", 0, js_i2c_scan),
335
+ };
336
+
337
+ /* ── Module init ───────────────────────────────────────────────────── */
338
+
339
+ static int mik__i2c_module_init(JSContext* ctx, JSModuleDef* m) {
340
+ JSValue ctor = JS_NewCFunction2(ctx, js_i2c_constructor, "I2c", 2, JS_CFUNC_constructor, 0);
341
+ JS_SetModuleExport(ctx, m, "I2c", ctor);
342
+ return 0;
343
+ }
344
+
345
+ static JSModuleDef* mik__i2c_init(JSContext* ctx) {
346
+ JSRuntime* rt = JS_GetRuntime(ctx);
347
+
348
+ /* Register class (once per runtime) */
349
+ JS_NewClassID(rt, &mik_i2c_class_id);
350
+ JS_NewClass(rt, mik_i2c_class_id, &mik_i2c_class);
351
+
352
+ /* Create prototype with methods */
353
+ JSValue proto = JS_NewObject(ctx);
354
+ JS_SetPropertyFunctionList(ctx, proto, mik_i2c_proto_funcs, countof(mik_i2c_proto_funcs));
355
+ JS_SetClassProto(ctx, mik_i2c_class_id, proto); /* consumed */
356
+
357
+ /* Register module */
358
+ JSModuleDef* m = JS_NewCModule(ctx, "native:i2c", mik__i2c_module_init);
359
+ if (!m) return nullptr;
360
+ JS_AddModuleExport(ctx, m, "I2c");
361
+ return m;
362
+ }
363
+
364
+ MIK_REGISTER_MODULE(i2c, "native:i2c", mik__i2c_init, nullptr, nullptr)