#include #include #include "wled.h" #define WASM_STACK_SLOTS 1024 #define NATIVE_STACK_SIZE (32*1024) // For (most) devices that cannot allocate a 64KiB wasm page #define WASM_MEMORY_LIMIT 4096 /*unsigned char app_wasm[] = { 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x03, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x2b, 0x04, 0x03, 0x6c, 0x65, 0x64, 0x03, 0x6e, 0x6f, 0x77, 0x00, 0x00, 0x03, 0x6c, 0x65, 0x64, 0x03, 0x6c, 0x65, 0x6e, 0x00, 0x00, 0x03, 0x6c, 0x65, 0x64, 0x03, 0x73, 0x65, 0x74, 0x00, 0x01, 0x03, 0x6c, 0x65, 0x64, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x00, 0x00, 0x03, 0x02, 0x01, 0x02, 0x05, 0x03, 0x01, 0x00, 0x00, 0x07, 0x0f, 0x02, 0x02, 0x66, 0x78, 0x00, 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x45, 0x01, 0x43, 0x01, 0x04, 0x7f, 0x10, 0x00, 0x41, 0x02, 0x76, 0x21, 0x00, 0x10, 0x01, 0x21, 0x02, 0x03, 0x40, 0x20, 0x01, 0x20, 0x02, 0x49, 0x04, 0x40, 0x20, 0x01, 0x20, 0x00, 0x41, 0xff, 0x01, 0x71, 0x22, 0x03, 0x20, 0x03, 0x41, 0x01, 0x76, 0x41, 0x00, 0x10, 0x02, 0x20, 0x00, 0x10, 0x03, 0x41, 0x04, 0x76, 0x41, 0x01, 0x6a, 0x6a, 0x21, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x00, 0x20, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x52, 0x4c, 0x0e, 0x2e, 0x2f, 0x61, 0x70, 0x70, 0x2e, 0x77, 0x61, 0x73, 0x6d, 0x2e, 0x6d, 0x61, 0x70 };*/ //unsigned int app_wasm_len = 201; /* * API bindings * * Note: each RawFunction should complete with one of these calls: * m3ApiReturn(val) - Returns a value * m3ApiSuccess() - Returns void (and no traps) * m3ApiTrap(trap) - Returns a trap */ m3ApiRawFunction(m3_arduino_millis) { m3ApiReturnType (uint32_t) m3ApiReturn(millis()); } m3ApiRawFunction(m3_arduino_delay) { m3ApiGetArg (uint32_t, ms) // You can also trace API calls Serial.print("api: delay "); Serial.println(ms); //delay(ms); m3ApiSuccess(); } // This maps pin modes from arduino_wasm_api.h // to actual platform-specific values uint8_t mapPinMode(uint8_t mode) { switch(mode) { case 0: return INPUT; case 1: return OUTPUT; case 2: return INPUT_PULLUP; } return INPUT; } m3ApiRawFunction(m3_arduino_pinMode) { m3ApiGetArg (uint32_t, pin) m3ApiGetArg (uint32_t, mode) pinMode(pin, (uint8_t)mapPinMode(mode)); m3ApiSuccess(); } m3ApiRawFunction(m3_arduino_digitalWrite) { m3ApiGetArg (uint32_t, pin) m3ApiGetArg (uint32_t, value) digitalWrite(pin, value); m3ApiSuccess(); } m3ApiRawFunction(m3_arduino_getPinLED) { m3ApiReturnType (uint32_t) m3ApiReturn(2); } m3ApiRawFunction(m3_arduino_print) { m3ApiGetArgMem (const uint8_t *, buf) m3ApiGetArg (uint32_t, len) Serial.write(buf, len); m3ApiSuccess(); } m3ApiRawFunction(m3_led_now) { m3ApiReturnType(uint32_t) m3ApiReturn(wasmfx.now()); } m3ApiRawFunction(m3_led_speed) { m3ApiReturnType(uint32_t) m3ApiReturn(wasmfx.speed());//strip._segments[strip._segment_index].speed); } m3ApiRawFunction(m3_led_intensity) { m3ApiReturnType(uint32_t) m3ApiReturn(wasmfx.intensity());//strip._segments[strip._segment_index].intensity); } m3ApiRawFunction(m3_led_len) { m3ApiReturnType(uint32_t) m3ApiReturn(wasmfx.len());//strip._virtualSegmentLength); } m3ApiRawFunction(m3_led_fill) { m3ApiGetArg (uint32_t, color) strip.fill(color); m3ApiSuccess(); } m3ApiRawFunction(m3_led_set) { m3ApiGetArg (uint32_t, index) m3ApiGetArg (uint32_t, r) m3ApiGetArg (uint32_t, g) m3ApiGetArg (uint32_t, b) strip.setPixelColor(index, r,g,b); m3ApiSuccess(); } m3ApiRawFunction(m3_led_rgb) { m3ApiGetArg (uint32_t, r) m3ApiGetArg (uint32_t, g) m3ApiGetArg (uint32_t, b) m3ApiReturnType(uint32_t) uint32_t c = (r << 16) + (g << 8) + b; m3ApiReturn(c); } M3Result LinkArduino (IM3Runtime runtime) { IM3Module module = runtime->modules; const char* arduino = "arduino"; const char* led = "led"; m3_LinkRawFunction (module, arduino, "millis", "i()", &m3_arduino_millis); m3_LinkRawFunction (module, arduino, "delay", "v(i)", &m3_arduino_delay); //temp m3_LinkRawFunction (module, arduino, "pinMode", "v(ii)", &m3_arduino_pinMode); //temp m3_LinkRawFunction (module, arduino, "digitalWrite", "v(ii)", &m3_arduino_digitalWrite); //temp // Test functions m3_LinkRawFunction (module, arduino, "getPinLED", "i()", &m3_arduino_getPinLED); //temp m3_LinkRawFunction (module, arduino, "print", "v(*i)", &m3_arduino_print); //WLED functions m3_LinkRawFunction (module, led, "now", "i()", &m3_led_now); m3_LinkRawFunction (module, led, "speed", "i()", &m3_led_speed); m3_LinkRawFunction (module, led, "intensity", "i()", &m3_led_intensity); m3_LinkRawFunction (module, led, "len", "i()", &m3_led_len); m3_LinkRawFunction (module, led, "fill", "v(i)", &m3_led_fill); m3_LinkRawFunction (module, led, "set", "v(iiii)",&m3_led_set); m3_LinkRawFunction (module, led, "rgb", "i(iii)", &m3_led_rgb); return m3Err_none; } /* * Engine start, liftoff! */ #define FATAL(func, msg) { Serial.print("Fatal: " func " "); Serial.println(msg); return; } M3Result result; IM3Environment env; IM3Runtime runtime; IM3Module module; IM3Function fu; //uint32_t app_wasm_len = MAX_WASM_BIN_SIZE; //uint8_t* app_wasm = nullptr; void wasm_task(void*) { result = m3Err_none; env = m3_NewEnvironment (); if (!env) FATAL("NewEnv", "failed"); runtime = m3_NewRuntime (env, WASM_STACK_SLOTS, NULL); if (!runtime) FATAL("NewRt", "failed"); #ifdef WASM_MEMORY_LIMIT runtime->memoryLimit = WASM_MEMORY_LIMIT; #endif if (!wasm_buffer) { //from filesystem (fx.wasm) wasm_buffer_len = MAX_WASM_BIN_SIZE; if (!readToBuffer("/fx.wasm", &wasm_buffer, &wasm_buffer_len)) { result = "fload"; return; } } //Serial.println(app_wasm_len); //Serial.println((uint32_t)app_wasm); if (wasm_buffer == nullptr) { result = "npr"; return; } result = m3_ParseModule (env, &module, wasm_buffer, wasm_buffer_len); if (result) FATAL("Prs", result); delete[] wasm_buffer; wasm_buffer = nullptr; wasm_buffer_len = 0; result = m3_LoadModule (runtime, module); if (result) FATAL("Load", result); result = LinkArduino (runtime); if (result) FATAL("Lnk", result); result = m3_FindFunction (&fu, runtime, "fx"); if (result) FATAL("Func", result); Serial.println(F("WASM init success!")); wasm_state = WASM_STATE_READY; } void wasmInit() { if (runtime || env) wasmEnd(); wasm_task(NULL); } volatile bool wasmRunning = false; void wasmRun(void * parameter) { result = m3_CallV(fu); if (result) { M3ErrorInfo info; m3_GetErrorInfo (runtime, &info); Serial.print("Err: "); Serial.print(result); Serial.print(" ("); Serial.print(info.message); Serial.println(")"); if (info.file && strlen(info.file) && info.line) { Serial.print("At "); Serial.print(info.file); Serial.print(":"); Serial.println(info.line); } wasm_state = WASM_STATE_ERROR; } wasmRunning = false; #ifdef ESP32 vTaskDelete(NULL); #endif } void wasmRunTask() { //re-init after wasm_buffer refresh if (wasm_state == WASM_STATE_STALE) wasmInit(); if (wasm_state != WASM_STATE_READY) return; if (result) { Serial.print("WASM run error"); Serial.println(result); wasm_state = WASM_STATE_ERROR; return; } #ifdef ESP32 // On ESP32, we can launch in a separate thread wasmRunning = true; unsigned long startTime = millis(); TaskHandle_t xHandle = NULL; xTaskCreate(&wasmRun, "wasm3", 8096, NULL, 1, &xHandle); while (wasmRunning) { if (millis() - startTime > 250) { //bail if (xHandle != NULL) vTaskDelete(xHandle); wasmRunning = false; } } #else wasmRun(nullptr); //no hang protection (e.g. "while (true);") #endif } void wasmEnd() { //if (module) m3_FreeModule(module); module = nullptr; Serial.println("F"); if (runtime) m3_FreeRuntime(runtime); runtime = nullptr; if (env) m3_FreeEnvironment(env); env = nullptr; Serial.println("F later"); wasm_state = WASM_STATE_UNLOADED; }