@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,330 @@
1
+ #include <cstring>
2
+
3
+ #include "driver/spi_master.h"
4
+ #include "soc/soc_caps.h"
5
+ #include "mikrojs/private.h"
6
+ #include "mikrojs/utils.h"
7
+
8
+ #define MIK_SPI_TAG "native:spi"
9
+ #define MIK_SPI_DEFAULT_FREQ 1000000
10
+ #define MIK_SPI_DEFAULT_MODE 0
11
+
12
+ static JSClassID mik_spi_class_id;
13
+
14
+ typedef struct {
15
+ spi_device_handle_t device;
16
+ spi_host_device_t host;
17
+ int32_t clk;
18
+ int32_t mosi;
19
+ int32_t miso;
20
+ int32_t cs;
21
+ int32_t freq;
22
+ int32_t mode;
23
+ bool begun;
24
+ } MIKSPIState;
25
+
26
+ /* ── Helpers ───────────────────────────────────────────────────────── */
27
+
28
+ static MIKSPIState* mik__spi_get(JSContext* ctx, JSValue this_val) {
29
+ return static_cast<MIKSPIState*>(JS_GetOpaque2(ctx, this_val, mik_spi_class_id));
30
+ }
31
+
32
+ /* ── Finalizer ─────────────────────────────────────────────────────── */
33
+
34
+ static void mik__spi_finalizer(JSRuntime* rt, JSValue val) {
35
+ auto* s = static_cast<MIKSPIState*>(JS_GetOpaque(val, mik_spi_class_id));
36
+ if (!s) return;
37
+ if (s->begun) {
38
+ spi_bus_remove_device(s->device);
39
+ spi_bus_free(s->host);
40
+ }
41
+ free(s);
42
+ }
43
+
44
+ static JSClassDef mik_spi_class = {
45
+ .class_name = "Spi",
46
+ .finalizer = mik__spi_finalizer,
47
+ };
48
+
49
+ /* ── Constructor ───────────────────────────────────────────────────── */
50
+
51
+ static JSValue js_spi_constructor(JSContext* ctx, JSValue new_target, int argc, JSValue* argv) {
52
+ if (argc < 2) return JS_ThrowTypeError(ctx, "Spi requires hostNo and options arguments");
53
+
54
+ int32_t host_no;
55
+ if (JS_ToInt32(ctx, &host_no, argv[0])) return JS_EXCEPTION;
56
+ /* SOC_SPI_PERIPH_NUM includes SPI1 (flash), so user-available hosts are 1..N-1 */
57
+ if (host_no < 1 || host_no > SOC_SPI_PERIPH_NUM - 1)
58
+ return JS_ThrowRangeError(ctx, "hostNo must be 1..%d", SOC_SPI_PERIPH_NUM - 1);
59
+
60
+ auto* s = static_cast<MIKSPIState*>(calloc(1, sizeof(MIKSPIState)));
61
+ if (!s) return JS_ThrowOutOfMemory(ctx);
62
+
63
+ /* SPI2_HOST is 1, SPI3_HOST is 2, etc. — host_no maps directly */
64
+ s->host = static_cast<spi_host_device_t>(host_no);
65
+ s->clk = -1;
66
+ s->mosi = -1;
67
+ s->miso = -1;
68
+ s->cs = -1;
69
+ s->freq = MIK_SPI_DEFAULT_FREQ;
70
+ s->mode = MIK_SPI_DEFAULT_MODE;
71
+ s->begun = false;
72
+
73
+ /* Parse options object (required) */
74
+ if (!JS_IsObject(argv[1])) {
75
+ free(s);
76
+ return JS_ThrowTypeError(ctx, "Spi options must be an object");
77
+ }
78
+
79
+ JSValue opts = argv[1];
80
+ JSValue v;
81
+
82
+ v = JS_GetPropertyStr(ctx, opts, "clk");
83
+ if (!JS_IsUndefined(v)) {
84
+ if (JS_ToInt32(ctx, &s->clk, v)) {
85
+ JS_FreeValue(ctx, v);
86
+ free(s);
87
+ return JS_EXCEPTION;
88
+ }
89
+ }
90
+ JS_FreeValue(ctx, v);
91
+
92
+ v = JS_GetPropertyStr(ctx, opts, "mosi");
93
+ if (!JS_IsUndefined(v)) {
94
+ if (JS_ToInt32(ctx, &s->mosi, v)) {
95
+ JS_FreeValue(ctx, v);
96
+ free(s);
97
+ return JS_EXCEPTION;
98
+ }
99
+ }
100
+ JS_FreeValue(ctx, v);
101
+
102
+ v = JS_GetPropertyStr(ctx, opts, "miso");
103
+ if (!JS_IsUndefined(v)) {
104
+ if (JS_ToInt32(ctx, &s->miso, v)) {
105
+ JS_FreeValue(ctx, v);
106
+ free(s);
107
+ return JS_EXCEPTION;
108
+ }
109
+ }
110
+ JS_FreeValue(ctx, v);
111
+
112
+ v = JS_GetPropertyStr(ctx, opts, "cs");
113
+ if (!JS_IsUndefined(v)) {
114
+ if (JS_ToInt32(ctx, &s->cs, v)) {
115
+ JS_FreeValue(ctx, v);
116
+ free(s);
117
+ return JS_EXCEPTION;
118
+ }
119
+ }
120
+ JS_FreeValue(ctx, v);
121
+
122
+ v = JS_GetPropertyStr(ctx, opts, "freq");
123
+ if (!JS_IsUndefined(v)) {
124
+ int32_t freq;
125
+ if (JS_ToInt32(ctx, &freq, v)) {
126
+ JS_FreeValue(ctx, v);
127
+ free(s);
128
+ return JS_EXCEPTION;
129
+ }
130
+ s->freq = freq;
131
+ }
132
+ JS_FreeValue(ctx, v);
133
+
134
+ v = JS_GetPropertyStr(ctx, opts, "mode");
135
+ if (!JS_IsUndefined(v)) {
136
+ if (JS_ToInt32(ctx, &s->mode, v)) {
137
+ JS_FreeValue(ctx, v);
138
+ free(s);
139
+ return JS_EXCEPTION;
140
+ }
141
+ if (s->mode < 0 || s->mode > 3) {
142
+ JS_FreeValue(ctx, v);
143
+ free(s);
144
+ return JS_ThrowRangeError(ctx, "SPI mode must be 0-3");
145
+ }
146
+ }
147
+ JS_FreeValue(ctx, v);
148
+
149
+ JSValue obj = JS_NewObjectClass(ctx, mik_spi_class_id);
150
+ if (JS_IsException(obj)) {
151
+ free(s);
152
+ return obj;
153
+ }
154
+ JS_SetOpaque(obj, s);
155
+ return obj;
156
+ }
157
+
158
+ /* ── Methods ───────────────────────────────────────────────────────── */
159
+
160
+ static JSValue js_spi_begin(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
161
+ auto* s = mik__spi_get(ctx, this_val);
162
+ if (!s) return JS_EXCEPTION;
163
+ if (s->begun) return mik__result_ok_void(ctx); // idempotent
164
+
165
+ if (s->clk < 0 || s->mosi < 0)
166
+ return mik__result_err_tag(ctx, "MissingPins");
167
+
168
+ /* Initialize the SPI bus */
169
+ spi_bus_config_t bus_cfg = {};
170
+ bus_cfg.mosi_io_num = s->mosi;
171
+ bus_cfg.miso_io_num = s->miso; // -1 if not set
172
+ bus_cfg.sclk_io_num = s->clk;
173
+ bus_cfg.quadwp_io_num = -1;
174
+ bus_cfg.quadhd_io_num = -1;
175
+ bus_cfg.max_transfer_sz = 32768;
176
+
177
+ esp_err_t err = spi_bus_initialize(s->host, &bus_cfg, SPI_DMA_CH_AUTO);
178
+ if (err != ESP_OK)
179
+ return mik__result_err_named(ctx, "BusInitFailed",
180
+ "SPI bus init failed: %s", esp_err_to_name(err));
181
+
182
+ /* Add device to the bus */
183
+ spi_device_interface_config_t dev_cfg = {};
184
+ dev_cfg.clock_speed_hz = s->freq;
185
+ dev_cfg.mode = s->mode;
186
+ dev_cfg.spics_io_num = s->cs; // -1 if not set (manual CS)
187
+ dev_cfg.queue_size = 1;
188
+ /* Write-only devices (displays) don't need dummy bits for high clock speeds */
189
+ if (s->miso < 0) {
190
+ dev_cfg.flags = SPI_DEVICE_NO_DUMMY;
191
+ }
192
+
193
+ err = spi_bus_add_device(s->host, &dev_cfg, &s->device);
194
+ if (err != ESP_OK) {
195
+ spi_bus_free(s->host);
196
+ return mik__result_err_named(ctx, "AddDeviceFailed",
197
+ "failed to add SPI device: %s", esp_err_to_name(err));
198
+ }
199
+
200
+ s->begun = true;
201
+ return mik__result_ok_void(ctx);
202
+ }
203
+
204
+ static JSValue js_spi_end(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
205
+ auto* s = mik__spi_get(ctx, this_val);
206
+ if (!s) return JS_EXCEPTION;
207
+ if (!s->begun) return mik__result_ok_void(ctx); // idempotent
208
+
209
+ spi_bus_remove_device(s->device);
210
+ s->device = nullptr;
211
+ spi_bus_free(s->host);
212
+ s->begun = false;
213
+ return mik__result_ok_void(ctx);
214
+ }
215
+
216
+ static JSValue js_spi_transfer(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
217
+ auto* s = mik__spi_get(ctx, this_val);
218
+ if (!s) return JS_EXCEPTION;
219
+ if (!s->begun) return mik__result_err_tag(ctx, "NotStarted");
220
+
221
+ /* Extract data from Uint8Array or ArrayBuffer */
222
+ size_t data_len;
223
+ uint8_t* data;
224
+ size_t offset, elem_size;
225
+ JSValue ab = JS_GetTypedArrayBuffer(ctx, argv[0], &offset, &data_len, &elem_size);
226
+ if (!JS_IsException(ab)) {
227
+ data = JS_GetArrayBuffer(ctx, &data_len, ab);
228
+ JS_FreeValue(ctx, ab);
229
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 1");
230
+ data += offset;
231
+ } else {
232
+ JSValue exc = JS_GetException(ctx);
233
+ JS_FreeValue(ctx, exc);
234
+ data = JS_GetArrayBuffer(ctx, &data_len, argv[0]);
235
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 1");
236
+ }
237
+
238
+ /* Allocate receive buffer with js_malloc (MIK_NewUint8Array takes ownership) */
239
+ auto* rx_buf = static_cast<uint8_t*>(js_malloc(ctx, data_len));
240
+ if (!rx_buf) return JS_EXCEPTION;
241
+
242
+ spi_transaction_t txn = {};
243
+ txn.length = data_len * 8;
244
+ txn.tx_buffer = data;
245
+ txn.rx_buffer = rx_buf;
246
+
247
+ esp_err_t err = spi_device_polling_transmit(s->device, &txn);
248
+ if (err != ESP_OK) {
249
+ js_free(ctx, rx_buf);
250
+ return mik__result_err_named(ctx, "TransferFailed",
251
+ "SPI transfer failed: %s", esp_err_to_name(err));
252
+ }
253
+
254
+ JSValue result = MIK_NewUint8Array(ctx, rx_buf, data_len);
255
+ return mik__result_ok(ctx, result);
256
+ }
257
+
258
+ static JSValue js_spi_write(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
259
+ auto* s = mik__spi_get(ctx, this_val);
260
+ if (!s) return JS_EXCEPTION;
261
+ if (!s->begun)
262
+ return mik__result_err_tag(ctx, "NotStarted");
263
+
264
+ /* Extract data from Uint8Array or ArrayBuffer */
265
+ size_t data_len;
266
+ uint8_t* data;
267
+ size_t offset, elem_size;
268
+ JSValue ab = JS_GetTypedArrayBuffer(ctx, argv[0], &offset, &data_len, &elem_size);
269
+ if (!JS_IsException(ab)) {
270
+ data = JS_GetArrayBuffer(ctx, &data_len, ab);
271
+ JS_FreeValue(ctx, ab);
272
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 1");
273
+ data += offset;
274
+ } else {
275
+ JSValue exc = JS_GetException(ctx);
276
+ JS_FreeValue(ctx, exc);
277
+ data = JS_GetArrayBuffer(ctx, &data_len, argv[0]);
278
+ if (!data) return JS_ThrowTypeError(ctx, "expected Uint8Array as argument 1");
279
+ }
280
+
281
+ spi_transaction_t txn = {};
282
+ txn.length = data_len * 8;
283
+ txn.tx_buffer = data;
284
+ txn.rx_buffer = nullptr;
285
+
286
+ esp_err_t err = spi_device_polling_transmit(s->device, &txn);
287
+ if (err != ESP_OK)
288
+ return mik__result_err_named(ctx, "WriteFailed",
289
+ "SPI write failed: %s", esp_err_to_name(err));
290
+
291
+ return mik__result_ok_void(ctx);
292
+ }
293
+
294
+ /* ── Prototype ─────────────────────────────────────────────────────── */
295
+
296
+ static const JSCFunctionListEntry mik_spi_proto_funcs[] = {
297
+ MIK_CFUNC_DEF("begin", 0, js_spi_begin),
298
+ MIK_CFUNC_DEF("end", 0, js_spi_end),
299
+ MIK_CFUNC_DEF("transfer", 1, js_spi_transfer),
300
+ MIK_CFUNC_DEF("write", 1, js_spi_write),
301
+ };
302
+
303
+ /* ── Module init ───────────────────────────────────────────────────── */
304
+
305
+ static int mik__spi_module_init(JSContext* ctx, JSModuleDef* m) {
306
+ JSValue ctor = JS_NewCFunction2(ctx, js_spi_constructor, "Spi", 2, JS_CFUNC_constructor, 0);
307
+ JS_SetModuleExport(ctx, m, "Spi", ctor);
308
+ return 0;
309
+ }
310
+
311
+ static JSModuleDef* mik__spi_init(JSContext* ctx) {
312
+ JSRuntime* rt = JS_GetRuntime(ctx);
313
+
314
+ /* Register class (once per runtime) */
315
+ JS_NewClassID(rt, &mik_spi_class_id);
316
+ JS_NewClass(rt, mik_spi_class_id, &mik_spi_class);
317
+
318
+ /* Create prototype with methods */
319
+ JSValue proto = JS_NewObject(ctx);
320
+ JS_SetPropertyFunctionList(ctx, proto, mik_spi_proto_funcs, countof(mik_spi_proto_funcs));
321
+ JS_SetClassProto(ctx, mik_spi_class_id, proto); /* consumed */
322
+
323
+ /* Register module */
324
+ JSModuleDef* m = JS_NewCModule(ctx, "native:spi", mik__spi_module_init);
325
+ if (!m) return nullptr;
326
+ JS_AddModuleExport(ctx, m, "Spi");
327
+ return m;
328
+ }
329
+
330
+ MIK_REGISTER_MODULE(spi, "native:spi", mik__spi_init, nullptr, nullptr)