Compare commits
	
		
			29 Commits
		
	
	
		
			copilot/fi
			...
			copilot/fi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 70c7600b44 | ||
|   | 705f2035f4 | ||
|   | 65efcb351e | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 72ad39d6a7 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 5950204d34 | ||
|   | 46df9410b3 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | b7e4cd0d9a | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 0becd61323 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 3f2e92c4c5 | ||
|   | 666a59ff53 | ||
|   | ce7ca3f2d2 | ||
|   | 87092ccb80 | ||
|   | a037d99469 | ||
|   | 8cc5d64819 | ||
|   | 4c948cca13 | ||
|   | 5cb8dc3978 | ||
|   | 8fc87aa17d | ||
|   | da7f107273 | ||
|   | d5d7fde30f | ||
|   | 6f914d79b1 | ||
|   | dd13c2df47 | ||
|   | 8aeb9e1abe | ||
|   | cfad0b8a52 | ||
|   | f15c1fbca6 | ||
|   | 46f3bc0ced | ||
|   | 8baa6a4616 | ||
|   | 85d4db83ed | ||
|   | 5146926723 | ||
|   | 4ac7eb7eb2 | 
							
								
								
									
										4
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							| @@ -8,7 +8,7 @@ Always reference these instructions first and fallback to search or bash command | ||||
|  | ||||
| ### Initial Setup | ||||
| - Install Node.js 20+ (specified in `.nvmrc`): Check your version with `node --version` | ||||
| - Install dependencies: `npm install` (takes ~5 seconds) | ||||
| - Install dependencies: `npm ci` (takes ~5 seconds) | ||||
| - Install PlatformIO for hardware builds: `pip install -r requirements.txt` (takes ~60 seconds) | ||||
|  | ||||
| ### Build and Test Workflow | ||||
| @@ -135,4 +135,4 @@ The GitHub Actions workflow: | ||||
| 4. Compiles firmware for multiple hardware targets | ||||
| 5. Uploads build artifacts | ||||
|  | ||||
| Match this workflow in your local development to ensure CI success. | ||||
| Match this workflow in your local development to ensure CI success. | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							| @@ -27,6 +27,8 @@ jobs: | ||||
|         with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }}  | ||||
|           sinceTag: v0.15.0 | ||||
|           # Exclude issues that were closed without resolution from changelog | ||||
|           exclude-labels: 'stale,wontfix,duplicate,invalid' | ||||
|       - name: Update Nightly Release | ||||
|         uses: andelf/nightly-release@main | ||||
|         env: | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -33,6 +33,6 @@ | ||||
|           run: | | ||||
|             jq -n \ | ||||
|               --arg content "Pull Request #${PR_NUMBER} \"${PR_TITLE}\" merged by ${ACTOR} | ||||
|             ${PR_URL}" \ | ||||
|             ${PR_URL}. It will be included in the next nightly builds, please test" \ | ||||
|               '{content: $content}' \ | ||||
|               | curl -H "Content-Type: application/json" -d @- ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }} | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -24,7 +24,9 @@ jobs: | ||||
|       with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }}  | ||||
|           sinceTag: v0.15.0 | ||||
|           maxIssues: 500        | ||||
|           maxIssues: 500 | ||||
|           # Exclude issues that were closed without resolution from changelog | ||||
|           exclude-labels: 'stale,wontfix,duplicate,invalid'        | ||||
|     - name: Create draft release | ||||
|       uses: softprops/action-gh-release@v1 | ||||
|       with: | ||||
|   | ||||
							
								
								
									
										3
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
								
							| @@ -5,6 +5,9 @@ on: | ||||
|     paths: | ||||
|       - usermods/** | ||||
|       - .github/workflows/usermods.yml | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - usermods/** | ||||
|      | ||||
| jobs: | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,6 @@ lib_deps = ${esp8266.lib_deps} | ||||
| ;  robtillaart/SHT85@~0.3.3 | ||||
| ;  ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug | ||||
| ;  https://github.com/blazoncek/QuickESPNow.git#optional-debug  ;; exludes debug library | ||||
| ;  bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following | ||||
| ;  ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE | ||||
|  | ||||
| build_unflags = ${common.build_unflags} | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| default_envs = usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3 | ||||
|  | ||||
| [env:usermods_esp32] | ||||
| extends = env:esp32dev_V4 | ||||
| extends = env:esp32dev | ||||
| custom_usermods = ${usermods.custom_usermods} | ||||
| board_build.partitions = ${esp32.extreme_partitions}  ; We're gonna need a bigger boat | ||||
|  | ||||
| @@ -28,4 +28,4 @@ board_build.partitions = ${esp32.extreme_partitions}  ; We're gonna need a bigge | ||||
|  | ||||
|  | ||||
| [usermods] | ||||
| # Added in CI | ||||
| # Added in CI | ||||
|   | ||||
							
								
								
									
										48
									
								
								usermods/pov_display/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								usermods/pov_display/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| ## POV Display usermod | ||||
|  | ||||
| This usermod adds a new effect called “POV Image”. | ||||
|  | ||||
|  | ||||
|  | ||||
| ###How does it work? | ||||
| With proper configuration (see below) the main segment will display a single row of pixels from an image stored on the ESP. | ||||
| It displays the image row by row at a high refresh rate. | ||||
| If you move the pixel segment at the right speed, you will see the full image floating in the air thanks to the persistence of vision. | ||||
| RGB LEDs only (no RGBW), with grouping set to 1 and spacing set to 0. | ||||
| Best results with high-density strips (e.g., 144 LEDs/m). | ||||
|  | ||||
| To get it working: | ||||
| - Resize your image. The height must match the number of LEDs in your strip/segment. | ||||
| - Rotate your image 90° clockwise (height becomes width). | ||||
| - Upload a BMP image (24-bit, uncompressed) to the ESP filesystem using the “/edit” URL. | ||||
| - Select the “POV Image” effect. | ||||
| - Set the segment name to the absolute filesystem path of the image (e.g., “/myimage.bmp”). | ||||
| - The path is case-sensitive and must start with “/”. | ||||
| - Rotate the pixel strip at approximately 20 RPM. | ||||
| - Tune as needed so that one full revolution maps to the image width (if the image appears stretched or compressed, adjust RPM slightly). | ||||
| - Enjoy the show! | ||||
|  | ||||
| Notes: | ||||
| - Only 24-bit uncompressed BMP files are supported. | ||||
| - The image must fit into ~64 KB of RAM (width × height × 3 bytes, plus row padding to a 4-byte boundary). | ||||
| - Examples (approximate, excluding row padding): | ||||
|   - 128×128 (49,152 bytes) fits. | ||||
|   - 160×160 (76,800 bytes) does NOT fit. | ||||
|   - 96×192 (55,296 bytes) fits; padding may add a small overhead. | ||||
| - If the rendered image appears mirrored or upside‑down, rotate 90° the other way or flip horizontally in your editor and try again. | ||||
| - The path must be absolute. | ||||
|  | ||||
| ### Requirements | ||||
| - 1D rotating LED strip/segment (POV setup). Ensure the segment length equals the number of physical LEDs. | ||||
| - BMP image saved as 24‑bit, uncompressed (no alpha, no palette). | ||||
| - Sufficient free RAM (~64 KB) for the image buffer. | ||||
|  | ||||
| ### Troubleshooting | ||||
| - Nothing displays: verify the file exists at the exact absolute path (case‑sensitive) and is a 24‑bit uncompressed BMP. | ||||
| - Garbled colors or wrong orientation: re‑export as 24‑bit BMP and retry the rotation/flip guidance above. | ||||
| - Image too large: reduce width and/or height until it fits within ~64 KB (see examples). | ||||
| - Path issues: confirm you uploaded the file via the “/edit” URL and can see it in the filesystem browser. | ||||
|  | ||||
| ### Safety | ||||
| - Secure the rotating assembly and keep clear of moving parts. | ||||
| - Balance the strip/hub to minimize vibration before running at speed. | ||||
							
								
								
									
										146
									
								
								usermods/pov_display/bmpimage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								usermods/pov_display/bmpimage.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| #include "bmpimage.h" | ||||
| #define BUF_SIZE 64000 | ||||
|  | ||||
| byte * _buffer = nullptr; | ||||
|  | ||||
| uint16_t read16(File &f) { | ||||
|   uint16_t result; | ||||
|   f.read((uint8_t *)&result,2); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| uint32_t read32(File &f) { | ||||
|   uint32_t result; | ||||
|   f.read((uint8_t *)&result,4); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| bool BMPimage::init(const char * fn) { | ||||
|     File bmpFile; | ||||
|     int bmpDepth; | ||||
|     //first, check if filename exists | ||||
|     if (!WLED_FS.exists(fn)) { | ||||
|       return false; | ||||
|     } | ||||
|      | ||||
|     bmpFile = WLED_FS.open(fn); | ||||
|     if (!bmpFile) { | ||||
|       _valid=false; | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     //so, the file exists and is opened | ||||
|     // Parse BMP header | ||||
|     uint16_t header = read16(bmpFile); | ||||
|     if(header != 0x4D42) { // BMP signature | ||||
|       _valid=false; | ||||
|       bmpFile.close(); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     //read and ingnore file size | ||||
|     read32(bmpFile); | ||||
|     (void)read32(bmpFile); // Read & ignore creator bytes | ||||
|     _imageOffset = read32(bmpFile); // Start of image data | ||||
|     // Read DIB header | ||||
|     read32(bmpFile); | ||||
|     _width  = read32(bmpFile); | ||||
|     _height = read32(bmpFile); | ||||
|     if(read16(bmpFile) != 1) { // # planes -- must be '1' | ||||
|         _valid=false; | ||||
|         bmpFile.close(); | ||||
|         return false; | ||||
|     } | ||||
|     bmpDepth = read16(bmpFile); // bits per pixel | ||||
|     if((bmpDepth != 24) || (read32(bmpFile) != 0)) { // 0 = uncompressed { | ||||
|         _width=0; | ||||
|         _valid=false; | ||||
|         bmpFile.close(); | ||||
|         return false; | ||||
|     } | ||||
|     // If _height is negative, image is in top-down order. | ||||
|     // This is not canon but has been observed in the wild. | ||||
|     if(_height < 0) { | ||||
|         _height = -_height; | ||||
|     } | ||||
|     //now, we have successfully got all the basics | ||||
|     // BMP rows are padded (if needed) to 4-byte boundary | ||||
|     _rowSize = (_width * 3 + 3) & ~3; | ||||
|     //check image size - if it is too large, it will be unusable | ||||
|     if (_rowSize*_height>BUF_SIZE) { | ||||
|       _valid=false; | ||||
|       bmpFile.close(); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     bmpFile.close(); | ||||
|     // Ensure filename fits our buffer (segment name length constraint). | ||||
|     size_t len = strlen(fn); | ||||
|     if (len > WLED_MAX_SEGNAME_LEN) { | ||||
|       return false; | ||||
|     } | ||||
|     strncpy(filename, fn, sizeof(filename)); | ||||
|     filename[sizeof(filename) - 1] = '\0'; | ||||
|     _valid = true; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void BMPimage::clear(){ | ||||
|     strcpy(filename, ""); | ||||
|     _width=0; | ||||
|     _height=0; | ||||
|     _rowSize=0; | ||||
|     _imageOffset=0; | ||||
|     _loaded=false; | ||||
|     _valid=false; | ||||
| } | ||||
|  | ||||
| bool BMPimage::load(){ | ||||
|     const size_t size = (size_t)_rowSize * (size_t)_height; | ||||
|     if (size > BUF_SIZE) { | ||||
|         return false; | ||||
|     } | ||||
|     File bmpFile = WLED_FS.open(filename); | ||||
|     if (!bmpFile) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (_buffer != nullptr) free(_buffer); | ||||
|     _buffer = (byte*)malloc(size); | ||||
|     if (_buffer == nullptr) return false; | ||||
|  | ||||
|     bmpFile.seek(_imageOffset); | ||||
|     const size_t readBytes = bmpFile.read(_buffer, size); | ||||
|     bmpFile.close(); | ||||
|     if (readBytes != size) { | ||||
|         _loaded = false; | ||||
|         return false; | ||||
|     } | ||||
|     _loaded = true; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| byte* BMPimage::line(uint16_t n){ | ||||
|     if (_loaded) { | ||||
|         return (_buffer+n*_rowSize); | ||||
|     } else { | ||||
|         return NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| uint32_t BMPimage::pixelColor(uint16_t x, uint16_t  y){ | ||||
|     uint32_t pos; | ||||
|     byte b,g,r; //colors | ||||
|     if (! _loaded) { | ||||
|       return 0; | ||||
|     } | ||||
|     if ( (x>=_width) || (y>=_height) ) { | ||||
|       return 0; | ||||
|     } | ||||
|     pos=y*_rowSize + 3*x; | ||||
|     //get colors. Note that in BMP files, they go in BGR order | ||||
|     b= _buffer[pos++]; | ||||
|     g= _buffer[pos++]; | ||||
|     r= _buffer[pos]; | ||||
|     return (r<<16|g<<8|b); | ||||
| } | ||||
							
								
								
									
										50
									
								
								usermods/pov_display/bmpimage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								usermods/pov_display/bmpimage.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #ifndef _BMPIMAGE_H | ||||
| #define _BMPIMAGE_H | ||||
| #include "Arduino.h" | ||||
| #include "wled.h" | ||||
|  | ||||
| /* | ||||
|  * This class describes a bitmap image. Each object refers to a bmp file on | ||||
|  * filesystem fatfs. | ||||
|  * To initialize, call init(), passign to it name of a bitmap file | ||||
|  * at the root of fatfs filesystem: | ||||
|  * | ||||
|  * BMPimage myImage; | ||||
|  * myImage.init("logo.bmp"); | ||||
|  * | ||||
|  * For performance reasons, before actually usign the image, you need to load | ||||
|  * it from filesystem to RAM: | ||||
|  * myImage.load(); | ||||
|  * All load() operations use the same reserved buffer in RAM, so you can only | ||||
|  * have one file loaded at a time. Before loading a new file, always unload the | ||||
|  * previous one: | ||||
|  * myImage.unload(); | ||||
|  */ | ||||
|  | ||||
| class BMPimage { | ||||
|     public: | ||||
|         int height()    {return _height; } | ||||
|         int width()     {return _width;  } | ||||
|         int rowSize()   {return _rowSize;} | ||||
|         bool isLoaded() {return _loaded; } | ||||
|         bool load(); | ||||
|         void unload()   {_loaded=false;  } | ||||
|         byte * line(uint16_t n); | ||||
|         uint32_t pixelColor(uint16_t x,uint16_t  y); | ||||
|         bool init(const char* fn); | ||||
|         void clear(); | ||||
|         char * getFilename() {return filename;}; | ||||
|  | ||||
|     private: | ||||
|         char filename[WLED_MAX_SEGNAME_LEN+1]=""; | ||||
|         int _width=0; | ||||
|         int _height=0; | ||||
|         int _rowSize=0; | ||||
|         int _imageOffset=0; | ||||
|         bool _loaded=false; | ||||
|         bool _valid=false; | ||||
| }; | ||||
|  | ||||
| extern byte * _buffer; | ||||
|  | ||||
| #endif | ||||
| @@ -1,7 +1,5 @@ | ||||
| { | ||||
|   "name:": "pov_display", | ||||
|   "build": { "libArchive": false}, | ||||
|   "dependencies": { | ||||
|     "bitbank2/PNGdec":"^1.0.3" | ||||
|   } | ||||
|   "platforms": ["espressif32"] | ||||
| } | ||||
							
								
								
									
										47
									
								
								usermods/pov_display/pov.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								usermods/pov_display/pov.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| #include "pov.h" | ||||
|  | ||||
| POV::POV() {} | ||||
|  | ||||
| void POV::showLine(const byte * line, uint16_t size){ | ||||
|     uint16_t i, pos; | ||||
|     uint8_t r, g, b; | ||||
|     if (!line) { | ||||
|         // All-black frame on null input | ||||
|         for (i = 0; i < SEGLEN; i++) { | ||||
|             SEGMENT.setPixelColor(i, CRGB::Black); | ||||
|         } | ||||
|         strip.show(); | ||||
|         lastLineUpdate = micros(); | ||||
|         return; | ||||
|     } | ||||
|     for (i = 0; i < SEGLEN; i++) { | ||||
|         if (i < size) { | ||||
|             pos = 3 * i; | ||||
|             // using bgr order | ||||
|             b = line[pos++]; | ||||
|             g = line[pos++]; | ||||
|             r = line[pos]; | ||||
|             SEGMENT.setPixelColor(i, CRGB(r, g, b)); | ||||
|         } else { | ||||
|             SEGMENT.setPixelColor(i, CRGB::Black); | ||||
|         } | ||||
|     } | ||||
|     strip.show(); | ||||
|     lastLineUpdate = micros(); | ||||
| } | ||||
|  | ||||
| bool POV::loadImage(const char * filename){ | ||||
|   if(!image.init(filename)) return false; | ||||
|   if(!image.load()) return false; | ||||
|   currentLine=0; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| int16_t POV::showNextLine(){ | ||||
|     if (!image.isLoaded()) return 0; | ||||
|     //move to next line | ||||
|     showLine(image.line(currentLine), image.width()); | ||||
|     currentLine++; | ||||
|     if (currentLine == image.height()) {currentLine=0;} | ||||
|     return currentLine; | ||||
| } | ||||
							
								
								
									
										42
									
								
								usermods/pov_display/pov.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								usermods/pov_display/pov.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #ifndef _POV_H | ||||
| #define _POV_H | ||||
| #include "bmpimage.h" | ||||
|  | ||||
|  | ||||
| class POV { | ||||
|     public: | ||||
|         POV(); | ||||
|  | ||||
|         /* Shows one line. line should be pointer to array which holds  pixel colors | ||||
|          * (3 bytes per pixel, in BGR order). Note: 3, not 4!!! | ||||
|          *  size should be size of array (number of pixels, not number of bytes) | ||||
|          */ | ||||
|         void showLine(const byte * line, uint16_t size); | ||||
|  | ||||
|         /* Reads from file an image and making it current image */ | ||||
|         bool loadImage(const char * filename); | ||||
|  | ||||
|         /* Show next line of active image | ||||
|            Retunrs the index of next line to be shown (not yet shown!) | ||||
|            If it retunrs 0, it means we have completed showing the image and | ||||
|             next call will start again | ||||
|         */ | ||||
|         int16_t showNextLine(); | ||||
|  | ||||
|         //time since strip was last updated, in micro sec | ||||
|         uint32_t timeSinceUpdate() {return (micros()-lastLineUpdate);} | ||||
|  | ||||
|  | ||||
|         BMPimage * currentImage() {return ℑ} | ||||
|  | ||||
|         char * getFilename() {return image.getFilename();} | ||||
|  | ||||
|     private: | ||||
|         BMPimage image; | ||||
|         int16_t  currentLine=0;     //next line to be shown | ||||
|         uint32_t lastLineUpdate=0; //time in microseconds | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| #endif | ||||
| @@ -1,88 +1,75 @@ | ||||
| #include "wled.h" | ||||
| #include <PNGdec.h> | ||||
| #include "pov.h" | ||||
|  | ||||
| void * openFile(const char *filename, int32_t *size) { | ||||
|     f = WLED_FS.open(filename); | ||||
|     *size = f.size(); | ||||
|     return &f; | ||||
| } | ||||
| static const char _data_FX_MODE_POV_IMAGE[] PROGMEM = "POV Image@!;;;;"; | ||||
|  | ||||
| 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<File *>(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<File *>(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(); | ||||
| } | ||||
| static POV s_pov; | ||||
|  | ||||
| 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; | ||||
|     } | ||||
|   Segment& mainseg = strip.getMainSegment(); | ||||
|   const char* segName = mainseg.name; | ||||
|   if (!segName) { | ||||
|      return FRAMETIME; | ||||
|    } | ||||
|   // Only proceed for files ending with .bmp (case-insensitive) | ||||
|   size_t segLen = strlen(segName); | ||||
|   if (segLen < 4) return FRAMETIME; | ||||
|   const char* ext = segName + (segLen - 4); | ||||
|   // compare case-insensitive to ".bmp" | ||||
|   if (!((ext[0]=='.') && | ||||
|         (ext[1]=='b' || ext[1]=='B') && | ||||
|         (ext[2]=='m' || ext[2]=='M') && | ||||
|         (ext[3]=='p' || ext[3]=='P'))) { | ||||
|     return FRAMETIME; | ||||
|   } | ||||
|  | ||||
|   const char* current = s_pov.getFilename(); | ||||
|   if (current && strcmp(segName, current) == 0) { | ||||
|      s_pov.showNextLine(); | ||||
|      return FRAMETIME; | ||||
|    } | ||||
|  | ||||
|   static unsigned long s_lastLoadAttemptMs = 0; | ||||
|   unsigned long nowMs = millis(); | ||||
|   // Retry at most twice per second if the image is not yet loaded. | ||||
|   if (nowMs - s_lastLoadAttemptMs < 500) return FRAMETIME; | ||||
|   s_lastLoadAttemptMs = nowMs; | ||||
|   s_pov.loadImage(segName); | ||||
|   return FRAMETIME; | ||||
| } | ||||
|  | ||||
| class PovDisplayUsermod : public Usermod | ||||
| { | ||||
|   public: | ||||
|     static const char _data_FX_MODE_POV_IMAGE[] PROGMEM = "POV Image@!;;;1"; | ||||
| class PovDisplayUsermod : public Usermod { | ||||
| protected: | ||||
|   bool enabled = false; //WLEDMM | ||||
|   const char *_name; //WLEDMM | ||||
|   bool initDone = false; //WLEDMM | ||||
|   unsigned long lastTime = 0; //WLEDMM | ||||
| public: | ||||
|  | ||||
|     PNG png; | ||||
|     File f; | ||||
|   PovDisplayUsermod(const char *name, bool enabled) | ||||
|     : enabled(enabled) , _name(name) {} | ||||
|    | ||||
|   void setup() override { | ||||
|     strip.addEffect(255, &mode_pov_image, _data_FX_MODE_POV_IMAGE); | ||||
|     //initDone removed (unused) | ||||
|   } | ||||
|  | ||||
|     void setup() { | ||||
| 	strip.addEffect(255, &mode_pov_image, _data_FX_MODE_POV_IMAGE); | ||||
|  | ||||
|   void loop() override { | ||||
|     // if usermod is disabled or called during strip updating just exit | ||||
|     // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly | ||||
|     if (!enabled || strip.isUpdating()) return; | ||||
|  | ||||
|     // do your magic here | ||||
|     if (millis() - lastTime > 1000) { | ||||
|       lastTime = millis(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     void loop() { | ||||
|     } | ||||
|  | ||||
|     uint16_t getId() | ||||
|     { | ||||
|       return USERMOD_ID_POV_DISPLAY; | ||||
|     } | ||||
|  | ||||
|     void connected() {} | ||||
|   uint16_t getId() override { | ||||
|     return USERMOD_ID_POV_DISPLAY; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|  | ||||
| static PovDisplayUsermod pov_display; | ||||
| REGISTER_USERMOD(pov_display); | ||||
| static PovDisplayUsermod pov_display("POV Display", false); | ||||
| REGISTER_USERMOD(pov_display); | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								usermods/pov_display/pov_display.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								usermods/pov_display/pov_display.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 988 KiB | 
| @@ -7528,9 +7528,9 @@ uint16_t mode_2Ddistortionwaves() { | ||||
|       byte valueG = gdistort + ((a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 ))<<1); | ||||
|       byte valueB = bdistort + ((a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 ))<<1); | ||||
|  | ||||
|       valueR = gamma8(cos8_t(valueR)); | ||||
|       valueG = gamma8(cos8_t(valueG)); | ||||
|       valueB = gamma8(cos8_t(valueB)); | ||||
|       valueR = cos8_t(valueR); | ||||
|       valueG = cos8_t(valueG); | ||||
|       valueB = cos8_t(valueB); | ||||
|  | ||||
|       if(SEGMENT.palette == 0) { | ||||
|         // use RGB values (original color mode) | ||||
|   | ||||
| @@ -282,6 +282,7 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) { | ||||
|       _t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings | ||||
|       _t->_start = millis();                              // restart countdown | ||||
|       _t->_dur   = dur; | ||||
|       _t->_prevPaletteBlends = 0; | ||||
|       if (_t->_oldSegment) { | ||||
|         _t->_oldSegment->palette = _t->_palette;          // restore original palette and colors (from start of transition) | ||||
|         for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i]; | ||||
| @@ -368,6 +369,7 @@ void Segment::beginDraw(uint16_t prog) { | ||||
|     // minimum blend time is 100ms maximum is 65535ms | ||||
|     #ifndef WLED_SAVE_RAM | ||||
|     unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; | ||||
|     if(noOfBlends > 255) noOfBlends = 255; // safety check | ||||
|     for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, Segment::_currentPalette, 48); | ||||
|     Segment::_currentPalette = _t->_palT; // copy transitioning/temporary palette | ||||
|     #else | ||||
| @@ -1192,8 +1194,9 @@ void WS2812FX::finalizeInit() { | ||||
|     if (busEnd > _length) _length = busEnd; | ||||
|     // This must be done after all buses have been created, as some kinds (parallel I2S) interact | ||||
|     bus->begin(); | ||||
|     bus->setBrightness(bri); | ||||
|     bus->setBrightness(scaledBri(bri)); | ||||
|   } | ||||
|   BusManager::initializeABL(); // init brightness limiter | ||||
|   DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); | ||||
|  | ||||
|   Segment::maxWidth  = _length; | ||||
| @@ -1295,7 +1298,7 @@ static uint8_t _add       (uint8_t a, uint8_t b) { unsigned t = a + b; return t | ||||
| static uint8_t _subtract  (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0; } | ||||
| static uint8_t _difference(uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); } | ||||
| static uint8_t _average   (uint8_t a, uint8_t b) { return (a + b) >> 1; } | ||||
| #ifdef CONFIG_IDF_TARGET_ESP32C3 | ||||
| #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) | ||||
| static uint8_t _multiply  (uint8_t a, uint8_t b) { return ((a * b) + 255) >> 8; } // faster than division on C3 but slightly less accurate | ||||
| #else | ||||
| static uint8_t _multiply  (uint8_t a, uint8_t b) { return (a * b) / 255; } // origianl uses a & b in range [0,1] | ||||
| @@ -1306,10 +1309,10 @@ static uint8_t _darken    (uint8_t a, uint8_t b) { return a < b ? a : b; } | ||||
| static uint8_t _screen    (uint8_t a, uint8_t b) { return 255 - _multiply(~a,~b); } // 255 - (255-a)*(255-b)/255 | ||||
| static uint8_t _overlay   (uint8_t a, uint8_t b) { return b < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); } | ||||
| static uint8_t _hardlight (uint8_t a, uint8_t b) { return a < 128 ? 2 * _multiply(a,b) : (255 - 2 * _multiply(~a,~b)); } | ||||
| #ifdef CONFIG_IDF_TARGET_ESP32C3 | ||||
| static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a) + 255) >> 8) + 2 * a * b + 255) >> 8; } // Pegtop's formula (1 - 2a)b^2 + 2ab | ||||
| #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) | ||||
| static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a))) + ((2 * a * b + 256) << 8)) >> 16; } // Pegtop's formula (1 - 2a)b^2 | ||||
| #else | ||||
| static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) / 255 + 2 * a * b) / 255; } // Pegtop's formula (1 - 2a)b^2 + 2ab | ||||
| static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) + 255 * 2 * a * b) / (255 * 255); } // Pegtop's formula (1 - 2a)b^2 + 2ab | ||||
| #endif | ||||
| static uint8_t _dodge     (uint8_t a, uint8_t b) { return _divide(~a,b); } | ||||
| static uint8_t _burn      (uint8_t a, uint8_t b) { return ~_divide(a,~b); } | ||||
| @@ -1548,66 +1551,6 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { | ||||
|   Segment::setClippingRect(0, 0);             // disable clipping for overlays | ||||
| } | ||||
|  | ||||
| // To disable brightness limiter we either set output max current to 0 or single LED current to 0 | ||||
| static uint8_t estimateCurrentAndLimitBri(uint8_t brightness, uint32_t *pixels) { | ||||
|   unsigned milliAmpsMax = BusManager::ablMilliampsMax(); | ||||
|   if (milliAmpsMax > 0) { | ||||
|     unsigned milliAmpsTotal = 0; | ||||
|     unsigned avgMilliAmpsPerLED = 0; | ||||
|     unsigned lengthDigital = 0; | ||||
|     bool useWackyWS2815PowerModel = false; | ||||
|  | ||||
|     for (size_t i = 0; i < BusManager::getNumBusses(); i++) { | ||||
|       const Bus *bus = BusManager::getBus(i); | ||||
|       if (!(bus && bus->isDigital() && bus->isOk())) continue; | ||||
|       unsigned maPL = bus->getLEDCurrent(); | ||||
|       if (maPL == 0 || bus->getMaxCurrent() > 0) continue; // skip buses with 0 mA per LED or max current per bus defined (PP-ABL) | ||||
|       if (maPL == 255) { | ||||
|         useWackyWS2815PowerModel = true; | ||||
|         maPL = 12; // WS2815 uses 12mA per channel | ||||
|       } | ||||
|       avgMilliAmpsPerLED += maPL * bus->getLength(); | ||||
|       lengthDigital += bus->getLength(); | ||||
|       // sum up the usage of each LED on digital bus | ||||
|       uint32_t busPowerSum = 0; | ||||
|       for (unsigned j = 0; j < bus->getLength(); j++) { | ||||
|         uint32_t c = pixels[j + bus->getStart()]; | ||||
|         byte r = R(c), g = G(c), b = B(c), w = W(c); | ||||
|         if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation | ||||
|           busPowerSum += (max(max(r,g),b)) * 3; | ||||
|         } else { | ||||
|           busPowerSum += (r + g + b + w); | ||||
|         } | ||||
|       } | ||||
|       // RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less | ||||
|       if (bus->hasWhite()) { | ||||
|         busPowerSum *= 3; | ||||
|         busPowerSum >>= 2; //same as /= 4 | ||||
|       } | ||||
|       // powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps | ||||
|       milliAmpsTotal += (busPowerSum * maPL * brightness) / (765*255); | ||||
|     } | ||||
|     if (lengthDigital > 0) { | ||||
|       avgMilliAmpsPerLED /= lengthDigital; | ||||
|  | ||||
|       if (milliAmpsMax > MA_FOR_ESP && avgMilliAmpsPerLED > 0) { //0 mA per LED and too low numbers turn off calculation | ||||
|         unsigned powerBudget = (milliAmpsMax - MA_FOR_ESP); //80/120mA for ESP power | ||||
|         if (powerBudget > lengthDigital) { //each LED uses about 1mA in standby, exclude that from power budget | ||||
|           powerBudget -= lengthDigital; | ||||
|         } else { | ||||
|           powerBudget = 0; | ||||
|         } | ||||
|         if (milliAmpsTotal > powerBudget) { | ||||
|           //scale brightness down to stay in current limit | ||||
|           unsigned scaleB = powerBudget * 255 / milliAmpsTotal; | ||||
|           brightness = ((brightness * scaleB) >> 8) + 1; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return brightness; | ||||
| } | ||||
|  | ||||
| void WS2812FX::show() { | ||||
|   if (!_pixels) return; // no pixels allocated, nothing to show | ||||
|  | ||||
| @@ -1635,10 +1578,6 @@ void WS2812FX::show() { | ||||
|   show_callback callback = _callback; | ||||
|   if (callback) callback(); // will call setPixelColor or setRealtimePixelColor | ||||
|  | ||||
|   // determine ABL brightness | ||||
|   uint8_t newBri = estimateCurrentAndLimitBri(_brightness, _pixels); | ||||
|   if (newBri != _brightness) BusManager::setBrightness(newBri); | ||||
|  | ||||
|   // paint actual pixels | ||||
|   int oldCCT = Bus::getCCT(); // store original CCT value (since it is global) | ||||
|   // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1) | ||||
| @@ -1649,7 +1588,11 @@ void WS2812FX::show() { | ||||
|     if (_pixelCCT) { // cctFromRgb already exluded at allocation | ||||
|       if (i == 0 || _pixelCCT[i-1] != _pixelCCT[i]) BusManager::setSegmentCCT(_pixelCCT[i], correctWB); | ||||
|     } | ||||
|     BusManager::setPixelColor(getMappedPixelIndex(i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32(_pixels[i])); | ||||
|  | ||||
|     uint32_t c = _pixels[i]; // need a copy, do not modify _pixels directly (no byte access allowed on ESP32) | ||||
|     if(c > 0 && !(realtimeMode && arlsDisableGammaCorrection)) | ||||
|         c = gamma32(c); // apply gamma correction if enabled note: applying gamma after brightness has too much color loss | ||||
|     BusManager::setPixelColor(getMappedPixelIndex(i), c); | ||||
|   } | ||||
|   Bus::setCCT(oldCCT);  // restore old CCT for ABL adjustments | ||||
|  | ||||
| @@ -1661,9 +1604,6 @@ void WS2812FX::show() { | ||||
|   // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods | ||||
|   BusManager::show(); | ||||
|  | ||||
|   // restore brightness for next frame | ||||
|   if (newBri != _brightness) BusManager::setBrightness(_brightness); | ||||
|  | ||||
|   if (diff > 0) { // skip calculation if no time has passed | ||||
|     size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math | ||||
|     _cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1);   // "+FPS_CALC_AVG/2" for proper rounding | ||||
| @@ -1728,7 +1668,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { | ||||
|   if (_brightness == 0) { //unfreeze all segments on power off | ||||
|     for (const Segment &seg : _segments) seg.freeze = false; // freeze is mutable | ||||
|   } | ||||
|   BusManager::setBrightness(b); | ||||
|   BusManager::setBrightness(scaledBri(b)); | ||||
|   if (!direct) { | ||||
|     unsigned long t = millis(); | ||||
|     if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon | ||||
|   | ||||
| @@ -1118,7 +1118,7 @@ bool initParticleSystem2D(ParticleSystem2D *&PartSys, uint32_t requestedsources, | ||||
|       allocsuccess = true; | ||||
|       break; // allocation succeeded | ||||
|     } | ||||
|     numparticles /= 2; // cut number of particles in half and try again | ||||
|     numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned | ||||
|     PSPRINTLN(F("PS 2D alloc failed, trying with less particles...")); | ||||
|   } | ||||
|   if (!allocsuccess) { | ||||
| @@ -1815,7 +1815,7 @@ bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedso | ||||
|       allocsuccess = true; | ||||
|       break; // allocation succeeded | ||||
|     } | ||||
|     numparticles /= 2; // cut number of particles in half and try again | ||||
|     numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned | ||||
|     PSPRINTLN(F("PS 1D alloc failed, trying with less particles...")); | ||||
|   } | ||||
|   if (!allocsuccess) { | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include "core_esp8266_waveform.h" | ||||
| #endif | ||||
| #include "const.h" | ||||
| #include "colors.h" | ||||
| #include "pin_manager.h" | ||||
| #include "bus_manager.h" | ||||
| #include "bus_wrapper.h" | ||||
| @@ -144,6 +145,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) | ||||
|   if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; } | ||||
|   if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; } | ||||
|   _frequencykHz = 0U; | ||||
|   _colorSum = 0; | ||||
|   _pins[0] = bc.pins[0]; | ||||
|   if (is2Pin(bc.type)) { | ||||
|     if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { | ||||
| @@ -186,80 +188,62 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) | ||||
| //Stay safe with high amperage and have a reasonable safety margin! | ||||
| //I am NOT to be held liable for burned down garages or houses! | ||||
|  | ||||
| // To disable brightness limiter we either set output max current to 0 or single LED current to 0 | ||||
| uint8_t BusDigital::estimateCurrentAndLimitBri() const { | ||||
|   bool useWackyWS2815PowerModel = false; | ||||
|   byte actualMilliampsPerLed = _milliAmpsPerLed; | ||||
|  | ||||
|   if (_milliAmpsMax < MA_FOR_ESP/BusManager::getNumBusses() || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation | ||||
|     return _bri; | ||||
|   } | ||||
| // note on ABL implementation: | ||||
| // ABL is set up in finalizeInit() | ||||
| // scaled color channels are summed in BusDigital::setPixelColor() | ||||
| // the used current is estimated and limited in BusManager::show() | ||||
| // if limit is set too low, brightness is limited to 1 to at least show some light | ||||
| // to disable brightness limiter for a bus, set LED current to 0 | ||||
|  | ||||
| void BusDigital::estimateCurrent() { | ||||
|   uint32_t actualMilliampsPerLed = _milliAmpsPerLed; | ||||
|   if (_milliAmpsPerLed == 255) { | ||||
|     useWackyWS2815PowerModel = true; | ||||
|     // use wacky WS2815 power model, see WLED issue #549 | ||||
|     _colorSum *= 3; // sum is sum of max value for each color, need to multiply by three to account for clrUnitsPerChannel being 3*255 | ||||
|     actualMilliampsPerLed = 12; // from testing an actual strip | ||||
|   } | ||||
|   // _colorSum has all the values of color channels summed, max would be getLength()*(3*255 + (255 if hasWhite()): convert to milliAmps | ||||
|   uint32_t clrUnitsPerChannel = hasWhite() ? 4*255 : 3*255; | ||||
|   _milliAmpsTotal = ((uint64_t)_colorSum * actualMilliampsPerLed) / clrUnitsPerChannel + getLength(); // add 1mA standby current per LED to total (WS2812: ~0.7mA, WS2815: ~2mA) | ||||
| } | ||||
|  | ||||
|   unsigned powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power | ||||
|   if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget | ||||
|     powerBudget -= getLength(); | ||||
|   } else { | ||||
|     powerBudget = 0; | ||||
|   } | ||||
| void BusDigital::applyBriLimit(uint8_t newBri) { | ||||
|   // a newBri of 0 means calculate per-bus brightness limit | ||||
|   if (newBri == 0) { | ||||
|     if (_milliAmpsLimit == 0 || _milliAmpsTotal == 0) return; // ABL not used for this bus | ||||
|     newBri = 255; | ||||
|  | ||||
|   uint32_t busPowerSum = 0; | ||||
|   for (unsigned i = 0; i < getLength(); i++) {  //sum up the usage of each LED | ||||
|     uint32_t c = getPixelColor(i); // always returns original or restored color without brightness scaling | ||||
|     byte r = R(c), g = G(c), b = B(c), w = W(c); | ||||
|  | ||||
|     if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation | ||||
|       busPowerSum += (max(max(r,g),b)) * 3; | ||||
|     if (_milliAmpsLimit > getLength()) { // each LED uses about 1mA in standby | ||||
|       if (_milliAmpsTotal > _milliAmpsLimit) { | ||||
|         // scale brightness down to stay in current limit | ||||
|         newBri = ((uint32_t)_milliAmpsLimit * 255) / _milliAmpsTotal + 1; // +1 to avoid 0 brightness | ||||
|         _milliAmpsTotal = _milliAmpsLimit; | ||||
|       } | ||||
|     } else { | ||||
|       busPowerSum += (r + g + b + w); | ||||
|       newBri = 1; // limit too low, set brightness to 1, this will dim down all colors to minimum since we use video scaling | ||||
|       _milliAmpsTotal = getLength(); // estimate bus current as minimum | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less | ||||
|     busPowerSum *= 3; | ||||
|     busPowerSum >>= 2; //same as /= 4 | ||||
|   } | ||||
|  | ||||
|   // powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps | ||||
|   BusDigital::_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * _bri) / (765*255); | ||||
|  | ||||
|   uint8_t newBri = _bri; | ||||
|   if (BusDigital::_milliAmpsTotal > powerBudget) { | ||||
|     //scale brightness down to stay in current limit | ||||
|     unsigned scaleB = powerBudget * 255 / BusDigital::_milliAmpsTotal; | ||||
|     newBri = (_bri * scaleB) / 256 + 1; | ||||
|     BusDigital::_milliAmpsTotal = powerBudget; | ||||
|     //_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * newBri) / (765*255); | ||||
|   } | ||||
|   return newBri; | ||||
| } | ||||
|  | ||||
| void BusDigital::show() { | ||||
|   BusDigital::_milliAmpsTotal = 0; | ||||
|   if (!_valid) return; | ||||
|  | ||||
|   uint8_t cctWW = 0, cctCW = 0; | ||||
|   unsigned newBri = estimateCurrentAndLimitBri();  // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere()) | ||||
|   if (newBri < _bri) { | ||||
|     PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits | ||||
|   if (newBri < 255) { | ||||
|     uint8_t cctWW = 0, cctCW = 0; | ||||
|     unsigned hwLen = _len; | ||||
|     if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus | ||||
|     for (unsigned i = 0; i < hwLen; i++) { | ||||
|       // use 0 as color order, actual order does not matter here as we just update the channel values as-is | ||||
|       uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri); | ||||
|       if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus | ||||
|       PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness | ||||
|       uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder); // need to revert color order for correct color scaling and CCT calc in case white is swapped | ||||
|       uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, i, co); | ||||
|       c = color_fade(c, newBri, true); // apply additional dimming  note: using inline version is a bit faster but overhead of getPixelColor() dominates the speed impact by far | ||||
|       if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); | ||||
|       PolyBus::setPixelColor(_busPtr, _iType, i, c, co, (cctCW<<8) | cctWW); // repaint all pixels with new brightness | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   _colorSum = 0; // reset for next frame | ||||
| } | ||||
|  | ||||
| void BusDigital::show() { | ||||
|   if (!_valid) return; | ||||
|   PolyBus::show(_busPtr, _iType, _skip); // faster if buffer consistency is not important (no skipped LEDs) | ||||
|   // restore bus brightness to its original value | ||||
|   // this is done right after show, so this is only OK if LED updates are completed before show() returns | ||||
|   // or async show has a separate buffer (ESP32 RMT and I2S are ok) | ||||
|   if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, _bri); | ||||
| } | ||||
|  | ||||
| bool BusDigital::canShow() const { | ||||
| @@ -267,12 +251,6 @@ bool BusDigital::canShow() const { | ||||
|   return PolyBus::canShow(_busPtr, _iType); | ||||
| } | ||||
|  | ||||
| void BusDigital::setBrightness(uint8_t b) { | ||||
|   if (_bri == b) return; | ||||
|   Bus::setBrightness(b); | ||||
|   PolyBus::setBrightness(_busPtr, _iType, b); | ||||
| } | ||||
|  | ||||
| //If LEDs are skipped, it is possible to use the first as a status LED. | ||||
| //TODO only show if no new show due in the next 50ms | ||||
| void BusDigital::setStatusPixel(uint32_t c) { | ||||
| @@ -286,13 +264,25 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { | ||||
|   if (!_valid) return; | ||||
|   if (hasWhite()) c = autoWhiteCalc(c); | ||||
|   if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT | ||||
|   c = color_fade(c, _bri, true); // apply brightness | ||||
|  | ||||
|   if (BusManager::_useABL) { | ||||
|     // if using ABL, sum all color channels to estimate current and limit brightness in show() | ||||
|     uint8_t r = R(c), g = G(c), b = B(c); | ||||
|     if (_milliAmpsPerLed < 255) { // normal ABL | ||||
|       _colorSum += r + g + b + W(c); | ||||
|     } else { // wacky WS2815 power model, ignore white channel, use max of RGB (issue #549) | ||||
|       _colorSum += ((r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (_reversed) pix = _len - pix -1; | ||||
|   pix += _skip; | ||||
|   unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); | ||||
|   const uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); | ||||
|   if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs | ||||
|     unsigned pOld = pix; | ||||
|     pix = IC_INDEX_WS2812_1CH_3X(pix); | ||||
|     uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri); | ||||
|     uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co); | ||||
|     switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set) | ||||
|       case 0: c = RGBW32(R(cOld), W(c)   , B(cOld), 0); break; | ||||
|       case 1: c = RGBW32(W(c)   , G(cOld), B(cOld), 0); break; | ||||
| @@ -309,17 +299,17 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { | ||||
|   PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw); | ||||
| } | ||||
|  | ||||
| // returns original color if global buffering is enabled, else returns lossly restored color from bus | ||||
| // returns lossly restored color from bus | ||||
| uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { | ||||
|   if (!_valid) return 0; | ||||
|   if (_reversed) pix = _len - pix -1; | ||||
|   pix += _skip; | ||||
|   const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); | ||||
|   const uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); | ||||
|   uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri); | ||||
|   if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs | ||||
|     unsigned r = R(c); | ||||
|     unsigned g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed? | ||||
|     unsigned b = _reversed ? G(c) : B(c); | ||||
|     uint8_t r = R(c); | ||||
|     uint8_t g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed? | ||||
|     uint8_t b = _reversed ? G(c) : B(c); | ||||
|     switch (pix % 3) { // get only the single channel | ||||
|       case 0: c = RGBW32(g, g, g, g); break; | ||||
|       case 1: c = RGBW32(r, r, r, r); break; | ||||
| @@ -471,10 +461,7 @@ void BusPwm::setPixelColor(unsigned pix, uint32_t c) { | ||||
|   if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { | ||||
|     c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT | ||||
|   } | ||||
|   uint8_t r = R(c); | ||||
|   uint8_t g = G(c); | ||||
|   uint8_t b = B(c); | ||||
|   uint8_t w = W(c); | ||||
|   uint8_t r = R(c), g = G(c), b = B(c), w = W(c); | ||||
|  | ||||
|   switch (_type) { | ||||
|     case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation | ||||
| @@ -649,10 +636,7 @@ BusOnOff::BusOnOff(const BusConfig &bc) | ||||
| void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { | ||||
|   if (pix != 0 || !_valid) return; //only react to first pixel | ||||
|   c = autoWhiteCalc(c); | ||||
|   uint8_t r = R(c); | ||||
|   uint8_t g = G(c); | ||||
|   uint8_t b = B(c); | ||||
|   uint8_t w = W(c); | ||||
|   uint8_t r = R(c), g = G(c), b = B(c), w = W(c); | ||||
|   _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; | ||||
| } | ||||
|  | ||||
| @@ -964,13 +948,13 @@ void BusManager::off() { | ||||
|   #ifdef ESP32_DATA_IDLE_HIGH | ||||
|   esp32RMTInvertIdle(); | ||||
|   #endif | ||||
|   _gMilliAmpsUsed = 0; // reset, assume no LED idle current if relay is off | ||||
| } | ||||
|  | ||||
| void BusManager::show() { | ||||
|   _gMilliAmpsUsed = 0; | ||||
|   applyABL(); // apply brightness limit, updates _gMilliAmpsUsed | ||||
|   for (auto &bus : busses) { | ||||
|     bus->show(); | ||||
|     _gMilliAmpsUsed += bus->getUsedCurrent(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1003,6 +987,85 @@ bool BusManager::canAllShow() { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void BusManager::initializeABL() { | ||||
|   _useABL = false; // reset | ||||
|   if (_gMilliAmpsMax > 0) { | ||||
|     // check global brightness limit | ||||
|     for (auto &bus : busses) { | ||||
|       if (bus->isDigital() && bus->getLEDCurrent() > 0) { | ||||
|         _useABL = true; // at least one bus has valid LED current | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     // check per bus brightness limit | ||||
|     unsigned numABLbuses = 0; | ||||
|     for (auto &bus : busses) { | ||||
|       if (bus->isDigital() && bus->getLEDCurrent() > 0 && bus->getMaxCurrent() > 0) | ||||
|         numABLbuses++; // count ABL enabled buses | ||||
|     } | ||||
|     if (numABLbuses > 0) { | ||||
|       _useABL = true; // at least one bus has ABL set | ||||
|       uint32_t ESPshare = MA_FOR_ESP / numABLbuses; // share of ESP current per ABL bus | ||||
|       for (auto &bus : busses) { | ||||
|         if (bus->isDigital()) { | ||||
|           BusDigital &busd = static_cast<BusDigital&>(*bus); | ||||
|           uint32_t busLength = busd.getLength(); | ||||
|           uint32_t busDemand = busLength * busd.getLEDCurrent(); | ||||
|           uint32_t busMax    = busd.getMaxCurrent(); | ||||
|           if (busMax > ESPshare)  busMax -= ESPshare; | ||||
|           if (busMax < busLength) busMax  = busLength; // give each LED 1mA, ABL will dim down to minimum | ||||
|           if (busDemand == 0) busMax = 0; // no LED current set, disable ABL for this bus | ||||
|           busd.setCurrentLimit(busMax); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void BusManager::applyABL() { | ||||
|   if (_useABL) { | ||||
|     unsigned milliAmpsSum = 0; // use temporary variable to always return a valid _gMilliAmpsUsed to UI | ||||
|     unsigned totalLEDs = 0; | ||||
|     for (auto &bus : busses) { | ||||
|       if (bus->isDigital() && bus->isOk()) { | ||||
|         BusDigital &busd = static_cast<BusDigital&>(*bus); | ||||
|         busd.estimateCurrent(); // sets _milliAmpsTotal, current is estimated for all buses even if they have the limit set to 0 | ||||
|         if (_gMilliAmpsMax == 0) | ||||
|           busd.applyBriLimit(0); // apply per bus ABL limit, updates _milliAmpsTotal if limit reached | ||||
|         milliAmpsSum += busd.getUsedCurrent(); | ||||
|         totalLEDs += busd.getLength(); // sum total number of LEDs for global Limit | ||||
|       } | ||||
|     } | ||||
|     // check global current limit and apply global ABL limit, total current is summed above | ||||
|     if (_gMilliAmpsMax > 0) { | ||||
|       uint8_t  newBri = 255; | ||||
|       uint32_t globalMax = _gMilliAmpsMax > MA_FOR_ESP ? _gMilliAmpsMax - MA_FOR_ESP : 1; // subtract ESP current consumption, fully limit if too low | ||||
|       if (globalMax > totalLEDs) { // check if budget is larger than standby current | ||||
|         if (milliAmpsSum > globalMax) { | ||||
|           newBri = globalMax * 255 / milliAmpsSum + 1; // scale brightness down to stay in current limit, +1 to avoid 0 brightness | ||||
|           milliAmpsSum = globalMax; // update total used current | ||||
|         } | ||||
|       } else { | ||||
|         newBri = 1; // limit too low, set brightness to minimum | ||||
|         milliAmpsSum = totalLEDs; // estimate total used current as minimum | ||||
|       } | ||||
|  | ||||
|       // apply brightness limit to each bus, if its 255 it will only reset _colorSum | ||||
|       for (auto &bus : busses) { | ||||
|         if (bus->isDigital() && bus->isOk()) { | ||||
|           BusDigital &busd = static_cast<BusDigital&>(*bus); | ||||
|           if (busd.getLEDCurrent() > 0)  // skip buses with LED current set to 0 | ||||
|             busd.applyBriLimit(newBri); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     _gMilliAmpsUsed = milliAmpsSum; | ||||
|   } | ||||
|   else | ||||
|     _gMilliAmpsUsed = 0; // reset, we have no current estimation without ABL | ||||
| } | ||||
|  | ||||
| ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; } | ||||
|  | ||||
|  | ||||
| @@ -1018,3 +1081,4 @@ uint16_t BusDigital::_milliAmpsTotal = 0; | ||||
| std::vector<std::unique_ptr<Bus>> BusManager::busses; | ||||
| uint16_t BusManager::_gMilliAmpsUsed = 0; | ||||
| uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT; | ||||
| bool BusManager::_useABL = false; | ||||
|   | ||||
| @@ -238,7 +238,6 @@ class BusDigital : public Bus { | ||||
|  | ||||
|     void show() override; | ||||
|     bool canShow() const override; | ||||
|     void setBrightness(uint8_t b) override; | ||||
|     void setStatusPixel(uint32_t c) override; | ||||
|     [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; | ||||
|     void setColorOrder(uint8_t colorOrder) override; | ||||
| @@ -250,6 +249,9 @@ class BusDigital : public Bus { | ||||
|     uint16_t getLEDCurrent() const override  { return _milliAmpsPerLed; } | ||||
|     uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } | ||||
|     uint16_t getMaxCurrent() const override  { return _milliAmpsMax; } | ||||
|     void     setCurrentLimit(uint16_t milliAmps) { _milliAmpsLimit = milliAmps; } | ||||
|     void     estimateCurrent(); // estimate used current from summed colors | ||||
|     void     applyBriLimit(uint8_t newBri); | ||||
|     size_t   getBusSize() const override; | ||||
|     void begin() override; | ||||
|     void cleanup(); | ||||
| @@ -262,8 +264,10 @@ class BusDigital : public Bus { | ||||
|     uint8_t  _pins[2]; | ||||
|     uint8_t  _iType; | ||||
|     uint16_t _frequencykHz; | ||||
|     uint8_t  _milliAmpsPerLed; | ||||
|     uint16_t _milliAmpsMax; | ||||
|     uint8_t  _milliAmpsPerLed; | ||||
|     uint16_t _milliAmpsLimit; | ||||
|     uint32_t _colorSum; // total color value for the bus, updated in setPixelColor(), used to estimate current | ||||
|     void    *_busPtr; | ||||
|  | ||||
|     static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show() | ||||
| @@ -278,8 +282,6 @@ class BusDigital : public Bus { | ||||
|       } | ||||
|       return c; | ||||
|     } | ||||
|  | ||||
|     uint8_t  estimateCurrentAndLimitBri() const; | ||||
| }; | ||||
|  | ||||
|  | ||||
| @@ -422,8 +424,8 @@ struct BusConfig { | ||||
| }; | ||||
|  | ||||
|  | ||||
| //fine tune power estimation constants for your setup | ||||
| //you can set it to 0 if the ESP is powered by USB and the LEDs by external | ||||
| // milliamps used by ESP (for power estimation) | ||||
| // you can set it to 0 if the ESP is powered by USB and the LEDs by external | ||||
| #ifndef MA_FOR_ESP | ||||
|   #ifdef ESP8266 | ||||
|     #define MA_FOR_ESP         80 //how much mA does the ESP use (Wemos D1 about 80mA) | ||||
| @@ -438,6 +440,7 @@ namespace BusManager { | ||||
|   //extern std::vector<Bus*> busses; | ||||
|   extern uint16_t _gMilliAmpsUsed; | ||||
|   extern uint16_t _gMilliAmpsMax; | ||||
|   extern bool     _useABL; | ||||
|  | ||||
|   #ifdef ESP32_DATA_IDLE_HIGH | ||||
|   void    esp32RMTInvertIdle() ; | ||||
| @@ -453,6 +456,8 @@ namespace BusManager { | ||||
|   //inline uint16_t ablMilliampsMax()             { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; } | ||||
|   inline uint16_t ablMilliampsMax()             { return _gMilliAmpsMax; }  // used for compatibility reasons (and enabling virtual global ABL) | ||||
|   inline void     setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;} | ||||
|   void            initializeABL();              // setup automatic brightness limiter parameters, call once after buses are initialized | ||||
|   void            applyABL();                   // apply automatic brightness limiter, global or per bus | ||||
|  | ||||
|   void useParallelOutput(); // workaround for inaccessible PolyBus | ||||
|   bool hasParallelOutput(); // workaround for inaccessible PolyBus | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| #define BusWrapper_h | ||||
|  | ||||
| //#define NPB_CONF_4STEP_CADENCE | ||||
| #include "NeoPixelBusLg.h" | ||||
| #include "NeoPixelBus.h" | ||||
|  | ||||
| //Hardware SPI Pins | ||||
| #define P_8266_HS_MOSI 13 | ||||
| @@ -141,65 +141,65 @@ | ||||
| /*** ESP8266 Neopixel methods ***/ | ||||
| #ifdef ESP8266 | ||||
| //RGB | ||||
| #define B_8266_U0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16) | ||||
| #define B_8266_U0_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16) | ||||
| //RGBW | ||||
| #define B_8266_U0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod>   //4 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod>   //4 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>    //4 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin) | ||||
| #define B_8266_U0_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method>   //4 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method>   //4 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod>    //4 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin) | ||||
| //400Kbps | ||||
| #define B_8266_U0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod, NeoGammaNullMethod>   //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod, NeoGammaNullMethod>   //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod, NeoGammaNullMethod>     //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin) | ||||
| #define B_8266_U0_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod>   //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod>   //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod>     //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin) | ||||
| //TM1814 (RGBW) | ||||
| #define B_8266_U0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method, NeoGammaNullMethod> | ||||
| #define B_8266_U1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method, NeoGammaNullMethod> | ||||
| #define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod> | ||||
| #define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod> | ||||
| #define B_8266_U0_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method> | ||||
| #define B_8266_U1_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method> | ||||
| #define B_8266_DM_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method> | ||||
| #define B_8266_BB_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method> | ||||
| //TM1829 (RGB) | ||||
| #define B_8266_U0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod> | ||||
| #define B_8266_U1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod> | ||||
| #define B_8266_DM_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod> | ||||
| #define B_8266_BB_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod> | ||||
| #define B_8266_U0_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method> | ||||
| #define B_8266_U1_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method> | ||||
| #define B_8266_DM_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method> | ||||
| #define B_8266_BB_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method> | ||||
| //UCS8903 | ||||
| #define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16) | ||||
| #define B_8266_U0_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266Dma800KbpsMethod>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16) | ||||
| //UCS8904 RGBW | ||||
| #define B_8266_U0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod>   //4 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod>   //4 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>    //4 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin) | ||||
| #define B_8266_U0_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Uart0Ws2813Method>   //4 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method>   //4 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod>    //4 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin) | ||||
| //APA106 | ||||
| #define B_8266_U0_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart0Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart1Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266DmaApa106Method, NeoGammaNullMethod>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16) | ||||
| #define B_8266_U0_APA106_3 NeoPixelBus<NeoRbgFeature, NeoEsp8266Uart0Apa106Method> //3 chan, esp8266, gpio1 | ||||
| #define B_8266_U1_APA106_3 NeoPixelBus<NeoRbgFeature, NeoEsp8266Uart1Apa106Method> //3 chan, esp8266, gpio2 | ||||
| #define B_8266_DM_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266DmaApa106Method>  //3 chan, esp8266, gpio3 | ||||
| #define B_8266_BB_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBangApa106Method> //3 chan, esp8266, bb (any pin but 16) | ||||
| //FW1906 GRBCW | ||||
| #define B_8266_U0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod>   //esp8266, gpio1 | ||||
| #define B_8266_U1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod>   //esp8266, gpio2 | ||||
| #define B_8266_DM_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod>   //esp8266, gpio3 | ||||
| #define B_8266_BB_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod>   //esp8266, bb | ||||
| #define B_8266_U0_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Uart0Ws2813Method>   //esp8266, gpio1 | ||||
| #define B_8266_U1_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Uart1Ws2813Method>   //esp8266, gpio2 | ||||
| #define B_8266_DM_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266Dma800KbpsMethod>   //esp8266, gpio3 | ||||
| #define B_8266_BB_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp8266BitBang800KbpsMethod>   //esp8266, bb | ||||
| //WS2805 GRBCW | ||||
| #define B_8266_U0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart0Ws2805Method, NeoGammaNullMethod> //esp8266, gpio1 | ||||
| #define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2 | ||||
| #define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3 | ||||
| #define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb | ||||
| #define B_8266_U0_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266Uart0Ws2805Method> //esp8266, gpio1 | ||||
| #define B_8266_U1_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method> //esp8266, gpio2 | ||||
| #define B_8266_DM_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method> //esp8266, gpio3 | ||||
| #define B_8266_BB_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method> //esp8266, bb | ||||
| //TM1914 (RGB) | ||||
| #define B_8266_U0_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method, NeoGammaNullMethod> | ||||
| #define B_8266_U1_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method, NeoGammaNullMethod> | ||||
| #define B_8266_DM_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method, NeoGammaNullMethod> | ||||
| #define B_8266_BB_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method, NeoGammaNullMethod> | ||||
| #define B_8266_U0_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method> | ||||
| #define B_8266_U1_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method> | ||||
| #define B_8266_DM_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method> | ||||
| #define B_8266_BB_TM1914_3 NeoPixelBus<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method> | ||||
| //Sm16825 (RGBWC) | ||||
| #define B_8266_U0_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> | ||||
| #define B_8266_U1_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> | ||||
| #define B_8266_DM_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_8266_BB_SM16825_5 NeoPixelBusLg<NeoRgbwcSm16825eFeature, NeoEsp8266BitBangWs2813Method, NeoGammaNullMethod> | ||||
| #define B_8266_U0_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Uart0Ws2813Method> | ||||
| #define B_8266_U1_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Uart1Ws2813Method> | ||||
| #define B_8266_DM_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266Dma800KbpsMethod> | ||||
| #define B_8266_BB_SM16825_5 NeoPixelBus<NeoRgbwcSm16825eFeature, NeoEsp8266BitBangWs2813Method> | ||||
| #endif | ||||
|  | ||||
| /*** ESP32 Neopixel methods ***/ | ||||
| @@ -245,84 +245,84 @@ | ||||
| #endif | ||||
|  | ||||
| //RGB | ||||
| #define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3, C3 | ||||
| //#define B_32_IN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2sNWs2812xMethod, NeoGammaNullMethod> // ESP32 (dynamic I2S selection) | ||||
| #define B_32_I2_NEO_3 NeoPixelBusLg<NeoGrbFeature, X1Ws2812xMethod, NeoGammaNullMethod> // ESP32, S2, S3 (automatic I2S selection, see typedef above) | ||||
| #define B_32_IP_NEO_3 NeoPixelBusLg<NeoGrbFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S (ESP32, S2, S3) | ||||
| #define B_32_RN_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod> // ESP32, S2, S3, C3 | ||||
| //#define B_32_IN_NEO_3 NeoPixelBus<NeoGrbFeature, NeoEsp32I2sNWs2812xMethod> // ESP32 (dynamic I2S selection) | ||||
| #define B_32_I2_NEO_3 NeoPixelBus<NeoGrbFeature, X1Ws2812xMethod> // ESP32, S2, S3 (automatic I2S selection, see typedef above) | ||||
| #define B_32_IP_NEO_3 NeoPixelBus<NeoGrbFeature, X8Ws2812xMethod> // parallel I2S (ESP32, S2, S3) | ||||
| //RGBW | ||||
| #define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNSk6812Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X1Sk6812Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_NEO_4 NeoPixelBusLg<NeoGrbwFeature, X8Sk6812Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_NEO_4 NeoPixelBus<NeoGrbwFeature, NeoEsp32RmtNSk6812Method> | ||||
| #define B_32_I2_NEO_4 NeoPixelBus<NeoGrbwFeature, X1Sk6812Method> | ||||
| #define B_32_IP_NEO_4 NeoPixelBus<NeoGrbwFeature, X8Sk6812Method> // parallel I2S | ||||
| //400Kbps | ||||
| #define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_32_I2_400_3 NeoPixelBusLg<NeoGrbFeature, X1400KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_32_IP_400_3 NeoPixelBusLg<NeoGrbFeature, X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_400_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod> | ||||
| #define B_32_I2_400_3 NeoPixelBus<NeoGrbFeature, X1400KbpsMethod> | ||||
| #define B_32_IP_400_3 NeoPixelBus<NeoGrbFeature, X8400KbpsMethod> // parallel I2S | ||||
| //TM1814 (RGBW) | ||||
| #define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X1Tm1814Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, X8Tm1814Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method> | ||||
| #define B_32_I2_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, X1Tm1814Method> | ||||
| #define B_32_IP_TM1_4 NeoPixelBus<NeoWrgbTm1814Feature, X8Tm1814Method> // parallel I2S | ||||
| //TM1829 (RGB) | ||||
| #define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_TM2_3 NeoPixelBusLg<NeoBrgFeature, X1Tm1829Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_TM2_3 NeoPixelBusLg<NeoBrgFeature, X8Tm1829Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_TM2_3 NeoPixelBus<NeoBrgFeature, NeoEsp32RmtNTm1829Method> | ||||
| #define B_32_I2_TM2_3 NeoPixelBus<NeoBrgFeature, X1Tm1829Method> | ||||
| #define B_32_IP_TM2_3 NeoPixelBus<NeoBrgFeature, X8Tm1829Method> // parallel I2S | ||||
| //UCS8903 | ||||
| #define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> | ||||
| #define B_32_I2_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X1800KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_32_IP_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod> | ||||
| #define B_32_I2_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, X1800KbpsMethod> | ||||
| #define B_32_IP_UCS_3 NeoPixelBus<NeoRgbUcs8903Feature, X8800KbpsMethod> // parallel I2S | ||||
| //UCS8904 | ||||
| #define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> | ||||
| #define B_32_I2_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X1800KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_32_IP_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S | ||||
| #define B_32_RN_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod> | ||||
| #define B_32_I2_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, X1800KbpsMethod> | ||||
| #define B_32_IP_UCS_4 NeoPixelBus<NeoRgbwUcs8904Feature, X8800KbpsMethod>// parallel I2S | ||||
| //APA106 | ||||
| #define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_APA106_3 NeoPixelBusLg<NeoGrbFeature, X1Apa106Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_APA106_3 NeoPixelBusLg<NeoGrbFeature, X8Apa106Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_APA106_3 NeoPixelBus<NeoGrbFeature, NeoEsp32RmtNApa106Method> | ||||
| #define B_32_I2_APA106_3 NeoPixelBus<NeoGrbFeature, X1Apa106Method> | ||||
| #define B_32_IP_APA106_3 NeoPixelBus<NeoGrbFeature, X8Apa106Method> // parallel I2S | ||||
| //FW1906 GRBCW | ||||
| #define B_32_RN_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> | ||||
| #define B_32_I2_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X1800KbpsMethod, NeoGammaNullMethod> | ||||
| #define B_32_IP_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_FW6_5 NeoPixelBus<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod> | ||||
| #define B_32_I2_FW6_5 NeoPixelBus<NeoGrbcwxFeature, X1800KbpsMethod> | ||||
| #define B_32_IP_FW6_5 NeoPixelBus<NeoGrbcwxFeature, X8800KbpsMethod> // parallel I2S | ||||
| //WS2805 RGBWC | ||||
| #define B_32_RN_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X1Ws2805Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_2805_5 NeoPixelBusLg<NeoGrbwwFeature, X8Ws2805Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_2805_5 NeoPixelBus<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method> | ||||
| #define B_32_I2_2805_5 NeoPixelBus<NeoGrbwwFeature, X1Ws2805Method> | ||||
| #define B_32_IP_2805_5 NeoPixelBus<NeoGrbwwFeature, X8Ws2805Method> // parallel I2S | ||||
| //TM1914 (RGB) | ||||
| #define B_32_RN_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method, NeoGammaNullMethod> | ||||
| #define B_32_I2_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X1Tm1914Method, NeoGammaNullMethod> | ||||
| #define B_32_IP_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, X8Tm1914Method, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method> | ||||
| #define B_32_I2_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, X1Tm1914Method> | ||||
| #define B_32_IP_TM1914_3 NeoPixelBus<NeoGrbTm1914Feature, X8Tm1914Method> // parallel I2S | ||||
| //Sm16825 (RGBWC) | ||||
| #define B_32_RN_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod> | ||||
| #define B_32_I2_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X1Ws2812xMethod, NeoGammaNullMethod> | ||||
| #define B_32_IP_SM16825_5 NeoPixelBusLg<NeoRgbcwSm16825eFeature, X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S | ||||
| #define B_32_RN_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, NeoEsp32RmtNWs2812xMethod> | ||||
| #define B_32_I2_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, X1Ws2812xMethod> | ||||
| #define B_32_IP_SM16825_5 NeoPixelBus<NeoRgbcwSm16825eFeature, X8Ws2812xMethod> // parallel I2S | ||||
| #endif | ||||
|  | ||||
| //APA102 | ||||
| #ifdef WLED_USE_ETHERNET | ||||
| // fix for #2542 (by @BlackBird77) | ||||
| #define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarEsp32HspiHzMethod, NeoGammaNullMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9) | ||||
| #define B_HS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarEsp32HspiHzMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9) | ||||
| #else | ||||
| #define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarSpiHzMethod, NeoGammaNullMethod> //hardware VSPI | ||||
| #define B_HS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarSpiHzMethod> //hardware VSPI | ||||
| #endif | ||||
| #define B_SS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarMethod, NeoGammaNullMethod>    //soft SPI | ||||
| #define B_SS_DOT_3 NeoPixelBus<DotStarBgrFeature, DotStarMethod>    //soft SPI | ||||
|  | ||||
| //LPD8806 | ||||
| #define B_HS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806SpiHzMethod, NeoGammaNullMethod> | ||||
| #define B_SS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806Method, NeoGammaNullMethod> | ||||
| #define B_HS_LPD_3 NeoPixelBus<Lpd8806GrbFeature, Lpd8806SpiHzMethod> | ||||
| #define B_SS_LPD_3 NeoPixelBus<Lpd8806GrbFeature, Lpd8806Method> | ||||
|  | ||||
| //LPD6803 | ||||
| #define B_HS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803SpiHzMethod, NeoGammaNullMethod> | ||||
| #define B_SS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803Method, NeoGammaNullMethod> | ||||
| #define B_HS_LPO_3 NeoPixelBus<Lpd6803GrbFeature, Lpd6803SpiHzMethod> | ||||
| #define B_SS_LPO_3 NeoPixelBus<Lpd6803GrbFeature, Lpd6803Method> | ||||
|  | ||||
| //WS2801 | ||||
| #ifdef WLED_USE_ETHERNET | ||||
| #define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>, NeoGammaNullMethod> | ||||
| #define B_HS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>> | ||||
| #else | ||||
| #define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801SpiHzMethod, NeoGammaNullMethod> | ||||
| #define B_HS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801SpiHzMethod> | ||||
| #endif | ||||
| #define B_SS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801Method, NeoGammaNullMethod> | ||||
| #define B_SS_WS1_3 NeoPixelBus<NeoRbgFeature, Ws2801Method> | ||||
|  | ||||
| //P9813 | ||||
| #define B_HS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813SpiHzMethod, NeoGammaNullMethod> | ||||
| #define B_SS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813Method, NeoGammaNullMethod> | ||||
| #define B_HS_P98_3 NeoPixelBus<P9813BgrFeature, P9813SpiHzMethod> | ||||
| #define B_SS_P98_3 NeoPixelBus<P9813BgrFeature, P9813Method> | ||||
|  | ||||
| // 48bit & 64bit to 24bit & 32bit RGB(W) conversion | ||||
| #define toRGBW32(c) (RGBW32((c>>40)&0xFF, (c>>24)&0xFF, (c>>8)&0xFF, (c>>56)&0xFF)) | ||||
| @@ -896,102 +896,6 @@ class PolyBus { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) { | ||||
|     switch (busType) { | ||||
|       case I_NONE: break; | ||||
|     #ifdef ESP8266 | ||||
|       case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_FW6_5: (static_cast<B_8266_U0_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_FW6_5: (static_cast<B_8266_U1_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_FW6_5: (static_cast<B_8266_DM_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_FW6_5: (static_cast<B_8266_BB_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_2805_5: (static_cast<B_8266_U0_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U0_SM16825_5: (static_cast<B_8266_U0_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_U1_SM16825_5: (static_cast<B_8266_U1_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_DM_SM16825_5: (static_cast<B_8266_DM_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_8266_BB_SM16825_5: (static_cast<B_8266_BB_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|     #endif | ||||
|     #ifdef ARDUINO_ARCH_ESP32 | ||||
|       // RMT buses | ||||
|       case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|       // I2S1 bus or paralell buses | ||||
|       #ifndef CONFIG_IDF_TARGET_ESP32C3 | ||||
|       case I_32_I2_NEO_3: if (_useParallelI2S) (static_cast<B_32_IP_NEO_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_NEO_4: if (_useParallelI2S) (static_cast<B_32_IP_NEO_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_NEO_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_400_3: if (_useParallelI2S) (static_cast<B_32_IP_400_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_400_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_TM1_4: if (_useParallelI2S) (static_cast<B_32_IP_TM1_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_TM2_3: if (_useParallelI2S) (static_cast<B_32_IP_TM2_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM2_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_UCS_3: if (_useParallelI2S) (static_cast<B_32_IP_UCS_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_UCS_4: if (_useParallelI2S) (static_cast<B_32_IP_UCS_4*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_UCS_4*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_APA106_3: if (_useParallelI2S) (static_cast<B_32_IP_APA106_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_APA106_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_FW6_5: if (_useParallelI2S) (static_cast<B_32_IP_FW6_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_FW6_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_2805_5: if (_useParallelI2S) (static_cast<B_32_IP_2805_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_2805_5*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_TM1914_3: if (_useParallelI2S) (static_cast<B_32_IP_TM1914_3*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_TM1914_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_32_I2_SM16825_5: if (_useParallelI2S) (static_cast<B_32_IP_SM16825_5*>(busPtr))->SetLuminance(b); else (static_cast<B_32_I2_SM16825_5*>(busPtr))->SetLuminance(b); break; | ||||
|       #endif | ||||
|     #endif | ||||
|       case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); break; | ||||
|       case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   [[gnu::hot]] static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { | ||||
|     RgbwColor col(0,0,0,0); | ||||
|     switch (busType) { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|  * color blend function, based on FastLED blend function | ||||
|  * the calculation for each color is: result = (A*(amountOfA) + A + B*(amountOfB) + B) / 256 with amountOfA = 255 - amountOfB | ||||
|  */ | ||||
| uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { | ||||
| uint32_t IRAM_ATTR color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { | ||||
|   // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance | ||||
|   const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF;     // mask for R and B channels or W and G if negated (poorman's SIMD; https://github.com/wled/WLED/pull/4568#discussion_r1986587221) | ||||
|   uint32_t rb1 =  color1       & TWO_CHANNEL_MASK;  // extract R & B channels from color1 | ||||
| @@ -64,26 +64,26 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) | ||||
|  * fades color toward black | ||||
|  * if using "video" method the resulting color will never become black unless it is already black | ||||
|  */ | ||||
|  | ||||
| uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) | ||||
| { | ||||
| uint32_t IRAM_ATTR color_fade(uint32_t c1, uint8_t amount, bool video) { | ||||
|   if (c1 == 0 || amount == 0) return 0; // black or no change | ||||
|   if (amount == 255) return c1; | ||||
|   if (c1 == BLACK || amount == 0) return BLACK; | ||||
|   uint32_t scaledcolor; // color order is: W R G B from MSB to LSB | ||||
|   uint32_t scale = amount; // 32bit for faster calculation | ||||
|   uint32_t addRemains = 0; | ||||
|   if (!video) scale++; // add one for correct scaling using bitshifts | ||||
|   else { // video scaling: make sure colors do not dim to zero if they started non-zero | ||||
|     addRemains  = R(c1) ? 0x00010000 : 0; | ||||
|     addRemains |= G(c1) ? 0x00000100 : 0; | ||||
|     addRemains |= B(c1) ? 0x00000001 : 0; | ||||
|     addRemains |= W(c1) ? 0x01000000 : 0; | ||||
|  | ||||
|   if (!video) amount++; // add one for correct scaling using bitshifts | ||||
|   else { | ||||
|     // video scaling: make sure colors do not dim to zero if they started non-zero unless they distort the hue | ||||
|     uint8_t r = byte(c1>>16), g = byte(c1>>8), b = byte(c1), w = byte(c1>>24); // extract r, g, b, w channels | ||||
|     uint8_t maxc = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); // determine dominant channel for hue preservation | ||||
|     uint8_t quarterMax = maxc >> 2; // note: using half of max results in color artefacts | ||||
|     addRemains  = r && r > quarterMax ? 0x00010000 : 0; | ||||
|     addRemains |= g && g > quarterMax ? 0x00000100 : 0; | ||||
|     addRemains |= b && b > quarterMax ? 0x00000001 : 0; | ||||
|     addRemains |= w ? 0x01000000 : 0; | ||||
|   } | ||||
|   const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; | ||||
|   uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * scale) >> 8) &  TWO_CHANNEL_MASK; // scale red and blue | ||||
|   uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * scale) & ~TWO_CHANNEL_MASK; // scale white and green | ||||
|   scaledcolor = (rb | wg) + addRemains; | ||||
|   return scaledcolor; | ||||
|   uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) &  TWO_CHANNEL_MASK; // scale red and blue | ||||
|   uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK; // scale white and green | ||||
|   return (rb | wg) + addRemains; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -92,7 +92,7 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) | ||||
|    note: inputs are 32bit to speed up the function, useful input value ranges are 0-255 | ||||
|  */ | ||||
| uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten) { | ||||
|     if(rgb == 0 | hueShift + lighten + brighten == 0) return rgb; // black or no change | ||||
|     if (rgb == 0 | hueShift + lighten + brighten == 0) return rgb; // black or no change | ||||
|     CHSV32 hsv; | ||||
|     rgb2hsv(rgb, hsv); //convert to HSV | ||||
|     hsv.h += (hueShift << 8); // shift hue (hue is 16 bits) | ||||
| @@ -104,8 +104,7 @@ uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_ | ||||
| } | ||||
|  | ||||
| // 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) | ||||
| uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) | ||||
| { | ||||
| uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) { | ||||
|   if (blendType == LINEARBLEND_NOWRAP) { | ||||
|     index = (index * 0xF0) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping | ||||
|   } | ||||
| @@ -120,16 +119,16 @@ uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t | ||||
|     else ++entry; | ||||
|     unsigned f2 = (lo4 << 4); | ||||
|     unsigned f1 = 256 - f2; | ||||
|     red1   = (red1 * f1 + (unsigned)entry->r * f2) >> 8; // note: using color_blend() is 20% slower | ||||
|     red1   = (red1   * f1 + (unsigned)entry->r * f2) >> 8; // note: using color_blend() is slower | ||||
|     green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; | ||||
|     blue1  = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; | ||||
|     blue1  = (blue1  * f1 + (unsigned)entry->b * f2) >> 8; | ||||
|   } | ||||
|   if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted | ||||
|     // actually color_fade(c1, brightness) | ||||
|     // actually same as color_fade(), using color_fade() is slower | ||||
|     uint32_t scale = brightness + 1; // adjust for rounding (bitshift) | ||||
|     red1   = (red1 * scale) >> 8; // note: using color_fade() is 30% slower | ||||
|     red1   = (red1   * scale) >> 8; | ||||
|     green1 = (green1 * scale) >> 8; | ||||
|     blue1  = (blue1 * scale) >> 8; | ||||
|     blue1  = (blue1  * scale) >> 8; | ||||
|   } | ||||
|   return RGBW32(red1,green1,blue1,0); | ||||
| } | ||||
| @@ -589,10 +588,13 @@ uint8_t NeoGammaWLEDMethod::gammaT_inv[256]; | ||||
| void NeoGammaWLEDMethod::calcGammaTable(float gamma) | ||||
| { | ||||
|   float gamma_inv = 1.0f / gamma; // inverse gamma | ||||
|   for (size_t i = 0; i < 256; i++) { | ||||
|   for (size_t i = 1; i < 256; i++) { | ||||
|     gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f); | ||||
|     gammaT_inv[i] = (int)(powf((float)i / 255.0f, gamma_inv) * 255.0f + 0.5f); | ||||
|     gammaT_inv[i] = (int)(powf(((float)i - 0.5f) / 255.0f, gamma_inv) * 255.0f + 0.5f); | ||||
|     //DEBUG_PRINTF_P(PSTR("gammaT[%d] = %d gammaT_inv[%d] = %d\n"), i, gammaT[i], i, gammaT_inv[i]); | ||||
|   } | ||||
|   gammaT[0] = 0; | ||||
|   gammaT_inv[0] = 0; | ||||
| } | ||||
|  | ||||
| uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct(uint8_t value) | ||||
| @@ -601,21 +603,6 @@ uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct(uint8_t value) | ||||
|   return gammaT[value]; | ||||
| } | ||||
|  | ||||
| // used for color gamma correction | ||||
| uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct32(uint32_t color) | ||||
| { | ||||
|   if (!gammaCorrectCol) return color; | ||||
|   uint8_t w = W(color); | ||||
|   uint8_t r = R(color); | ||||
|   uint8_t g = G(color); | ||||
|   uint8_t b = B(color); | ||||
|   w = gammaT[w]; | ||||
|   r = gammaT[r]; | ||||
|   g = gammaT[g]; | ||||
|   b = gammaT[b]; | ||||
|   return RGBW32(r, g, b, w); | ||||
| } | ||||
|  | ||||
| uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::inverseGamma32(uint32_t color) | ||||
| { | ||||
|   if (!gammaCorrectCol) return color; | ||||
|   | ||||
							
								
								
									
										144
									
								
								wled00/colors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								wled00/colors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| #pragma once | ||||
| #ifndef WLED_COLORS_H | ||||
| #define WLED_COLORS_H | ||||
|  | ||||
| /* | ||||
|  * Color structs and color utility functions | ||||
|  */ | ||||
| #include <vector> | ||||
| #include "FastLED.h" | ||||
|  | ||||
| #define ColorFromPalette ColorFromPaletteWLED // override fastled version | ||||
|  | ||||
| // CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color | ||||
| // use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts | ||||
| // it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB | ||||
| struct CRGBW { | ||||
|     union { | ||||
|         uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB) | ||||
|         struct { | ||||
|             uint8_t b; | ||||
|             uint8_t g; | ||||
|             uint8_t r; | ||||
|             uint8_t w; | ||||
|         }; | ||||
|         uint8_t raw[4];   // Access as an array in the order B, G, R, W | ||||
|     }; | ||||
|  | ||||
|     // Default constructor | ||||
|     inline CRGBW() __attribute__((always_inline)) = default; | ||||
|  | ||||
|     // Constructor from a 32-bit color (0xWWRRGGBB) | ||||
|     constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {} | ||||
|  | ||||
|     // Constructor with r, g, b, w values | ||||
|     constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {} | ||||
|  | ||||
|     // Constructor from CRGB | ||||
|     constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {} | ||||
|  | ||||
|     // Access as an array | ||||
|     inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } | ||||
|  | ||||
|     // Assignment from 32-bit color | ||||
|     inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; } | ||||
|  | ||||
|     // Assignment from r, g, b, w | ||||
|     inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; } | ||||
|  | ||||
|     // Conversion operator to uint32_t | ||||
|     inline operator uint32_t() const __attribute__((always_inline)) { | ||||
|       return color32; | ||||
|     } | ||||
|     /* | ||||
|     // Conversion operator to CRGB | ||||
|     inline operator CRGB() const __attribute__((always_inline)) { | ||||
|       return CRGB(r, g, b); | ||||
|     } | ||||
|  | ||||
|     CRGBW& scale32 (uint8_t scaledown) // 32bit math | ||||
|     { | ||||
|       if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit | ||||
|       uint32_t scale = scaledown + 1; | ||||
|       uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue | ||||
|       uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green | ||||
|           color32 =  rb | wg; | ||||
|       return *this; | ||||
|     }*/ | ||||
|  | ||||
| }; | ||||
|  | ||||
| struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions | ||||
|   union { | ||||
|     struct { | ||||
|         uint16_t h;  // hue | ||||
|         uint8_t s;   // saturation | ||||
|         uint8_t v;   // value | ||||
|     }; | ||||
|     uint32_t raw;    // 32bit access | ||||
|   }; | ||||
|   inline CHSV32() __attribute__((always_inline)) = default; // default constructor | ||||
|  | ||||
|     /// Allow construction from hue, saturation, and value | ||||
|     /// @param ih input hue | ||||
|     /// @param is input saturation | ||||
|     /// @param iv input value | ||||
|   inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v | ||||
|         : h(ih), s(is), v(iv) {} | ||||
|   inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v | ||||
|         : h((uint16_t)ih << 8), s(is), v(iv) {} | ||||
|   inline CHSV32(const CHSV& chsv) __attribute__((always_inline))  // constructor from CHSV | ||||
|     : h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {} | ||||
|   inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV | ||||
| }; | ||||
| extern bool gammaCorrectCol; | ||||
| // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) | ||||
| class NeoGammaWLEDMethod { | ||||
|   public: | ||||
|     [[gnu::hot]] static uint8_t Correct(uint8_t value);             // apply Gamma to single channel | ||||
|     [[gnu::hot]] static uint32_t inverseGamma32(uint32_t color);    // apply inverse Gamma to RGBW32 color | ||||
|     static void calcGammaTable(float gamma);                        // re-calculates & fills gamma tables | ||||
|     static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; }  // get value from Gamma table (WLED specific, not used by NPB) | ||||
|     static inline uint8_t rawInverseGamma8(uint8_t val) { return gammaT_inv[val]; }  // get value from inverse Gamma table (WLED specific, not used by NPB) | ||||
|     static inline uint32_t Correct32(uint32_t color) { // apply Gamma to RGBW32 color (WLED specific, not used by NPB) | ||||
|       if (!gammaCorrectCol) return color; // no gamma correction | ||||
|       uint8_t  w = byte(color>>24), r = byte(color>>16), g = byte(color>>8), b = byte(color); // extract r, g, b, w channels | ||||
|       w = gammaT[w]; r = gammaT[r]; g = gammaT[g]; b = gammaT[b]; | ||||
|       return (uint32_t(w) << 24) | (uint32_t(r) << 16) | (uint32_t(g) << 8) | uint32_t(b); | ||||
|     } | ||||
|   private: | ||||
|     static uint8_t gammaT[]; | ||||
|     static uint8_t gammaT_inv[]; | ||||
| }; | ||||
| #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) | ||||
| #define gamma8(c)  NeoGammaWLEDMethod::rawGamma8(c) | ||||
| #define gamma32inv(c) NeoGammaWLEDMethod::inverseGamma32(c) | ||||
| #define gamma8inv(c)  NeoGammaWLEDMethod::rawInverseGamma8(c) | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend); | ||||
| inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); }; | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false); | ||||
| [[gnu::hot, gnu::pure]] uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten); | ||||
| [[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); | ||||
| CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); | ||||
| CRGBPalette16 generateRandomPalette(); | ||||
| void loadCustomPalettes(); | ||||
| extern std::vector<CRGBPalette16> customPalettes; | ||||
| inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); } | ||||
| inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } | ||||
| void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); | ||||
| void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); | ||||
| void rgb2hsv(const uint32_t rgb, CHSV32& hsv); | ||||
| inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv | ||||
| void colorKtoRGB(uint16_t kelvin, byte* rgb); | ||||
| void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb | ||||
| void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO | ||||
| void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO | ||||
| void colorFromDecOrHexString(byte* rgb, const char* in); | ||||
| bool colorFromHexString(byte* rgb, const char* in); | ||||
| uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); | ||||
| uint16_t approximateKelvinFromRGB(uint32_t rgb); | ||||
| void setRandomColor(byte* rgb); | ||||
|  | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video = false); | ||||
|  | ||||
| #endif | ||||
| @@ -107,6 +107,7 @@ Y: <input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()" | ||||
| 				Sf[`P${p}H`].value = ph; | ||||
| 			} | ||||
| 		} | ||||
| 		UI(); // Update the preview after generating panels | ||||
| 	} | ||||
|  | ||||
| 	function expand(o,i) | ||||
|   | ||||
| @@ -258,10 +258,10 @@ Static subnet mask:<br> | ||||
| 			<h3>Ethernet Type</h3> | ||||
| 			<select name="ETH"> | ||||
| 				<option value="0">None</option> | ||||
| 				<option value="6">IoTorero/ESP32Deux/RGB2Go</option> | ||||
| 				<option value="9">ABC! WLED V43 & compatible</option> | ||||
| 				<option value="2">ESP32-POE</option> | ||||
| 				<option value="11">ESP32-POE-WROVER</option> | ||||
| 				<option value="6">ESP32Deux/RGB2Go</option> | ||||
| 				<option value="7">KIT-VE</option> | ||||
| 				<option value="12">LILYGO T-POE Pro</option> | ||||
| 				<option value="8">QuinLED-Dig-Octa & T-ETH-POE</option> | ||||
|   | ||||
| @@ -191,7 +191,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8 | ||||
|         // only change brightness if value changed | ||||
|         if (bri != e131_data[dataOffset]) {                                         | ||||
|           bri = e131_data[dataOffset]; | ||||
|           strip.setBrightness(scaledBri(bri), false); | ||||
|           strip.setBrightness(bri, false); | ||||
|           stateUpdated(CALL_MODE_WS_SEND); | ||||
|         } | ||||
|         return; | ||||
|   | ||||
| @@ -73,133 +73,6 @@ typedef struct WiFiConfig { | ||||
|   } | ||||
| } wifi_config; | ||||
|  | ||||
| //colors.cpp | ||||
| #define ColorFromPalette ColorFromPaletteWLED // override fastled version | ||||
|  | ||||
| // CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color | ||||
| // use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts | ||||
| // it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB | ||||
| struct CRGBW { | ||||
|     union { | ||||
|         uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB) | ||||
|         struct { | ||||
|             uint8_t b; | ||||
|             uint8_t g; | ||||
|             uint8_t r; | ||||
|             uint8_t w; | ||||
|         }; | ||||
|         uint8_t raw[4];   // Access as an array in the order B, G, R, W | ||||
|     }; | ||||
|  | ||||
|     // Default constructor | ||||
|     inline CRGBW() __attribute__((always_inline)) = default; | ||||
|  | ||||
|     // Constructor from a 32-bit color (0xWWRRGGBB) | ||||
|     constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {} | ||||
|  | ||||
|     // Constructor with r, g, b, w values | ||||
|     constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {} | ||||
|  | ||||
|     // Constructor from CRGB | ||||
|     constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {} | ||||
|  | ||||
|     // Access as an array | ||||
|     inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } | ||||
|  | ||||
|     // Assignment from 32-bit color | ||||
|     inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; } | ||||
|  | ||||
|     // Assignment from r, g, b, w | ||||
|     inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; } | ||||
|  | ||||
|     // Conversion operator to uint32_t | ||||
|     inline operator uint32_t() const __attribute__((always_inline)) { | ||||
|       return color32; | ||||
|     } | ||||
|     /* | ||||
|     // Conversion operator to CRGB | ||||
|     inline operator CRGB() const __attribute__((always_inline)) { | ||||
|       return CRGB(r, g, b); | ||||
|     } | ||||
|  | ||||
|     CRGBW& scale32 (uint8_t scaledown) // 32bit math | ||||
|     { | ||||
|       if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit | ||||
|       uint32_t scale = scaledown + 1; | ||||
|       uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue | ||||
|       uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green | ||||
|           color32 =  rb | wg; | ||||
|       return *this; | ||||
|     }*/ | ||||
|  | ||||
| }; | ||||
|  | ||||
| struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions | ||||
|   union { | ||||
|     struct { | ||||
|         uint16_t h;  // hue | ||||
|         uint8_t s;   // saturation | ||||
|         uint8_t v;   // value | ||||
|     }; | ||||
|     uint32_t raw;    // 32bit access | ||||
|   }; | ||||
|   inline CHSV32() __attribute__((always_inline)) = default; // default constructor | ||||
|  | ||||
|     /// Allow construction from hue, saturation, and value | ||||
|     /// @param ih input hue | ||||
|     /// @param is input saturation | ||||
|     /// @param iv input value | ||||
|   inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v | ||||
|         : h(ih), s(is), v(iv) {} | ||||
|   inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v | ||||
|         : h((uint16_t)ih << 8), s(is), v(iv) {} | ||||
|   inline CHSV32(const CHSV& chsv) __attribute__((always_inline))  // constructor from CHSV | ||||
|     : h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {} | ||||
|   inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV | ||||
| }; | ||||
| // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) | ||||
| class NeoGammaWLEDMethod { | ||||
|   public: | ||||
|     [[gnu::hot]] static uint8_t Correct(uint8_t value);         // apply Gamma to single channel | ||||
|     [[gnu::hot]] static uint32_t Correct32(uint32_t color);     // apply Gamma to RGBW32 color (WLED specific, not used by NPB) | ||||
|     [[gnu::hot]] static uint32_t inverseGamma32(uint32_t color); // apply inverse Gamma to RGBW32 color | ||||
|     static void calcGammaTable(float gamma);                    // re-calculates & fills gamma tables | ||||
|     static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; }  // get value from Gamma table (WLED specific, not used by NPB) | ||||
|     static inline uint8_t rawInverseGamma8(uint8_t val) { return gammaT_inv[val]; }  // get value from inverse Gamma table (WLED specific, not used by NPB) | ||||
|   private: | ||||
|     static uint8_t gammaT[]; | ||||
|     static uint8_t gammaT_inv[]; | ||||
| }; | ||||
| #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) | ||||
| #define gamma8(c)  NeoGammaWLEDMethod::rawGamma8(c) | ||||
| #define gamma32inv(c) NeoGammaWLEDMethod::inverseGamma32(c) | ||||
| #define gamma8inv(c)  NeoGammaWLEDMethod::rawInverseGamma8(c) | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend); | ||||
| inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); }; | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false); | ||||
| [[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); | ||||
| [[gnu::hot, gnu::pure]] uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten); | ||||
| [[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); | ||||
| CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); | ||||
| CRGBPalette16 generateRandomPalette(); | ||||
| void loadCustomPalettes(); | ||||
| extern std::vector<CRGBPalette16> customPalettes; | ||||
| inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); } | ||||
| inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } | ||||
| void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); | ||||
| void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); | ||||
| void rgb2hsv(const uint32_t rgb, CHSV32& hsv); | ||||
| inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv | ||||
| void colorKtoRGB(uint16_t kelvin, byte* rgb); | ||||
| void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb | ||||
| void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO | ||||
| void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO | ||||
| void colorFromDecOrHexString(byte* rgb, const char* in); | ||||
| bool colorFromHexString(byte* rgb, const char* in); | ||||
| uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); | ||||
| uint16_t approximateKelvinFromRGB(uint32_t rgb); | ||||
| void setRandomColor(byte* rgb); | ||||
|  | ||||
| //dmx_output.cpp | ||||
| void initDMXOutput(); | ||||
| void handleDMXOutput(); | ||||
|   | ||||
| @@ -58,7 +58,7 @@ void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t | ||||
|   // set multiple pixels if upscaling | ||||
|   for (int16_t i = 0; i < (activeSeg->width()+(gifWidth-1)) / gifWidth; i++) { | ||||
|     for (int16_t j = 0; j < (activeSeg->height()+(gifHeight-1)) / gifHeight; j++) { | ||||
|       activeSeg->setPixelColorXY(outX + i, outY + j, gamma8(red), gamma8(green), gamma8(blue)); | ||||
|       activeSeg->setPixelColorXY(outX + i, outY + j, red, green, blue); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -312,7 +312,7 @@ static bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0) | ||||
|     jsonTransitionOnce = true; | ||||
|     if (seg.isInTransition()) seg.startTransition(0); // setting transition time to 0 will stop transition in next frame | ||||
|     strip.setTransition(0); | ||||
|     strip.setBrightness(scaledBri(bri), true); | ||||
|     strip.setBrightness(bri, true); | ||||
|  | ||||
|     // freeze and init to black | ||||
|     if (!seg.freeze) { | ||||
|   | ||||
| @@ -57,7 +57,7 @@ void toggleOnOff() | ||||
| //scales the brightness with the briMultiplier factor | ||||
| byte scaledBri(byte in) | ||||
| { | ||||
|   unsigned val = ((uint16_t)in*briMultiplier)/100; | ||||
|   unsigned val = ((unsigned)in*briMultiplier)/100; | ||||
|   if (val > 255) val = 255; | ||||
|   return (byte)val; | ||||
| } | ||||
| @@ -68,7 +68,7 @@ void applyBri() { | ||||
|   if (realtimeOverride || !(realtimeMode && arlsForceMaxBri)) | ||||
|   { | ||||
|     //DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld); | ||||
|     strip.setBrightness(scaledBri(briT)); | ||||
|     strip.setBrightness(briT); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -424,7 +424,7 @@ void realtimeLock(uint32_t timeoutMs, byte md) | ||||
|     } | ||||
|     // if strip is off (bri==0) and not already in RTM | ||||
|     if (briT == 0) { | ||||
|       strip.setBrightness(scaledBri(briLast), true); | ||||
|       strip.setBrightness(briLast, true); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -434,14 +434,14 @@ void realtimeLock(uint32_t timeoutMs, byte md) | ||||
|   realtimeMode = md; | ||||
|  | ||||
|   if (realtimeOverride) return; | ||||
|   if (arlsForceMaxBri) strip.setBrightness(scaledBri(255), true); | ||||
|   if (arlsForceMaxBri) strip.setBrightness(255, true); | ||||
|   if (briT > 0 && md == REALTIME_MODE_GENERIC) strip.show(); | ||||
| } | ||||
|  | ||||
| void exitRealtime() { | ||||
|   if (!realtimeMode) return; | ||||
|   if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE; | ||||
|   strip.setBrightness(scaledBri(bri), true); | ||||
|   strip.setBrightness(bri, true); | ||||
|   realtimeTimeout = 0; // cancel realtime mode immediately | ||||
|   realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately | ||||
|   realtimeIP[0] = 0; | ||||
|   | ||||
							
								
								
									
										205
									
								
								wled00/util.cpp
									
									
									
									
									
								
							
							
						
						
									
										205
									
								
								wled00/util.cpp
									
									
									
									
									
								
							| @@ -720,125 +720,140 @@ void *realloc_malloc(void *ptr, size_t size) { | ||||
| // checks if the ESP reboots multiple times due to a crash or watchdog timeout | ||||
| // if a bootloop is detected: restore settings from backup, then reset settings, then switch boot image (and repeat) | ||||
|  | ||||
| #define BOOTLOOP_THRESHOLD      5     // number of consecutive crashes to trigger bootloop detection | ||||
| #define BOOTLOOP_ACTION_RESTORE 0     // default action: restore config from /bak.cfg.json | ||||
| #define BOOTLOOP_ACTION_RESET   1     // if restore does not work, reset config (rename /cfg.json to /rst.cfg.json) | ||||
| #define BOOTLOOP_ACTION_OTA     2     // swap the boot partition | ||||
| #define BOOTLOOP_ACTION_DUMP    3     // nothing seems to help, dump files to serial and reboot (until hardware reset) | ||||
| #define BOOTLOOP_INTERVAL_MILLIS 120000  // time limit between crashes: 120 seconds (2 minutes) | ||||
| #define BOOTLOOP_THRESHOLD       5     // number of consecutive crashes to trigger bootloop detection | ||||
| #define BOOTLOOP_ACTION_RESTORE  0     // default action: restore config from /bkp.cfg.json | ||||
| #define BOOTLOOP_ACTION_RESET    1     // if restore does not work, reset config (rename /cfg.json to /rst.cfg.json) | ||||
| #define BOOTLOOP_ACTION_OTA      2     // swap the boot partition | ||||
| #define BOOTLOOP_ACTION_DUMP     3     // nothing seems to help, dump files to serial and reboot (until hardware reset) | ||||
|  | ||||
| // Platform-agnostic abstraction | ||||
| enum class ResetReason { | ||||
|   Power, | ||||
|   Software, | ||||
|   Crash, | ||||
|   Brownout | ||||
| }; | ||||
|  | ||||
| #ifdef ESP8266 | ||||
| #define BOOTLOOP_INTERVAL_TICKS (5 * 160000) // time limit between crashes: ~5 seconds in RTC ticks | ||||
| #define BOOT_TIME_IDX       0 // index in RTC memory for boot time | ||||
| #define CRASH_COUNTER_IDX   1 // index in RTC memory for crash counter | ||||
| #define ACTIONT_TRACKER_IDX 2 // index in RTC memory for boot action | ||||
| // Place variables in RTC memory via references, since RTC memory is not exposed via the linker in the Non-OS SDK | ||||
| // Use an offset of 32 as there's some hints that the first 128 bytes of "user" memory are used by the OTA system | ||||
| // Ref: https://github.com/esp8266/Arduino/blob/78d0d0aceacc1553f45ad8154592b0af22d1eede/cores/esp8266/Esp.cpp#L168 | ||||
| static volatile uint32_t& bl_last_boottime = *(RTC_USER_MEM + 32); | ||||
| static volatile uint32_t& bl_crashcounter = *(RTC_USER_MEM + 33); | ||||
| static volatile uint32_t& bl_actiontracker = *(RTC_USER_MEM + 34); | ||||
|  | ||||
| static inline ResetReason rebootReason() { | ||||
|   uint32_t resetReason = system_get_rst_info()->reason; | ||||
|   if (resetReason == REASON_EXCEPTION_RST | ||||
|       || resetReason == REASON_WDT_RST | ||||
|       || resetReason == REASON_SOFT_WDT_RST) | ||||
|       return ResetReason::Crash; | ||||
|   if (resetReason == REASON_SOFT_RESTART) | ||||
|     return ResetReason::Software; | ||||
|   return ResetReason::Power; | ||||
| } | ||||
|  | ||||
| static inline uint32_t getRtcMillis() { return system_get_rtc_time() / 160; };  // rtc ticks ~160000Hz | ||||
|  | ||||
| #else | ||||
| #define BOOTLOOP_INTERVAL_TICKS 5000  // time limit between crashes: ~5 seconds in milliseconds | ||||
| // variables in RTC_NOINIT memory persist between reboots (but not on hardware reset) | ||||
| RTC_NOINIT_ATTR static uint32_t bl_last_boottime; | ||||
| RTC_NOINIT_ATTR static uint32_t bl_crashcounter; | ||||
| RTC_NOINIT_ATTR static uint32_t bl_actiontracker; | ||||
|  | ||||
| static inline ResetReason rebootReason() { | ||||
|   esp_reset_reason_t reason = esp_reset_reason(); | ||||
|   if (reason == ESP_RST_BROWNOUT) return ResetReason::Brownout; | ||||
|   if (reason == ESP_RST_SW) return ResetReason::Software; | ||||
|   if (reason == ESP_RST_PANIC || reason == ESP_RST_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_TASK_WDT) return ResetReason::Crash; | ||||
|   return ResetReason::Power; | ||||
| } | ||||
|  | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) | ||||
| static inline uint32_t getRtcMillis() { return esp_rtc_get_time_us() / 1000; } | ||||
| #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0) | ||||
| static inline uint32_t getRtcMillis() { return rtc_time_slowclk_to_us(rtc_time_get(), rtc_clk_slow_freq_get_hz()) / 1000; } | ||||
| #endif | ||||
|  | ||||
| void bootloopCheckOTA() { bl_actiontracker = BOOTLOOP_ACTION_OTA; } // swap boot image if bootloop is detected instead of restoring config | ||||
|  | ||||
| #endif | ||||
|  | ||||
| // detect bootloop by checking the reset reason and the time since last boot | ||||
| static bool detectBootLoop() { | ||||
| #if !defined(ESP8266) | ||||
|   #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) | ||||
|     uint32_t rtctime = esp_rtc_get_time_us() / 1000;  // convert to milliseconds | ||||
|   #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0) | ||||
|     uint64_t rtc_ticks = rtc_time_get(); | ||||
|     uint32_t rtctime = rtc_time_slowclk_to_us(rtc_ticks, rtc_clk_slow_freq_get_hz()) / 1000;  // convert to milliseconds | ||||
|   #endif | ||||
|   uint32_t rtctime = getRtcMillis(); | ||||
|   bool result = false; | ||||
|  | ||||
|   esp_reset_reason_t reason = esp_reset_reason(); | ||||
|  | ||||
|   if (!(reason == ESP_RST_PANIC || reason == ESP_RST_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_TASK_WDT)) { | ||||
|     // no crash detected, init variables | ||||
|     bl_crashcounter = 0; | ||||
|     bl_last_boottime = rtctime; | ||||
|     if(reason != ESP_RST_SW) | ||||
|   switch(rebootReason()) { | ||||
|     case ResetReason::Power: | ||||
|       bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // init action tracker if not an intentional reboot (e.g. from OTA or bootloop handler) | ||||
|   } else if (reason == ESP_RST_BROWNOUT) { | ||||
|     // crash due to brownout can't be detected unless using flash memory to store bootloop variables | ||||
|     // this is a simpler way to preemtively revert the config in case current brownout is caused by a bad choice of settings | ||||
|     DEBUG_PRINTLN(F("brownout detected")); | ||||
|     //restoreConfig(); // TODO: blindly restoring config if brownout detected is a bad idea, need a better way (if at all) | ||||
|   } else { | ||||
|     uint32_t rebootinterval = rtctime - bl_last_boottime; | ||||
|     bl_last_boottime = rtctime; // store current runtime for next reboot | ||||
|     if (rebootinterval < BOOTLOOP_INTERVAL_TICKS) { | ||||
|       bl_crashcounter++; | ||||
|       if (bl_crashcounter >= BOOTLOOP_THRESHOLD) { | ||||
|         DEBUG_PRINTLN(F("!BOOTLOOP DETECTED!")); | ||||
|         bl_crashcounter = 0; | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| #else // ESP8266 | ||||
|   rst_info* resetreason = system_get_rst_info(); | ||||
|   uint32_t  bl_last_boottime; | ||||
|   uint32_t  bl_crashcounter; | ||||
|   uint32_t  bl_actiontracker; | ||||
|   uint32_t  rtctime = system_get_rtc_time(); | ||||
|       // fall through | ||||
|     case ResetReason::Software: | ||||
|       // no crash detected, reset counter | ||||
|       bl_crashcounter = 0; | ||||
|       break; | ||||
|  | ||||
|   if (!(resetreason->reason == REASON_EXCEPTION_RST || resetreason->reason == REASON_WDT_RST)) { | ||||
|     // no crash detected, init variables | ||||
|     bl_crashcounter = 0; | ||||
|     ESP.rtcUserMemoryWrite(BOOT_TIME_IDX, &rtctime, sizeof(uint32_t)); | ||||
|     ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t)); | ||||
|     if(resetreason->reason != REASON_SOFT_RESTART) { | ||||
|       bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // init action tracker if not an intentional reboot (e.g. from OTA or bootloop handler) | ||||
|       ESP.rtcUserMemoryWrite(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t)); | ||||
|     } | ||||
|   } else { | ||||
|     // system has crashed | ||||
|     ESP.rtcUserMemoryRead(BOOT_TIME_IDX, &bl_last_boottime, sizeof(uint32_t)); | ||||
|     ESP.rtcUserMemoryRead(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t)); | ||||
|     uint32_t rebootinterval = rtctime - bl_last_boottime; | ||||
|     ESP.rtcUserMemoryWrite(BOOT_TIME_IDX, &rtctime, sizeof(uint32_t)); // store current ticks for next reboot | ||||
|     if (rebootinterval < BOOTLOOP_INTERVAL_TICKS) { | ||||
|       bl_crashcounter++; | ||||
|       ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t)); | ||||
|       if (bl_crashcounter >= BOOTLOOP_THRESHOLD) { | ||||
|         DEBUG_PRINTLN(F("BOOTLOOP DETECTED")); | ||||
|     case ResetReason::Crash: | ||||
|     { | ||||
|       DEBUG_PRINTLN(F("crash detected!")); | ||||
|       uint32_t rebootinterval = rtctime - bl_last_boottime; | ||||
|       if (rebootinterval < BOOTLOOP_INTERVAL_MILLIS) { | ||||
|         bl_crashcounter++; | ||||
|         if (bl_crashcounter >= BOOTLOOP_THRESHOLD) { | ||||
|           DEBUG_PRINTLN(F("!BOOTLOOP DETECTED!")); | ||||
|           bl_crashcounter = 0; | ||||
|           result = true; | ||||
|         } | ||||
|       } else { | ||||
|         // Reset counter on long intervals to track only consecutive short-interval crashes | ||||
|         bl_crashcounter = 0; | ||||
|         ESP.rtcUserMemoryWrite(CRASH_COUNTER_IDX, &bl_crashcounter, sizeof(uint32_t)); | ||||
|         return true; | ||||
|         // TODO: crash reporting goes here | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|     case ResetReason::Brownout: | ||||
|       // crash due to brownout can't be detected unless using flash memory to store bootloop variables | ||||
|       DEBUG_PRINTLN(F("brownout detected")); | ||||
|       //restoreConfig(); // TODO: blindly restoring config if brownout detected is a bad idea, need a better way (if at all) | ||||
|       break; | ||||
|   } | ||||
| #endif | ||||
|   return false; // no bootloop detected | ||||
|  | ||||
|   bl_last_boottime = rtctime; // store current runtime for next reboot | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| void handleBootLoop() { | ||||
|   DEBUG_PRINTLN(F("checking for bootloop")); | ||||
|   DEBUG_PRINTF_P(PSTR("checking for bootloop: time %d, counter %d, action %d\n"), bl_last_boottime, bl_crashcounter, bl_actiontracker); | ||||
|   if (!detectBootLoop()) return; // no bootloop detected | ||||
| #ifdef ESP8266 | ||||
|   uint32_t bl_actiontracker; | ||||
|   ESP.rtcUserMemoryRead(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t)); | ||||
| #endif | ||||
|   if (bl_actiontracker == BOOTLOOP_ACTION_RESTORE) { | ||||
|     restoreConfig(); // note: if this fails, could reset immediately. instead just let things play out and save a few lines of code | ||||
|     bl_actiontracker = BOOTLOOP_ACTION_RESET; // reset config if it keeps bootlooping | ||||
|   } else if (bl_actiontracker == BOOTLOOP_ACTION_RESET) { | ||||
|     resetConfig(); | ||||
|     bl_actiontracker = BOOTLOOP_ACTION_OTA; // swap boot partition if it keeps bootlooping. On ESP8266 this is the same as BOOTLOOP_ACTION_NONE | ||||
|   } | ||||
|  | ||||
|   switch(bl_actiontracker) { | ||||
|     case BOOTLOOP_ACTION_RESTORE: | ||||
|       restoreConfig(); | ||||
|       ++bl_actiontracker; | ||||
|       break; | ||||
|     case BOOTLOOP_ACTION_RESET: | ||||
|       resetConfig(); | ||||
|       ++bl_actiontracker; | ||||
|       break; | ||||
|     case BOOTLOOP_ACTION_OTA: | ||||
| #ifndef ESP8266 | ||||
|   else if (bl_actiontracker == BOOTLOOP_ACTION_OTA) { | ||||
|     if(Update.canRollBack()) { | ||||
|       DEBUG_PRINTLN(F("Swapping boot partition...")); | ||||
|       Update.rollBack(); // swap boot partition | ||||
|     } | ||||
|     bl_actiontracker = BOOTLOOP_ACTION_DUMP; // out of options | ||||
|   } | ||||
|   #endif | ||||
|   else | ||||
|     dumpFilesToSerial(); | ||||
| #ifdef ESP8266 | ||||
|   ESP.rtcUserMemoryWrite(ACTIONT_TRACKER_IDX, &bl_actiontracker, sizeof(uint32_t)); | ||||
|       if(Update.canRollBack()) { | ||||
|         DEBUG_PRINTLN(F("Swapping boot partition...")); | ||||
|         Update.rollBack(); // swap boot partition | ||||
|       } | ||||
|       ++bl_actiontracker; | ||||
|       break; | ||||
| #else | ||||
|       // fall through | ||||
| #endif | ||||
|     case BOOTLOOP_ACTION_DUMP: | ||||
|       dumpFilesToSerial(); | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   ESP.restart(); // restart cleanly and don't wait for another crash | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -190,12 +190,10 @@ void WLED::loop() | ||||
|     doInitBusses = false; | ||||
|     DEBUG_PRINTLN(F("Re-init busses.")); | ||||
|     bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses) | ||||
|     BusManager::removeAll(); | ||||
|     strip.finalizeInit(); // will create buses and also load default ledmap if present | ||||
|     BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005 | ||||
|     if (aligned) strip.makeAutoSegments(); | ||||
|     else strip.fixInvalidSegments(); | ||||
|     BusManager::setBrightness(bri); // fix re-initialised bus' brightness | ||||
|     BusManager::setBrightness(scaledBri(bri)); // fix re-initialised bus' brightness #4005 and #4824 | ||||
|     configNeedsWrite = true; | ||||
|   } | ||||
|   if (loadLedmap >= 0) { | ||||
|   | ||||
| @@ -194,6 +194,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>; | ||||
| #include "fcn_declare.h" | ||||
| #include "NodeStruct.h" | ||||
| #include "pin_manager.h" | ||||
| #include "colors.h" | ||||
| #include "bus_manager.h" | ||||
| #include "FX.h" | ||||
|  | ||||
| @@ -733,10 +734,10 @@ WLED_GLOBAL bool receiveNotificationPalette    _INIT(true);       // apply palet | ||||
| WLED_GLOBAL bool receiveSegmentOptions         _INIT(false);      // apply segment options | ||||
| WLED_GLOBAL bool receiveSegmentBounds          _INIT(false);      // apply segment bounds (start, stop, offset) | ||||
| WLED_GLOBAL bool receiveDirect _INIT(true);                       // receive UDP/Hyperion realtime | ||||
| WLED_GLOBAL bool notifyDirect _INIT(false);                       // send notification if change via UI or HTTP API | ||||
| WLED_GLOBAL bool notifyButton _INIT(false);                       // send if updated by button or infrared remote | ||||
| WLED_GLOBAL bool notifyDirect _INIT(true);                        // send notification if change via UI or HTTP API | ||||
| WLED_GLOBAL bool notifyButton _INIT(true);                        // send if updated by button or infrared remote | ||||
| WLED_GLOBAL bool notifyAlexa  _INIT(false);                       // send notification if updated via Alexa | ||||
| WLED_GLOBAL bool notifyHue    _INIT(true);                        // send notification if Hue light changes | ||||
| WLED_GLOBAL bool notifyHue    _INIT(false);                       // send notification if Hue light changes | ||||
| #endif | ||||
|  | ||||
| // effects | ||||
|   | ||||
		Reference in New Issue
	
	Block a user