From ed5eb2822827399d9dfeb55db9a5068bb2bec8f9 Mon Sep 17 00:00:00 2001 From: Arthur Suzuki Date: Wed, 22 Nov 2023 02:36:06 +0100 Subject: [PATCH] Added POV image effect Setup is really easy, after first boot and WiFi/LEDs setup: go to wled.local/edit and upload a couple image to WLed's filesystem. Only PNG is supported right now, further support for GIF is planned. The image should be as wide as the 1D segment you want to apply to. When done, go to the Effect page on the UI, select "POV Image" effect. You could also update the image with a post to the JSON-API like this: curl -X POST http://[wled]/json/state -d '{"seg":{"id":0,"fx":114,"f":"/axel.png"}}' The segment should move at around 120RPM (that's 2revolutions per seconds) for an image to showup. More informations and pictures here : https://lumina.toys --- platformio_override.sample.ini | 4 + usermods/pov_display/usermod_pov_display.h | 85 ++++++++++++++++++++++ wled00/const.h | 1 + wled00/usermods_list.cpp | 8 ++ 4 files changed, 98 insertions(+) create mode 100644 usermods/pov_display/usermod_pov_display.h diff --git a/platformio_override.sample.ini b/platformio_override.sample.ini index be959e46a3..7f6524922a 100644 --- a/platformio_override.sample.ini +++ b/platformio_override.sample.ini @@ -29,6 +29,8 @@ lib_deps = ${esp8266.lib_deps} ; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug ; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library ; ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE +; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following + build_unflags = ${common.build_unflags} build_flags = ${common.build_flags} ${esp8266.build_flags} ; @@ -152,6 +154,8 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; -D TACHO_PIN=33 ; -D PWM_PIN=32 ; +; Use POV Display usermod +; -D USERMOD_POV_DISPLAY ; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16) ; -D STATUSLED=16 ; diff --git a/usermods/pov_display/usermod_pov_display.h b/usermods/pov_display/usermod_pov_display.h new file mode 100644 index 0000000000..b1fc0dba60 --- /dev/null +++ b/usermods/pov_display/usermod_pov_display.h @@ -0,0 +1,85 @@ +#pragma once +#include "wled.h" +#include + +void * openFile(const char *filename, int32_t *size) { + f = WLED_FS.open(filename); + *size = f.size(); + return &f; +} + +void closeFile(void *handle) { + if (f) f.close(); +} + +int32_t readFile(PNGFILE *pFile, uint8_t *pBuf, int32_t iLen) +{ + int32_t iBytesRead; + iBytesRead = iLen; + File *f = static_cast(pFile->fHandle); + // Note: If you read a file all the way to the last byte, seek() stops working + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around + if (iBytesRead <= 0) + return 0; + iBytesRead = (int32_t)f->read(pBuf, iBytesRead); + pFile->iPos = f->position(); + return iBytesRead; +} + +int32_t seekFile(PNGFILE *pFile, int32_t iPosition) +{ + int i = micros(); + File *f = static_cast(pFile->fHandle); + f->seek(iPosition); + pFile->iPos = (int32_t)f->position(); + i = micros() - i; + return pFile->iPos; +} + +void draw(PNGDRAW *pDraw) { + uint16_t usPixels[SEGLEN]; + png.getLineAsRGB565(pDraw, usPixels, PNG_RGB565_LITTLE_ENDIAN, 0xffffffff); + for(int x=0; x < SEGLEN; x++) { + uint16_t color = usPixels[x]; + byte r = ((color >> 11) & 0x1F); + byte g = ((color >> 5) & 0x3F); + byte b = (color & 0x1F); + SEGMENT.setPixelColor(x, RGBW32(r,g,b,0)); + } + strip.show(); +} + +uint16_t mode_pov_image(void) { + const char * filepath = SEGMENT.name; + int rc = png.open(filepath, openFile, closeFile, readFile, seekFile, draw); + if (rc == PNG_SUCCESS) { + rc = png.decode(NULL, 0); + png.close(); + return FRAMETIME; + } + return FRAMETIME; +} + +class PovDisplayUsermod : public Usermod +{ + public: + static const char _data_FX_MODE_POV_IMAGE[] PROGMEM = "POV Image@!;;;1"; + + PNG png; + File f; + + void setup() { + strip.addEffect(255, &mode_pov_image, _data_FX_MODE_POV_IMAGE); + } + + void loop() { + } + + uint16_t getId() + { + return USERMOD_ID_POV_DISPLAY; + } + + void connected() {} +}; diff --git a/wled00/const.h b/wled00/const.h index 0ff70e47d5..72e1a3bae5 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -200,6 +200,7 @@ #define USERMOD_ID_INA226 50 //Usermod "usermod_ina226.h" #define USERMOD_ID_AHT10 51 //Usermod "usermod_aht10.h" #define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h" +#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index a39aa5f415..be2239d657 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -210,6 +210,10 @@ #include "../usermods/LDR_Dusk_Dawn_v2/usermod_LDR_Dusk_Dawn_v2.h" #endif +#ifdef USERMOD_POV_DISPLAY + #include "../usermods/pov_display/usermod_pov_display.h" +#endif + #ifdef USERMOD_STAIRCASE_WIPE #include "../usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h" #endif @@ -454,4 +458,8 @@ void registerUsermods() #ifdef USERMOD_LD2410 usermods.add(new LD2410Usermod()); #endif + + #ifdef USERMOD_POV_DISPLAY + usermods.add(new PovDisplayUsermod()); + #endif }