diff --git a/platformio.ini b/platformio.ini index 6bb17516..595d0f0e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -158,6 +158,7 @@ lib_compat_mode = strict lib_deps = fastled/FastLED @ 3.4.0 IRremoteESP8266 @ 2.7.18 + wasm3/Wasm3 @ 0.5.0 https://github.com/lorol/LITTLEFS.git https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bbca1c97..78fd845b 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -38,6 +38,23 @@ uint16_t WS2812FX::mode_static(void) { } +/* + * Custom mode. Executes WebAssembly fx() function + */ +uint16_t WS2812FX::mode_custom(void) { + wasm.call(); + return FRAMETIME; +} + + +//testing TEMP +uint16_t WS2812FX::mode_benchmark(void) { + wasm.call(); + return FRAMETIME; +} + + + /* * Blink/strobe function * Alternate between color1 and color2 diff --git a/wled00/FX.h b/wled00/FX.h index c791a4cc..e45434ae 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -113,7 +113,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) -#define MODE_COUNT 118 +#define MODE_COUNT 120 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -233,6 +233,8 @@ #define FX_MODE_BLENDS 115 #define FX_MODE_TV_SIMULATOR 116 #define FX_MODE_DYNAMIC_SMOOTH 117 +#define FX_MODE_CUSTOM 118 +#define FX_MODE_BENCHMARK 119 class WS2812FX { @@ -594,6 +596,8 @@ class WS2812FX { _mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends; _mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator; _mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth; + _mode[FX_MODE_CUSTOM] = &WS2812FX::mode_custom; + _mode[FX_MODE_BENCHMARK] = &WS2812FX::mode_benchmark; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -816,7 +820,9 @@ class WS2812FX { mode_candy_cane(void), mode_blends(void), mode_tv_simulator(void), - mode_dynamic_smooth(void); + mode_dynamic_smooth(void), + mode_custom(void), + mode_benchmark(void); private: uint32_t crgb_to_col(CRGB fastled); @@ -892,6 +898,7 @@ class WS2812FX { ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element friend class ColorTransition; + friend class WASMVM; uint16_t realPixelIndex(uint16_t i), diff --git a/wled00/wasm.cpp b/wled00/wasm.cpp new file mode 100644 index 00000000..f7087af2 --- /dev/null +++ b/wled00/wasm.cpp @@ -0,0 +1,255 @@ +#include +#include + +#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, 0x11, 0x04, 0x60, + 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, + 0x00, 0x01, 0x7f, 0x02, 0x4e, 0x04, 0x07, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x6e, 0x6f, 0x09, 0x67, 0x65, 0x74, 0x50, 0x69, 0x6e, 0x4c, 0x45, 0x44, + 0x00, 0x03, 0x07, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x07, 0x70, + 0x69, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x07, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x0c, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x07, 0x61, 0x72, 0x64, 0x75, + 0x69, 0x6e, 0x6f, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x00, 0x02, 0x03, + 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x13, 0x02, 0x06, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x00, 0x04, 0x0a, 0x3a, 0x01, 0x38, 0x01, 0x01, 0x7f, + 0x41, 0x80, 0x08, 0x10, 0x00, 0x22, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0x01, 0x10, 0x01, 0x03, 0x40, 0x41, 0x80, 0x08, 0x28, 0x02, 0x00, + 0x41, 0x01, 0x10, 0x02, 0x41, 0xe4, 0x00, 0x10, 0x03, 0x41, 0x80, 0x08, + 0x28, 0x02, 0x00, 0x41, 0x00, 0x10, 0x02, 0x41, 0x84, 0x07, 0x10, 0x03, + 0x0c, 0x00, 0x0b, 0x00, 0x0b +}; +unsigned int app_wasm_len = 197; + +/* + * 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 + */ + +class WASMVM { + WS2812FX inst = nullptr; + + 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(LED_PIN); + } + + 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(strip.now()); + } + + m3ApiRawFunction(m3_led_speed) { + m3ApiReturnType(uint32_t) + m3ApiReturn(strip._segments[strip._segment_index].speed); + } + + m3ApiRawFunction(m3_led_intensity) { + m3ApiReturnType(uint32_t) + m3ApiReturn(strip._segments[strip._segment_index].intensity); + } + + m3ApiRawFunction(m3_led_len) { + m3ApiReturnType(uint32_t) + m3ApiReturn(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, color) + + strip.setPixelColor(index, color); + + 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(ii)", &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; } + + void wasm_task(void*) + { + M3Result result = m3Err_none; + + IM3Environment env = m3_NewEnvironment (); + if (!env) FATAL("NewEnv", "failed"); + + IM3Runtime runtime = m3_NewRuntime (env, WASM_STACK_SLOTS, NULL); + if (!runtime) FATAL("NewRt", "failed"); + + #ifdef WASM_MEMORY_LIMIT + runtime->memoryLimit = WASM_MEMORY_LIMIT; + #endif + + IM3Module module; + result = m3_ParseModule (env, &module, app_wasm, app_wasm_len); + if (result) FATAL("ParseModule", result); + + result = m3_LoadModule (runtime, module); + if (result) FATAL("LoadModule", result); + + result = LinkArduino (runtime); + if (result) FATAL("LinkArd", result); + + IM3Function f; + result = m3_FindFunction (&f, runtime, "_start"); + if (result) FATAL("FindFunc", result); + + Serial.println("Run WASM..."); + + result = m3_CallV (f); + + // Should not arrive here + + 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); + } + } + } + + public void setup(WS2812FX* instance) + { + if (!instance) return; + inst = instance; + Serial.println("\nWasm3 v" M3_VERSION " (" M3_ARCH "), build " __DATE__ " " __TIME__); + + #ifdef ESP32 + // On ESP32, we can launch in a separate thread + xTaskCreate(&wasm_task, "wasm3", NATIVE_STACK_SIZE, NULL, 5, NULL); + #else + wasm_task(NULL); + #endif + } + +} \ No newline at end of file