mirror of
				https://github.com/wled/WLED.git
				synced 2025-11-04 08:39:02 +00:00 
			
		
		
		
	Compare commits
	
		
			39 Commits
		
	
	
		
			dnd-output
			...
			v0.15.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					faadb67eb0 | ||
| 
						 | 
					a111a2e7a1 | ||
| 
						 | 
					32864d8986 | ||
| 
						 | 
					d7bebc2659 | ||
| 
						 | 
					af410ae2d0 | ||
| 
						 | 
					1891cc816f | ||
| 
						 | 
					9a4073e606 | ||
| 
						 | 
					b78229d1e2 | ||
| 
						 | 
					71b242874f | ||
| 
						 | 
					9328e6faca | ||
| 
						 | 
					bbacc2daae | ||
| 
						 | 
					3e22f9cabb | ||
| 
						 | 
					685ad83d4b | ||
| 
						 | 
					62ddb18a1a | ||
| 
						 | 
					6a12378475 | ||
| 
						 | 
					d26b3108da | ||
| 
						 | 
					c89e4576b4 | ||
| 
						 | 
					bc79f44a26 | ||
| 
						 | 
					7ece14ff3f | ||
| 
						 | 
					0b3643132b | ||
| 
						 | 
					a5693fbf8d | ||
| 
						 | 
					5c5b70f52b | ||
| 
						 | 
					ae97e388a6 | ||
| 
						 | 
					37cddcaacc | ||
| 
						 | 
					a1b332fc78 | ||
| 
						 | 
					86d7c24513 | ||
| 
						 | 
					b28add3b8b | ||
| 
						 | 
					5fd3a513a4 | ||
| 
						 | 
					b98a8a10b0 | ||
| 
						 | 
					2bee2793ef | ||
| 
						 | 
					2f6fa66f4d | ||
| 
						 | 
					5d38acd787 | ||
| 
						 | 
					e607fcb5c5 | ||
| 
						 | 
					8a18555ae4 | ||
| 
						 | 
					beb709dc8f | ||
| 
						 | 
					7a58c69a80 | ||
| 
						 | 
					1082c85789 | ||
| 
						 | 
					568d2edd96 | ||
| 
						 | 
					d2d56ebbd2 | 
@@ -2,7 +2,12 @@
 | 
			
		||||
 | 
			
		||||
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
 | 
			
		||||
ARG VARIANT="3"
 | 
			
		||||
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT}
 | 
			
		||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
 | 
			
		||||
 | 
			
		||||
# [Option] Install Node.js
 | 
			
		||||
ARG INSTALL_NODE="true"
 | 
			
		||||
ARG NODE_VERSION="lts/*"
 | 
			
		||||
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
 | 
			
		||||
 | 
			
		||||
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
 | 
			
		||||
# COPY requirements.txt /tmp/pip-tmp/
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,10 @@
 | 
			
		||||
		"context": "..",
 | 
			
		||||
		"args": { 
 | 
			
		||||
			// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9
 | 
			
		||||
			"VARIANT": "3"
 | 
			
		||||
			"VARIANT": "3",
 | 
			
		||||
			// Options
 | 
			
		||||
			"INSTALL_NODE": "true",
 | 
			
		||||
			"NODE_VERSION": "lts/*"
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -24,8 +27,7 @@
 | 
			
		||||
	// risk to running the build directly on the host.
 | 
			
		||||
	// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
 | 
			
		||||
 | 
			
		||||
	"customizations": {
 | 
			
		||||
		"vscode": {
 | 
			
		||||
	// Set *default* container specific settings.json values on container create.
 | 
			
		||||
	"settings": { 
 | 
			
		||||
		"terminal.integrated.shell.linux": "/bin/bash",
 | 
			
		||||
		"python.pythonPath": "/usr/local/bin/python",
 | 
			
		||||
@@ -41,18 +43,18 @@
 | 
			
		||||
		"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
 | 
			
		||||
		"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	// Add the IDs of extensions you want installed when the container is created.
 | 
			
		||||
	"extensions": [
 | 
			
		||||
		"ms-python.python",
 | 
			
		||||
		"platformio.platformio-ide"
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	],
 | 
			
		||||
 | 
			
		||||
	// Use 'forwardPorts' to make a list of ports inside the container available locally.
 | 
			
		||||
	// "forwardPorts": [],
 | 
			
		||||
 | 
			
		||||
	// Use 'postCreateCommand' to run commands after the container is created.
 | 
			
		||||
	"postCreateCommand": "bash -i -c 'nvm install && npm ci'",
 | 
			
		||||
	"postCreateCommand": "npm install",
 | 
			
		||||
 | 
			
		||||
	// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
 | 
			
		||||
	"remoteUser": "vscode"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,2 @@
 | 
			
		||||
github: [Aircoookie,blazoncek,DedeHai,lost-hope,willmmiles]
 | 
			
		||||
github: [Aircoookie,blazoncek]
 | 
			
		||||
custom: ['https://paypal.me/Aircoookie','https://paypal.me/blazoncek']
 | 
			
		||||
thanks_dev: u/gh/netmindz
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							@@ -80,7 +80,7 @@ body:
 | 
			
		||||
    id: terms
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Code of Conduct
 | 
			
		||||
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/wled-dev/WLED/blob/main/CODE_OF_CONDUCT.md)
 | 
			
		||||
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
 | 
			
		||||
      options:
 | 
			
		||||
        - label: I agree to follow this project's Code of Conduct
 | 
			
		||||
          required: true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										172
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										172
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							@@ -1,172 +0,0 @@
 | 
			
		||||
# WLED - ESP32/ESP8266 LED Controller Firmware
 | 
			
		||||
 | 
			
		||||
WLED is a fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs and SPI-based chipsets. The project consists of C++ firmware for microcontrollers and a modern web interface.
 | 
			
		||||
 | 
			
		||||
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
 | 
			
		||||
 | 
			
		||||
## Working Effectively
 | 
			
		||||
 | 
			
		||||
### Initial Setup
 | 
			
		||||
- Install Node.js 20+ (specified in `.nvmrc`): Check your version with `node --version`
 | 
			
		||||
- Install dependencies: `npm ci` (takes ~5 seconds)
 | 
			
		||||
- Install PlatformIO for hardware builds: `pip install -r requirements.txt` (takes ~60 seconds)
 | 
			
		||||
 | 
			
		||||
### Build and Test Workflow
 | 
			
		||||
- **ALWAYS build web UI first**: `npm run build` -- takes 3 seconds. NEVER CANCEL.
 | 
			
		||||
- **Run tests**: `npm test` -- takes 40 seconds. NEVER CANCEL. Set timeout to 2+ minutes.
 | 
			
		||||
- **Development mode**: `npm run dev` -- monitors file changes and auto-rebuilds web UI
 | 
			
		||||
- **Hardware firmware build**: `pio run -e [environment]` -- takes 15+ minutes. NEVER CANCEL. Set timeout to 30+ minutes.
 | 
			
		||||
 | 
			
		||||
### Build Process Details
 | 
			
		||||
The build has two main phases:
 | 
			
		||||
1. **Web UI Generation** (`npm run build`):
 | 
			
		||||
   - Processes files in `wled00/data/` (HTML, CSS, JS)
 | 
			
		||||
   - Minifies and compresses web content 
 | 
			
		||||
   - Generates `wled00/html_*.h` files with embedded web content
 | 
			
		||||
   - **CRITICAL**: Must be done before any hardware build
 | 
			
		||||
 | 
			
		||||
2. **Hardware Compilation** (`pio run`):
 | 
			
		||||
   - Compiles C++ firmware for various ESP32/ESP8266 targets
 | 
			
		||||
   - Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`
 | 
			
		||||
   - List all targets: `pio run --list-targets`
 | 
			
		||||
 | 
			
		||||
## Before Finishing Work
 | 
			
		||||
 | 
			
		||||
**CRITICAL: You MUST complete ALL of these steps before marking your work as complete:**
 | 
			
		||||
 | 
			
		||||
1. **Run the test suite**: `npm test` -- Set timeout to 2+ minutes. NEVER CANCEL.
 | 
			
		||||
   - All tests MUST pass
 | 
			
		||||
   - If tests fail, fix the issue before proceeding
 | 
			
		||||
 | 
			
		||||
2. **Build at least one hardware environment**: `pio run -e esp32dev` -- Set timeout to 30+ minutes. NEVER CANCEL.
 | 
			
		||||
   - Choose `esp32dev` as it's a common, representative environment
 | 
			
		||||
   - See "Hardware Compilation" section above for the full list of common environments
 | 
			
		||||
   - The build MUST complete successfully without errors
 | 
			
		||||
   - If the build fails, fix the issue before proceeding
 | 
			
		||||
   - **DO NOT skip this step** - it validates that firmware compiles with your changes
 | 
			
		||||
 | 
			
		||||
3. **For web UI changes only**: Manually test the interface
 | 
			
		||||
   - See "Manual Testing Scenarios" section below
 | 
			
		||||
   - Verify the UI loads and functions correctly
 | 
			
		||||
 | 
			
		||||
**If any of these validation steps fail, you MUST fix the issues before finishing. Do NOT mark work as complete with failing builds or tests.**
 | 
			
		||||
 | 
			
		||||
## Validation and Testing
 | 
			
		||||
 | 
			
		||||
### Web UI Testing
 | 
			
		||||
- **ALWAYS validate web UI changes manually**:
 | 
			
		||||
  - Start local server: `cd wled00/data && python3 -m http.server 8080`
 | 
			
		||||
  - Open `http://localhost:8080/index.htm` in browser
 | 
			
		||||
  - Test basic functionality: color picker, effects, settings pages
 | 
			
		||||
- **Check for JavaScript errors** in browser console
 | 
			
		||||
 | 
			
		||||
### Code Validation
 | 
			
		||||
- **No automated linting configured** - follow existing code style in files you edit
 | 
			
		||||
- **Code style**: Use tabs for web files (.html/.css/.js), spaces (2 per level) for C++ files
 | 
			
		||||
- **C++ formatting available**: `clang-format` is installed but not in CI
 | 
			
		||||
- **Always run tests before finishing**: `npm test`
 | 
			
		||||
- **MANDATORY: Always run a hardware build before finishing** (see "Before Finishing Work" section below)
 | 
			
		||||
 | 
			
		||||
### Manual Testing Scenarios
 | 
			
		||||
After making changes to web UI, always test:
 | 
			
		||||
- **Load main interface**: Verify index.htm loads without errors
 | 
			
		||||
- **Navigation**: Test switching between main page and settings pages
 | 
			
		||||
- **Color controls**: Verify color picker and brightness controls work
 | 
			
		||||
- **Effects**: Test effect selection and parameter changes
 | 
			
		||||
- **Settings**: Test form submission and validation
 | 
			
		||||
 | 
			
		||||
## Common Tasks
 | 
			
		||||
 | 
			
		||||
### Repository Structure
 | 
			
		||||
```
 | 
			
		||||
wled00/                 # Main firmware source (C++)
 | 
			
		||||
  ├── data/            # Web interface files 
 | 
			
		||||
  │   ├── index.htm    # Main UI
 | 
			
		||||
  │   ├── settings*.htm # Settings pages
 | 
			
		||||
  │   └── *.js/*.css   # Frontend resources
 | 
			
		||||
  ├── *.cpp/*.h        # Firmware source files
 | 
			
		||||
  └── html_*.h         # Generated embedded web files (DO NOT EDIT)
 | 
			
		||||
tools/                 # Build tools (Node.js)
 | 
			
		||||
  ├── cdata.js         # Web UI build script
 | 
			
		||||
  └── cdata-test.js    # Test suite
 | 
			
		||||
platformio.ini         # Hardware build configuration
 | 
			
		||||
package.json           # Node.js dependencies and scripts
 | 
			
		||||
.github/workflows/     # CI/CD pipelines
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Key Files and Their Purpose
 | 
			
		||||
- `wled00/data/index.htm` - Main web interface
 | 
			
		||||
- `wled00/data/settings*.htm` - Configuration pages  
 | 
			
		||||
- `tools/cdata.js` - Converts web files to C++ headers
 | 
			
		||||
- `wled00/wled.h` - Main firmware configuration
 | 
			
		||||
- `platformio.ini` - Hardware build targets and settings
 | 
			
		||||
 | 
			
		||||
### Development Workflow
 | 
			
		||||
1. **For web UI changes**:
 | 
			
		||||
   - Edit files in `wled00/data/`
 | 
			
		||||
   - Run `npm run build` to regenerate headers
 | 
			
		||||
   - Test with local HTTP server
 | 
			
		||||
   - Run `npm test` to validate build system
 | 
			
		||||
 | 
			
		||||
2. **For firmware changes**:
 | 
			
		||||
   - Edit files in `wled00/` (but NOT `html_*.h` files)
 | 
			
		||||
   - Ensure web UI is built first (`npm run build`)
 | 
			
		||||
   - Build firmware: `pio run -e [target]`
 | 
			
		||||
   - Flash to device: `pio run -e [target] --target upload`
 | 
			
		||||
 | 
			
		||||
3. **For both web and firmware**:
 | 
			
		||||
   - Always build web UI first
 | 
			
		||||
   - Test web interface manually
 | 
			
		||||
   - Build and test firmware if making firmware changes
 | 
			
		||||
 | 
			
		||||
## Build Timing and Timeouts
 | 
			
		||||
 | 
			
		||||
**IMPORTANT: Use these timeout values when running builds:**
 | 
			
		||||
 | 
			
		||||
- **Web UI build** (`npm run build`): 3 seconds typical - Set timeout to 30 seconds minimum
 | 
			
		||||
- **Test suite** (`npm test`): 40 seconds typical - Set timeout to 120 seconds (2 minutes) minimum  
 | 
			
		||||
- **Hardware builds** (`pio run -e [target]`): 15-20 minutes typical for first build - Set timeout to 1800 seconds (30 minutes) minimum
 | 
			
		||||
  - Subsequent builds are faster due to caching
 | 
			
		||||
  - First builds download toolchains and dependencies which takes significant time
 | 
			
		||||
- **NEVER CANCEL long-running builds** - PlatformIO downloads and compilation require patience
 | 
			
		||||
 | 
			
		||||
**When validating your changes before finishing, you MUST wait for the hardware build to complete successfully. Set the timeout appropriately and be patient.**
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
### Common Issues
 | 
			
		||||
- **Build fails with missing html_*.h**: Run `npm run build` first
 | 
			
		||||
- **Web UI looks broken**: Check browser console for JavaScript errors
 | 
			
		||||
- **PlatformIO network errors**: Try again, downloads can be flaky
 | 
			
		||||
- **Node.js version issues**: Ensure Node.js 20+ is installed (check `.nvmrc`)
 | 
			
		||||
 | 
			
		||||
### When Things Go Wrong
 | 
			
		||||
- **Clear generated files**: `rm -f wled00/html_*.h` then rebuild
 | 
			
		||||
- **Force web UI rebuild**: `npm run build -- --force` or `npm run build -- -f`
 | 
			
		||||
- **Clean PlatformIO cache**: `pio run --target clean`
 | 
			
		||||
- **Reinstall dependencies**: `rm -rf node_modules && npm install`
 | 
			
		||||
 | 
			
		||||
## Important Notes
 | 
			
		||||
 | 
			
		||||
- **DO NOT edit `wled00/html_*.h` files** - they are auto-generated
 | 
			
		||||
- **Always commit both source files AND generated html_*.h files**
 | 
			
		||||
- **Web UI must be built before firmware compilation**
 | 
			
		||||
- **Test web interface manually after any web UI changes**
 | 
			
		||||
- **Use VS Code with PlatformIO extension for best development experience**
 | 
			
		||||
- **Hardware builds require appropriate ESP32/ESP8266 development board**
 | 
			
		||||
 | 
			
		||||
## CI/CD Pipeline
 | 
			
		||||
 | 
			
		||||
**The GitHub Actions CI workflow will:**
 | 
			
		||||
1. Installs Node.js and Python dependencies
 | 
			
		||||
2. Runs `npm test` to validate build system (MUST pass)
 | 
			
		||||
3. Builds web UI with `npm run build` (automatically run by PlatformIO)
 | 
			
		||||
4. Compiles firmware for ALL hardware targets listed in `default_envs` (MUST succeed for all)
 | 
			
		||||
5. Uploads build artifacts
 | 
			
		||||
 | 
			
		||||
**To ensure CI success, you MUST locally:**
 | 
			
		||||
- Run `npm test` and ensure it passes
 | 
			
		||||
- Run `pio run -e esp32dev` (or another common environment from "Hardware Compilation" section) and ensure it completes successfully
 | 
			
		||||
- If either fails locally, it WILL fail in CI
 | 
			
		||||
 | 
			
		||||
**Match this workflow in your local development to ensure CI success. Do not mark work complete until you have validated builds locally.**
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@@ -26,7 +26,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build Environments
 | 
			
		||||
    name: Build Enviornments
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: get_default_envs
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -38,12 +38,8 @@ jobs:
 | 
			
		||||
    - name: Set up Node.js
 | 
			
		||||
      uses: actions/setup-node@v4
 | 
			
		||||
      with:
 | 
			
		||||
        node-version-file: '.nvmrc'
 | 
			
		||||
        cache: 'npm'
 | 
			
		||||
    - run: |
 | 
			
		||||
        npm ci
 | 
			
		||||
        VERSION=`date +%y%m%d0`
 | 
			
		||||
        sed -i -r -e "s/define VERSION .+/define VERSION $VERSION/" wled00/wled.h
 | 
			
		||||
    - run: npm ci
 | 
			
		||||
    - name: Cache PlatformIO
 | 
			
		||||
      uses: actions/cache@v4
 | 
			
		||||
      with:
 | 
			
		||||
@@ -60,7 +56,6 @@ jobs:
 | 
			
		||||
          cache: 'pip'
 | 
			
		||||
    - name: Install PlatformIO
 | 
			
		||||
      run: pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
    - name: Build firmware
 | 
			
		||||
      run: pio run -e ${{ matrix.environment }}
 | 
			
		||||
    - uses: actions/upload-artifact@v4
 | 
			
		||||
@@ -79,7 +74,7 @@ jobs:
 | 
			
		||||
      - name: Use Node.js
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version-file: '.nvmrc'
 | 
			
		||||
          node-version: '20.x'
 | 
			
		||||
          cache: 'npm'
 | 
			
		||||
      - run: npm ci
 | 
			
		||||
      - run: npm test
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,49 +0,0 @@
 | 
			
		||||
 | 
			
		||||
name: Deploy Nightly
 | 
			
		||||
on:
 | 
			
		||||
  # This can be used to automatically publish nightlies at UTC nighttime
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '0 2 * * *' # run at 2 AM UTC
 | 
			
		||||
  # This can be used to allow manually triggering nightlies from the web interface
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  wled_build:
 | 
			
		||||
    uses: ./.github/workflows/build.yml
 | 
			
		||||
  nightly:
 | 
			
		||||
    name: Deploy nightly
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: wled_build
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Download artifacts
 | 
			
		||||
        uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
      - name: Show Files
 | 
			
		||||
        run: ls -la
 | 
			
		||||
      - name: "✏️ Generate release changelog"
 | 
			
		||||
        id: changelog
 | 
			
		||||
        uses: janheinrichmerker/action-github-changelog-generator@v2.3
 | 
			
		||||
        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:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        with:
 | 
			
		||||
          tag_name: nightly
 | 
			
		||||
          name: 'Nightly Release $$'
 | 
			
		||||
          prerelease: true
 | 
			
		||||
          body: ${{ steps.changelog.outputs.changelog }}
 | 
			
		||||
          files: |
 | 
			
		||||
            *.bin
 | 
			
		||||
            *.bin.gz
 | 
			
		||||
      - name: Repository Dispatch
 | 
			
		||||
        uses: peter-evans/repository-dispatch@v3
 | 
			
		||||
        with:
 | 
			
		||||
          repository: wled/WLED-WebInstaller
 | 
			
		||||
          event-type: release-nightly
 | 
			
		||||
          token: ${{ secrets.PAT_PUBLIC }}
 | 
			
		||||
							
								
								
									
										38
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
								
							@@ -1,38 +0,0 @@
 | 
			
		||||
    name: Notify Discord on PR Merge
 | 
			
		||||
    on:
 | 
			
		||||
      workflow_dispatch:
 | 
			
		||||
      pull_request_target:
 | 
			
		||||
        types: [closed]
 | 
			
		||||
 | 
			
		||||
    jobs:
 | 
			
		||||
      notify:
 | 
			
		||||
        runs-on: ubuntu-latest
 | 
			
		||||
        if: github.event.pull_request.merged == true
 | 
			
		||||
        steps:
 | 
			
		||||
        - name: Get User Permission
 | 
			
		||||
          id: checkAccess
 | 
			
		||||
          uses: actions-cool/check-user-permission@v2
 | 
			
		||||
          with:
 | 
			
		||||
            require: write
 | 
			
		||||
            username: ${{ github.triggering_actor }}
 | 
			
		||||
          env:
 | 
			
		||||
            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        - name: Check User Permission
 | 
			
		||||
          if: steps.checkAccess.outputs.require-result == 'false'
 | 
			
		||||
          run: |
 | 
			
		||||
            echo "${{ github.triggering_actor }} does not have permissions on this repo."
 | 
			
		||||
            echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}"
 | 
			
		||||
            echo "Job originally triggered by ${{ github.actor }}"
 | 
			
		||||
            exit 1
 | 
			
		||||
        - name: Send Discord notification
 | 
			
		||||
          env:
 | 
			
		||||
            PR_NUMBER: ${{ github.event.pull_request.number }}
 | 
			
		||||
            PR_TITLE: ${{ github.event.pull_request.title }}
 | 
			
		||||
            PR_URL: ${{ github.event.pull_request.html_url }}
 | 
			
		||||
            ACTOR: ${{ github.actor }}
 | 
			
		||||
          run: |
 | 
			
		||||
            jq -n \
 | 
			
		||||
              --arg content "Pull Request #${PR_NUMBER} \"${PR_TITLE}\" merged by ${ACTOR}
 | 
			
		||||
            ${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 }}
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,19 +18,9 @@ jobs:
 | 
			
		||||
    - uses: actions/download-artifact@v4
 | 
			
		||||
      with:
 | 
			
		||||
        merge-multiple: true
 | 
			
		||||
    - name: "✏️ Generate release changelog"
 | 
			
		||||
      id: changelog
 | 
			
		||||
      uses: janheinrichmerker/action-github-changelog-generator@v2.3
 | 
			
		||||
      with:
 | 
			
		||||
          token: ${{ secrets.GITHUB_TOKEN }} 
 | 
			
		||||
          sinceTag: v0.15.0
 | 
			
		||||
          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:
 | 
			
		||||
        body: ${{ steps.changelog.outputs.changelog }}
 | 
			
		||||
        draft: True
 | 
			
		||||
        files: |
 | 
			
		||||
          *.bin
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								.github/workflows/test.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/test.yaml
									
									
									
									
										vendored
									
									
								
							@@ -1,13 +0,0 @@
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  dispatch:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Repository Dispatch
 | 
			
		||||
        uses: peter-evans/repository-dispatch@v3
 | 
			
		||||
        with:
 | 
			
		||||
          repository: wled/WLED-WebInstaller
 | 
			
		||||
          event-type: release-nightly
 | 
			
		||||
          token: ${{ secrets.PAT_PUBLIC }}
 | 
			
		||||
							
								
								
									
										74
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										74
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,74 +0,0 @@
 | 
			
		||||
name: Usermod CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - usermods/**
 | 
			
		||||
      - .github/workflows/usermods.yml
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
      - usermods/**
 | 
			
		||||
    
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  get_usermod_envs:
 | 
			
		||||
    name: Gather Usermods
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: actions/setup-python@v5
 | 
			
		||||
      with:
 | 
			
		||||
        python-version: '3.12'
 | 
			
		||||
        cache: 'pip'
 | 
			
		||||
    - name: Install PlatformIO
 | 
			
		||||
      run: pip install -r requirements.txt
 | 
			
		||||
    - name: Get default environments
 | 
			
		||||
      id: envs
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT
 | 
			
		||||
    outputs:
 | 
			
		||||
      usermods: ${{ steps.envs.outputs.usermods }}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build Enviornments
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: get_usermod_envs
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        usermod: ${{ fromJSON(needs.get_usermod_envs.outputs.usermods) }}
 | 
			
		||||
        environment: [usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3]
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Set up Node.js
 | 
			
		||||
      uses: actions/setup-node@v4
 | 
			
		||||
      with:
 | 
			
		||||
        node-version-file: '.nvmrc'
 | 
			
		||||
        cache: 'npm'
 | 
			
		||||
    - run: npm ci
 | 
			
		||||
    - name: Cache PlatformIO
 | 
			
		||||
      uses: actions/cache@v4
 | 
			
		||||
      with:
 | 
			
		||||
        path: |
 | 
			
		||||
              ~/.platformio/.cache
 | 
			
		||||
              ~/.buildcache
 | 
			
		||||
              build_output
 | 
			
		||||
        key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
 | 
			
		||||
        restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
 | 
			
		||||
    - name: Set up Python
 | 
			
		||||
      uses: actions/setup-python@v5
 | 
			
		||||
      with:
 | 
			
		||||
          python-version: '3.12'
 | 
			
		||||
          cache: 'pip'
 | 
			
		||||
    - name: Install PlatformIO
 | 
			
		||||
      run: pip install -r requirements.txt
 | 
			
		||||
    - name: Add usermods environment
 | 
			
		||||
      run: |
 | 
			
		||||
        cp -v usermods/platformio_override.usermods.ini platformio_override.ini
 | 
			
		||||
        echo >> platformio_override.ini
 | 
			
		||||
        echo  "custom_usermods = ${{ matrix.usermod }}" >> platformio_override.ini
 | 
			
		||||
        cat platformio_override.ini
 | 
			
		||||
 | 
			
		||||
    - name: Build firmware
 | 
			
		||||
      run: pio run -e ${{ matrix.environment }}    
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,8 +7,6 @@
 | 
			
		||||
.pioenvs
 | 
			
		||||
.piolibdeps
 | 
			
		||||
.vscode
 | 
			
		||||
compile_commands.json
 | 
			
		||||
__pycache__/
 | 
			
		||||
 | 
			
		||||
esp01-update.sh
 | 
			
		||||
platformio_override.ini
 | 
			
		||||
@@ -17,7 +15,6 @@ wled-update.sh
 | 
			
		||||
 | 
			
		||||
/build_output/
 | 
			
		||||
/node_modules/
 | 
			
		||||
/logs/
 | 
			
		||||
 | 
			
		||||
/wled00/extLibs
 | 
			
		||||
/wled00/LittleFS
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,5 +1,23 @@
 | 
			
		||||
## WLED changelog
 | 
			
		||||
 | 
			
		||||
#### Build 2412100
 | 
			
		||||
-   WLED 0.15.0 release
 | 
			
		||||
-   Usermod BME280: Fix "Unit of Measurement" for temperature
 | 
			
		||||
-   WiFi reconnect bugfix (@blazoncek)
 | 
			
		||||
 | 
			
		||||
#### Build 2411250
 | 
			
		||||
-   WLED 0.15.0-rc1 release
 | 
			
		||||
-   Add support for esp32S3_wroom2 (#4243 by @softhack007)
 | 
			
		||||
-   Fix mixed LED SK6812 and ws2812b booloop (#4301 by @willmmiles)
 | 
			
		||||
-   Improved FPS calculation (by DedeHai)
 | 
			
		||||
-   Fix crashes when using HTTP API within MQTT (#4269 by @willmmiles)
 | 
			
		||||
-   Fix array overflow in exploding_fireworks (#4120 by @willmmiles)
 | 
			
		||||
-   Fix MQTT topic buffer length (#4293 by @WouterGritter)
 | 
			
		||||
-   Fix SparkFunDMX fix for possible array bounds violation in DMX.write (by @softhack007)
 | 
			
		||||
-   Allow TV Simulator on single LED segments (by @softhack007)
 | 
			
		||||
-   Fix WLED_RELEASE_NAME (by @netmindz)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Build 2410270
 | 
			
		||||
-   WLED 0.15.0-b7 release
 | 
			
		||||
-   Re-license the WLED project from MIT to EUPL (#4194 by @Aircoookie)
 | 
			
		||||
@@ -173,7 +191,7 @@
 | 
			
		||||
-   v0.15.0-b2
 | 
			
		||||
-   WS2805 support (RGB + WW + CW, 600kbps)
 | 
			
		||||
-   Unified PSRAM use
 | 
			
		||||
-   NeoPixelBus v2.7.9 (for future WS2805 support)
 | 
			
		||||
-   NeoPixelBus v2.7.9
 | 
			
		||||
-   Ubiquitous PSRAM mode for all variants of ESP32
 | 
			
		||||
-   SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC)
 | 
			
		||||
-   Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ A good description helps us to review and understand your proposed changes. For
 | 
			
		||||
 | 
			
		||||
### Target branch for pull requests
 | 
			
		||||
 | 
			
		||||
Please make all PRs against the `main` branch.
 | 
			
		||||
Please make all PRs against the `0_15` branch.
 | 
			
		||||
 | 
			
		||||
### Updating your code
 | 
			
		||||
While the PR is open - and under review by maintainers - you may be asked to modify your PR source code.
 | 
			
		||||
@@ -27,7 +27,7 @@ Github will pick up the changes so your PR stays up-to-date.
 | 
			
		||||
> For example, we regularly lost review comments when the PR author force-pushes code changes. So, pretty please, do not force-push.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
You can find a collection of very useful tips and tricks here: https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR
 | 
			
		||||
You can find a collection of very useful tips and tricks here: https://github.com/Aircoookie/WLED/wiki/How-to-properly-submit-a-PR
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Code style
 | 
			
		||||
 
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "build": {
 | 
			
		||||
        "arduino":{
 | 
			
		||||
            "ldscript": "esp32s3_out.ld",
 | 
			
		||||
            "memory_type": "qio_opi",
 | 
			
		||||
            "partitions": "default_16MB.csv"
 | 
			
		||||
        },
 | 
			
		||||
        "core": "esp32",
 | 
			
		||||
        "extra_flags": [
 | 
			
		||||
			"-DARDUINO_TTGO_T7_S3",
 | 
			
		||||
            "-DBOARD_HAS_PSRAM",
 | 
			
		||||
            "-DARDUINO_USB_MODE=1"
 | 
			
		||||
        ],
 | 
			
		||||
        "f_cpu": "240000000L",
 | 
			
		||||
        "f_flash": "80000000L",
 | 
			
		||||
        "flash_mode": "qio",
 | 
			
		||||
        "hwids": [
 | 
			
		||||
            [
 | 
			
		||||
                "0X303A",
 | 
			
		||||
                "0x1001"
 | 
			
		||||
            ]
 | 
			
		||||
        ],
 | 
			
		||||
        "mcu": "esp32s3",
 | 
			
		||||
        "variant": "esp32s3"
 | 
			
		||||
    },
 | 
			
		||||
    "connectivity": [
 | 
			
		||||
        "wifi",
 | 
			
		||||
        "bluetooth"
 | 
			
		||||
    ],
 | 
			
		||||
    "debug": {
 | 
			
		||||
        "openocd_target": "esp32s3.cfg"
 | 
			
		||||
    },
 | 
			
		||||
    "frameworks": [
 | 
			
		||||
        "arduino",
 | 
			
		||||
        "espidf"
 | 
			
		||||
    ],
 | 
			
		||||
    "name": "LILYGO T3-S3",
 | 
			
		||||
    "upload": {
 | 
			
		||||
        "flash_size": "16MB",
 | 
			
		||||
        "maximum_ram_size": 327680,
 | 
			
		||||
        "maximum_size": 16777216,
 | 
			
		||||
        "require_upload_port": true,
 | 
			
		||||
        "speed": 921600
 | 
			
		||||
    },
 | 
			
		||||
    "url": "https://www.aliexpress.us/item/3256804591247074.html",
 | 
			
		||||
    "vendor": "LILYGO"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "build": {
 | 
			
		||||
      "arduino": {
 | 
			
		||||
        "ldscript": "esp32s3_out.ld",
 | 
			
		||||
        "memory_type": "qio_qspi"
 | 
			
		||||
      },
 | 
			
		||||
      "core": "esp32",
 | 
			
		||||
      "extra_flags": [
 | 
			
		||||
        "-DBOARD_HAS_PSRAM",
 | 
			
		||||
        "-DARDUINO_LOLIN_S3_MINI",
 | 
			
		||||
        "-DARDUINO_USB_MODE=1"
 | 
			
		||||
      ],
 | 
			
		||||
      "f_cpu": "240000000L",
 | 
			
		||||
      "f_flash": "80000000L",
 | 
			
		||||
      "flash_mode": "qio",
 | 
			
		||||
      "hwids": [
 | 
			
		||||
        [
 | 
			
		||||
          "0x303A",
 | 
			
		||||
          "0x8167"
 | 
			
		||||
        ]
 | 
			
		||||
      ],
 | 
			
		||||
      "mcu": "esp32s3",
 | 
			
		||||
      "variant": "lolin_s3_mini"
 | 
			
		||||
    },
 | 
			
		||||
    "connectivity": [
 | 
			
		||||
      "bluetooth",
 | 
			
		||||
      "wifi"
 | 
			
		||||
    ],
 | 
			
		||||
    "debug": {
 | 
			
		||||
      "openocd_target": "esp32s3.cfg"
 | 
			
		||||
    },
 | 
			
		||||
    "frameworks": [
 | 
			
		||||
      "arduino",
 | 
			
		||||
      "espidf"
 | 
			
		||||
    ],
 | 
			
		||||
    "name": "WEMOS LOLIN S3 Mini",
 | 
			
		||||
    "upload": {
 | 
			
		||||
      "flash_size": "4MB",
 | 
			
		||||
      "maximum_ram_size": 327680,
 | 
			
		||||
      "maximum_size": 4194304,
 | 
			
		||||
      "require_upload_port": true,
 | 
			
		||||
      "speed": 460800
 | 
			
		||||
    },
 | 
			
		||||
    "url": "https://www.wemos.cc/en/latest/s3/index.html",
 | 
			
		||||
    "vendor": "WEMOS"
 | 
			
		||||
}
 | 
			
		||||
  
 | 
			
		||||
@@ -1,504 +0,0 @@
 | 
			
		||||
/* esp8266_waveform imported from platform source code
 | 
			
		||||
   Modified for WLED to work around a fault in the NMI handling,
 | 
			
		||||
   which can result in the system locking up and hard WDT crashes.
 | 
			
		||||
 | 
			
		||||
   Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_phase.cpp
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  esp8266_waveform - General purpose waveform generation and control,
 | 
			
		||||
                     supporting outputs on all pins in parallel.
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2018 Earle F. Philhower, III.  All rights reserved.
 | 
			
		||||
  Copyright (c) 2020 Dirk O. Kaar.
 | 
			
		||||
 | 
			
		||||
  The core idea is to have a programmable waveform generator with a unique
 | 
			
		||||
  high and low period (defined in microseconds or CPU clock cycles).  TIMER1 is
 | 
			
		||||
  set to 1-shot mode and is always loaded with the time until the next edge
 | 
			
		||||
  of any live waveforms.
 | 
			
		||||
 | 
			
		||||
  Up to one waveform generator per pin supported.
 | 
			
		||||
 | 
			
		||||
  Each waveform generator is synchronized to the ESP clock cycle counter, not the
 | 
			
		||||
  timer.  This allows for removing interrupt jitter and delay as the counter
 | 
			
		||||
  always increments once per 80MHz clock.  Changes to a waveform are
 | 
			
		||||
  contiguous and only take effect on the next waveform transition,
 | 
			
		||||
  allowing for smooth transitions.
 | 
			
		||||
 | 
			
		||||
  This replaces older tone(), analogWrite(), and the Servo classes.
 | 
			
		||||
 | 
			
		||||
  Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount()
 | 
			
		||||
  clock cycle time, or an interval measured in clock cycles, but not TIMER1
 | 
			
		||||
  cycles (which may be 2 CPU clock cycles @ 160MHz).
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "core_esp8266_waveform.h"
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "ets_sys.h"
 | 
			
		||||
#include <atomic>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// ----- @willmmiles begin patch -----
 | 
			
		||||
// Linker magic
 | 
			
		||||
extern "C" void usePWMFixedNMI(void) {};
 | 
			
		||||
 | 
			
		||||
// NMI crash workaround
 | 
			
		||||
// Sometimes the NMI fails to return, stalling the CPU.  When this happens,
 | 
			
		||||
// the next NMI gets a return address /inside the NMI handler function/.
 | 
			
		||||
// We work around this by caching the last NMI return address, and restoring
 | 
			
		||||
// the epc3 and eps3 registers to the previous values if the observed epc3
 | 
			
		||||
// happens to be pointing to the _NMILevelVector function.
 | 
			
		||||
extern "C" void _NMILevelVector();
 | 
			
		||||
extern "C" void _UserExceptionVector_1(); // the next function after _NMILevelVector
 | 
			
		||||
static inline IRAM_ATTR void nmiCrashWorkaround() {
 | 
			
		||||
  static uintptr_t epc3_backup, eps3_backup;
 | 
			
		||||
 | 
			
		||||
  uintptr_t epc3, eps3;
 | 
			
		||||
  __asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3));
 | 
			
		||||
  if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) {
 | 
			
		||||
    // Address is good; save backup
 | 
			
		||||
    epc3_backup = epc3;
 | 
			
		||||
    eps3_backup = eps3;
 | 
			
		||||
  } else {
 | 
			
		||||
    // Address is inside the NMI handler -- restore from backup
 | 
			
		||||
    __asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// ----- @willmmiles end patch -----
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// No-op calls to override the PWM implementation
 | 
			
		||||
extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; }
 | 
			
		||||
extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; }
 | 
			
		||||
extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
 | 
			
		||||
constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160;
 | 
			
		||||
// Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz
 | 
			
		||||
constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000);
 | 
			
		||||
// Maximum servicing time for any single IRQ
 | 
			
		||||
constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18);
 | 
			
		||||
// The latency between in-ISR rearming of the timer and the earliest firing
 | 
			
		||||
constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2);
 | 
			
		||||
// The SDK and hardware take some time to actually get to our NMI code
 | 
			
		||||
constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ?
 | 
			
		||||
  microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
 | 
			
		||||
 | 
			
		||||
// for INFINITE, the NMI proceeds on the waveform without expiry deadline.
 | 
			
		||||
// for EXPIRES, the NMI expires the waveform automatically on the expiry ccy.
 | 
			
		||||
// for UPDATEEXPIRY, the NMI recomputes the exact expiry ccy and transitions to EXPIRES.
 | 
			
		||||
// for UPDATEPHASE, the NMI recomputes the target timings
 | 
			
		||||
// for INIT, the NMI initializes nextPeriodCcy, and if expiryCcy != 0 includes UPDATEEXPIRY.
 | 
			
		||||
enum class WaveformMode : uint8_t {INFINITE = 0, EXPIRES = 1, UPDATEEXPIRY = 2, UPDATEPHASE = 3, INIT = 4};
 | 
			
		||||
 | 
			
		||||
// Waveform generator can create tones, PWM, and servos
 | 
			
		||||
typedef struct {
 | 
			
		||||
  uint32_t nextPeriodCcy; // ESP clock cycle when a period begins.
 | 
			
		||||
  uint32_t endDutyCcy;    // ESP clock cycle when going from duty to off
 | 
			
		||||
  int32_t dutyCcys;       // Set next off cycle at low->high to maintain phase
 | 
			
		||||
  int32_t adjDutyCcys;    // Temporary correction for next period
 | 
			
		||||
  int32_t periodCcys;     // Set next phase cycle at low->high to maintain phase
 | 
			
		||||
  uint32_t expiryCcy;     // For time-limited waveform, the CPU clock cycle when this waveform must stop. If WaveformMode::UPDATE, temporarily holds relative ccy count
 | 
			
		||||
  WaveformMode mode;
 | 
			
		||||
  bool autoPwm;           // perform PWM duty to idle cycle ratio correction under high load at the expense of precise timings
 | 
			
		||||
} Waveform;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
  static struct {
 | 
			
		||||
    Waveform pins[17];             // State of all possible pins
 | 
			
		||||
    uint32_t states = 0;           // Is the pin high or low, updated in NMI so no access outside the NMI code
 | 
			
		||||
    uint32_t enabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
 | 
			
		||||
 | 
			
		||||
    // Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine
 | 
			
		||||
    int32_t toSetBits = 0;     // Message to the NMI handler to start/modify exactly one waveform
 | 
			
		||||
    int32_t toDisableBits = 0; // Message to the NMI handler to disable exactly one pin from waveform generation
 | 
			
		||||
 | 
			
		||||
    // toSetBits temporaries
 | 
			
		||||
    // cheaper than packing them in every Waveform, since we permit only one use at a time
 | 
			
		||||
    uint32_t phaseCcy;      // positive phase offset ccy count  
 | 
			
		||||
    int8_t alignPhase;      // < 0 no phase alignment, otherwise starts waveform in relative phase offset to given pin
 | 
			
		||||
 | 
			
		||||
    uint32_t(*timer1CB)() = nullptr;
 | 
			
		||||
 | 
			
		||||
    bool timer1Running = false;
 | 
			
		||||
 | 
			
		||||
    uint32_t nextEventCcy;
 | 
			
		||||
  } waveform;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Interrupt on/off control
 | 
			
		||||
static IRAM_ATTR void timer1Interrupt();
 | 
			
		||||
 | 
			
		||||
// Non-speed critical bits
 | 
			
		||||
#pragma GCC optimize ("Os")
 | 
			
		||||
 | 
			
		||||
static void initTimer() {
 | 
			
		||||
  timer1_disable();
 | 
			
		||||
  ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
 | 
			
		||||
  ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
 | 
			
		||||
  timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
 | 
			
		||||
  waveform.timer1Running = true;
 | 
			
		||||
  timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void IRAM_ATTR deinitTimer() {
 | 
			
		||||
  ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
 | 
			
		||||
  timer1_disable();
 | 
			
		||||
  timer1_isr_init();
 | 
			
		||||
  waveform.timer1Running = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
 | 
			
		||||
// Set a callback.  Pass in NULL to stop it
 | 
			
		||||
void setTimer1Callback_weak(uint32_t (*fn)()) {
 | 
			
		||||
  waveform.timer1CB = fn;
 | 
			
		||||
  std::atomic_thread_fence(std::memory_order_acq_rel);
 | 
			
		||||
  if (!waveform.timer1Running && fn) {
 | 
			
		||||
    initTimer();
 | 
			
		||||
  } else if (waveform.timer1Running && !fn && !waveform.enabled) {
 | 
			
		||||
    deinitTimer();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start up a waveform on a pin, or change the current one.  Will change to the new
 | 
			
		||||
// waveform smoothly on next low->high transition.  For immediate change, stopWaveform()
 | 
			
		||||
// first, then it will immediately begin.
 | 
			
		||||
int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
 | 
			
		||||
  uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) {
 | 
			
		||||
  uint32_t periodCcys = highCcys + lowCcys;
 | 
			
		||||
  if (periodCcys < MAXIRQTICKSCCYS) {
 | 
			
		||||
    if (!highCcys) {
 | 
			
		||||
      periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
 | 
			
		||||
    }
 | 
			
		||||
    else if (!lowCcys) {
 | 
			
		||||
      highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // sanity checks, including mixed signed/unsigned arithmetic safety
 | 
			
		||||
  if ((pin > 16) || isFlashInterfacePin(pin) || (alignPhase > 16) ||
 | 
			
		||||
    static_cast<int32_t>(periodCcys) <= 0 ||
 | 
			
		||||
    static_cast<int32_t>(highCcys) < 0 || static_cast<int32_t>(lowCcys) < 0) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  Waveform& wave = waveform.pins[pin];
 | 
			
		||||
  wave.dutyCcys = highCcys;
 | 
			
		||||
  wave.adjDutyCcys = 0;
 | 
			
		||||
  wave.periodCcys = periodCcys;
 | 
			
		||||
  wave.autoPwm = autoPwm;
 | 
			
		||||
  waveform.alignPhase = (alignPhase < 0) ? -1 : alignPhase;
 | 
			
		||||
  waveform.phaseCcy = phaseOffsetCcys;
 | 
			
		||||
 | 
			
		||||
  std::atomic_thread_fence(std::memory_order_acquire);
 | 
			
		||||
  const uint32_t pinBit = 1UL << pin;
 | 
			
		||||
  if (!(waveform.enabled & pinBit)) {
 | 
			
		||||
    // wave.nextPeriodCcy and wave.endDutyCcy are initialized by the ISR
 | 
			
		||||
    wave.expiryCcy = runTimeCcys; // in WaveformMode::INIT, temporarily hold relative cycle count
 | 
			
		||||
    wave.mode = WaveformMode::INIT;
 | 
			
		||||
    if (!wave.dutyCcys) {
 | 
			
		||||
      // If initially at zero duty cycle, force GPIO off
 | 
			
		||||
      if (pin == 16) {
 | 
			
		||||
        GP16O = 0;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        GPOC = pinBit;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
    waveform.toSetBits = 1UL << pin;
 | 
			
		||||
    std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
    if (!waveform.timer1Running) {
 | 
			
		||||
      initTimer();
 | 
			
		||||
    }
 | 
			
		||||
    else if (T1V > IRQLATENCYCCYS) {
 | 
			
		||||
      // Must not interfere if Timer is due shortly
 | 
			
		||||
      timer1_write(IRQLATENCYCCYS);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI
 | 
			
		||||
    std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
    if (runTimeCcys) {
 | 
			
		||||
      wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count
 | 
			
		||||
      wave.mode = WaveformMode::UPDATEEXPIRY;
 | 
			
		||||
      std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
      waveform.toSetBits = 1UL << pin;
 | 
			
		||||
    } else if (alignPhase >= 0) {
 | 
			
		||||
      // @willmmiles new feature
 | 
			
		||||
      wave.mode = WaveformMode::UPDATEPHASE; // recalculate start
 | 
			
		||||
      std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
      waveform.toSetBits = 1UL << pin;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  std::atomic_thread_fence(std::memory_order_acq_rel);
 | 
			
		||||
  while (waveform.toSetBits) {
 | 
			
		||||
    esp_yield(); // Wait for waveform to update
 | 
			
		||||
    std::atomic_thread_fence(std::memory_order_acquire);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stops a waveform on a pin
 | 
			
		||||
IRAM_ATTR int stopWaveform_weak(uint8_t pin) {
 | 
			
		||||
  // Can't possibly need to stop anything if there is no timer active
 | 
			
		||||
  if (!waveform.timer1Running) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // If user sends in a pin >16 but <32, this will always point to a 0 bit
 | 
			
		||||
  // If they send >=32, then the shift will result in 0 and it will also return false
 | 
			
		||||
  std::atomic_thread_fence(std::memory_order_acquire);
 | 
			
		||||
  const uint32_t pinBit = 1UL << pin;
 | 
			
		||||
  if (waveform.enabled & pinBit) {
 | 
			
		||||
    waveform.toDisableBits = 1UL << pin;
 | 
			
		||||
    std::atomic_thread_fence(std::memory_order_release);
 | 
			
		||||
    // Must not interfere if Timer is due shortly
 | 
			
		||||
    if (T1V > IRQLATENCYCCYS) {
 | 
			
		||||
      timer1_write(IRQLATENCYCCYS);
 | 
			
		||||
    }
 | 
			
		||||
    while (waveform.toDisableBits) {
 | 
			
		||||
      /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
 | 
			
		||||
      std::atomic_thread_fence(std::memory_order_acquire);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!waveform.enabled && !waveform.timer1CB) {
 | 
			
		||||
    deinitTimer();
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Speed critical bits
 | 
			
		||||
#pragma GCC optimize ("O2")
 | 
			
		||||
 | 
			
		||||
// For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted.
 | 
			
		||||
// Using constexpr makes sure that the CPU clock frequency is compile-time fixed.
 | 
			
		||||
static inline IRAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) {
 | 
			
		||||
  if (ISCPUFREQ160MHZ) {
 | 
			
		||||
    return isCPU2X ? ccys : (ccys >> 1);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    return isCPU2X ? (ccys << 1) : ccys;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static IRAM_ATTR void timer1Interrupt() {
 | 
			
		||||
  const uint32_t isrStartCcy = ESP.getCycleCount();
 | 
			
		||||
  //int32_t clockDrift = isrStartCcy - waveform.nextEventCcy;
 | 
			
		||||
 | 
			
		||||
  // ----- @willmmiles begin patch -----
 | 
			
		||||
  nmiCrashWorkaround();
 | 
			
		||||
  // ----- @willmmiles end patch -----
 | 
			
		||||
 | 
			
		||||
  const bool isCPU2X = CPU2X & 1;
 | 
			
		||||
  if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) {
 | 
			
		||||
    // Handle enable/disable requests from main app.
 | 
			
		||||
    waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off
 | 
			
		||||
    // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
 | 
			
		||||
    waveform.toDisableBits = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (waveform.toSetBits) {
 | 
			
		||||
    const int toSetPin = __builtin_ffs(waveform.toSetBits) - 1;
 | 
			
		||||
    Waveform& wave = waveform.pins[toSetPin];
 | 
			
		||||
    switch (wave.mode) {
 | 
			
		||||
    case WaveformMode::INIT:
 | 
			
		||||
      waveform.states &= ~waveform.toSetBits; // Clear the state of any just started
 | 
			
		||||
      if (waveform.alignPhase >= 0 && waveform.enabled & (1UL << waveform.alignPhase)) {
 | 
			
		||||
        wave.nextPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        wave.nextPeriodCcy = waveform.nextEventCcy;
 | 
			
		||||
      }
 | 
			
		||||
      if (!wave.expiryCcy) {
 | 
			
		||||
        wave.mode = WaveformMode::INFINITE;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      // fall through
 | 
			
		||||
    case WaveformMode::UPDATEEXPIRY:
 | 
			
		||||
      // in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
 | 
			
		||||
      wave.expiryCcy = wave.nextPeriodCcy + scaleCcys(wave.expiryCcy, isCPU2X);
 | 
			
		||||
      wave.mode = WaveformMode::EXPIRES;
 | 
			
		||||
      break;
 | 
			
		||||
    // @willmmiles new feature
 | 
			
		||||
    case WaveformMode::UPDATEPHASE:
 | 
			
		||||
      // in WaveformMode::UPDATEPHASE, we recalculate the targets
 | 
			
		||||
      if ((waveform.alignPhase >= 0) && (waveform.enabled & (1UL << waveform.alignPhase))) {
 | 
			
		||||
        // Compute phase shift to realign with target
 | 
			
		||||
        auto const newPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
 | 
			
		||||
        auto const period = scaleCcys(wave.periodCcys, isCPU2X);
 | 
			
		||||
        auto shift = ((static_cast<int32_t> (newPeriodCcy - wave.nextPeriodCcy) + period/2) % period) - (period/2);
 | 
			
		||||
        wave.nextPeriodCcy += static_cast<uint32_t>(shift);
 | 
			
		||||
        if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
 | 
			
		||||
          wave.endDutyCcy = wave.nextPeriodCcy;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    waveform.toSetBits = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Exit the loop if the next event, if any, is sufficiently distant.
 | 
			
		||||
  const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
 | 
			
		||||
  uint32_t busyPins = waveform.enabled;
 | 
			
		||||
  waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
 | 
			
		||||
 | 
			
		||||
  uint32_t now = ESP.getCycleCount();
 | 
			
		||||
  uint32_t isrNextEventCcy = now;
 | 
			
		||||
  while (busyPins) {
 | 
			
		||||
    if (static_cast<int32_t>(isrNextEventCcy - now) > IRQLATENCYCCYS) {
 | 
			
		||||
      waveform.nextEventCcy = isrNextEventCcy;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    isrNextEventCcy = waveform.nextEventCcy;
 | 
			
		||||
    uint32_t loopPins = busyPins;
 | 
			
		||||
    while (loopPins) {
 | 
			
		||||
      const int pin = __builtin_ffsl(loopPins) - 1;
 | 
			
		||||
      const uint32_t pinBit = 1UL << pin;
 | 
			
		||||
      loopPins ^= pinBit;
 | 
			
		||||
 | 
			
		||||
      Waveform& wave = waveform.pins[pin];
 | 
			
		||||
 | 
			
		||||
/* @willmmiles - wtf?  We don't want to accumulate drift
 | 
			
		||||
      if (clockDrift) {
 | 
			
		||||
        wave.endDutyCcy += clockDrift;
 | 
			
		||||
        wave.nextPeriodCcy += clockDrift;
 | 
			
		||||
        wave.expiryCcy += clockDrift;
 | 
			
		||||
      }
 | 
			
		||||
*/          
 | 
			
		||||
 | 
			
		||||
      uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy;
 | 
			
		||||
      if (WaveformMode::EXPIRES == wave.mode &&
 | 
			
		||||
        static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) >= 0 &&
 | 
			
		||||
        static_cast<int32_t>(now - wave.expiryCcy) >= 0) {
 | 
			
		||||
        // Disable any waveforms that are done
 | 
			
		||||
        waveform.enabled ^= pinBit;
 | 
			
		||||
        busyPins ^= pinBit;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        const int32_t overshootCcys = now - waveNextEventCcy;
 | 
			
		||||
        if (overshootCcys >= 0) {
 | 
			
		||||
          const int32_t periodCcys = scaleCcys(wave.periodCcys, isCPU2X);
 | 
			
		||||
          if (waveform.states & pinBit) {
 | 
			
		||||
            // active configuration and forward are 100% duty
 | 
			
		||||
            if (wave.periodCcys == wave.dutyCcys) {
 | 
			
		||||
              wave.nextPeriodCcy += periodCcys;
 | 
			
		||||
              wave.endDutyCcy = wave.nextPeriodCcy;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              if (wave.autoPwm) {
 | 
			
		||||
                wave.adjDutyCcys += overshootCcys;
 | 
			
		||||
              }
 | 
			
		||||
              waveform.states ^= pinBit;
 | 
			
		||||
              if (16 == pin) {
 | 
			
		||||
                GP16O = 0;
 | 
			
		||||
              }
 | 
			
		||||
              else {
 | 
			
		||||
                GPOC = pinBit;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            waveNextEventCcy = wave.nextPeriodCcy;
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            wave.nextPeriodCcy += periodCcys;
 | 
			
		||||
            if (!wave.dutyCcys) {
 | 
			
		||||
              wave.endDutyCcy = wave.nextPeriodCcy;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              int32_t dutyCcys = scaleCcys(wave.dutyCcys, isCPU2X);
 | 
			
		||||
              if (dutyCcys <= wave.adjDutyCcys) {
 | 
			
		||||
                dutyCcys >>= 1;
 | 
			
		||||
                wave.adjDutyCcys -= dutyCcys;
 | 
			
		||||
              }
 | 
			
		||||
              else if (wave.adjDutyCcys) {
 | 
			
		||||
                dutyCcys -= wave.adjDutyCcys;
 | 
			
		||||
                wave.adjDutyCcys = 0;
 | 
			
		||||
              }
 | 
			
		||||
              wave.endDutyCcy = now + dutyCcys;
 | 
			
		||||
              if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
 | 
			
		||||
                wave.endDutyCcy = wave.nextPeriodCcy;
 | 
			
		||||
              }
 | 
			
		||||
              waveform.states |= pinBit;
 | 
			
		||||
              if (16 == pin) {
 | 
			
		||||
                GP16O = 1;
 | 
			
		||||
              }
 | 
			
		||||
              else {
 | 
			
		||||
                GPOS = pinBit;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            waveNextEventCcy = wave.endDutyCcy;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (WaveformMode::EXPIRES == wave.mode && static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) > 0) {
 | 
			
		||||
            waveNextEventCcy = wave.expiryCcy;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (static_cast<int32_t>(waveNextEventCcy - isrTimeoutCcy) >= 0) {
 | 
			
		||||
          busyPins ^= pinBit;
 | 
			
		||||
          if (static_cast<int32_t>(waveform.nextEventCcy - waveNextEventCcy) > 0) {
 | 
			
		||||
            waveform.nextEventCcy = waveNextEventCcy;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else if (static_cast<int32_t>(isrNextEventCcy - waveNextEventCcy) > 0) {
 | 
			
		||||
          isrNextEventCcy = waveNextEventCcy;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      now = ESP.getCycleCount();
 | 
			
		||||
    }
 | 
			
		||||
    //clockDrift = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t callbackCcys = 0;
 | 
			
		||||
  if (waveform.timer1CB) {
 | 
			
		||||
    callbackCcys = scaleCcys(waveform.timer1CB(), isCPU2X);
 | 
			
		||||
  }
 | 
			
		||||
  now = ESP.getCycleCount();
 | 
			
		||||
  int32_t nextEventCcys = waveform.nextEventCcy - now;
 | 
			
		||||
  // Account for unknown duration of timer1CB().
 | 
			
		||||
  if (waveform.timer1CB && nextEventCcys > callbackCcys) {
 | 
			
		||||
    waveform.nextEventCcy = now + callbackCcys;
 | 
			
		||||
    nextEventCcys = callbackCcys;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
 | 
			
		||||
  int32_t deltaIrqCcys = DELTAIRQCCYS;
 | 
			
		||||
  int32_t irqLatencyCcys = IRQLATENCYCCYS;
 | 
			
		||||
  if (isCPU2X) {
 | 
			
		||||
    nextEventCcys >>= 1;
 | 
			
		||||
    deltaIrqCcys >>= 1;
 | 
			
		||||
    irqLatencyCcys >>= 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Firing timer too soon, the NMI occurs before ISR has returned.
 | 
			
		||||
  if (nextEventCcys < irqLatencyCcys + deltaIrqCcys) {
 | 
			
		||||
    waveform.nextEventCcy = now + IRQLATENCYCCYS + DELTAIRQCCYS;
 | 
			
		||||
    nextEventCcys = irqLatencyCcys;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    nextEventCcys -= deltaIrqCcys;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Register access is fast and edge IRQ was configured before.
 | 
			
		||||
  T1L = nextEventCcys;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										717
									
								
								lib/ESP8266PWM/src/core_esp8266_waveform_pwm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										717
									
								
								lib/ESP8266PWM/src/core_esp8266_waveform_pwm.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,717 @@
 | 
			
		||||
/* esp8266_waveform imported from platform source code
 | 
			
		||||
   Modified for WLED to work around a fault in the NMI handling,
 | 
			
		||||
   which can result in the system locking up and hard WDT crashes.
 | 
			
		||||
 | 
			
		||||
   Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_pwm.cpp
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  esp8266_waveform - General purpose waveform generation and control,
 | 
			
		||||
                     supporting outputs on all pins in parallel.
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2018 Earle F. Philhower, III.  All rights reserved.
 | 
			
		||||
 | 
			
		||||
  The core idea is to have a programmable waveform generator with a unique
 | 
			
		||||
  high and low period (defined in microseconds or CPU clock cycles).  TIMER1
 | 
			
		||||
  is set to 1-shot mode and is always loaded with the time until the next
 | 
			
		||||
  edge of any live waveforms.
 | 
			
		||||
 | 
			
		||||
  Up to one waveform generator per pin supported.
 | 
			
		||||
 | 
			
		||||
  Each waveform generator is synchronized to the ESP clock cycle counter, not
 | 
			
		||||
  the timer.  This allows for removing interrupt jitter and delay as the
 | 
			
		||||
  counter always increments once per 80MHz clock.  Changes to a waveform are
 | 
			
		||||
  contiguous and only take effect on the next waveform transition,
 | 
			
		||||
  allowing for smooth transitions.
 | 
			
		||||
 | 
			
		||||
  This replaces older tone(), analogWrite(), and the Servo classes.
 | 
			
		||||
 | 
			
		||||
  Everywhere in the code where "cycles" is used, it means ESP.getCycleCount()
 | 
			
		||||
  clock cycle count, or an interval measured in CPU clock cycles, but not
 | 
			
		||||
  TIMER1 cycles (which may be 2 CPU clock cycles @ 160MHz).
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <coredecls.h>
 | 
			
		||||
#include "ets_sys.h"
 | 
			
		||||
#include "core_esp8266_waveform.h"
 | 
			
		||||
#include "user_interface.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
 | 
			
		||||
// Linker magic
 | 
			
		||||
void usePWMFixedNMI() {};
 | 
			
		||||
 | 
			
		||||
// Maximum delay between IRQs
 | 
			
		||||
#define MAXIRQUS (10000)
 | 
			
		||||
 | 
			
		||||
// Waveform generator can create tones, PWM, and servos
 | 
			
		||||
typedef struct {
 | 
			
		||||
  uint32_t nextServiceCycle;   // ESP cycle timer when a transition required
 | 
			
		||||
  uint32_t expiryCycle;        // For time-limited waveform, the cycle when this waveform must stop
 | 
			
		||||
  uint32_t timeHighCycles;     // Actual running waveform period (adjusted using desiredCycles)
 | 
			
		||||
  uint32_t timeLowCycles;      //
 | 
			
		||||
  uint32_t desiredHighCycles;  // Ideal waveform period to drive the error signal
 | 
			
		||||
  uint32_t desiredLowCycles;   //
 | 
			
		||||
  uint32_t lastEdge;           // Cycle when this generator last changed
 | 
			
		||||
} Waveform;
 | 
			
		||||
 | 
			
		||||
class WVFState {
 | 
			
		||||
public:
 | 
			
		||||
  Waveform waveform[17];        // State of all possible pins
 | 
			
		||||
  uint32_t waveformState = 0;   // Is the pin high or low, updated in NMI so no access outside the NMI code
 | 
			
		||||
  uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
 | 
			
		||||
 | 
			
		||||
  // Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
 | 
			
		||||
  uint32_t waveformToEnable = 0;  // Message to the NMI handler to start a waveform on a inactive pin
 | 
			
		||||
  uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
 | 
			
		||||
 | 
			
		||||
  uint32_t waveformToChange = 0; // Mask of pin to change. One bit set in main app, cleared when effected in the NMI
 | 
			
		||||
  uint32_t waveformNewHigh = 0;
 | 
			
		||||
  uint32_t waveformNewLow = 0;
 | 
			
		||||
 | 
			
		||||
  uint32_t (*timer1CB)() = NULL;
 | 
			
		||||
 | 
			
		||||
  // Optimize the NMI inner loop by keeping track of the min and max GPIO that we
 | 
			
		||||
  // are generating.  In the common case (1 PWM) these may be the same pin and
 | 
			
		||||
  // we can avoid looking at the other pins.
 | 
			
		||||
  uint16_t startPin = 0;
 | 
			
		||||
  uint16_t endPin = 0;
 | 
			
		||||
};
 | 
			
		||||
static WVFState wvfState;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Ensure everything is read/written to RAM
 | 
			
		||||
#define MEMBARRIER() { __asm__ volatile("" ::: "memory"); }
 | 
			
		||||
 | 
			
		||||
// Non-speed critical bits
 | 
			
		||||
#pragma GCC optimize ("Os")
 | 
			
		||||
 | 
			
		||||
// Interrupt on/off control
 | 
			
		||||
static IRAM_ATTR void timer1Interrupt();
 | 
			
		||||
static bool timerRunning = false;
 | 
			
		||||
 | 
			
		||||
static __attribute__((noinline)) void initTimer() {
 | 
			
		||||
  if (!timerRunning) {
 | 
			
		||||
    timer1_disable();
 | 
			
		||||
    ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
 | 
			
		||||
    ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
 | 
			
		||||
    timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
 | 
			
		||||
    timerRunning = true;
 | 
			
		||||
    timer1_write(microsecondsToClockCycles(10));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static IRAM_ATTR void forceTimerInterrupt() {
 | 
			
		||||
  if (T1L > microsecondsToClockCycles(10)) {
 | 
			
		||||
    T1L = microsecondsToClockCycles(10);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PWM implementation using special purpose state machine
 | 
			
		||||
//
 | 
			
		||||
// Keep an ordered list of pins with the delta in cycles between each
 | 
			
		||||
// element, with a terminal entry making up the remainder of the PWM
 | 
			
		||||
// period.  With this method sum(all deltas) == PWM period clock cycles.
 | 
			
		||||
//
 | 
			
		||||
// At t=0 set all pins high and set the timeout for the 1st edge.
 | 
			
		||||
// On interrupt, if we're at the last element reset to t=0 state
 | 
			
		||||
// Otherwise, clear that pin down and set delay for next element
 | 
			
		||||
// and so forth.
 | 
			
		||||
 | 
			
		||||
constexpr int maxPWMs = 8;
 | 
			
		||||
 | 
			
		||||
// PWM machine state
 | 
			
		||||
typedef struct PWMState {
 | 
			
		||||
  uint32_t mask; // Bitmask of active pins
 | 
			
		||||
  uint32_t cnt;  // How many entries
 | 
			
		||||
  uint32_t idx;  // Where the state machine is along the list
 | 
			
		||||
  uint8_t  pin[maxPWMs + 1];
 | 
			
		||||
  uint32_t delta[maxPWMs + 1];
 | 
			
		||||
  uint32_t nextServiceCycle;  // Clock cycle for next step
 | 
			
		||||
  struct PWMState *pwmUpdate; // Set by main code, cleared by ISR
 | 
			
		||||
} PWMState;
 | 
			
		||||
 | 
			
		||||
static PWMState pwmState;
 | 
			
		||||
static uint32_t _pwmFreq = 1000;
 | 
			
		||||
static uint32_t _pwmPeriod = microsecondsToClockCycles(1000000UL) / _pwmFreq;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// If there are no more scheduled activities, shut down Timer 1.
 | 
			
		||||
// Otherwise, do nothing.
 | 
			
		||||
static IRAM_ATTR void disableIdleTimer() {
 | 
			
		||||
 if (timerRunning && !wvfState.waveformEnabled && !pwmState.cnt && !wvfState.timer1CB) {
 | 
			
		||||
    ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
 | 
			
		||||
    timer1_disable();
 | 
			
		||||
    timer1_isr_init();
 | 
			
		||||
    timerRunning = false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Notify the NMI that a new PWM state is available through the mailbox.
 | 
			
		||||
// Wait for mailbox to be emptied (either busy or delay() as needed)
 | 
			
		||||
static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) {
 | 
			
		||||
  p->pwmUpdate = nullptr;
 | 
			
		||||
  pwmState.pwmUpdate = p;
 | 
			
		||||
  MEMBARRIER();
 | 
			
		||||
  forceTimerInterrupt();
 | 
			
		||||
  while (pwmState.pwmUpdate) {
 | 
			
		||||
    if (idle) {
 | 
			
		||||
      esp_yield();
 | 
			
		||||
    }
 | 
			
		||||
    MEMBARRIER();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Called when analogWriteFreq() changed to update the PWM total period
 | 
			
		||||
//extern void _setPWMFreq_weak(uint32_t freq) __attribute__((weak)); 
 | 
			
		||||
void _setPWMFreq_weak(uint32_t freq) {
 | 
			
		||||
  _pwmFreq = freq;
 | 
			
		||||
 | 
			
		||||
  // Convert frequency into clock cycles
 | 
			
		||||
  uint32_t cc = microsecondsToClockCycles(1000000UL) / freq;
 | 
			
		||||
 | 
			
		||||
  // Simple static adjustment to bring period closer to requested due to overhead
 | 
			
		||||
  // Empirically determined as a constant PWM delay and a function of the number of PWMs
 | 
			
		||||
#if F_CPU == 80000000
 | 
			
		||||
  cc -= ((microsecondsToClockCycles(pwmState.cnt) * 13) >> 4) + 110;
 | 
			
		||||
#else
 | 
			
		||||
  cc -= ((microsecondsToClockCycles(pwmState.cnt) * 10) >> 4) + 75;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (cc == _pwmPeriod) {
 | 
			
		||||
    return; // No change
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _pwmPeriod = cc;
 | 
			
		||||
 | 
			
		||||
  if (pwmState.cnt) {
 | 
			
		||||
    PWMState p;  // The working copy since we can't edit the one in use
 | 
			
		||||
    p.mask = 0;
 | 
			
		||||
    p.cnt = 0;
 | 
			
		||||
    for (uint32_t i = 0; i < pwmState.cnt; i++) {
 | 
			
		||||
      auto pin = pwmState.pin[i];
 | 
			
		||||
      _addPWMtoList(p, pin, wvfState.waveform[pin].desiredHighCycles, wvfState.waveform[pin].desiredLowCycles);
 | 
			
		||||
    }
 | 
			
		||||
    // Update and wait for mailbox to be emptied
 | 
			
		||||
    initTimer();
 | 
			
		||||
    _notifyPWM(&p, true);
 | 
			
		||||
    disableIdleTimer();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static void _setPWMFreq_bound(uint32_t freq) __attribute__((weakref("_setPWMFreq_weak")));
 | 
			
		||||
void _setPWMFreq(uint32_t freq) { 
 | 
			
		||||
  _setPWMFreq_bound(freq);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Helper routine to remove an entry from the state machine
 | 
			
		||||
// and clean up any marked-off entries
 | 
			
		||||
static void _cleanAndRemovePWM(PWMState *p, int pin) {
 | 
			
		||||
  uint32_t leftover = 0;
 | 
			
		||||
  uint32_t in, out;
 | 
			
		||||
  for (in = 0, out = 0; in < p->cnt; in++) {
 | 
			
		||||
    if ((p->pin[in] != pin) && (p->mask & (1<<p->pin[in]))) {
 | 
			
		||||
        p->pin[out] = p->pin[in];
 | 
			
		||||
        p->delta[out] = p->delta[in] + leftover;
 | 
			
		||||
        leftover = 0;
 | 
			
		||||
        out++;
 | 
			
		||||
    } else {
 | 
			
		||||
        leftover += p->delta[in];
 | 
			
		||||
        p->mask &= ~(1<<p->pin[in]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  p->cnt = out;
 | 
			
		||||
  // Final pin is never used: p->pin[out] = 0xff;
 | 
			
		||||
  p->delta[out] = p->delta[in] + leftover;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Disable PWM on a specific pin (i.e. when a digitalWrite or analogWrite(0%/100%))
 | 
			
		||||
//extern bool _stopPWM_weak(uint8_t pin) __attribute__((weak));
 | 
			
		||||
IRAM_ATTR bool _stopPWM_weak(uint8_t pin) {
 | 
			
		||||
  if (!((1<<pin) & pwmState.mask)) {
 | 
			
		||||
    return false; // Pin not actually active
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  PWMState p;  // The working copy since we can't edit the one in use
 | 
			
		||||
  p = pwmState;
 | 
			
		||||
 | 
			
		||||
  // In _stopPWM we just clear the mask but keep everything else
 | 
			
		||||
  // untouched to save IRAM.  The main startPWM will handle cleanup.
 | 
			
		||||
  p.mask &= ~(1<<pin);
 | 
			
		||||
  if (!p.mask) {
 | 
			
		||||
    // If all have been stopped, then turn PWM off completely
 | 
			
		||||
    p.cnt = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Update and wait for mailbox to be emptied, no delay (could be in ISR)
 | 
			
		||||
  _notifyPWM(&p, false);
 | 
			
		||||
  // Possibly shut down the timer completely if we're done
 | 
			
		||||
  disableIdleTimer();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static bool _stopPWM_bound(uint8_t pin) __attribute__((weakref("_stopPWM_weak")));
 | 
			
		||||
IRAM_ATTR bool _stopPWM(uint8_t pin) {
 | 
			
		||||
  return _stopPWM_bound(pin);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range) {
 | 
			
		||||
  // Stash the val and range so we can re-evaluate the fraction
 | 
			
		||||
  // should the user change PWM frequency.  This allows us to
 | 
			
		||||
  // give as great a precision as possible.  We know by construction
 | 
			
		||||
  // that the waveform for this pin will be inactive so we can borrow
 | 
			
		||||
  // memory from that structure.
 | 
			
		||||
  wvfState.waveform[pin].desiredHighCycles = val;  // Numerator == high
 | 
			
		||||
  wvfState.waveform[pin].desiredLowCycles = range; // Denominator == low
 | 
			
		||||
 | 
			
		||||
  uint32_t cc = (_pwmPeriod * val) / range;
 | 
			
		||||
 | 
			
		||||
  // Clip to sane values in the case we go from OK to not-OK when adjusting frequencies
 | 
			
		||||
  if (cc == 0) {
 | 
			
		||||
    cc = 1;
 | 
			
		||||
  } else if (cc >= _pwmPeriod) {
 | 
			
		||||
    cc = _pwmPeriod - 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (p.cnt == 0) {
 | 
			
		||||
    // Starting up from scratch, special case 1st element and PWM period
 | 
			
		||||
    p.pin[0] = pin;
 | 
			
		||||
    p.delta[0] = cc;
 | 
			
		||||
   // Final pin is never used: p.pin[1] = 0xff;
 | 
			
		||||
    p.delta[1] = _pwmPeriod - cc;
 | 
			
		||||
  } else {
 | 
			
		||||
    uint32_t ttl = 0;
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    // Skip along until we're at the spot to insert
 | 
			
		||||
    for (i=0; (i <= p.cnt) && (ttl + p.delta[i] < cc); i++) {
 | 
			
		||||
      ttl += p.delta[i];
 | 
			
		||||
    }
 | 
			
		||||
    // Shift everything out by one to make space for new edge
 | 
			
		||||
    for (int32_t j = p.cnt; j >= (int)i; j--) {
 | 
			
		||||
      p.pin[j + 1] = p.pin[j];
 | 
			
		||||
      p.delta[j + 1] = p.delta[j];
 | 
			
		||||
    }
 | 
			
		||||
    int off = cc - ttl; // The delta from the last edge to the one we're inserting
 | 
			
		||||
    p.pin[i] = pin;
 | 
			
		||||
    p.delta[i] = off; // Add the delta to this new pin
 | 
			
		||||
    p.delta[i + 1] -= off; // And subtract it from the follower to keep sum(deltas) constant
 | 
			
		||||
  }
 | 
			
		||||
  p.cnt++;
 | 
			
		||||
  p.mask |= 1<<pin;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Called by analogWrite(1...99%) to set the PWM duty in clock cycles
 | 
			
		||||
//extern bool _setPWM_weak(int pin, uint32_t val, uint32_t range) __attribute__((weak));
 | 
			
		||||
bool _setPWM_weak(int pin, uint32_t val, uint32_t range) {
 | 
			
		||||
  stopWaveform(pin);
 | 
			
		||||
  PWMState p;  // Working copy
 | 
			
		||||
  p = pwmState;
 | 
			
		||||
  // Get rid of any entries for this pin
 | 
			
		||||
  _cleanAndRemovePWM(&p, pin);
 | 
			
		||||
  // And add it to the list, in order
 | 
			
		||||
  if (p.cnt >= maxPWMs) {
 | 
			
		||||
    return false; // No space left
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Sanity check for all-on/off
 | 
			
		||||
  uint32_t cc = (_pwmPeriod * val) / range;
 | 
			
		||||
  if ((cc == 0) || (cc >= _pwmPeriod)) {
 | 
			
		||||
    digitalWrite(pin, cc ? HIGH : LOW);
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _addPWMtoList(p, pin, val, range);
 | 
			
		||||
 | 
			
		||||
  // Set mailbox and wait for ISR to copy it over
 | 
			
		||||
  initTimer();
 | 
			
		||||
  _notifyPWM(&p, true);
 | 
			
		||||
  disableIdleTimer();
 | 
			
		||||
 | 
			
		||||
  // Potentially recalculate the PWM period if we've added another pin
 | 
			
		||||
  _setPWMFreq(_pwmFreq);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static bool _setPWM_bound(int pin, uint32_t val, uint32_t range) __attribute__((weakref("_setPWM_weak")));
 | 
			
		||||
bool _setPWM(int pin, uint32_t val, uint32_t range) {
 | 
			
		||||
  return _setPWM_bound(pin, val, range);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Start up a waveform on a pin, or change the current one.  Will change to the new
 | 
			
		||||
// waveform smoothly on next low->high transition.  For immediate change, stopWaveform()
 | 
			
		||||
// first, then it will immediately begin.
 | 
			
		||||
//extern int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm)  __attribute__((weak));
 | 
			
		||||
int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles,
 | 
			
		||||
                             int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) {
 | 
			
		||||
  (void) alignPhase;
 | 
			
		||||
  (void) phaseOffsetUS;
 | 
			
		||||
  (void) autoPwm;
 | 
			
		||||
 | 
			
		||||
   if ((pin > 16) || isFlashInterfacePin(pin) || (timeHighCycles == 0)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  Waveform *wave = &wvfState.waveform[pin];
 | 
			
		||||
  wave->expiryCycle = runTimeCycles ? ESP.getCycleCount() + runTimeCycles : 0;
 | 
			
		||||
  if (runTimeCycles && !wave->expiryCycle) {
 | 
			
		||||
    wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _stopPWM(pin); // Make sure there's no PWM live here
 | 
			
		||||
 | 
			
		||||
  uint32_t mask = 1<<pin;
 | 
			
		||||
  MEMBARRIER();
 | 
			
		||||
  if (wvfState.waveformEnabled & mask) {
 | 
			
		||||
    // Make sure no waveform changes are waiting to be applied
 | 
			
		||||
    while (wvfState.waveformToChange) {
 | 
			
		||||
      esp_yield(); // Wait for waveform to update
 | 
			
		||||
      MEMBARRIER();
 | 
			
		||||
    }
 | 
			
		||||
    wvfState.waveformNewHigh = timeHighCycles;
 | 
			
		||||
    wvfState.waveformNewLow = timeLowCycles;
 | 
			
		||||
    MEMBARRIER();
 | 
			
		||||
    wvfState.waveformToChange = mask;
 | 
			
		||||
    // The waveform will be updated some time in the future on the next period for the signal
 | 
			
		||||
  } else { //  if (!(wvfState.waveformEnabled & mask)) {
 | 
			
		||||
    wave->timeHighCycles = timeHighCycles;
 | 
			
		||||
    wave->desiredHighCycles = timeHighCycles;
 | 
			
		||||
    wave->timeLowCycles = timeLowCycles;
 | 
			
		||||
    wave->desiredLowCycles = timeLowCycles;
 | 
			
		||||
    wave->lastEdge = 0;
 | 
			
		||||
    wave->nextServiceCycle = ESP.getCycleCount() + microsecondsToClockCycles(1);
 | 
			
		||||
    wvfState.waveformToEnable |= mask;
 | 
			
		||||
    MEMBARRIER();
 | 
			
		||||
    initTimer();
 | 
			
		||||
    forceTimerInterrupt();
 | 
			
		||||
    while (wvfState.waveformToEnable) {
 | 
			
		||||
      esp_yield(); // Wait for waveform to update
 | 
			
		||||
      MEMBARRIER();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static int startWaveformClockCycles_bound(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) __attribute__((weakref("startWaveformClockCycles_weak")));
 | 
			
		||||
int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) {
 | 
			
		||||
  return startWaveformClockCycles_bound(pin, timeHighCycles, timeLowCycles, runTimeCycles, alignPhase, phaseOffsetUS, autoPwm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// This version falls-thru to the proper startWaveformClockCycles call and is invariant across waveform generators
 | 
			
		||||
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS,
 | 
			
		||||
                  int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) {
 | 
			
		||||
  return startWaveformClockCycles_bound(pin,
 | 
			
		||||
    microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS),
 | 
			
		||||
    microsecondsToClockCycles(runTimeUS), alignPhase, microsecondsToClockCycles(phaseOffsetUS), autoPwm);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Set a callback.  Pass in NULL to stop it
 | 
			
		||||
//extern void setTimer1Callback_weak(uint32_t (*fn)()) __attribute__((weak));
 | 
			
		||||
void setTimer1Callback_weak(uint32_t (*fn)()) {
 | 
			
		||||
  wvfState.timer1CB = fn;
 | 
			
		||||
  if (fn) {
 | 
			
		||||
    initTimer();
 | 
			
		||||
    forceTimerInterrupt();
 | 
			
		||||
  }
 | 
			
		||||
  disableIdleTimer();
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static void setTimer1Callback_bound(uint32_t (*fn)()) __attribute__((weakref("setTimer1Callback_weak")));
 | 
			
		||||
void setTimer1Callback(uint32_t (*fn)()) {
 | 
			
		||||
  setTimer1Callback_bound(fn);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Stops a waveform on a pin
 | 
			
		||||
//extern int stopWaveform_weak(uint8_t pin) __attribute__((weak));
 | 
			
		||||
IRAM_ATTR int stopWaveform_weak(uint8_t pin) {
 | 
			
		||||
  // Can't possibly need to stop anything if there is no timer active
 | 
			
		||||
  if (!timerRunning) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // If user sends in a pin >16 but <32, this will always point to a 0 bit
 | 
			
		||||
  // If they send >=32, then the shift will result in 0 and it will also return false
 | 
			
		||||
  uint32_t mask = 1<<pin;
 | 
			
		||||
  if (wvfState.waveformEnabled & mask) {
 | 
			
		||||
    wvfState.waveformToDisable = mask;
 | 
			
		||||
    // Cancel any pending updates for this waveform, too.
 | 
			
		||||
    if (wvfState.waveformToChange & mask) {
 | 
			
		||||
        wvfState.waveformToChange = 0;
 | 
			
		||||
    }
 | 
			
		||||
    forceTimerInterrupt();
 | 
			
		||||
    while (wvfState.waveformToDisable) {
 | 
			
		||||
      MEMBARRIER(); // If it wasn't written yet, it has to be by now
 | 
			
		||||
      /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  disableIdleTimer();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
static int stopWaveform_bound(uint8_t pin) __attribute__((weakref("stopWaveform_weak")));
 | 
			
		||||
IRAM_ATTR int stopWaveform(uint8_t pin) {
 | 
			
		||||
  return stopWaveform_bound(pin);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Speed critical bits
 | 
			
		||||
#pragma GCC optimize ("O2")
 | 
			
		||||
 | 
			
		||||
// Normally would not want two copies like this, but due to different
 | 
			
		||||
// optimization levels the inline attribute gets lost if we try the
 | 
			
		||||
// other version.
 | 
			
		||||
static inline IRAM_ATTR uint32_t GetCycleCountIRQ() {
 | 
			
		||||
  uint32_t ccount;
 | 
			
		||||
  __asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
 | 
			
		||||
  return ccount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find the earliest cycle as compared to right now
 | 
			
		||||
static inline IRAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) {
 | 
			
		||||
    uint32_t now = GetCycleCountIRQ();
 | 
			
		||||
    int32_t da = a - now;
 | 
			
		||||
    int32_t db = b - now;
 | 
			
		||||
    return (da < db) ? a : b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- @willmmiles begin patch -----
 | 
			
		||||
// NMI crash workaround
 | 
			
		||||
// Sometimes the NMI fails to return, stalling the CPU.  When this happens,
 | 
			
		||||
// the next NMI gets a return address /inside the NMI handler function/.
 | 
			
		||||
// We work around this by caching the last NMI return address, and restoring
 | 
			
		||||
// the epc3 and eps3 registers to the previous values if the observed epc3
 | 
			
		||||
// happens to be pointing to the _NMILevelVector function.
 | 
			
		||||
extern void _NMILevelVector();
 | 
			
		||||
extern void _UserExceptionVector_1(); // the next function after _NMILevelVector
 | 
			
		||||
static inline IRAM_ATTR void nmiCrashWorkaround() {
 | 
			
		||||
  static uintptr_t epc3_backup, eps3_backup;
 | 
			
		||||
 | 
			
		||||
  uintptr_t epc3, eps3;
 | 
			
		||||
  __asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3));
 | 
			
		||||
  if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) {
 | 
			
		||||
    // Address is good; save backup
 | 
			
		||||
    epc3_backup = epc3;
 | 
			
		||||
    eps3_backup = eps3;
 | 
			
		||||
  } else {
 | 
			
		||||
    // Address is inside the NMI handler -- restore from backup
 | 
			
		||||
    __asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// ----- @willmmiles end patch -----
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// The SDK and hardware take some time to actually get to our NMI code, so
 | 
			
		||||
// decrement the next IRQ's timer value by a bit so we can actually catch the
 | 
			
		||||
// real CPU cycle counter we want for the waveforms.
 | 
			
		||||
 | 
			
		||||
// The SDK also sometimes is running at a different speed the the Arduino core
 | 
			
		||||
// so the ESP cycle counter is actually running at a variable speed.
 | 
			
		||||
// adjust(x) takes care of adjusting a delta clock cycle amount accordingly.
 | 
			
		||||
#if F_CPU == 80000000
 | 
			
		||||
  #define DELTAIRQ (microsecondsToClockCycles(9)/4)
 | 
			
		||||
  #define adjust(x) ((x) << (turbo ? 1 : 0))
 | 
			
		||||
#else
 | 
			
		||||
  #define DELTAIRQ (microsecondsToClockCycles(9)/8)
 | 
			
		||||
  #define adjust(x) ((x) >> 0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// When the time to the next edge is greater than this, RTI and set another IRQ to minimize CPU usage
 | 
			
		||||
#define MINIRQTIME microsecondsToClockCycles(6)
 | 
			
		||||
 | 
			
		||||
static IRAM_ATTR void timer1Interrupt() {
 | 
			
		||||
  // ----- @willmmiles begin patch -----
 | 
			
		||||
  nmiCrashWorkaround();
 | 
			
		||||
  // ----- @willmmiles end patch -----
 | 
			
		||||
 | 
			
		||||
  // Flag if the core is at 160 MHz, for use by adjust()
 | 
			
		||||
  bool turbo = (*(uint32_t*)0x3FF00014) & 1 ? true : false;
 | 
			
		||||
 | 
			
		||||
  uint32_t nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS);
 | 
			
		||||
  uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14);
 | 
			
		||||
 | 
			
		||||
  if (wvfState.waveformToEnable || wvfState.waveformToDisable) {
 | 
			
		||||
    // Handle enable/disable requests from main app
 | 
			
		||||
    wvfState.waveformEnabled = (wvfState.waveformEnabled & ~wvfState.waveformToDisable) | wvfState.waveformToEnable; // Set the requested waveforms on/off
 | 
			
		||||
    wvfState.waveformState &= ~wvfState.waveformToEnable;  // And clear the state of any just started
 | 
			
		||||
    wvfState.waveformToEnable = 0;
 | 
			
		||||
    wvfState.waveformToDisable = 0;
 | 
			
		||||
    // No mem barrier.  Globals must be written to RAM on ISR exit.
 | 
			
		||||
    // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
 | 
			
		||||
    wvfState.startPin = __builtin_ffs(wvfState.waveformEnabled) - 1;
 | 
			
		||||
    // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
 | 
			
		||||
    wvfState.endPin = 32 - __builtin_clz(wvfState.waveformEnabled);
 | 
			
		||||
  } else if (!pwmState.cnt && pwmState.pwmUpdate) {
 | 
			
		||||
    // Start up the PWM generator by copying from the mailbox
 | 
			
		||||
    pwmState.cnt = 1;
 | 
			
		||||
    pwmState.idx = 1; // Ensure copy this cycle, cause it to start at t=0
 | 
			
		||||
    pwmState.nextServiceCycle = GetCycleCountIRQ(); // Do it this loop!
 | 
			
		||||
    // No need for mem barrier here.  Global must be written by IRQ exit
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool done = false;
 | 
			
		||||
  if (wvfState.waveformEnabled || pwmState.cnt) {
 | 
			
		||||
    do {
 | 
			
		||||
      nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS);
 | 
			
		||||
 | 
			
		||||
      // PWM state machine implementation
 | 
			
		||||
      if (pwmState.cnt) {
 | 
			
		||||
        int32_t cyclesToGo;
 | 
			
		||||
        do {
 | 
			
		||||
            cyclesToGo = pwmState.nextServiceCycle - GetCycleCountIRQ();
 | 
			
		||||
            if (cyclesToGo < 0) {
 | 
			
		||||
                if (pwmState.idx == pwmState.cnt) { // Start of pulses, possibly copy new
 | 
			
		||||
                  if (pwmState.pwmUpdate) {
 | 
			
		||||
                    // Do the memory copy from temp to global and clear mailbox
 | 
			
		||||
                    pwmState = *(PWMState*)pwmState.pwmUpdate;
 | 
			
		||||
                  }
 | 
			
		||||
                  GPOS = pwmState.mask; // Set all active pins high
 | 
			
		||||
                  if (pwmState.mask & (1<<16)) {
 | 
			
		||||
                    GP16O = 1;
 | 
			
		||||
                  }
 | 
			
		||||
                  pwmState.idx = 0;
 | 
			
		||||
                } else {
 | 
			
		||||
                  do {
 | 
			
		||||
                    // Drop the pin at this edge
 | 
			
		||||
                    if (pwmState.mask & (1<<pwmState.pin[pwmState.idx])) {
 | 
			
		||||
                      GPOC = 1<<pwmState.pin[pwmState.idx];
 | 
			
		||||
                      if (pwmState.pin[pwmState.idx] == 16) {
 | 
			
		||||
                        GP16O = 0;
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                    pwmState.idx++;
 | 
			
		||||
                    // Any other pins at this same PWM value will have delta==0, drop them too.
 | 
			
		||||
                  } while (pwmState.delta[pwmState.idx] == 0);
 | 
			
		||||
                }
 | 
			
		||||
                // Preserve duty cycle over PWM period by using now+xxx instead of += delta
 | 
			
		||||
                cyclesToGo = adjust(pwmState.delta[pwmState.idx]);
 | 
			
		||||
                pwmState.nextServiceCycle = GetCycleCountIRQ() + cyclesToGo;
 | 
			
		||||
            }
 | 
			
		||||
            nextEventCycle = earliest(nextEventCycle, pwmState.nextServiceCycle);
 | 
			
		||||
        } while (pwmState.cnt && (cyclesToGo < 100));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (auto i = wvfState.startPin; i <= wvfState.endPin; i++) {
 | 
			
		||||
        uint32_t mask = 1<<i;
 | 
			
		||||
 | 
			
		||||
        // If it's not on, ignore!
 | 
			
		||||
        if (!(wvfState.waveformEnabled & mask)) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Waveform *wave = &wvfState.waveform[i];
 | 
			
		||||
        uint32_t now = GetCycleCountIRQ();
 | 
			
		||||
 | 
			
		||||
        // Disable any waveforms that are done
 | 
			
		||||
        if (wave->expiryCycle) {
 | 
			
		||||
          int32_t expiryToGo = wave->expiryCycle - now;
 | 
			
		||||
          if (expiryToGo < 0) {
 | 
			
		||||
              // Done, remove!
 | 
			
		||||
              if (i == 16) {
 | 
			
		||||
                GP16O = 0;
 | 
			
		||||
              } 
 | 
			
		||||
              GPOC = mask;
 | 
			
		||||
              wvfState.waveformEnabled &= ~mask;
 | 
			
		||||
              continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for toggles
 | 
			
		||||
        int32_t cyclesToGo = wave->nextServiceCycle - now;
 | 
			
		||||
        if (cyclesToGo < 0) {
 | 
			
		||||
          uint32_t nextEdgeCycles;
 | 
			
		||||
          uint32_t desired = 0;
 | 
			
		||||
          uint32_t *timeToUpdate;
 | 
			
		||||
          wvfState.waveformState ^= mask;
 | 
			
		||||
          if (wvfState.waveformState & mask) {
 | 
			
		||||
            if (i == 16) {
 | 
			
		||||
              GP16O = 1;
 | 
			
		||||
            }
 | 
			
		||||
            GPOS = mask;
 | 
			
		||||
 | 
			
		||||
            if (wvfState.waveformToChange & mask) {
 | 
			
		||||
              // Copy over next full-cycle timings
 | 
			
		||||
              wave->timeHighCycles = wvfState.waveformNewHigh;
 | 
			
		||||
              wave->desiredHighCycles = wvfState.waveformNewHigh;
 | 
			
		||||
              wave->timeLowCycles = wvfState.waveformNewLow;
 | 
			
		||||
              wave->desiredLowCycles = wvfState.waveformNewLow;
 | 
			
		||||
              wave->lastEdge = 0;
 | 
			
		||||
              wvfState.waveformToChange = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (wave->lastEdge) {
 | 
			
		||||
              desired = wave->desiredLowCycles;
 | 
			
		||||
              timeToUpdate = &wave->timeLowCycles;
 | 
			
		||||
            }
 | 
			
		||||
            nextEdgeCycles = wave->timeHighCycles;
 | 
			
		||||
          } else {
 | 
			
		||||
            if (i == 16) {
 | 
			
		||||
              GP16O = 0;
 | 
			
		||||
            }
 | 
			
		||||
            GPOC = mask;
 | 
			
		||||
            desired = wave->desiredHighCycles;
 | 
			
		||||
            timeToUpdate = &wave->timeHighCycles;
 | 
			
		||||
            nextEdgeCycles = wave->timeLowCycles;
 | 
			
		||||
          }
 | 
			
		||||
          if (desired) {
 | 
			
		||||
            desired = adjust(desired);
 | 
			
		||||
            int32_t err = desired - (now - wave->lastEdge);
 | 
			
		||||
            if (abs(err) < desired) { // If we've lost > the entire phase, ignore this error signal
 | 
			
		||||
                err /= 2;
 | 
			
		||||
                *timeToUpdate += err;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          nextEdgeCycles = adjust(nextEdgeCycles);
 | 
			
		||||
          wave->nextServiceCycle = now + nextEdgeCycles;
 | 
			
		||||
          wave->lastEdge = now;
 | 
			
		||||
        }
 | 
			
		||||
        nextEventCycle = earliest(nextEventCycle, wave->nextServiceCycle);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur
 | 
			
		||||
      uint32_t now = GetCycleCountIRQ();
 | 
			
		||||
      int32_t cycleDeltaNextEvent = nextEventCycle - now;
 | 
			
		||||
      int32_t cyclesLeftTimeout = timeoutCycle - now;
 | 
			
		||||
      done = (cycleDeltaNextEvent > MINIRQTIME) || (cyclesLeftTimeout < 0);
 | 
			
		||||
    } while (!done);
 | 
			
		||||
  } // if (wvfState.waveformEnabled)
 | 
			
		||||
 | 
			
		||||
  if (wvfState.timer1CB) {
 | 
			
		||||
    nextEventCycle = earliest(nextEventCycle, GetCycleCountIRQ() + wvfState.timer1CB());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t nextEventCycles = nextEventCycle - GetCycleCountIRQ();
 | 
			
		||||
 | 
			
		||||
  if (nextEventCycles < MINIRQTIME) {
 | 
			
		||||
    nextEventCycles = MINIRQTIME;
 | 
			
		||||
  }
 | 
			
		||||
  nextEventCycles -= DELTAIRQ;
 | 
			
		||||
 | 
			
		||||
  // Do it here instead of global function to save time and because we know it's edge-IRQ
 | 
			
		||||
  T1L = nextEventCycles >> (turbo ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
@@ -1,469 +0,0 @@
 | 
			
		||||
/*-------------------------------------------------------------------------
 | 
			
		||||
NeoPixel driver for ESP32 RMTs using High-priority Interrupt
 | 
			
		||||
 | 
			
		||||
(NB. This cannot be mixed with the non-HI driver.)
 | 
			
		||||
 | 
			
		||||
Written by Will M. Miles.
 | 
			
		||||
 | 
			
		||||
I invest time and resources providing this open source code,
 | 
			
		||||
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------
 | 
			
		||||
This file is part of the Makuna/NeoPixelBus library.
 | 
			
		||||
 | 
			
		||||
NeoPixelBus is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU Lesser General Public License as
 | 
			
		||||
published by the Free Software Foundation, either version 3 of
 | 
			
		||||
the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
NeoPixelBus is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
License along with NeoPixel.  If not, see
 | 
			
		||||
<http://www.gnu.org/licenses/>.
 | 
			
		||||
-------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#if defined(ARDUINO_ARCH_ESP32)
 | 
			
		||||
 | 
			
		||||
// Use the NeoEspRmtSpeed types from the driver-based implementation
 | 
			
		||||
#include <NeoPixelBus.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace NeoEsp32RmtHiMethodDriver {
 | 
			
		||||
    // Install the driver for a specific channel, specifying timing properties
 | 
			
		||||
    esp_err_t Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t resetDuration);
 | 
			
		||||
 | 
			
		||||
    // Remove the driver on a specific channel
 | 
			
		||||
    esp_err_t Uninstall(rmt_channel_t channel);
 | 
			
		||||
 | 
			
		||||
    // Write a buffer of data to a specific channel.
 | 
			
		||||
    // Buffer reference is held until write completes.
 | 
			
		||||
    esp_err_t Write(rmt_channel_t channel, const uint8_t *src, size_t src_size);
 | 
			
		||||
 | 
			
		||||
    // Wait until transaction is complete.
 | 
			
		||||
    esp_err_t WaitForTxDone(rmt_channel_t channel, TickType_t wait_time);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T_SPEED, typename T_CHANNEL> class NeoEsp32RmtHIMethodBase
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    typedef NeoNoSettings SettingsObject;
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize)  :
 | 
			
		||||
        _sizeData(pixelCount * elementSize + settingsSize),
 | 
			
		||||
        _pin(pin)
 | 
			
		||||
    {
 | 
			
		||||
        construct();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
 | 
			
		||||
        _sizeData(pixelCount* elementSize + settingsSize),
 | 
			
		||||
        _pin(pin),
 | 
			
		||||
        _channel(channel)
 | 
			
		||||
    {
 | 
			
		||||
        construct();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~NeoEsp32RmtHIMethodBase()
 | 
			
		||||
    {
 | 
			
		||||
        // wait until the last send finishes before destructing everything
 | 
			
		||||
        // arbitrary time out of 10 seconds
 | 
			
		||||
        ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
 | 
			
		||||
 | 
			
		||||
        ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Uninstall(_channel.RmtChannelNumber));
 | 
			
		||||
 | 
			
		||||
        gpio_matrix_out(_pin, SIG_GPIO_OUT_IDX, false, false);
 | 
			
		||||
        pinMode(_pin, INPUT);
 | 
			
		||||
 | 
			
		||||
        free(_dataEditing);
 | 
			
		||||
        free(_dataSending);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsReadyToUpdate() const
 | 
			
		||||
    {
 | 
			
		||||
        return (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT_SILENT_TIMEOUT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 0)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Initialize()
 | 
			
		||||
    {
 | 
			
		||||
        rmt_config_t config = {};
 | 
			
		||||
 | 
			
		||||
        config.rmt_mode = RMT_MODE_TX;
 | 
			
		||||
        config.channel = _channel.RmtChannelNumber;
 | 
			
		||||
        config.gpio_num = static_cast<gpio_num_t>(_pin);
 | 
			
		||||
        config.mem_block_num = 1;
 | 
			
		||||
        config.tx_config.loop_en = false;
 | 
			
		||||
 | 
			
		||||
        config.tx_config.idle_output_en = true;
 | 
			
		||||
        config.tx_config.idle_level = T_SPEED::IdleLevel;
 | 
			
		||||
 | 
			
		||||
        config.tx_config.carrier_en = false;
 | 
			
		||||
        config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
 | 
			
		||||
 | 
			
		||||
        config.clk_div = T_SPEED::RmtClockDivider;
 | 
			
		||||
 | 
			
		||||
        ESP_ERROR_CHECK(rmt_config(&config));   // Uses ESP library
 | 
			
		||||
        ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Install(_channel.RmtChannelNumber, T_SPEED::RmtBit0, T_SPEED::RmtBit1, T_SPEED::RmtDurationReset));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Update(bool maintainBufferConsistency)
 | 
			
		||||
    {
 | 
			
		||||
        // wait for not actively sending data
 | 
			
		||||
        // this will time out at 10 seconds, an arbitrarily long period of time
 | 
			
		||||
        // and do nothing if this happens
 | 
			
		||||
        if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
 | 
			
		||||
        {
 | 
			
		||||
            // now start the RMT transmit with the editing buffer before we swap
 | 
			
		||||
            ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::Write(_channel.RmtChannelNumber, _dataEditing, _sizeData));
 | 
			
		||||
 | 
			
		||||
            if (maintainBufferConsistency)
 | 
			
		||||
            {
 | 
			
		||||
                // copy editing to sending,
 | 
			
		||||
                // this maintains the contract that "colors present before will
 | 
			
		||||
                // be the same after", otherwise GetPixelColor will be inconsistent
 | 
			
		||||
                memcpy(_dataSending, _dataEditing, _sizeData);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // swap so the user can modify without affecting the async operation
 | 
			
		||||
            std::swap(_dataSending, _dataEditing);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool AlwaysUpdate()
 | 
			
		||||
    {
 | 
			
		||||
        // this method requires update to be called only if changes to buffer
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SwapBuffers()
 | 
			
		||||
    {
 | 
			
		||||
        std::swap(_dataSending, _dataEditing);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t* getData() const
 | 
			
		||||
    {
 | 
			
		||||
        return _dataEditing;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    size_t getDataSize() const
 | 
			
		||||
    {
 | 
			
		||||
        return _sizeData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void applySettings([[maybe_unused]] const SettingsObject& settings)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    const size_t  _sizeData;      // Size of '_data*' buffers
 | 
			
		||||
    const uint8_t _pin;            // output pin number
 | 
			
		||||
    const T_CHANNEL _channel; // holds instance for multi channel support
 | 
			
		||||
 | 
			
		||||
    // Holds data stream which include LED color values and other settings as needed
 | 
			
		||||
    uint8_t*  _dataEditing;   // exposed for get and set
 | 
			
		||||
    uint8_t*  _dataSending;   // used for async send using RMT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void construct()
 | 
			
		||||
    {
 | 
			
		||||
        _dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
 | 
			
		||||
        // data cleared later in Begin()
 | 
			
		||||
 | 
			
		||||
        _dataSending = static_cast<uint8_t*>(malloc(_sizeData));
 | 
			
		||||
        // no need to initialize it, it gets overwritten on every send
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// normal
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHINWs2805Method NeoEsp32RmtHINWs2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI0Ws2805Method NeoEsp32RmtHI0Ws2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method;
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel2>  NeoEsp32RmtHI2Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI2Ws2805Method NeoEsp32RmtHI2Ws2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel3>  NeoEsp32RmtHI3Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI3Ws2805Method NeoEsp32RmtHI3Ws2814Method;
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel4>  NeoEsp32RmtHI4Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI4Ws2805Method NeoEsp32RmtHI4Ws2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel5>  NeoEsp32RmtHI5Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI5Ws2805Method NeoEsp32RmtHI5Ws2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel6>  NeoEsp32RmtHI6Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI6Ws2805Method NeoEsp32RmtHI6Ws2814Method;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel7>  NeoEsp32RmtHI7Sk6812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903Method;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method;
 | 
			
		||||
 | 
			
		||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
 | 
			
		||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
 | 
			
		||||
 | 
			
		||||
// inverted
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHINWs2805InvertedMethod NeoEsp32RmtHINWs2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI0Ws2805InvertedMethod NeoEsp32RmtHI0Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel2>  NeoEsp32RmtHI2Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI2Ws2805InvertedMethod NeoEsp32RmtHI2Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel3>  NeoEsp32RmtHI3Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI3Ws2805InvertedMethod NeoEsp32RmtHI3Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel4>  NeoEsp32RmtHI4Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI4Ws2805InvertedMethod NeoEsp32RmtHI4Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel5>  NeoEsp32RmtHI5Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI5Ws2805InvertedMethod NeoEsp32RmtHI5Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel6>  NeoEsp32RmtHI6Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI6Ws2805InvertedMethod NeoEsp32RmtHI6Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel7>  NeoEsp32RmtHI7Sk6812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903InvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsInvertedMethod;
 | 
			
		||||
typedef NeoEsp32RmtHI7Ws2805InvertedMethod NeoEsp32RmtHI7Ws2814InvertedMethod;
 | 
			
		||||
 | 
			
		||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
 | 
			
		||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "NeoESP32RmtHI",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "platforms": ["espressif32"],
 | 
			
		||||
  "dependencies": [
 | 
			
		||||
    {
 | 
			
		||||
      "owner": "makuna",      
 | 
			
		||||
      "name": "NeoPixelBus",
 | 
			
		||||
      "version": "^2.8.3"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}  
 | 
			
		||||
@@ -1,263 +0,0 @@
 | 
			
		||||
/* RMT ISR shim
 | 
			
		||||
 * Bridges from a high-level interrupt to the C++ code.
 | 
			
		||||
 *
 | 
			
		||||
 * This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI)
 | 
			
		||||
 | 
			
		||||
#include <freertos/xtensa_context.h>
 | 
			
		||||
#include "sdkconfig.h"
 | 
			
		||||
#include "soc/soc.h"
 | 
			
		||||
 | 
			
		||||
/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */
 | 
			
		||||
#ifndef CONFIG_BTDM_CTRL_HLI
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   Select interrupt based on system check level
 | 
			
		||||
   - Base ESP32: could be 4 or 5, depends on platform config
 | 
			
		||||
   - S2: 5
 | 
			
		||||
   - S3: 5
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
 | 
			
		||||
/* Use level 4 */
 | 
			
		||||
#define RFI_X               4
 | 
			
		||||
#define xt_highintx         xt_highint4
 | 
			
		||||
#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
 | 
			
		||||
/* Use level 5 */
 | 
			
		||||
#define RFI_X               5
 | 
			
		||||
#define xt_highintx         xt_highint5
 | 
			
		||||
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
 | 
			
		||||
 | 
			
		||||
// Register map, based on interrupt level
 | 
			
		||||
#define EPC_X               (EPC + RFI_X)
 | 
			
		||||
#define EXCSAVE_X           (EXCSAVE + RFI_X)
 | 
			
		||||
 | 
			
		||||
// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined?
 | 
			
		||||
#define sp a1
 | 
			
		||||
 | 
			
		||||
/* Interrupt stack size, for C code. */
 | 
			
		||||
#define RMT_INTR_STACK_SIZE  512
 | 
			
		||||
 | 
			
		||||
/* Save area for the CPU state:
 | 
			
		||||
 * - 64 words for the general purpose registers
 | 
			
		||||
 * - 7 words for some of the special registers:
 | 
			
		||||
 *   - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
 | 
			
		||||
 *   - SAR, LBEG, LEND, LCOUNT — since the C code might use these
 | 
			
		||||
 *   - EPC1 — since the C code might cause window overflow exceptions
 | 
			
		||||
 * This is not laid out as standard exception frame structure
 | 
			
		||||
 * for simplicity of the save/restore code.
 | 
			
		||||
 */
 | 
			
		||||
#define REG_FILE_SIZE         (64 * 4)
 | 
			
		||||
#define SPECREG_OFFSET        REG_FILE_SIZE
 | 
			
		||||
#define SPECREG_SIZE          (7 * 4)
 | 
			
		||||
#define REG_SAVE_AREA_SIZE    (SPECREG_OFFSET + SPECREG_SIZE)
 | 
			
		||||
 | 
			
		||||
    .data
 | 
			
		||||
_rmt_intr_stack:
 | 
			
		||||
    .space      RMT_INTR_STACK_SIZE
 | 
			
		||||
_rmt_save_ctx:
 | 
			
		||||
    .space      REG_SAVE_AREA_SIZE
 | 
			
		||||
 | 
			
		||||
    .section .iram1,"ax"
 | 
			
		||||
    .global     xt_highintx
 | 
			
		||||
    .type       xt_highintx,@function
 | 
			
		||||
    .align      4
 | 
			
		||||
 | 
			
		||||
xt_highintx:
 | 
			
		||||
 | 
			
		||||
    movi    a0, _rmt_save_ctx
 | 
			
		||||
    /* save 4 lower registers */
 | 
			
		||||
    s32i    a1, a0, 4
 | 
			
		||||
    s32i    a2, a0, 8
 | 
			
		||||
    s32i    a3, a0, 12
 | 
			
		||||
    rsr     a2, EXCSAVE_X  /* holds the value of a0 */
 | 
			
		||||
    s32i    a2, a0, 0
 | 
			
		||||
 | 
			
		||||
    /* Save special registers */
 | 
			
		||||
    addi    a0, a0, SPECREG_OFFSET
 | 
			
		||||
    rsr     a2, WINDOWBASE
 | 
			
		||||
    s32i    a2, a0, 0
 | 
			
		||||
    rsr     a2, WINDOWSTART
 | 
			
		||||
    s32i    a2, a0, 4
 | 
			
		||||
    rsr     a2, SAR
 | 
			
		||||
    s32i    a2, a0, 8
 | 
			
		||||
    #if XCHAL_HAVE_LOOPS
 | 
			
		||||
    rsr     a2, LBEG
 | 
			
		||||
    s32i    a2, a0, 12
 | 
			
		||||
    rsr     a2, LEND
 | 
			
		||||
    s32i    a2, a0, 16
 | 
			
		||||
    rsr     a2, LCOUNT
 | 
			
		||||
    s32i    a2, a0, 20
 | 
			
		||||
    #endif
 | 
			
		||||
    rsr     a2, EPC1
 | 
			
		||||
    s32i    a2, a0, 24
 | 
			
		||||
 | 
			
		||||
    /* disable exception mode, window overflow */
 | 
			
		||||
    movi    a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM
 | 
			
		||||
    wsr     a0, PS
 | 
			
		||||
    rsync
 | 
			
		||||
 | 
			
		||||
    /* Save the remaining physical registers.
 | 
			
		||||
     * 4 registers are already saved, which leaves 60 registers to save.
 | 
			
		||||
     * (FIXME: consider the case when the CPU is configured with physical 32 registers)
 | 
			
		||||
     * These 60 registers are saved in 5 iterations, 12 registers at a time.
 | 
			
		||||
     */
 | 
			
		||||
    movi    a1, 5
 | 
			
		||||
    movi    a3, _rmt_save_ctx + 4 * 4
 | 
			
		||||
 | 
			
		||||
    /* This is repeated 5 times, each time the window is shifted by 12 registers.
 | 
			
		||||
     * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
 | 
			
		||||
     */
 | 
			
		||||
1:
 | 
			
		||||
    s32i    a4, a3, 0
 | 
			
		||||
    s32i    a5, a3, 4
 | 
			
		||||
    s32i    a6, a3, 8
 | 
			
		||||
    s32i    a7, a3, 12
 | 
			
		||||
    s32i    a8, a3, 16
 | 
			
		||||
    s32i    a9, a3, 20
 | 
			
		||||
    s32i    a10, a3, 24
 | 
			
		||||
    s32i    a11, a3, 28
 | 
			
		||||
    s32i    a12, a3, 32
 | 
			
		||||
    s32i    a13, a3, 36
 | 
			
		||||
    s32i    a14, a3, 40
 | 
			
		||||
    s32i    a15, a3, 44
 | 
			
		||||
 | 
			
		||||
    /* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
 | 
			
		||||
     * Copy a0-a3 to a12-15 to still have access to these values.
 | 
			
		||||
     * At the same time we can decrement the counter and adjust the save area pointer
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* a0 is constant (_rmt_save_ctx), no need to copy */
 | 
			
		||||
    addi    a13, a1, -1  /* copy and decrement the downcounter */
 | 
			
		||||
    /* a2 is scratch so no need to copy */
 | 
			
		||||
    addi    a15, a3, 48  /* copy and adjust the save area pointer */
 | 
			
		||||
    beqz    a13, 2f      /* have saved all registers ? */
 | 
			
		||||
    rotw    3            /* rotate the window and go back */
 | 
			
		||||
    j       1b
 | 
			
		||||
 | 
			
		||||
    /* the loop is complete */
 | 
			
		||||
2:
 | 
			
		||||
    rotw 4      /* this brings us back to the original window */
 | 
			
		||||
    /* a0 still points to _rmt_save_ctx */
 | 
			
		||||
 | 
			
		||||
    /* Can clear WINDOWSTART now, all registers are saved */
 | 
			
		||||
    rsr     a2, WINDOWBASE
 | 
			
		||||
    /* WINDOWSTART = (1 << WINDOWBASE) */
 | 
			
		||||
    movi    a3, 1
 | 
			
		||||
    ssl     a2
 | 
			
		||||
    sll     a3, a3
 | 
			
		||||
    wsr     a3, WINDOWSTART
 | 
			
		||||
 | 
			
		||||
_highint_stack_switch:
 | 
			
		||||
    movi    a0, 0
 | 
			
		||||
    movi    sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16
 | 
			
		||||
    s32e    a0, sp, -12         /* For GDB: set null SP */
 | 
			
		||||
    s32e    a0, sp, -16         /* For GDB: set null PC */
 | 
			
		||||
    movi    a0, _highint_stack_switch     /* For GDB: cosmetics, for the frame where stack switch happened */
 | 
			
		||||
 | 
			
		||||
    /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
 | 
			
		||||
    movi    a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE
 | 
			
		||||
    wsr     a6, PS
 | 
			
		||||
    rsync
 | 
			
		||||
 | 
			
		||||
    /* Call C handler */
 | 
			
		||||
    mov     a6, sp
 | 
			
		||||
    call4   NeoEsp32RmtMethodIsr
 | 
			
		||||
 | 
			
		||||
    l32e    sp, sp, -12                     /* switch back to the original stack */
 | 
			
		||||
 | 
			
		||||
    /* Done with C handler; re-enable exception mode, disabling window overflow */
 | 
			
		||||
    movi    a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM    /* TOCHECK */
 | 
			
		||||
    wsr     a2, PS
 | 
			
		||||
    rsync
 | 
			
		||||
 | 
			
		||||
    /* Restore the special registers.
 | 
			
		||||
     * WINDOWSTART will be restored near the end.
 | 
			
		||||
     */
 | 
			
		||||
    movi    a0, _rmt_save_ctx + SPECREG_OFFSET
 | 
			
		||||
    l32i    a2, a0, 8
 | 
			
		||||
    wsr     a2, SAR
 | 
			
		||||
    #if XCHAL_HAVE_LOOPS
 | 
			
		||||
    l32i    a2, a0, 12
 | 
			
		||||
    wsr     a2, LBEG
 | 
			
		||||
    l32i    a2, a0, 16
 | 
			
		||||
    wsr     a2, LEND
 | 
			
		||||
    l32i    a2, a0, 20
 | 
			
		||||
    wsr     a2, LCOUNT
 | 
			
		||||
    #endif
 | 
			
		||||
    l32i    a2, a0, 24
 | 
			
		||||
    wsr     a2, EPC1
 | 
			
		||||
 | 
			
		||||
    /* Restoring the physical registers.
 | 
			
		||||
     * This is the reverse to the saving process above.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* Rotate back to the final window, then start loading 12 registers at a time,
 | 
			
		||||
     * in 5 iterations.
 | 
			
		||||
     * Again, a1 is the downcounter and a3 is the save area pointer.
 | 
			
		||||
     * After each rotation, a1 and a3 are copied from a13 and a15.
 | 
			
		||||
     * To simplify the loop, we put the initial values into a13 and a15.
 | 
			
		||||
     */
 | 
			
		||||
    rotw     -4
 | 
			
		||||
    movi    a15, _rmt_save_ctx + 64 * 4  /* point to the end of the save area */
 | 
			
		||||
    movi    a13, 5
 | 
			
		||||
 | 
			
		||||
1:
 | 
			
		||||
    /* Copy a1 and a3 from their previous location,
 | 
			
		||||
     * at the same time decrementing and adjusting the save area pointer.
 | 
			
		||||
     */
 | 
			
		||||
    addi    a1, a13, -1
 | 
			
		||||
    addi    a3, a15, -48
 | 
			
		||||
 | 
			
		||||
    /* Load 12 registers */
 | 
			
		||||
    l32i    a4, a3, 0
 | 
			
		||||
    l32i    a5, a3, 4
 | 
			
		||||
    l32i    a6, a3, 8
 | 
			
		||||
    l32i    a7, a3, 12
 | 
			
		||||
    l32i    a8, a3, 16
 | 
			
		||||
    l32i    a9, a3, 20
 | 
			
		||||
    l32i    a10, a3, 24
 | 
			
		||||
    l32i    a11, a3, 28                                /* ensure PS and EPC written */
 | 
			
		||||
    l32i    a12, a3, 32
 | 
			
		||||
    l32i    a13, a3, 36
 | 
			
		||||
    l32i    a14, a3, 40
 | 
			
		||||
    l32i    a15, a3, 44
 | 
			
		||||
 | 
			
		||||
    /* Done with the loop? */
 | 
			
		||||
    beqz    a1, 2f
 | 
			
		||||
    /* If no, rotate the window and repeat */
 | 
			
		||||
    rotw    -3
 | 
			
		||||
    j       1b
 | 
			
		||||
 | 
			
		||||
2:
 | 
			
		||||
    /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
 | 
			
		||||
     * to be restored. Also need to restore WINDOWSTART, since all the general
 | 
			
		||||
     * registers are now in place.
 | 
			
		||||
     */
 | 
			
		||||
    movi    a0, _rmt_save_ctx
 | 
			
		||||
 | 
			
		||||
    l32i    a2, a0, SPECREG_OFFSET + 4
 | 
			
		||||
    wsr     a2, WINDOWSTART
 | 
			
		||||
 | 
			
		||||
    l32i    a1, a0, 4
 | 
			
		||||
    l32i    a2, a0, 8
 | 
			
		||||
    l32i    a3, a0, 12
 | 
			
		||||
    rsr     a0, EXCSAVE_X  /* holds the value of a0 before the interrupt handler */
 | 
			
		||||
 | 
			
		||||
    /* Return from the interrupt, restoring PS from EPS_X */
 | 
			
		||||
    rfi     RFI_X
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The linker has no reason to link in this file; all symbols it exports are already defined
 | 
			
		||||
   (weakly!) in the default int handler. Define a symbol here so we can use it to have the
 | 
			
		||||
   linker inspect this anyway. */
 | 
			
		||||
 | 
			
		||||
    .global ld_include_hli_vectors_rmt
 | 
			
		||||
ld_include_hli_vectors_rmt:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // CONFIG_BTDM_CTRL_HLI
 | 
			
		||||
#endif // XTensa
 | 
			
		||||
@@ -1,507 +0,0 @@
 | 
			
		||||
/*-------------------------------------------------------------------------
 | 
			
		||||
NeoPixel library helper functions for Esp32.
 | 
			
		||||
 | 
			
		||||
A BIG thanks to Andreas Merkle for the investigation and implementation of
 | 
			
		||||
a workaround to the GCC bug that drops method attributes from template methods
 | 
			
		||||
 | 
			
		||||
Written by Michael C. Miller.
 | 
			
		||||
 | 
			
		||||
I invest time and resources providing this open source code,
 | 
			
		||||
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------
 | 
			
		||||
This file is part of the Makuna/NeoPixelBus library.
 | 
			
		||||
 | 
			
		||||
NeoPixelBus is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU Lesser General Public License as
 | 
			
		||||
published by the Free Software Foundation, either version 3 of
 | 
			
		||||
the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
NeoPixelBus is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
License along with NeoPixel.  If not, see
 | 
			
		||||
<http://www.gnu.org/licenses/>.
 | 
			
		||||
-------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
 | 
			
		||||
#if defined(ARDUINO_ARCH_ESP32)
 | 
			
		||||
 | 
			
		||||
#include <algorithm>  
 | 
			
		||||
#include "esp_idf_version.h"
 | 
			
		||||
#include "NeoEsp32RmtHIMethod.h"
 | 
			
		||||
#include "soc/soc.h"
 | 
			
		||||
#include "soc/rmt_reg.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __riscv
 | 
			
		||||
#include "riscv/interrupt.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
 | 
			
		||||
#include "hal/rmt_ll.h"
 | 
			
		||||
#else
 | 
			
		||||
/* Shims for older ESP-IDF v3; we can safely assume original ESP32 */
 | 
			
		||||
#include "soc/rmt_struct.h"
 | 
			
		||||
 | 
			
		||||
// Selected RMT API functions borrowed from ESP-IDF v4.4.8
 | 
			
		||||
// components/hal/esp32/include/hal/rmt_ll.h
 | 
			
		||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    dev->conf_ch[channel].conf1.mem_rd_rst = 1;
 | 
			
		||||
    dev->conf_ch[channel].conf1.mem_rd_rst = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    dev->conf_ch[channel].conf1.tx_start = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    RMTMEM.chan[channel].data32[0].val = 0;
 | 
			
		||||
    dev->conf_ch[channel].conf1.tx_start = 0;
 | 
			
		||||
    dev->conf_ch[channel].conf1.mem_rd_rst = 1;
 | 
			
		||||
    dev->conf_ch[channel].conf1.mem_rd_rst = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_enable_pingpong(rmt_dev_t *dev, uint32_t channel, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    dev->apb_conf.mem_tx_wrap_en = enable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    dev->conf_ch[channel].conf1.tx_conti_mode = enable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline uint32_t rmt_ll_tx_get_channel_status(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    return dev->status_ch[channel];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
 | 
			
		||||
{
 | 
			
		||||
    dev->tx_lim_ch[channel].limit = limit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    if (enable) {
 | 
			
		||||
        dev->int_ena.val |= mask;
 | 
			
		||||
    } else {
 | 
			
		||||
        dev->int_ena.val &= ~mask;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_enable_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_ena.val &= ~(1 << (channel * 3));
 | 
			
		||||
    dev->int_ena.val |= (enable << (channel * 3));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_enable_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_ena.val &= ~(1 << (channel * 3 + 2));
 | 
			
		||||
    dev->int_ena.val |= (enable << (channel * 3 + 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_enable_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_ena.val &= ~(1 << (channel + 24));
 | 
			
		||||
    dev->int_ena.val |= (enable << (channel + 24));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_clear_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_clr.val = (1 << (channel * 3));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_clear_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_clr.val = (1 << (channel * 3 + 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline void rmt_ll_clear_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel)
 | 
			
		||||
{
 | 
			
		||||
    dev->int_clr.val = (1 << (channel + 24));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__attribute__((always_inline))
 | 
			
		||||
static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t status =  dev->int_st.val;
 | 
			
		||||
    return (status & 0xFF000000) >> 24;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// *********************************
 | 
			
		||||
// Select method for binding interrupt
 | 
			
		||||
//
 | 
			
		||||
// - If the Bluetooth driver has registered a high-level interrupt, piggyback on that API
 | 
			
		||||
// - If we're on a modern core, allocate the interrupt with the API (old cores are bugged)
 | 
			
		||||
// - Otherwise use the low-level hardware API to manually bind the interrupt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_BTDM_CTRL_HLI)
 | 
			
		||||
// Espressif's bluetooth driver offers a helpful sharing layer; bring in the interrupt management calls
 | 
			
		||||
#include "hal/interrupt_controller_hal.h"
 | 
			
		||||
extern "C" esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
 | 
			
		||||
 | 
			
		||||
#else /* !CONFIG_BTDM_CTRL_HLI*/
 | 
			
		||||
 | 
			
		||||
// Declare the our high-priority ISR handler
 | 
			
		||||
extern "C" void ld_include_hli_vectors_rmt();   // an object with an address, but no space
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
 | 
			
		||||
#include "soc/periph_defs.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Select level flag
 | 
			
		||||
#if defined(__riscv)
 | 
			
		||||
// RISCV chips don't block interrupts while scheduling; all we need to do is be higher than the WiFi ISR
 | 
			
		||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL3
 | 
			
		||||
#elif defined(CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5)
 | 
			
		||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL4
 | 
			
		||||
#else
 | 
			
		||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL5
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// ESP-IDF v3 cannot enable high priority interrupts through the API at all;
 | 
			
		||||
// and ESP-IDF v4 on XTensa cannot enable Level 5 due to incorrect interrupt descriptor tables
 | 
			
		||||
#if !defined(__XTENSA__) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) || ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5))
 | 
			
		||||
#define NEOESP32_RMT_CAN_USE_INTR_ALLOC
 | 
			
		||||
 | 
			
		||||
// XTensa cores require the assembly bridge
 | 
			
		||||
#ifdef __XTENSA__
 | 
			
		||||
#define HI_IRQ_HANDLER nullptr
 | 
			
		||||
#define HI_IRQ_HANDLER_ARG ld_include_hli_vectors_rmt
 | 
			
		||||
#else
 | 
			
		||||
#define HI_IRQ_HANDLER NeoEsp32RmtMethodIsr
 | 
			
		||||
#define HI_IRQ_HANDLER_ARG nullptr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
/* !CONFIG_BTDM_CTRL_HLI && !NEOESP32_RMT_CAN_USE_INTR_ALLOC */
 | 
			
		||||
// This is the index of the LV5 interrupt vector - see interrupt descriptor table in idf components/hal/esp32/interrupt_descriptor_table.c
 | 
			
		||||
#define ESP32_LV5_IRQ_INDEX 26
 | 
			
		||||
 | 
			
		||||
#endif  /* NEOESP32_RMT_CAN_USE_INTR_ALLOC */
 | 
			
		||||
#endif  /* CONFIG_BTDM_CTRL_HLI */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// RMT driver implementation
 | 
			
		||||
struct NeoEsp32RmtHIChannelState {
 | 
			
		||||
    uint32_t rmtBit0, rmtBit1;
 | 
			
		||||
    uint32_t resetDuration;
 | 
			
		||||
 | 
			
		||||
    const byte* txDataStart;    // data array
 | 
			
		||||
    const byte* txDataEnd;      // one past end
 | 
			
		||||
    const byte* txDataCurrent;      // current location
 | 
			
		||||
    size_t rmtOffset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Global variables
 | 
			
		||||
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
 | 
			
		||||
static intr_handle_t isrHandle = nullptr;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static NeoEsp32RmtHIChannelState** driverState = nullptr;
 | 
			
		||||
constexpr size_t rmtBatchSize =  RMT_MEM_ITEM_NUM / 2;
 | 
			
		||||
 | 
			
		||||
// Fill the RMT buffer memory
 | 
			
		||||
// This is implemented using many arguments instead of passing the structure object to ensure we do only one lookup
 | 
			
		||||
// All the arguments are passed in registers, so they don't need to be looked up again
 | 
			
		||||
static void IRAM_ATTR RmtFillBuffer(uint8_t channel, const byte** src_ptr, const byte* end, uint32_t bit0, uint32_t bit1, size_t* offset_ptr, size_t reserve) {
 | 
			
		||||
    // We assume that (rmtToWrite % 8) == 0
 | 
			
		||||
    size_t rmtToWrite = rmtBatchSize - reserve;
 | 
			
		||||
    rmt_item32_t* dest =(rmt_item32_t*)  &RMTMEM.chan[channel].data32[*offset_ptr + reserve]; // write directly in to RMT memory
 | 
			
		||||
    const byte* psrc = *src_ptr;
 | 
			
		||||
 | 
			
		||||
    *offset_ptr ^= rmtBatchSize;
 | 
			
		||||
 | 
			
		||||
    if (psrc != end) {
 | 
			
		||||
        while (rmtToWrite > 0) {
 | 
			
		||||
            uint8_t data = *psrc;
 | 
			
		||||
            for (uint8_t bit = 0; bit < 8; bit++)
 | 
			
		||||
            {
 | 
			
		||||
                dest->val = (data & 0x80) ? bit1 : bit0;
 | 
			
		||||
                dest++;
 | 
			
		||||
                data <<= 1;
 | 
			
		||||
            }
 | 
			
		||||
            rmtToWrite -= 8;
 | 
			
		||||
            psrc++;
 | 
			
		||||
 | 
			
		||||
            if (psrc == end) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        *src_ptr = psrc;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rmtToWrite > 0) {
 | 
			
		||||
        // Add end event
 | 
			
		||||
        rmt_item32_t bit0_val = {{.val = bit0 }};
 | 
			
		||||
        *dest = rmt_item32_t {{{ .duration0 = 0, .level0 = bit0_val.level1, .duration1 = 0, .level1 = bit0_val.level1 }}};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void IRAM_ATTR RmtStartWrite(uint8_t channel, NeoEsp32RmtHIChannelState& state) {
 | 
			
		||||
    // Reset context state
 | 
			
		||||
    state.rmtOffset = 0;
 | 
			
		||||
 | 
			
		||||
    // Fill the first part of the buffer with a reset event
 | 
			
		||||
    // FUTURE: we could do timing analysis with the last interrupt on this channel
 | 
			
		||||
    // Use 8 words to stay aligned with the buffer fill logic
 | 
			
		||||
    rmt_item32_t bit0_val = {{.val = state.rmtBit0 }};
 | 
			
		||||
    rmt_item32_t fill = {{{ .duration0 = 100, .level0 = bit0_val.level1, .duration1 = 100, .level1 = bit0_val.level1 }}};
 | 
			
		||||
    rmt_item32_t* dest = (rmt_item32_t*) &RMTMEM.chan[channel].data32[0];
 | 
			
		||||
    for (auto i = 0; i < 7; ++i) dest[i] = fill;
 | 
			
		||||
    fill.duration1 = state.resetDuration > 1400 ? (state.resetDuration - 1400) : 100;
 | 
			
		||||
    dest[7] = fill;
 | 
			
		||||
 | 
			
		||||
    // Fill the remaining buffer with real data
 | 
			
		||||
    RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 8);
 | 
			
		||||
    RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
 | 
			
		||||
 | 
			
		||||
    // Start operation
 | 
			
		||||
    rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
 | 
			
		||||
    rmt_ll_tx_reset_pointer(&RMT, channel);
 | 
			
		||||
    rmt_ll_tx_start(&RMT, channel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" void IRAM_ATTR NeoEsp32RmtMethodIsr(void *arg) {
 | 
			
		||||
    // Tx threshold interrupt
 | 
			
		||||
    uint32_t status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
 | 
			
		||||
    while (status) {
 | 
			
		||||
        uint8_t channel = __builtin_ffs(status) - 1;                
 | 
			
		||||
        if (driverState[channel]) {            
 | 
			
		||||
            // Normal case
 | 
			
		||||
            NeoEsp32RmtHIChannelState& state = *driverState[channel];
 | 
			
		||||
            RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Danger - another driver got invoked?
 | 
			
		||||
            rmt_ll_tx_stop(&RMT, channel);
 | 
			
		||||
        }
 | 
			
		||||
        rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
 | 
			
		||||
        status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Wrapper around the register analysis defines
 | 
			
		||||
// For all currently supported chips, this is constant for all channels; but this is not true of *all* ESP32
 | 
			
		||||
static inline bool _RmtStatusIsTransmitting(rmt_channel_t channel, uint32_t status) {
 | 
			
		||||
    uint32_t v;
 | 
			
		||||
    switch(channel) {
 | 
			
		||||
#ifdef RMT_STATE_CH0        
 | 
			
		||||
        case 0: v = (status >> RMT_STATE_CH0_S) & RMT_STATE_CH0_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH1
 | 
			
		||||
        case 1: v = (status >> RMT_STATE_CH1_S) & RMT_STATE_CH1_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH2
 | 
			
		||||
        case 2: v = (status >> RMT_STATE_CH2_S) & RMT_STATE_CH2_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH3        
 | 
			
		||||
        case 3: v = (status >> RMT_STATE_CH3_S) & RMT_STATE_CH3_V; break;
 | 
			
		||||
#endif        
 | 
			
		||||
#ifdef RMT_STATE_CH4
 | 
			
		||||
        case 4: v = (status >> RMT_STATE_CH4_S) & RMT_STATE_CH4_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH5
 | 
			
		||||
        case 5: v = (status >> RMT_STATE_CH5_S) & RMT_STATE_CH5_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH6
 | 
			
		||||
        case 6: v = (status >> RMT_STATE_CH6_S) & RMT_STATE_CH6_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RMT_STATE_CH7
 | 
			
		||||
        case 7: v = (status >> RMT_STATE_CH7_S) & RMT_STATE_CH7_V; break;
 | 
			
		||||
#endif
 | 
			
		||||
        default: v = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return v != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
esp_err_t NeoEsp32RmtHiMethodDriver::Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t reset) {
 | 
			
		||||
    // Validate channel number
 | 
			
		||||
    if (channel >= RMT_CHANNEL_MAX) {
 | 
			
		||||
        return ESP_ERR_INVALID_ARG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esp_err_t err = ESP_OK;
 | 
			
		||||
    if (!driverState) {
 | 
			
		||||
        // First time init
 | 
			
		||||
        driverState = reinterpret_cast<NeoEsp32RmtHIChannelState**>(heap_caps_calloc(RMT_CHANNEL_MAX, sizeof(NeoEsp32RmtHIChannelState*), MALLOC_CAP_INTERNAL));
 | 
			
		||||
        if (!driverState) return ESP_ERR_NO_MEM;
 | 
			
		||||
        
 | 
			
		||||
        // Ensure all interrupts are cleared before binding
 | 
			
		||||
        RMT.int_ena.val = 0;
 | 
			
		||||
        RMT.int_clr.val = 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
        // Bind interrupt handler
 | 
			
		||||
#if defined(CONFIG_BTDM_CTRL_HLI)
 | 
			
		||||
        // Bluetooth driver has taken the empty high-priority interrupt.  Fortunately, it allows us to
 | 
			
		||||
        // hook up another handler.
 | 
			
		||||
        err = hli_intr_register(NeoEsp32RmtMethodIsr, nullptr, (uintptr_t) &RMT.int_st, 0xFF000000);
 | 
			
		||||
        // 25 is the magic number of the bluetooth ISR on ESP32 - see soc/soc.h.
 | 
			
		||||
        intr_matrix_set(cpu_hal_get_core_id(), ETS_RMT_INTR_SOURCE, 25);
 | 
			
		||||
        intr_cntrl_ll_enable_interrupts(1<<25);
 | 
			
		||||
#elif defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
 | 
			
		||||
        // Use the platform code to allocate the interrupt
 | 
			
		||||
        // If we need the additional assembly bridge, we pass it as the "arg" to the IDF so it gets linked in       
 | 
			
		||||
        err = esp_intr_alloc(ETS_RMT_INTR_SOURCE, INT_LEVEL_FLAG | ESP_INTR_FLAG_IRAM, HI_IRQ_HANDLER, (void*) HI_IRQ_HANDLER_ARG, &isrHandle);
 | 
			
		||||
        //err = ESP_ERR_NOT_FINISHED;
 | 
			
		||||
#else
 | 
			
		||||
        // Broken IDF API does not allow us to reserve the interrupt; do it manually
 | 
			
		||||
        static volatile const void*  __attribute__((used)) pleaseLinkAssembly = (void*) ld_include_hli_vectors_rmt;
 | 
			
		||||
        intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, ESP32_LV5_IRQ_INDEX);
 | 
			
		||||
        ESP_INTR_ENABLE(ESP32_LV5_IRQ_INDEX);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (err != ESP_OK) {
 | 
			
		||||
            heap_caps_free(driverState);
 | 
			
		||||
            driverState = nullptr;
 | 
			
		||||
            return err;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (driverState[channel] != nullptr) {
 | 
			
		||||
        return ESP_ERR_INVALID_STATE;   // already in use
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIChannelState* state = reinterpret_cast<NeoEsp32RmtHIChannelState*>(heap_caps_calloc(1, sizeof(NeoEsp32RmtHIChannelState), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
 | 
			
		||||
    if (state == nullptr) {
 | 
			
		||||
        return ESP_ERR_NO_MEM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store timing information
 | 
			
		||||
    state->rmtBit0 = rmtBit0;
 | 
			
		||||
    state->rmtBit1 = rmtBit1;
 | 
			
		||||
    state->resetDuration = reset;
 | 
			
		||||
 | 
			
		||||
    // Initialize hardware
 | 
			
		||||
    rmt_ll_tx_stop(&RMT, channel);
 | 
			
		||||
    rmt_ll_tx_reset_pointer(&RMT, channel);
 | 
			
		||||
    rmt_ll_enable_tx_err_interrupt(&RMT, channel, false);
 | 
			
		||||
    rmt_ll_enable_tx_end_interrupt(&RMT, channel, false);
 | 
			
		||||
    rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
 | 
			
		||||
    rmt_ll_clear_tx_err_interrupt(&RMT, channel);
 | 
			
		||||
    rmt_ll_clear_tx_end_interrupt(&RMT, channel);
 | 
			
		||||
    rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
 | 
			
		||||
    
 | 
			
		||||
    rmt_ll_tx_enable_loop(&RMT, channel, false);
 | 
			
		||||
    rmt_ll_tx_enable_pingpong(&RMT, channel, true);
 | 
			
		||||
    rmt_ll_tx_set_limit(&RMT, channel, rmtBatchSize);
 | 
			
		||||
 | 
			
		||||
    driverState[channel] = state;
 | 
			
		||||
 | 
			
		||||
    rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true);
 | 
			
		||||
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t  NeoEsp32RmtHiMethodDriver::Uninstall(rmt_channel_t channel) {
 | 
			
		||||
    if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIChannelState* state = driverState[channel];
 | 
			
		||||
 | 
			
		||||
    WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
    // Done or not, we're out of here
 | 
			
		||||
    rmt_ll_tx_stop(&RMT, channel);
 | 
			
		||||
    rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
 | 
			
		||||
    driverState[channel] = nullptr;
 | 
			
		||||
    heap_caps_free(state);
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_BTDM_CTRL_HLI)  /* Cannot unbind from bluetooth ISR */
 | 
			
		||||
    // Turn off the driver ISR and release global state if none are left
 | 
			
		||||
    for (uint8_t channelIndex = 0; channelIndex < RMT_CHANNEL_MAX; ++channelIndex) {
 | 
			
		||||
        if (driverState[channelIndex]) return ESP_OK; // done
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
 | 
			
		||||
    esp_intr_free(isrHandle);
 | 
			
		||||
#else
 | 
			
		||||
    ESP_INTR_DISABLE(ESP32_LV5_IRQ_INDEX);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    heap_caps_free(driverState);
 | 
			
		||||
    driverState = nullptr;
 | 
			
		||||
#endif /* !defined(CONFIG_BTDM_CTRL_HLI) */
 | 
			
		||||
 | 
			
		||||
    return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t  NeoEsp32RmtHiMethodDriver::Write(rmt_channel_t channel, const uint8_t *src, size_t src_size) {
 | 
			
		||||
    if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIChannelState& state = *driverState[channel];
 | 
			
		||||
    esp_err_t result = WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
 | 
			
		||||
 | 
			
		||||
    if (result == ESP_OK) {
 | 
			
		||||
        state.txDataStart = src;
 | 
			
		||||
        state.txDataCurrent = src;
 | 
			
		||||
        state.txDataEnd = src + src_size;
 | 
			
		||||
        RmtStartWrite(channel, state);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickType_t wait_time) {
 | 
			
		||||
    if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
 | 
			
		||||
 | 
			
		||||
    NeoEsp32RmtHIChannelState& state = *driverState[channel];
 | 
			
		||||
    // yield-wait until wait_time
 | 
			
		||||
    esp_err_t rv = ESP_OK;
 | 
			
		||||
    uint32_t status;
 | 
			
		||||
    while(1) {
 | 
			
		||||
        status = rmt_ll_tx_get_channel_status(&RMT, channel);
 | 
			
		||||
        if (!_RmtStatusIsTransmitting(channel, status)) break;
 | 
			
		||||
        if (wait_time == 0) { rv = ESP_ERR_TIMEOUT; break; };
 | 
			
		||||
 | 
			
		||||
        TickType_t sleep = std::min(wait_time, (TickType_t) 5);
 | 
			
		||||
        vTaskDelay(sleep);
 | 
			
		||||
        wait_time -= sleep;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1899
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1899
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "wled",
 | 
			
		||||
  "version": "0.16.0-alpha",
 | 
			
		||||
  "version": "0.15.0",
 | 
			
		||||
  "description": "Tools for WLED project",
 | 
			
		||||
  "main": "tools/cdata.js",
 | 
			
		||||
  "directories": {
 | 
			
		||||
@@ -14,19 +14,19 @@
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git+https://github.com/wled-dev/WLED.git"
 | 
			
		||||
    "url": "git+https://github.com/Aircoookie/WLED.git"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "",
 | 
			
		||||
  "license": "ISC",
 | 
			
		||||
  "bugs": {
 | 
			
		||||
    "url": "https://github.com/wled-dev/WLED/issues"
 | 
			
		||||
    "url": "https://github.com/Aircoookie/WLED/issues"
 | 
			
		||||
  },
 | 
			
		||||
  "homepage": "https://github.com/wled-dev/WLED#readme",
 | 
			
		||||
  "homepage": "https://github.com/Aircoookie/WLED#readme",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "clean-css": "^5.3.3",
 | 
			
		||||
    "html-minifier-terser": "^7.2.0",
 | 
			
		||||
    "web-resource-inliner": "^7.0.0",
 | 
			
		||||
    "nodemon": "^3.1.9"
 | 
			
		||||
    "inliner": "^1.13.1",
 | 
			
		||||
    "nodemon": "^3.1.7"
 | 
			
		||||
  },
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "node": ">=20.0.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,3 @@
 | 
			
		||||
Import("env")
 | 
			
		||||
import shutil
 | 
			
		||||
Import('env')
 | 
			
		||||
 | 
			
		||||
node_ex = shutil.which("node")
 | 
			
		||||
# Check if Node.js is installed and present in PATH if it failed, abort the build
 | 
			
		||||
if node_ex is None:
 | 
			
		||||
    print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
 | 
			
		||||
    exitCode = env.Execute("null")
 | 
			
		||||
    exit(exitCode)
 | 
			
		||||
else:
 | 
			
		||||
    # Install the necessary node packages for the pre-build asset bundling script
 | 
			
		||||
    print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
 | 
			
		||||
    env.Execute("npm ci")
 | 
			
		||||
 | 
			
		||||
    # Call the bundling script
 | 
			
		||||
    exitCode = env.Execute("npm run build")
 | 
			
		||||
 | 
			
		||||
    # If it failed, abort the build
 | 
			
		||||
    if (exitCode):
 | 
			
		||||
      print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
 | 
			
		||||
      exit(exitCode)
 | 
			
		||||
env.Execute("npm run build")
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
Import('env')
 | 
			
		||||
from collections import deque
 | 
			
		||||
from pathlib import Path   # For OS-agnostic path manipulation
 | 
			
		||||
from click import secho
 | 
			
		||||
from SCons.Script import Exit
 | 
			
		||||
from platformio.builder.tools.piolib import LibBuilderBase
 | 
			
		||||
 | 
			
		||||
usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
 | 
			
		||||
 | 
			
		||||
# Utility functions
 | 
			
		||||
def find_usermod(mod: str) -> Path:
 | 
			
		||||
  """Locate this library in the usermods folder.
 | 
			
		||||
     We do this to avoid needing to rename a bunch of folders;
 | 
			
		||||
     this could be removed later
 | 
			
		||||
  """
 | 
			
		||||
  # Check name match
 | 
			
		||||
  mp = usermod_dir / mod
 | 
			
		||||
  if mp.exists():
 | 
			
		||||
    return mp
 | 
			
		||||
  mp = usermod_dir / f"{mod}_v2"
 | 
			
		||||
  if mp.exists():
 | 
			
		||||
    return mp
 | 
			
		||||
  mp = usermod_dir / f"usermod_v2_{mod}"
 | 
			
		||||
  if mp.exists():
 | 
			
		||||
    return mp
 | 
			
		||||
  raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
 | 
			
		||||
 | 
			
		||||
def is_wled_module(dep: LibBuilderBase) -> bool:
 | 
			
		||||
  """Returns true if the specified library is a wled module
 | 
			
		||||
  """
 | 
			
		||||
  return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-")
 | 
			
		||||
 | 
			
		||||
## Script starts here
 | 
			
		||||
# Process usermod option
 | 
			
		||||
usermods = env.GetProjectOption("custom_usermods","")
 | 
			
		||||
 | 
			
		||||
# Handle "all usermods" case
 | 
			
		||||
if usermods == '*':
 | 
			
		||||
  usermods = [f.name for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
 | 
			
		||||
else:
 | 
			
		||||
  usermods = usermods.split()
 | 
			
		||||
 | 
			
		||||
if usermods:
 | 
			
		||||
  # Inject usermods in to project lib_deps
 | 
			
		||||
  symlinks = [f"symlink://{find_usermod(mod).resolve()}" for mod in usermods]
 | 
			
		||||
  env.GetProjectConfig().set("env:" + env['PIOENV'], 'lib_deps', env.GetProjectOption('lib_deps') + symlinks)
 | 
			
		||||
 | 
			
		||||
# Utility function for assembling usermod include paths
 | 
			
		||||
def cached_add_includes(dep, dep_cache: set, includes: deque):
 | 
			
		||||
  """ Add dep's include paths to includes if it's not in the cache """
 | 
			
		||||
  if dep not in dep_cache:
 | 
			
		||||
    dep_cache.add(dep)
 | 
			
		||||
    for include in dep.get_include_dirs():
 | 
			
		||||
      if include not in includes:
 | 
			
		||||
        includes.appendleft(include)
 | 
			
		||||
      if usermod_dir not in Path(dep.src_dir).parents:
 | 
			
		||||
        # Recurse, but only for NON-usermods
 | 
			
		||||
        for subdep in dep.depbuilders:
 | 
			
		||||
          cached_add_includes(subdep, dep_cache, includes)
 | 
			
		||||
 | 
			
		||||
# Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies
 | 
			
		||||
# Save the old value
 | 
			
		||||
old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
 | 
			
		||||
 | 
			
		||||
# Our new wrapper
 | 
			
		||||
def wrapped_ConfigureProjectLibBuilder(xenv):
 | 
			
		||||
  # Call the wrapped function
 | 
			
		||||
  result = old_ConfigureProjectLibBuilder.clone(xenv)()
 | 
			
		||||
 | 
			
		||||
  # Fix up include paths
 | 
			
		||||
  # In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder
 | 
			
		||||
  wled_dir = xenv["PROJECT_SRC_DIR"]
 | 
			
		||||
  # Build a list of dependency include dirs
 | 
			
		||||
  # TODO: Find out if this is the order that PlatformIO/SCons puts them in??
 | 
			
		||||
  processed_deps = set()
 | 
			
		||||
  extra_include_dirs = deque()  # Deque used for fast prepend
 | 
			
		||||
  for dep in result.depbuilders:
 | 
			
		||||
     cached_add_includes(dep, processed_deps, extra_include_dirs)
 | 
			
		||||
 | 
			
		||||
  wled_deps = [dep for dep in result.depbuilders if is_wled_module(dep)]
 | 
			
		||||
 | 
			
		||||
  broken_usermods = []
 | 
			
		||||
  for dep in wled_deps:
 | 
			
		||||
    # Add the wled folder to the include path
 | 
			
		||||
    dep.env.PrependUnique(CPPPATH=str(wled_dir))
 | 
			
		||||
    # Add WLED's own dependencies
 | 
			
		||||
    for dir in extra_include_dirs:
 | 
			
		||||
      dep.env.PrependUnique(CPPPATH=str(dir))
 | 
			
		||||
    # Enforce that libArchive is not set; we must link them directly to the executable
 | 
			
		||||
    if dep.lib_archive:
 | 
			
		||||
      broken_usermods.append(dep)
 | 
			
		||||
 | 
			
		||||
  if broken_usermods:
 | 
			
		||||
    broken_usermods = [usermod.name for usermod in broken_usermods]
 | 
			
		||||
    secho(
 | 
			
		||||
      f"ERROR: libArchive=false is missing on usermod(s) {' '.join(broken_usermods)} -- modules will not compile in correctly",
 | 
			
		||||
      fg="red",
 | 
			
		||||
      err=True)
 | 
			
		||||
    Exit(1)
 | 
			
		||||
 | 
			
		||||
  # Save the depbuilders list for later validation
 | 
			
		||||
  xenv.Replace(WLED_MODULES=wled_deps)
 | 
			
		||||
 | 
			
		||||
  return result
 | 
			
		||||
 | 
			
		||||
# Apply the wrapper
 | 
			
		||||
env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder")
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
Import('env')
 | 
			
		||||
import subprocess
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
def get_github_repo():
 | 
			
		||||
    """Extract GitHub repository name from git remote URL.
 | 
			
		||||
    
 | 
			
		||||
    Uses the remote that the current branch tracks, falling back to 'origin'.
 | 
			
		||||
    This handles cases where repositories have multiple remotes or where the
 | 
			
		||||
    main remote is not named 'origin'.
 | 
			
		||||
    
 | 
			
		||||
    Returns:
 | 
			
		||||
        str: Repository name in 'owner/repo' format for GitHub repos,
 | 
			
		||||
             'unknown' for non-GitHub repos, missing git CLI, or any errors.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        remote_name = 'origin'  # Default fallback
 | 
			
		||||
        
 | 
			
		||||
        # Try to get the remote for the current branch
 | 
			
		||||
        try:
 | 
			
		||||
            # Get current branch name
 | 
			
		||||
            branch_result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], 
 | 
			
		||||
                                         capture_output=True, text=True, check=True)
 | 
			
		||||
            current_branch = branch_result.stdout.strip()
 | 
			
		||||
            
 | 
			
		||||
            # Get the remote for the current branch
 | 
			
		||||
            remote_result = subprocess.run(['git', 'config', f'branch.{current_branch}.remote'], 
 | 
			
		||||
                                         capture_output=True, text=True, check=True)
 | 
			
		||||
            tracked_remote = remote_result.stdout.strip()
 | 
			
		||||
            
 | 
			
		||||
            # Use the tracked remote if we found one
 | 
			
		||||
            if tracked_remote:
 | 
			
		||||
                remote_name = tracked_remote
 | 
			
		||||
        except subprocess.CalledProcessError:
 | 
			
		||||
            # If branch config lookup fails, continue with 'origin' as fallback
 | 
			
		||||
            pass
 | 
			
		||||
        
 | 
			
		||||
        # Get the remote URL for the determined remote
 | 
			
		||||
        result = subprocess.run(['git', 'remote', 'get-url', remote_name], 
 | 
			
		||||
                              capture_output=True, text=True, check=True)
 | 
			
		||||
        remote_url = result.stdout.strip()
 | 
			
		||||
        
 | 
			
		||||
        # Check if it's a GitHub URL
 | 
			
		||||
        if 'github.com' not in remote_url.lower():
 | 
			
		||||
            return 'unknown'
 | 
			
		||||
        
 | 
			
		||||
        # Parse GitHub URL patterns:
 | 
			
		||||
        # https://github.com/owner/repo.git
 | 
			
		||||
        # git@github.com:owner/repo.git
 | 
			
		||||
        # https://github.com/owner/repo
 | 
			
		||||
        
 | 
			
		||||
        # Remove .git suffix if present
 | 
			
		||||
        if remote_url.endswith('.git'):
 | 
			
		||||
            remote_url = remote_url[:-4]
 | 
			
		||||
        
 | 
			
		||||
        # Handle HTTPS URLs
 | 
			
		||||
        https_match = re.search(r'github\.com/([^/]+/[^/]+)', remote_url, re.IGNORECASE)
 | 
			
		||||
        if https_match:
 | 
			
		||||
            return https_match.group(1)
 | 
			
		||||
        
 | 
			
		||||
        # Handle SSH URLs
 | 
			
		||||
        ssh_match = re.search(r'github\.com:([^/]+/[^/]+)', remote_url, re.IGNORECASE)
 | 
			
		||||
        if ssh_match:
 | 
			
		||||
            return ssh_match.group(1)
 | 
			
		||||
        
 | 
			
		||||
        return 'unknown'
 | 
			
		||||
        
 | 
			
		||||
    except FileNotFoundError:
 | 
			
		||||
        # Git CLI is not installed or not in PATH
 | 
			
		||||
        return 'unknown'
 | 
			
		||||
    except subprocess.CalledProcessError:
 | 
			
		||||
        # Git command failed (e.g., not a git repo, no remote, etc.)
 | 
			
		||||
        return 'unknown'
 | 
			
		||||
    except Exception:
 | 
			
		||||
        # Any other unexpected error
 | 
			
		||||
        return 'unknown'
 | 
			
		||||
 | 
			
		||||
repo = get_github_repo()
 | 
			
		||||
env.Append(BUILD_FLAGS=[f'-DWLED_REPO=\\"{repo}\\"'])
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
import re
 | 
			
		||||
from pathlib import Path   # For OS-agnostic path manipulation
 | 
			
		||||
from typing import Iterable
 | 
			
		||||
from click import secho
 | 
			
		||||
from SCons.Script import Action, Exit
 | 
			
		||||
from platformio.builder.tools.piolib import LibBuilderBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_wled_module(env, dep: LibBuilderBase) -> bool:
 | 
			
		||||
  """Returns true if the specified library is a wled module
 | 
			
		||||
  """
 | 
			
		||||
  usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
 | 
			
		||||
  return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_lines(p: Path):
 | 
			
		||||
    """ Read in the contents of a file for analysis """
 | 
			
		||||
    with p.open("r", encoding="utf-8", errors="ignore") as f:
 | 
			
		||||
        return f.readlines()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_map_file_objects(map_file: list[str], dirs: Iterable[str]) -> set[str]:
 | 
			
		||||
    """ Identify which dirs contributed to the final build
 | 
			
		||||
 | 
			
		||||
        Returns the (sub)set of dirs that are found in the output ELF
 | 
			
		||||
    """
 | 
			
		||||
    # Pattern to match symbols in object directories
 | 
			
		||||
    # Join directories into alternation
 | 
			
		||||
    usermod_dir_regex = "|".join([re.escape(dir) for dir in dirs])
 | 
			
		||||
    # Matches nonzero address, any size, and any path in a matching directory
 | 
			
		||||
    object_path_regex = re.compile(r"0x0*[1-9a-f][0-9a-f]*\s+0x[0-9a-f]+\s+\S+[/\\](" + usermod_dir_regex + r")[/\\]\S+\.o")
 | 
			
		||||
 | 
			
		||||
    found = set()
 | 
			
		||||
    for line in map_file:
 | 
			
		||||
        matches = object_path_regex.findall(line)
 | 
			
		||||
        for m in matches:
 | 
			
		||||
            found.add(m)
 | 
			
		||||
    return found
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def count_usermod_objects(map_file: list[str]) -> int:
 | 
			
		||||
    """ Returns the number of usermod objects in the usermod list """
 | 
			
		||||
    # Count the number of entries in the usermods table section
 | 
			
		||||
    return len([x for x in map_file if ".dtors.tbl.usermods.1" in x])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_map_file(source, target, env):
 | 
			
		||||
    """ Validate that all modules appear in the output build """
 | 
			
		||||
    build_dir = Path(env.subst("$BUILD_DIR"))
 | 
			
		||||
    map_file_path = build_dir /  env.subst("${PROGNAME}.map")
 | 
			
		||||
 | 
			
		||||
    if not map_file_path.exists():
 | 
			
		||||
        secho(f"ERROR: Map file not found: {map_file_path}", fg="red", err=True)
 | 
			
		||||
        Exit(1)
 | 
			
		||||
 | 
			
		||||
    # Identify the WLED module builders, set by load_usermods.py
 | 
			
		||||
    module_lib_builders = env['WLED_MODULES']
 | 
			
		||||
 | 
			
		||||
    # Extract the values we care about
 | 
			
		||||
    modules = {Path(builder.build_dir).name: builder.name for builder in module_lib_builders}
 | 
			
		||||
    secho(f"INFO: {len(modules)} libraries linked as WLED optional/user modules")
 | 
			
		||||
 | 
			
		||||
    # Now parse the map file
 | 
			
		||||
    map_file_contents = read_lines(map_file_path)
 | 
			
		||||
    usermod_object_count = count_usermod_objects(map_file_contents)
 | 
			
		||||
    secho(f"INFO: {usermod_object_count} usermod object entries")
 | 
			
		||||
 | 
			
		||||
    confirmed_modules = check_map_file_objects(map_file_contents, modules.keys())
 | 
			
		||||
    missing_modules = [modname for mdir, modname in modules.items() if mdir not in confirmed_modules]
 | 
			
		||||
    if missing_modules:
 | 
			
		||||
        secho(
 | 
			
		||||
            f"ERROR: No object files from {missing_modules} found in linked output!",
 | 
			
		||||
            fg="red",
 | 
			
		||||
            err=True)
 | 
			
		||||
        Exit(1)
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
env.Append(LINKFLAGS=[env.subst("-Wl,--Map=${BUILD_DIR}/${PROGNAME}.map")])
 | 
			
		||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking linked optional modules (usermods) in map file'))
 | 
			
		||||
							
								
								
									
										212
									
								
								platformio.ini
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								platformio.ini
									
									
									
									
									
								
							@@ -10,7 +10,7 @@
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# CI/release binaries
 | 
			
		||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover, usermods
 | 
			
		||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover
 | 
			
		||||
 | 
			
		||||
src_dir  = ./wled00
 | 
			
		||||
data_dir = ./wled00/data
 | 
			
		||||
@@ -111,13 +111,10 @@ ldscript_4m1m = eagle.flash.4m1m.ld
 | 
			
		||||
[scripts_defaults]
 | 
			
		||||
extra_scripts =
 | 
			
		||||
  pre:pio-scripts/set_version.py
 | 
			
		||||
  pre:pio-scripts/set_repo.py
 | 
			
		||||
  post:pio-scripts/output_bins.py
 | 
			
		||||
  post:pio-scripts/strip-floats.py
 | 
			
		||||
  pre:pio-scripts/user_config_copy.py
 | 
			
		||||
  pre:pio-scripts/load_usermods.py
 | 
			
		||||
  pre:pio-scripts/build_ui.py
 | 
			
		||||
  post:pio-scripts/validate_modules.py  ;; double-check the build output usermods
 | 
			
		||||
  ; post:pio-scripts/obj-dump.py  ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
@@ -141,9 +138,9 @@ lib_compat_mode = strict
 | 
			
		||||
lib_deps =
 | 
			
		||||
    fastled/FastLED @ 3.6.0
 | 
			
		||||
    IRremoteESP8266 @ 2.8.2
 | 
			
		||||
    makuna/NeoPixelBus @ 2.8.3
 | 
			
		||||
    https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2
 | 
			
		||||
    marvinroger/AsyncMqttClient @ 0.9.0
 | 
			
		||||
    makuna/NeoPixelBus @ 2.8.0
 | 
			
		||||
    #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
 | 
			
		||||
    https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
 | 
			
		||||
  # for I2C interface
 | 
			
		||||
    ;Wire
 | 
			
		||||
  # ESP-NOW library
 | 
			
		||||
@@ -160,18 +157,25 @@ lib_deps =
 | 
			
		||||
    ;adafruit/Adafruit BMP280 Library @ 2.1.0
 | 
			
		||||
    ;adafruit/Adafruit CCS811 Library @ 1.0.4
 | 
			
		||||
    ;adafruit/Adafruit Si7021 Library @ 1.4.0
 | 
			
		||||
  #For ADS1115 sensor uncomment following
 | 
			
		||||
    ;adafruit/Adafruit BusIO @ 1.13.2
 | 
			
		||||
    ;adafruit/Adafruit ADS1X15 @ 2.4.0
 | 
			
		||||
  #For MAX1704x Lipo Monitor / Fuel Gauge uncomment following
 | 
			
		||||
    ; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
 | 
			
		||||
    ; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
 | 
			
		||||
  #For MPU6050 IMU uncomment follwoing
 | 
			
		||||
    ;electroniccats/MPU6050 @1.0.1
 | 
			
		||||
  # For -D USERMOD_ANIMARTRIX
 | 
			
		||||
  # CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms!
 | 
			
		||||
    ;https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7
 | 
			
		||||
  # SHT85
 | 
			
		||||
    ;robtillaart/SHT85@~0.3.3
 | 
			
		||||
  # Audioreactive usermod
 | 
			
		||||
    ;kosme/arduinoFFT @ 2.0.1
 | 
			
		||||
 | 
			
		||||
extra_scripts = ${scripts_defaults.extra_scripts}
 | 
			
		||||
 | 
			
		||||
[esp8266]
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags =
 | 
			
		||||
  -DESP8266
 | 
			
		||||
  -DFP_IN_IROM
 | 
			
		||||
@@ -231,110 +235,104 @@ lib_deps_compat =
 | 
			
		||||
  IRremoteESP8266 @ 2.8.2
 | 
			
		||||
  makuna/NeoPixelBus @ 2.7.9
 | 
			
		||||
  https://github.com/blazoncek/QuickESPNow.git#optional-debug
 | 
			
		||||
  https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
 | 
			
		||||
  https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
 | 
			
		||||
 | 
			
		||||
[esp32_all_variants]
 | 
			
		||||
lib_deps =
 | 
			
		||||
  esp32async/AsyncTCP @ 3.4.7
 | 
			
		||||
  bitbank2/AnimatedGIF@^1.4.7
 | 
			
		||||
  https://github.com/Aircoookie/GifDecoder#bc3af18
 | 
			
		||||
build_flags =
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_STACK_SIZE=8192
 | 
			
		||||
  -D WLED_ENABLE_GIF
 | 
			
		||||
 | 
			
		||||
[esp32]
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform_packages =
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${esp32_idf_V4.build_flags}
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
 | 
			
		||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
 | 
			
		||||
platform = espressif32@3.5.0
 | 
			
		||||
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
 | 
			
		||||
build_flags = -g
 | 
			
		||||
  -DARDUINO_ARCH_ESP32
 | 
			
		||||
  #-DCONFIG_LITTLEFS_FOR_IDF_3_2
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
 | 
			
		||||
  -D LOROL_LITTLEFS
 | 
			
		||||
  ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
 | 
			
		||||
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
 | 
			
		||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
 | 
			
		||||
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
 | 
			
		||||
big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv     ;; 1.8MB firmware, 256KB filesystem, coredump support
 | 
			
		||||
large_partitions = tools/WLED_ESP32_8MB.csv
 | 
			
		||||
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
 | 
			
		||||
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs
 | 
			
		||||
# additional build flags for audioreactive - must be applied globally
 | 
			
		||||
AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
 | 
			
		||||
AR_lib_deps =  ;; for pre-usermod-library platformio_override compatibility
 | 
			
		||||
 | 
			
		||||
lib_deps =
 | 
			
		||||
  https://github.com/lorol/LITTLEFS.git
 | 
			
		||||
  https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
 | 
			
		||||
  ${env.lib_deps}
 | 
			
		||||
# additional build flags for audioreactive
 | 
			
		||||
AR_build_flags = -D USERMOD_AUDIOREACTIVE 
 | 
			
		||||
  -D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
 | 
			
		||||
AR_lib_deps = kosme/arduinoFFT @ 2.0.1
 | 
			
		||||
 | 
			
		||||
[esp32_idf_V4]
 | 
			
		||||
;; build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
 | 
			
		||||
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
 | 
			
		||||
;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already.
 | 
			
		||||
;;
 | 
			
		||||
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
 | 
			
		||||
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
 | 
			
		||||
 | 
			
		||||
;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
 | 
			
		||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
platform = espressif32@ ~6.3.2
 | 
			
		||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
 | 
			
		||||
build_flags = -g
 | 
			
		||||
  -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
 | 
			
		||||
  -DARDUINO_ARCH_ESP32 -DESP32
 | 
			
		||||
  ${esp32_all_variants.build_flags}
 | 
			
		||||
  -D WLED_ENABLE_DMX_INPUT
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
 | 
			
		||||
lib_deps =
 | 
			
		||||
  ${esp32_all_variants.lib_deps}
 | 
			
		||||
  https://github.com/someweisguy/esp_dmx.git#47db25d
 | 
			
		||||
  https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
 | 
			
		||||
  ${env.lib_deps}
 | 
			
		||||
 | 
			
		||||
[esp32s2]
 | 
			
		||||
;; generic definitions for all ESP32-S2 boards
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
platform = espressif32@ ~6.3.2
 | 
			
		||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
 | 
			
		||||
build_flags = -g
 | 
			
		||||
  -DARDUINO_ARCH_ESP32
 | 
			
		||||
  -DARDUINO_ARCH_ESP32S2
 | 
			
		||||
  -DCONFIG_IDF_TARGET_ESP32S2=1
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
 | 
			
		||||
  -DCO
 | 
			
		||||
  -DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
 | 
			
		||||
  ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
 | 
			
		||||
  ;; ARDUINO_USB_CDC_ON_BOOT
 | 
			
		||||
  ${esp32_idf_V4.build_flags}
 | 
			
		||||
lib_deps =
 | 
			
		||||
  ${esp32_idf_V4.lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs
 | 
			
		||||
  https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
 | 
			
		||||
  ${env.lib_deps}
 | 
			
		||||
 | 
			
		||||
[esp32c3]
 | 
			
		||||
;; generic definitions for all ESP32-C3 boards
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
platform = espressif32@ ~6.3.2
 | 
			
		||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
 | 
			
		||||
build_flags = -g
 | 
			
		||||
  -DARDUINO_ARCH_ESP32
 | 
			
		||||
  -DARDUINO_ARCH_ESP32C3
 | 
			
		||||
  -DCONFIG_IDF_TARGET_ESP32C3=1
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -DCO
 | 
			
		||||
  -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
 | 
			
		||||
  ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
 | 
			
		||||
  ;; ARDUINO_USB_CDC_ON_BOOT
 | 
			
		||||
  ${esp32_idf_V4.build_flags}
 | 
			
		||||
lib_deps =
 | 
			
		||||
  ${esp32_idf_V4.lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
  https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
 | 
			
		||||
  ${env.lib_deps}
 | 
			
		||||
 | 
			
		||||
[esp32s3]
 | 
			
		||||
;; generic definitions for all ESP32-S3 boards
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
platform = espressif32@ ~6.3.2
 | 
			
		||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
 | 
			
		||||
build_flags = -g
 | 
			
		||||
  -DESP32
 | 
			
		||||
  -DARDUINO_ARCH_ESP32
 | 
			
		||||
  -DARDUINO_ARCH_ESP32S3
 | 
			
		||||
  -DCONFIG_IDF_TARGET_ESP32S3=1
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
 | 
			
		||||
  -DCO
 | 
			
		||||
  ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
 | 
			
		||||
  ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
 | 
			
		||||
  ${esp32_idf_V4.build_flags}
 | 
			
		||||
lib_deps =
 | 
			
		||||
  ${esp32_idf_V4.lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.large_partitions}   ;; default partioning for 8MB flash - can be overridden in build envs
 | 
			
		||||
  https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
 | 
			
		||||
  ${env.lib_deps}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
@@ -348,7 +346,6 @@ platform_packages = ${common.platform_packages}
 | 
			
		||||
board_build.ldscript = ${common.ldscript_4m1m}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
monitor_filters = esp8266_exception_decoder
 | 
			
		||||
 | 
			
		||||
@@ -358,15 +355,13 @@ extends = env:nodemcuv2
 | 
			
		||||
platform = ${esp8266.platform_compat}
 | 
			
		||||
platform_packages = ${esp8266.platform_packages_compat}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
 | 
			
		||||
 | 
			
		||||
[env:nodemcuv2_160]
 | 
			
		||||
extends = env:nodemcuv2
 | 
			
		||||
board_build.f_cpu = 160000000L
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
  -D USERMOD_AUDIOREACTIVE
 | 
			
		||||
 | 
			
		||||
[env:esp8266_2m]
 | 
			
		||||
board = esp_wroom_02
 | 
			
		||||
@@ -375,8 +370,6 @@ platform_packages = ${common.platform_packages}
 | 
			
		||||
board_build.ldscript = ${common.ldscript_2m512k}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:esp8266_2m_compat]
 | 
			
		||||
@@ -385,16 +378,12 @@ extends = env:esp8266_2m
 | 
			
		||||
platform = ${esp8266.platform_compat}
 | 
			
		||||
platform_packages = ${esp8266.platform_packages_compat}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
 | 
			
		||||
[env:esp8266_2m_160]
 | 
			
		||||
extends = env:esp8266_2m
 | 
			
		||||
board_build.f_cpu = 160000000L
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
  -D USERMOD_AUDIOREACTIVE
 | 
			
		||||
 | 
			
		||||
[env:esp01_1m_full]
 | 
			
		||||
board = esp01_1m
 | 
			
		||||
@@ -404,8 +393,6 @@ board_build.ldscript = ${common.ldscript_1m128k}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
 | 
			
		||||
  ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:esp01_1m_full_compat]
 | 
			
		||||
@@ -414,51 +401,51 @@ extends = env:esp01_1m_full
 | 
			
		||||
platform = ${esp8266.platform_compat}
 | 
			
		||||
platform_packages = ${esp8266.platform_packages_compat}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
 | 
			
		||||
[env:esp01_1m_full_160]
 | 
			
		||||
extends = env:esp01_1m_full
 | 
			
		||||
board_build.f_cpu = 160000000L
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA
 | 
			
		||||
  -D USERMOD_AUDIOREACTIVE
 | 
			
		||||
  ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM1D
 | 
			
		||||
  -D WLED_DISABLE_PARTICLESYSTEM2D
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
 | 
			
		||||
[env:esp32dev]
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
              -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
[env:esp32dev_8M]
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
platform_packages = ${esp32_idf_V4.platform_packages}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.partitions = ${esp32.large_partitions}
 | 
			
		||||
board_upload.flash_size = 8MB
 | 
			
		||||
board_upload.maximum_size = 8388608
 | 
			
		||||
; board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
; board_build.flash_mode = qio
 | 
			
		||||
 | 
			
		||||
[env:esp32dev_16M]
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
platform_packages = ${esp32_idf_V4.platform_packages}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.partitions = ${esp32.extreme_partitions}
 | 
			
		||||
board_upload.flash_size = 16MB
 | 
			
		||||
@@ -466,34 +453,53 @@ board_upload.maximum_size = 16777216
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
;[env:esp32dev_audioreactive]
 | 
			
		||||
;board = esp32dev
 | 
			
		||||
;platform = ${esp32.platform}
 | 
			
		||||
;platform_packages = ${esp32.platform_packages}
 | 
			
		||||
;build_unflags = ${common.build_unflags}
 | 
			
		||||
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
;  ${esp32.AR_build_flags}
 | 
			
		||||
;lib_deps = ${esp32.lib_deps}
 | 
			
		||||
;  ${esp32.AR_lib_deps}
 | 
			
		||||
;monitor_filters = esp32_exception_decoder
 | 
			
		||||
;board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
;; board_build.f_flash = 80000000L
 | 
			
		||||
;; board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
[env:esp32_eth]
 | 
			
		||||
board = esp32-poe
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
 | 
			
		||||
;  -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
[env:esp32_wrover]
 | 
			
		||||
extends = esp32_idf_V4
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform_packages = ${esp32_idf_V4.platform_packages}
 | 
			
		||||
board = ttgo-t7-v14-mini32
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
board_build.partitions = ${esp32.extended_partitions}
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
 | 
			
		||||
  -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
 | 
			
		||||
  -D DATA_PINS=25
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:esp32c3dev]
 | 
			
		||||
extends = esp32c3
 | 
			
		||||
platform = ${esp32c3.platform}
 | 
			
		||||
platform_packages = ${esp32c3.platform_packages}
 | 
			
		||||
framework = arduino
 | 
			
		||||
board = esp32-c3-devkitm-1
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
@@ -511,15 +517,17 @@ lib_deps = ${esp32c3.lib_deps}
 | 
			
		||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
 | 
			
		||||
board_build.arduino.memory_type = qio_opi     ;; use with PSRAM: 8MB or 16MB
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages = ${esp32s3.platform_packages}
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
 | 
			
		||||
  -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
  ;-D ARDUINO_USB_CDC_ON_BOOT=0  ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
 | 
			
		||||
  -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
 | 
			
		||||
  -DBOARD_HAS_PSRAM
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.extreme_partitions}
 | 
			
		||||
board_upload.flash_size = 16MB
 | 
			
		||||
board_upload.maximum_size = 16777216
 | 
			
		||||
@@ -532,15 +540,17 @@ monitor_filters = esp32_exception_decoder
 | 
			
		||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
 | 
			
		||||
board_build.arduino.memory_type = qio_opi     ;; use with PSRAM: 8MB or 16MB
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages = ${esp32s3.platform_packages}
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
 | 
			
		||||
  -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
  ;-D ARDUINO_USB_CDC_ON_BOOT=0  ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
 | 
			
		||||
  -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
 | 
			
		||||
  -DBOARD_HAS_PSRAM
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.large_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
@@ -550,10 +560,10 @@ monitor_filters = esp32_exception_decoder
 | 
			
		||||
;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1
 | 
			
		||||
;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi)
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages = ${esp32s3.platform_packages}
 | 
			
		||||
board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
 | 
			
		||||
board_build.arduino.memory_type = opi_opi
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
 | 
			
		||||
  -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
@@ -563,8 +573,10 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
 | 
			
		||||
  -D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED
 | 
			
		||||
  -D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1
 | 
			
		||||
  -D WLED_DEBUG
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
  -D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4  ;; I2S mic
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
 | 
			
		||||
board_build.partitions = ${esp32.extreme_partitions}
 | 
			
		||||
board_upload.flash_size = 16MB
 | 
			
		||||
@@ -575,15 +587,17 @@ monitor_filters = esp32_exception_decoder
 | 
			
		||||
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
 | 
			
		||||
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM 
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages = ${esp32s3.platform_packages}
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
 | 
			
		||||
  -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
 | 
			
		||||
  -DBOARD_HAS_PSRAM
 | 
			
		||||
  -DLOLIN_WIFI_FIX ; seems to work much better with this
 | 
			
		||||
  -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
@@ -591,11 +605,11 @@ monitor_filters = esp32_exception_decoder
 | 
			
		||||
 | 
			
		||||
[env:lolin_s2_mini]
 | 
			
		||||
platform = ${esp32s2.platform}
 | 
			
		||||
platform_packages = ${esp32s2.platform_packages}
 | 
			
		||||
board = lolin_s2_mini
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
custom_usermods = audioreactive
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
 | 
			
		||||
  -DARDUINO_USB_CDC_ON_BOOT=1
 | 
			
		||||
@@ -604,6 +618,7 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
 | 
			
		||||
  -DBOARD_HAS_PSRAM
 | 
			
		||||
  -DLOLIN_WIFI_FIX ; seems to work much better with this
 | 
			
		||||
  -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
  -D CONFIG_ASYNC_TCP_USE_WDT=0
 | 
			
		||||
  -D DATA_PINS=16
 | 
			
		||||
  -D HW_PIN_SCL=35
 | 
			
		||||
  -D HW_PIN_SDA=33
 | 
			
		||||
@@ -611,17 +626,6 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
 | 
			
		||||
  -D HW_PIN_DATASPI=11
 | 
			
		||||
  -D HW_PIN_MISOSPI=9
 | 
			
		||||
;  -D STATUSLED=15
 | 
			
		||||
  ${esp32.AR_build_flags}
 | 
			
		||||
lib_deps = ${esp32s2.lib_deps}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[env:usermods]
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_USERMODS\"
 | 
			
		||||
  -DTOUCH_CS=9
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
custom_usermods = *   ; Expands to all usermods in usermods folder
 | 
			
		||||
board_build.partitions = ${esp32.extreme_partitions}  ; We're gonna need a bigger boat
 | 
			
		||||
  ${esp32.AR_lib_deps}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
 | 
			
		||||
 | 
			
		||||
[platformio]
 | 
			
		||||
default_envs = WLED_generic8266_1M, esp32dev_V4_dio80  # put the name(s) of your own build environment here. You can define as many as you need
 | 
			
		||||
default_envs = WLED_tasmota_1M  # define as many as you need
 | 
			
		||||
 | 
			
		||||
#----------
 | 
			
		||||
# SAMPLE
 | 
			
		||||
@@ -28,11 +28,13 @@ 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
 | 
			
		||||
;  ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE
 | 
			
		||||
;  bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following
 | 
			
		||||
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
 | 
			
		||||
;
 | 
			
		||||
; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above.
 | 
			
		||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
 | 
			
		||||
; 
 | 
			
		||||
; Set a release name that may be used to distinguish required binary for flashing
 | 
			
		||||
;   -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
 | 
			
		||||
@@ -139,6 +141,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
 | 
			
		||||
;   -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
 | 
			
		||||
;
 | 
			
		||||
; Use Audioreactive usermod and configure I2S microphone
 | 
			
		||||
;   -D USERMOD_AUDIOREACTIVE
 | 
			
		||||
;   -D AUDIOPIN=-1
 | 
			
		||||
;   -D DMTYPE=1     # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
 | 
			
		||||
;   -D I2S_SDPIN=36
 | 
			
		||||
@@ -159,17 +162,12 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
 | 
			
		||||
;   -D SERVERNAME="\"WLED\""
 | 
			
		||||
;   
 | 
			
		||||
; set the number of LEDs
 | 
			
		||||
;   -D PIXEL_COUNTS=30
 | 
			
		||||
;   -D DEFAULT_LED_COUNT=30
 | 
			
		||||
; or this for multiple outputs
 | 
			
		||||
;   -D PIXEL_COUNTS=30,30
 | 
			
		||||
;
 | 
			
		||||
; set the default LED type
 | 
			
		||||
;   -D LED_TYPES=22    # see const.h (TYPE_xxxx)
 | 
			
		||||
; or this for multiple outputs
 | 
			
		||||
;   -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
 | 
			
		||||
;
 | 
			
		||||
; set default color order of your led strip
 | 
			
		||||
;   -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
 | 
			
		||||
;   -D DEFAULT_LED_TYPE=22    # see const.h (TYPE_xxxx)
 | 
			
		||||
;
 | 
			
		||||
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs
 | 
			
		||||
;   -D ABL_MILLIAMPS_DEFAULT=850
 | 
			
		||||
@@ -178,6 +176,9 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
 | 
			
		||||
; enable IR by setting remote type
 | 
			
		||||
;   -D IRTYPE=0   # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
 | 
			
		||||
;   
 | 
			
		||||
; set default color order of your led strip
 | 
			
		||||
;   -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
 | 
			
		||||
;
 | 
			
		||||
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
 | 
			
		||||
;   -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue   # needed only for classic ESP32 rev.1
 | 
			
		||||
;
 | 
			
		||||
@@ -235,11 +236,14 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLE
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:esp32dev_qio80]
 | 
			
		||||
extends = env:esp32dev  # we want to extend the existing esp32dev environment (and define only updated options)
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
 | 
			
		||||
@@ -247,23 +251,26 @@ board_build.flash_mode = qio
 | 
			
		||||
;; experimental ESP32 env using ESP-IDF V4.4.x
 | 
			
		||||
;; Warning: this build environment is not stable!!
 | 
			
		||||
;; please erase your device before installing.
 | 
			
		||||
extends = esp32_idf_V4  # based on newer "esp-idf V4" platform environment
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform_packages = ${esp32_idf_V4.platform_packages}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags}  ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}  ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions}
 | 
			
		||||
board_build.partitions = ${esp32_idf_V4.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
[env:esp32s2_saola]
 | 
			
		||||
extends = esp32s2
 | 
			
		||||
board = esp32-s2-saola-1
 | 
			
		||||
platform = ${esp32s2.platform}
 | 
			
		||||
platform_packages = ${esp32s2.platform_packages}
 | 
			
		||||
framework = arduino
 | 
			
		||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
upload_speed = 460800
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s2.build_flags}
 | 
			
		||||
  ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
 | 
			
		||||
  -DARDUINO_USB_CDC_ON_BOOT=1
 | 
			
		||||
@@ -272,7 +279,7 @@ lib_deps = ${esp32s2.lib_deps}
 | 
			
		||||
[env:esp32s3dev_8MB_PSRAM_qspi]
 | 
			
		||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
 | 
			
		||||
extends = env:esp32s3dev_8MB_PSRAM_opi
 | 
			
		||||
;board = um_tinys3 ;    -> needs workaround from https://github.com/wled-dev/WLED/pull/2905#issuecomment-1328049860
 | 
			
		||||
;board = um_tinys3 ;    -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
 | 
			
		||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
 | 
			
		||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or  4MB
 | 
			
		||||
 | 
			
		||||
@@ -300,7 +307,7 @@ platform = ${common.platform_wled_default}
 | 
			
		||||
platform_packages = ${common.platform_packages}
 | 
			
		||||
board_build.ldscript = ${common.ldscript_4m1m}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:d1_mini_debug]
 | 
			
		||||
@@ -355,44 +362,35 @@ board_upload.flash_size = 2MB
 | 
			
		||||
board_upload.maximum_size = 2097152
 | 
			
		||||
 | 
			
		||||
[env:wemos_shield_esp32]
 | 
			
		||||
extends = esp32              ;; use default esp32 platform
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
upload_speed = 460800
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
 | 
			
		||||
  -D DATA_PINS=16
 | 
			
		||||
  -D RLYPIN=19
 | 
			
		||||
  -D BTNPIN=17
 | 
			
		||||
  -D IRPIN=18
 | 
			
		||||
  -UWLED_USE_MY_CONFIG
 | 
			
		||||
  -D UWLED_USE_MY_CONFIG
 | 
			
		||||
  -D USERMOD_DALLASTEMPERATURE
 | 
			
		||||
  -D USERMOD_FOUR_LINE_DISPLAY
 | 
			
		||||
  -D TEMPERATURE_PIN=23
 | 
			
		||||
  -D USERMOD_AUDIOREACTIVE
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
  OneWire@~2.3.5          ;; needed for USERMOD_DALLASTEMPERATURE
 | 
			
		||||
  olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
 | 
			
		||||
  OneWire@~2.3.5
 | 
			
		||||
  olikraus/U8g2 @ ^2.28.8
 | 
			
		||||
  https://github.com/blazoncek/arduinoFFT.git
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
 | 
			
		||||
[env:esp32_pico-D4]
 | 
			
		||||
extends = esp32              ;; use default esp32 platform
 | 
			
		||||
board = pico32               ;; pico32-D4 is different from the standard esp32dev
 | 
			
		||||
                             ;; hardware details from https://github.com/srg74/WLED-ESP32-pico
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"'
 | 
			
		||||
  -D WLED_DISABLE_ADALIGHT   ;; no serial-to-USB chip on this board - better to disable serial protocols
 | 
			
		||||
  -D DATA_PINS=2,18          ;; LED pins
 | 
			
		||||
  -D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR
 | 
			
		||||
  -D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default
 | 
			
		||||
  ;; Audioreactive settings for on-board microphone (ICS-43432)
 | 
			
		||||
  -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14
 | 
			
		||||
  -D SR_SQUELCH=5 -D SR_GAIN=30
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
 | 
			
		||||
[env:m5atom]
 | 
			
		||||
extends = env:esp32dev  # we want to extend the existing esp32dev environment (and define only updated options)
 | 
			
		||||
board = esp32dev
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
 | 
			
		||||
lib_deps = ${esp32.lib_deps}
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
 | 
			
		||||
[env:sp501e]
 | 
			
		||||
board = esp_wroom_02
 | 
			
		||||
@@ -415,7 +413,7 @@ platform_packages = ${common.platform_packages}
 | 
			
		||||
board_build.ldscript = ${common.ldscript_2m512k}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
 | 
			
		||||
                                            -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
 | 
			
		||||
                                            -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:Athom_15w_RGBCW]        ;15w bulb
 | 
			
		||||
@@ -425,7 +423,7 @@ platform_packages = ${common.platform_packages}
 | 
			
		||||
board_build.ldscript = ${common.ldscript_2m512k}
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
 | 
			
		||||
                                            -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
 | 
			
		||||
                                            -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
 | 
			
		||||
lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
 | 
			
		||||
[env:Athom_3Pin_Controller]        ;small controller with only data
 | 
			
		||||
@@ -491,15 +489,17 @@ lib_deps = ${esp8266.lib_deps}
 | 
			
		||||
# EleksTube-IPS
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
[env:elekstube_ips]
 | 
			
		||||
extends = esp32              ;; use default esp32 platform
 | 
			
		||||
board = esp32dev
 | 
			
		||||
platform = ${esp32.platform}
 | 
			
		||||
platform_packages = ${esp32.platform_packages}
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
 | 
			
		||||
  -D USERMOD_RTC
 | 
			
		||||
  -D USERMOD_ELEKSTUBE_IPS
 | 
			
		||||
  -D DATA_PINS=12
 | 
			
		||||
  -D RLYPIN=27
 | 
			
		||||
  -D BTNPIN=34
 | 
			
		||||
  -D PIXEL_COUNTS=6
 | 
			
		||||
  -D DEFAULT_LED_COUNT=6
 | 
			
		||||
  # Display config
 | 
			
		||||
  -D ST7789_DRIVER
 | 
			
		||||
  -D TFT_WIDTH=135
 | 
			
		||||
@@ -513,99 +513,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
 | 
			
		||||
  -D SPI_FREQUENCY=40000000
 | 
			
		||||
  -D USER_SETUP_LOADED
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# Usermod examples
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# 433MHz RF remote example for esp32dev
 | 
			
		||||
[env:esp32dev_usermod_RF433]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433
 | 
			
		||||
lib_deps = ${env:esp32dev.lib_deps}
 | 
			
		||||
  sui77/rc-switch @ 2.6.4
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
# Hub75 examples
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
[env:esp32dev_hub75]
 | 
			
		||||
board = esp32dev
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
platform = ${esp32_idf_V4.platform}
 | 
			
		||||
platform_packages =
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags}
 | 
			
		||||
  -D WLED_RELEASE_NAME=\"ESP32_hub75\"
 | 
			
		||||
  -D WLED_ENABLE_HUB75MATRIX -D NO_GFX
 | 
			
		||||
  -D WLED_DEBUG_BUS
 | 
			
		||||
  ; -D WLED_DEBUG
 | 
			
		||||
lib_deps = ${esp32_idf_V4.lib_deps}
 | 
			
		||||
  https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git#3.0.11
 | 
			
		||||
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
lib_deps =
 | 
			
		||||
  ${esp32.lib_deps}
 | 
			
		||||
  TFT_eSPI @ ^2.3.70
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.flash_mode = dio
 | 
			
		||||
 | 
			
		||||
[env:esp32dev_hub75_forum_pinout]
 | 
			
		||||
extends = env:esp32dev_hub75
 | 
			
		||||
build_flags = ${common.build_flags}
 | 
			
		||||
              -D WLED_RELEASE_NAME=\"ESP32_hub75_forum_pinout\"
 | 
			
		||||
              -D WLED_ENABLE_HUB75MATRIX -D NO_GFX 
 | 
			
		||||
              -D ESP32_FORUM_PINOUT ;; enable for SmartMatrix default pins
 | 
			
		||||
              -D WLED_DEBUG_BUS
 | 
			
		||||
; -D WLED_DEBUG
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[env:adafruit_matrixportal_esp32s3]
 | 
			
		||||
; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75
 | 
			
		||||
board = adafruit_matrixportal_esp32s3
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages =
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
 | 
			
		||||
  -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
 | 
			
		||||
  -DBOARD_HAS_PSRAM
 | 
			
		||||
  -DLOLIN_WIFI_FIX ; seems to work much better with this
 | 
			
		||||
  -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
  -D WLED_ENABLE_HUB75MATRIX -D NO_GFX
 | 
			
		||||
  -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
 | 
			
		||||
  -D ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3
 | 
			
		||||
  -D WLED_DEBUG_BUS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
  https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a ;; S3_LCD_DIV_NUM fix
 | 
			
		||||
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
 | 
			
		||||
[env:esp32S3_PSRAM_HUB75]
 | 
			
		||||
;; MOONHUB HUB75 adapter board
 | 
			
		||||
board = lilygo-t7-s3
 | 
			
		||||
platform = ${esp32s3.platform}
 | 
			
		||||
platform_packages =
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
build_unflags = ${common.build_unflags}
 | 
			
		||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"esp32S3_16MB_PSRAM_HUB75\"
 | 
			
		||||
              -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
 | 
			
		||||
              -DBOARD_HAS_PSRAM
 | 
			
		||||
              -DLOLIN_WIFI_FIX ; seems to work much better with this
 | 
			
		||||
              -D WLED_WATCHDOG_TIMEOUT=0
 | 
			
		||||
              -D WLED_ENABLE_HUB75MATRIX -D NO_GFX 
 | 
			
		||||
              -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
 | 
			
		||||
              -D MOONHUB_S3_PINOUT ;; HUB75 pinout
 | 
			
		||||
              -D WLED_DEBUG_BUS
 | 
			
		||||
 | 
			
		||||
lib_deps = ${esp32s3.lib_deps}
 | 
			
		||||
           https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a ;; S3_LCD_DIV_NUM fix
 | 
			
		||||
 | 
			
		||||
board_build.partitions = ${esp32.default_partitions}
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.flash_mode = qio
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								readme.md
									
									
									
									
									
								
							@@ -1,20 +1,18 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <img src="/images/wled_logo_akemi.png">
 | 
			
		||||
  <a href="https://github.com/wled-dev/WLED/releases"><img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a>
 | 
			
		||||
  <a href="https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE"><img src="https://img.shields.io/github/license/wled-dev/wled?color=blue&style=flat-square"></a>
 | 
			
		||||
  <a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a>
 | 
			
		||||
  <a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
 | 
			
		||||
  <a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
 | 
			
		||||
  <a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
 | 
			
		||||
  <a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
 | 
			
		||||
  <a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
 | 
			
		||||
  <a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
 | 
			
		||||
  <a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
 | 
			
		||||
 | 
			
		||||
  </p>
 | 
			
		||||
 | 
			
		||||
# Welcome to WLED! ✨
 | 
			
		||||
# Welcome to my project WLED! ✨
 | 
			
		||||
 | 
			
		||||
A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
 | 
			
		||||
 | 
			
		||||
Originally created by [Aircoookie](https://github.com/Aircoookie)
 | 
			
		||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
 | 
			
		||||
 | 
			
		||||
## ⚙️ Features
 | 
			
		||||
- WS2812FX library with more than 100 special effects  
 | 
			
		||||
@@ -23,7 +21,7 @@ Originally created by [Aircoookie](https://github.com/Aircoookie)
 | 
			
		||||
- Segments to set different effects and colors to user defined parts of the LED string  
 | 
			
		||||
- Settings page - configuration via the network  
 | 
			
		||||
- Access Point and station mode - automatic failsafe AP  
 | 
			
		||||
- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance
 | 
			
		||||
- Up to 10 LED outputs per instance
 | 
			
		||||
- Support for RGBW strips  
 | 
			
		||||
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.  
 | 
			
		||||
- Presets can be used to automatically execute API calls  
 | 
			
		||||
@@ -34,7 +32,7 @@ Originally created by [Aircoookie](https://github.com/Aircoookie)
 | 
			
		||||
- Filesystem-based config for easier backup of presets and settings  
 | 
			
		||||
 | 
			
		||||
## 💡 Supported light control interfaces
 | 
			
		||||
- WLED app for [Android](https://play.google.com/store/apps/details?id=ca.cgagnier.wlednativeandroid) and [iOS](https://apps.apple.com/gb/app/wled-native/id6446207239)
 | 
			
		||||
- WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033)
 | 
			
		||||
- JSON and HTTP request APIs  
 | 
			
		||||
- MQTT   
 | 
			
		||||
- E1.31, Art-Net, DDP and TPM2.net
 | 
			
		||||
@@ -65,7 +63,6 @@ See [here](https://kno.wled.ge/basics/compatible-hardware)!
 | 
			
		||||
 | 
			
		||||
Licensed under the EUPL v1.2 license  
 | 
			
		||||
Credits [here](https://kno.wled.ge/about/contributors/)!
 | 
			
		||||
CORS proxy by [Corsfix](https://corsfix.com/)
 | 
			
		||||
 | 
			
		||||
Join the Discord server to discuss everything about WLED!
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
platformio>=6.1.17
 | 
			
		||||
platformio
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,28 @@
 | 
			
		||||
#
 | 
			
		||||
# This file is autogenerated by pip-compile with Python 3.11
 | 
			
		||||
# This file is autogenerated by pip-compile with Python 3.12
 | 
			
		||||
# by the following command:
 | 
			
		||||
#
 | 
			
		||||
#    pip-compile requirements.in
 | 
			
		||||
#    pip-compile
 | 
			
		||||
#
 | 
			
		||||
ajsonrpc==1.2.0
 | 
			
		||||
    # via platformio
 | 
			
		||||
anyio==4.8.0
 | 
			
		||||
anyio==4.6.0
 | 
			
		||||
    # via starlette
 | 
			
		||||
bottle==0.13.2
 | 
			
		||||
bottle==0.13.1
 | 
			
		||||
    # via platformio
 | 
			
		||||
certifi==2025.1.31
 | 
			
		||||
certifi==2024.8.30
 | 
			
		||||
    # via requests
 | 
			
		||||
charset-normalizer==3.4.1
 | 
			
		||||
charset-normalizer==3.3.2
 | 
			
		||||
    # via requests
 | 
			
		||||
click==8.1.8
 | 
			
		||||
click==8.1.7
 | 
			
		||||
    # via
 | 
			
		||||
    #   platformio
 | 
			
		||||
    #   uvicorn
 | 
			
		||||
colorama==0.4.6
 | 
			
		||||
    # via platformio
 | 
			
		||||
h11==0.16.0
 | 
			
		||||
    # via
 | 
			
		||||
    #   click
 | 
			
		||||
    #   platformio
 | 
			
		||||
h11==0.14.0
 | 
			
		||||
    # via
 | 
			
		||||
    #   uvicorn
 | 
			
		||||
    #   wsproto
 | 
			
		||||
@@ -28,31 +30,29 @@ idna==3.10
 | 
			
		||||
    # via
 | 
			
		||||
    #   anyio
 | 
			
		||||
    #   requests
 | 
			
		||||
marshmallow==3.26.1
 | 
			
		||||
marshmallow==3.22.0
 | 
			
		||||
    # via platformio
 | 
			
		||||
packaging==24.2
 | 
			
		||||
packaging==24.1
 | 
			
		||||
    # via marshmallow
 | 
			
		||||
platformio==6.1.17
 | 
			
		||||
platformio==6.1.16
 | 
			
		||||
    # via -r requirements.in
 | 
			
		||||
pyelftools==0.32
 | 
			
		||||
pyelftools==0.31
 | 
			
		||||
    # via platformio
 | 
			
		||||
pyserial==3.5
 | 
			
		||||
    # via platformio
 | 
			
		||||
requests==2.32.4
 | 
			
		||||
requests==2.32.3
 | 
			
		||||
    # via platformio
 | 
			
		||||
semantic-version==2.10.0
 | 
			
		||||
    # via platformio
 | 
			
		||||
sniffio==1.3.1
 | 
			
		||||
    # via anyio
 | 
			
		||||
starlette==0.45.3
 | 
			
		||||
starlette==0.39.1
 | 
			
		||||
    # via platformio
 | 
			
		||||
tabulate==0.9.0
 | 
			
		||||
    # via platformio
 | 
			
		||||
typing-extensions==4.12.2
 | 
			
		||||
    # via anyio
 | 
			
		||||
urllib3==2.5.0
 | 
			
		||||
urllib3==2.2.3
 | 
			
		||||
    # via requests
 | 
			
		||||
uvicorn==0.34.0
 | 
			
		||||
uvicorn==0.30.6
 | 
			
		||||
    # via platformio
 | 
			
		||||
wsproto==1.2.0
 | 
			
		||||
    # via platformio
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -17,7 +17,7 @@
 | 
			
		||||
 | 
			
		||||
const fs = require("node:fs");
 | 
			
		||||
const path = require("path");
 | 
			
		||||
const inline = require("web-resource-inliner");
 | 
			
		||||
const inliner = require("inliner");
 | 
			
		||||
const zlib = require("node:zlib");
 | 
			
		||||
const CleanCSS = require("clean-css");
 | 
			
		||||
const minifyHtml = require("html-minifier-terser").minify;
 | 
			
		||||
@@ -89,7 +89,7 @@ function adoptVersionAndRepo(html) {
 | 
			
		||||
    repoUrl = repoUrl.replace(/^git\+/, "");
 | 
			
		||||
    repoUrl = repoUrl.replace(/\.git$/, "");
 | 
			
		||||
    html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
 | 
			
		||||
    html = html.replaceAll("https://github.com/wled-dev/WLED", repoUrl);
 | 
			
		||||
    html = html.replaceAll("https://github.com/Aircoookie/WLED", repoUrl);
 | 
			
		||||
  }
 | 
			
		||||
  let version = packageJson.version;
 | 
			
		||||
  if (version) {
 | 
			
		||||
@@ -128,12 +128,7 @@ async function minify(str, type = "plain") {
 | 
			
		||||
 | 
			
		||||
async function writeHtmlGzipped(sourceFile, resultFile, page) {
 | 
			
		||||
  console.info("Reading " + sourceFile);
 | 
			
		||||
  inline.html({
 | 
			
		||||
    fileContent: fs.readFileSync(sourceFile, "utf8"),
 | 
			
		||||
    relativeTo: path.dirname(sourceFile),
 | 
			
		||||
    strict: true,
 | 
			
		||||
  },
 | 
			
		||||
    async function (error, html) {
 | 
			
		||||
  new inliner(sourceFile, async function (error, html) {
 | 
			
		||||
    if (error) throw error;
 | 
			
		||||
 | 
			
		||||
    html = adoptVersionAndRepo(html);
 | 
			
		||||
@@ -143,7 +138,7 @@ async function writeHtmlGzipped(sourceFile, resultFile, page) {
 | 
			
		||||
    console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
 | 
			
		||||
    const array = hexdump(result);
 | 
			
		||||
    let src = singleHeader;
 | 
			
		||||
      src += `const uint16_t PAGE_${page}_length = ${result.length};\n`;
 | 
			
		||||
    src += `const uint16_t PAGE_${page}_L = ${result.length};\n`;
 | 
			
		||||
    src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
 | 
			
		||||
    console.info("Writing " + resultFile);
 | 
			
		||||
    fs.writeFileSync(resultFile, src);
 | 
			
		||||
@@ -244,22 +239,9 @@ if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.ar
 | 
			
		||||
 | 
			
		||||
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
 | 
			
		||||
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
 | 
			
		||||
//writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
 | 
			
		||||
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
 | 
			
		||||
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
 | 
			
		||||
 | 
			
		||||
writeChunks(
 | 
			
		||||
  "wled00/data/cpal",
 | 
			
		||||
  [
 | 
			
		||||
    {
 | 
			
		||||
      file: "cpal.htm",
 | 
			
		||||
      name: "PAGE_cpal",
 | 
			
		||||
      method: "gzip",
 | 
			
		||||
      filter: "html-minify"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "wled00/html_cpal.h"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
writeChunks(
 | 
			
		||||
  "wled00/data",
 | 
			
		||||
  [
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ read -a JSON_TINY_TARGETS <<< $(replicate "json/nodes")
 | 
			
		||||
read -a JSON_SMALL_TARGETS <<< $(replicate "json/info")
 | 
			
		||||
read -a JSON_LARGE_TARGETS <<< $(replicate "json/si")
 | 
			
		||||
read -a JSON_LARGER_TARGETS <<< $(replicate "json/fxdata")
 | 
			
		||||
read -a INDEX_TARGETS <<< $(replicate "")
 | 
			
		||||
 | 
			
		||||
# Expand target URLS to full arguments for curl
 | 
			
		||||
TARGETS=(${TARGET_STR[@]})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										329
									
								
								tools/wled-tools
									
									
									
									
									
								
							
							
						
						
									
										329
									
								
								tools/wled-tools
									
									
									
									
									
								
							@@ -1,329 +0,0 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# WLED Tools
 | 
			
		||||
# A utility for managing WLED devices in a local network
 | 
			
		||||
# https://github.com/wled/WLED
 | 
			
		||||
 | 
			
		||||
# Color Definitions
 | 
			
		||||
GREEN="\e[32m"
 | 
			
		||||
RED="\e[31m"
 | 
			
		||||
BLUE="\e[34m"
 | 
			
		||||
YELLOW="\e[33m"
 | 
			
		||||
RESET="\e[0m"
 | 
			
		||||
 | 
			
		||||
# Logging function
 | 
			
		||||
log() {
 | 
			
		||||
    local category="$1"
 | 
			
		||||
    local color="$2"
 | 
			
		||||
    local text="$3"
 | 
			
		||||
 | 
			
		||||
    if [ "$quiet" = true ]; then
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ -t 1 ]; then  # Check if output is a terminal
 | 
			
		||||
        echo -e "${color}[${category}]${RESET} ${text}"
 | 
			
		||||
    else
 | 
			
		||||
        echo "[${category}] ${text}"
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Fetch a URL to a destination file, validating status codes.
 | 
			
		||||
# Usage: fetch "<url>" "<dest or empty>" "200 404"
 | 
			
		||||
fetch() {
 | 
			
		||||
    local url="$1"
 | 
			
		||||
    local dest="$2"
 | 
			
		||||
    local accepted="${3:-200}"
 | 
			
		||||
 | 
			
		||||
    # If no dest given, just discard body
 | 
			
		||||
    local out
 | 
			
		||||
    if [ -n "$dest" ]; then
 | 
			
		||||
        # Write to ".tmp" files first, then move when success, to ensure we don't write partial files
 | 
			
		||||
        out="${dest}.tmp"
 | 
			
		||||
    else
 | 
			
		||||
        out="/dev/null"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    response=$(curl --connect-timeout 5 --max-time 30 -s -w "%{http_code}" -o "$out" "$url")
 | 
			
		||||
    local curl_exit_code=$?
 | 
			
		||||
 | 
			
		||||
    if [ $curl_exit_code -ne 0 ]; then
 | 
			
		||||
        [ -n "$dest" ] && rm -f "$out"
 | 
			
		||||
        log "ERROR" "$RED" "Connection error during request to $url (curl exit code: $curl_exit_code)."
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    for code in $accepted; do
 | 
			
		||||
        if [ "$response" = "$code" ]; then
 | 
			
		||||
            # Accepted; only persist body for 2xx responses
 | 
			
		||||
            if [ -n "$dest" ]; then
 | 
			
		||||
                if [[ "$response" =~ ^2 ]]; then
 | 
			
		||||
                    mv "$out" "$dest"
 | 
			
		||||
                else
 | 
			
		||||
                    rm -f "$out"
 | 
			
		||||
                fi
 | 
			
		||||
            fi
 | 
			
		||||
            return 0
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    # not accepted
 | 
			
		||||
    [ -n "$dest" ] && rm -f "$out"
 | 
			
		||||
    log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)."
 | 
			
		||||
    return 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# POST a file to a URL, validating status codes.
 | 
			
		||||
# Usage: post_file "<url>" "<file>" "200"
 | 
			
		||||
post_file() {
 | 
			
		||||
    local url="$1"
 | 
			
		||||
    local file="$2"
 | 
			
		||||
    local accepted="${3:-200}"
 | 
			
		||||
 | 
			
		||||
    response=$(curl --connect-timeout 5 --max-time 300 -s -w "%{http_code}" -o /dev/null -X POST -F "file=@$file" "$url")
 | 
			
		||||
    local curl_exit_code=$?
 | 
			
		||||
 | 
			
		||||
    if [ $curl_exit_code -ne 0 ]; then
 | 
			
		||||
        log "ERROR" "$RED" "Connection error during POST to $url (curl exit code: $curl_exit_code)."
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    for code in $accepted; do
 | 
			
		||||
        if [ "$response" -eq "$code" ]; then
 | 
			
		||||
            return 0
 | 
			
		||||
        fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)."
 | 
			
		||||
    return 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Print help message
 | 
			
		||||
show_help() {
 | 
			
		||||
    cat << EOF
 | 
			
		||||
Usage: wled-tools.sh [OPTIONS] COMMAND [ARGS...]
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
  -h, --help              Show this help message and exit.
 | 
			
		||||
  -t, --target <IP/Host>  Specify a single WLED device by IP address or hostname.
 | 
			
		||||
  -D, --discover          Discover multiple WLED devices using mDNS.
 | 
			
		||||
  -d, --directory <Path>  Specify a directory for saving backups (default: working directory).
 | 
			
		||||
  -f, --firmware <File>   Specify the firmware file for updating devices.
 | 
			
		||||
  -q, --quiet             Suppress logging output (also makes discover output hostnames only).
 | 
			
		||||
 | 
			
		||||
Commands:
 | 
			
		||||
  backup      Backup the current state of a WLED device or multiple discovered devices.
 | 
			
		||||
  update      Update the firmware of a WLED device or multiple discovered devices.
 | 
			
		||||
  discover    Discover WLED devices using mDNS and list their IP addresses and names.
 | 
			
		||||
 | 
			
		||||
Examples:
 | 
			
		||||
  # Discover all WLED devices on the network
 | 
			
		||||
  ./wled-tools discover
 | 
			
		||||
 | 
			
		||||
  # Backup a specific WLED device
 | 
			
		||||
  ./wled-tools -t 192.168.1.100 backup
 | 
			
		||||
 | 
			
		||||
  # Backup all discovered WLED devices to a specific directory
 | 
			
		||||
  ./wled-tools -D -d /path/to/backups backup
 | 
			
		||||
 | 
			
		||||
  # Update firmware on all discovered WLED devices
 | 
			
		||||
  ./wled-tools -D -f /path/to/firmware.bin update
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Discover devices using mDNS
 | 
			
		||||
discover_devices() {  
 | 
			
		||||
    if ! command -v avahi-browse &> /dev/null; then  
 | 
			
		||||
        log "ERROR" "$RED" "'avahi-browse' is required but not installed, please install avahi-utils using your preferred package manager."
 | 
			
		||||
        exit 1  
 | 
			
		||||
    fi  
 | 
			
		||||
 | 
			
		||||
    # Map avahi responses to strings seperated by 0x1F (unit separator)
 | 
			
		||||
    mapfile -t raw_devices < <(avahi-browse _wled._tcp --terminate -r -p | awk -F';' '/^=/ {print $7"\x1F"$8"\x1F"$9}')  
 | 
			
		||||
 | 
			
		||||
    local devices_array=()  
 | 
			
		||||
    for device in "${raw_devices[@]}"; do  
 | 
			
		||||
        IFS=$'\x1F' read -r hostname address port <<< "$device"  
 | 
			
		||||
        devices_array+=("$hostname" "$address" "$port")  
 | 
			
		||||
    done  
 | 
			
		||||
 | 
			
		||||
    echo "${devices_array[@]}"  
 | 
			
		||||
}  
 | 
			
		||||
 | 
			
		||||
# Backup one device
 | 
			
		||||
backup_one() {
 | 
			
		||||
    local hostname="$1"
 | 
			
		||||
    local address="$2"
 | 
			
		||||
    local port="$3"
 | 
			
		||||
 | 
			
		||||
    log "INFO" "$YELLOW" "Backing up device config/presets/ir: $hostname ($address:$port)"
 | 
			
		||||
 | 
			
		||||
    mkdir -p "$backup_dir"
 | 
			
		||||
 | 
			
		||||
    local file_prefix="${backup_dir}/${hostname}"
 | 
			
		||||
 | 
			
		||||
    if ! fetch "http://$address:$port/cfg.json" "${file_prefix}.cfg.json"; then  
 | 
			
		||||
        log "ERROR" "$RED" "Failed to backup configuration for $hostname"  
 | 
			
		||||
        return 1  
 | 
			
		||||
    fi  
 | 
			
		||||
    
 | 
			
		||||
    if ! fetch "http://$address:$port/presets.json" "${file_prefix}.presets.json"; then  
 | 
			
		||||
        log "ERROR" "$RED" "Failed to backup presets for $hostname"  
 | 
			
		||||
        return 1  
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # ir.json is optional
 | 
			
		||||
    if ! fetch "http://$address:$port/ir.json" "${file_prefix}.ir.json" "200 404"; then
 | 
			
		||||
        log "ERROR" "$RED" "Failed to backup ir configs for $hostname" 
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname"
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Update one device
 | 
			
		||||
update_one() {
 | 
			
		||||
    local hostname="$1"
 | 
			
		||||
    local address="$2"
 | 
			
		||||
    local port="$3"
 | 
			
		||||
    local firmware="$4"
 | 
			
		||||
 | 
			
		||||
    log "INFO" "$YELLOW" "Starting firmware update for device: $hostname ($address:$port)"
 | 
			
		||||
 | 
			
		||||
    local url="http://$address:$port/update"
 | 
			
		||||
 | 
			
		||||
    if ! post_file "$url" "$firmware" "200"; then
 | 
			
		||||
        log "ERROR" "$RED" "Failed to update firmware for $hostname"
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    log "INFO" "$GREEN" "Successfully initiated firmware update for $hostname"
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Command-line arguments processing
 | 
			
		||||
command=""
 | 
			
		||||
target=""
 | 
			
		||||
discover=false
 | 
			
		||||
quiet=false
 | 
			
		||||
backup_dir="./"
 | 
			
		||||
firmware_file=""
 | 
			
		||||
 | 
			
		||||
if [ $# -eq 0 ]; then
 | 
			
		||||
    show_help
 | 
			
		||||
    exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
while [[ $# -gt 0 ]]; do
 | 
			
		||||
    case "$1" in
 | 
			
		||||
        -h|--help)
 | 
			
		||||
            show_help
 | 
			
		||||
            exit 0
 | 
			
		||||
            ;;
 | 
			
		||||
        -t|--target)
 | 
			
		||||
            if [ -z "$2" ] || [[ "$2" == -* ]]; then
 | 
			
		||||
                log "ERROR" "$RED" "The --target option requires an argument."
 | 
			
		||||
                exit 1
 | 
			
		||||
            fi
 | 
			
		||||
            target="$2"
 | 
			
		||||
            shift 2
 | 
			
		||||
            ;;
 | 
			
		||||
        -D|--discover)
 | 
			
		||||
            discover=true
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        -d|--directory)
 | 
			
		||||
            if [ -z "$2" ] || [[ "$2" == -* ]]; then
 | 
			
		||||
                log "ERROR" "$RED" "The --directory option requires an argument."
 | 
			
		||||
                exit 1
 | 
			
		||||
            fi
 | 
			
		||||
            backup_dir="$2"
 | 
			
		||||
            shift 2
 | 
			
		||||
            ;;
 | 
			
		||||
        -f|--firmware)
 | 
			
		||||
            if [ -z "$2" ] || [[ "$2" == -* ]]; then
 | 
			
		||||
                log "ERROR" "$RED" "The --firmware option requires an argument."
 | 
			
		||||
                exit 1
 | 
			
		||||
            fi
 | 
			
		||||
            firmware_file="$2"
 | 
			
		||||
            shift 2
 | 
			
		||||
            ;;
 | 
			
		||||
        -q|--quiet)
 | 
			
		||||
            quiet=true
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        backup|update|discover)
 | 
			
		||||
            command="$1"
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        *)
 | 
			
		||||
            log "ERROR" "$RED" "Unknown argument: $1"
 | 
			
		||||
            exit 1
 | 
			
		||||
            ;;
 | 
			
		||||
    esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Execute the appropriate command
 | 
			
		||||
case "$command" in
 | 
			
		||||
    discover)
 | 
			
		||||
        read -ra devices <<< "$(discover_devices)"
 | 
			
		||||
        for ((i=0; i<${#devices[@]}; i+=3)); do
 | 
			
		||||
            hostname="${devices[$i]}"
 | 
			
		||||
            address="${devices[$i+1]}"
 | 
			
		||||
            port="${devices[$i+2]}"
 | 
			
		||||
 | 
			
		||||
            if [ "$quiet" = true ]; then
 | 
			
		||||
                echo "$hostname"
 | 
			
		||||
            else
 | 
			
		||||
                log "INFO" "$BLUE" "Discovered device: Hostname=$hostname, Address=$address, Port=$port"
 | 
			
		||||
            fi
 | 
			
		||||
        done
 | 
			
		||||
        ;;
 | 
			
		||||
    backup)
 | 
			
		||||
        if [ -n "$target" ]; then
 | 
			
		||||
            # Assume target is both the hostname and address, with port 80
 | 
			
		||||
            backup_one "$target" "$target" "80"
 | 
			
		||||
        elif [ "$discover" = true ]; then
 | 
			
		||||
            read -ra devices <<< "$(discover_devices)"
 | 
			
		||||
            for ((i=0; i<${#devices[@]}; i+=3)); do
 | 
			
		||||
                hostname="${devices[$i]}"
 | 
			
		||||
                address="${devices[$i+1]}"
 | 
			
		||||
                port="${devices[$i+2]}"
 | 
			
		||||
                backup_one "$hostname" "$address" "$port"
 | 
			
		||||
            done
 | 
			
		||||
        else
 | 
			
		||||
            log "ERROR" "$RED" "No target specified. Use --target or --discover."
 | 
			
		||||
            exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        ;;
 | 
			
		||||
    update)
 | 
			
		||||
        # Validate firmware before proceeding
 | 
			
		||||
        if [ -z "$firmware_file" ] || [ ! -f "$firmware_file" ]; then
 | 
			
		||||
            log "ERROR" "$RED" "Please provide a file in --firmware that exists"
 | 
			
		||||
            exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        
 | 
			
		||||
        if [ -n "$target" ]; then
 | 
			
		||||
            # Assume target is both the hostname and address, with port 80
 | 
			
		||||
            update_one "$target" "$target" "80" "$firmware_file"
 | 
			
		||||
        elif [ "$discover" = true ]; then
 | 
			
		||||
            read -ra devices <<< "$(discover_devices)"
 | 
			
		||||
            for ((i=0; i<${#devices[@]}; i+=3)); do
 | 
			
		||||
                hostname="${devices[$i]}"
 | 
			
		||||
                address="${devices[$i+1]}"
 | 
			
		||||
                port="${devices[$i+2]}"
 | 
			
		||||
                update_one "$hostname" "$address" "$port" "$firmware_file"
 | 
			
		||||
            done
 | 
			
		||||
        else
 | 
			
		||||
            log "ERROR" "$RED" "No target specified. Use --target or --discover."
 | 
			
		||||
            exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        ;;
 | 
			
		||||
    *)
 | 
			
		||||
        show_help
 | 
			
		||||
        exit 1
 | 
			
		||||
        ;;
 | 
			
		||||
esac
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ADS1115_v2",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
 | 
			
		||||
    "Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -6,5 +6,5 @@ Configuration is performed via the Usermod menu. There are no parameters to set
 | 
			
		||||
 | 
			
		||||
## Installation 
 | 
			
		||||
 | 
			
		||||
Add 'ADS1115' to `custom_usermods` in your platformio environment.
 | 
			
		||||
 | 
			
		||||
Add the build flag `-D USERMOD_ADS1115` to your platformio environment.
 | 
			
		||||
Uncomment libraries with comment `#For ADS1115 sensor uncomment following`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <Adafruit_ADS1X15.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
@@ -251,6 +253,3 @@ class ADS1115Usermod : public Usermod {
 | 
			
		||||
        readings[activeChannel] = ads.computeVolts(results);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ADS1115Usermod ads1115_v2;
 | 
			
		||||
REGISTER_USERMOD(ads1115_v2);
 | 
			
		||||
@@ -22,9 +22,15 @@ Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `p
 | 
			
		||||
 | 
			
		||||
# Compiling
 | 
			
		||||
 | 
			
		||||
To enable, add 'AHT10' to `custom_usermods` in your platformio encrionment  (e.g. in `platformio_override.ini`)
 | 
			
		||||
To enable, compile with `USERMOD_AHT10` defined  (e.g. in `platformio_override.ini`)
 | 
			
		||||
```ini
 | 
			
		||||
[env:aht10_example]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} AHT10
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D USERMOD_AHT10
 | 
			
		||||
  ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp32.lib_deps}
 | 
			
		||||
  enjoyneering/AHT10@~1.1.0
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "AHT10_v2",
 | 
			
		||||
  "build": { "libArchive": false },  
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "enjoyneering/AHT10":"~1.1.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,4 +2,8 @@
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D USERMOD_AHT10
 | 
			
		||||
  ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp32.lib_deps}
 | 
			
		||||
  enjoyneering/AHT10@~1.1.0
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <AHT10.h>
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +54,12 @@ private:
 | 
			
		||||
    _lastTemperature = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~UsermodAHT10()
 | 
			
		||||
  {
 | 
			
		||||
    delete _aht;
 | 
			
		||||
    _aht = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifndef WLED_DISABLE_MQTT
 | 
			
		||||
  void mqttInitialize()
 | 
			
		||||
  {
 | 
			
		||||
@@ -314,15 +322,6 @@ public:
 | 
			
		||||
    _initDone = true;
 | 
			
		||||
    return configComplete;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~UsermodAHT10()
 | 
			
		||||
  {
 | 
			
		||||
    delete _aht;
 | 
			
		||||
    _aht = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char UsermodAHT10::_name[] PROGMEM = "AHTxx";
 | 
			
		||||
 | 
			
		||||
static UsermodAHT10 aht10_v2;
 | 
			
		||||
REGISTER_USERMOD(aht10_v2);
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -102,9 +103,9 @@ private:
 | 
			
		||||
    void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
 | 
			
		||||
        uint32_t ms = time.ms % 1000;
 | 
			
		||||
        uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
 | 
			
		||||
        setPixelColor(secondLed, scale32(secondColor, b0));
 | 
			
		||||
        setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
 | 
			
		||||
        uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
 | 
			
		||||
        setPixelColor(inc(secondLed, 1, secondsSegment), scale32(secondColor, b1));
 | 
			
		||||
        setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
 | 
			
		||||
@@ -191,7 +192,7 @@ public:
 | 
			
		||||
            // for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
 | 
			
		||||
            //     uint16_t trailLed = dec(secondLed, i, secondsSegment);
 | 
			
		||||
            //     uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
 | 
			
		||||
            //     setPixelColor(trailLed, scale32(secondColor, trailBright));
 | 
			
		||||
            //     setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -253,7 +254,3 @@ public:
 | 
			
		||||
        return USERMOD_ID_ANALOG_CLOCK;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static AnalogClockUsermod analog_clock;
 | 
			
		||||
REGISTER_USERMOD(analog_clock);
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Analog_Clock",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
 * 
 | 
			
		||||
 * See the accompanying README.md file for more info.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
class Animated_Staircase : public Usermod {
 | 
			
		||||
@@ -561,7 +562,3 @@ const char Animated_Staircase::_bottomEcho_pin[]            PROGMEM = "bottomEch
 | 
			
		||||
const char Animated_Staircase::_topEchoCm[]                 PROGMEM = "top-dist-cm";
 | 
			
		||||
const char Animated_Staircase::_bottomEchoCm[]              PROGMEM = "bottom-dist-cm";
 | 
			
		||||
const char Animated_Staircase::_togglePower[]               PROGMEM = "toggle-on-off";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static Animated_Staircase animated_staircase;
 | 
			
		||||
REGISTER_USERMOD(animated_staircase);
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
# Usermod Animated Staircase
 | 
			
		||||
 | 
			
		||||
This usermod makes your staircase look cool by illuminating it with an animation. It uses
 | 
			
		||||
PIR or ultrasonic sensors at the top and bottom of your stairs to:
 | 
			
		||||
 | 
			
		||||
@@ -12,15 +11,14 @@ The Animated Staircase can be controlled by the WLED API. Change settings such a
 | 
			
		||||
speed, on/off time and distance by sending an HTTP request, see below.
 | 
			
		||||
 | 
			
		||||
## WLED integration
 | 
			
		||||
 | 
			
		||||
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
 | 
			
		||||
 | 
			
		||||
Before compiling, you have to make the following modifications:
 | 
			
		||||
 | 
			
		||||
Edit your environment in `platformio_override.ini`
 | 
			
		||||
 | 
			
		||||
1. Open `platformio_override.ini`
 | 
			
		||||
2. add `Animated_Staircase` to the `custom_usermods` line for your environment
 | 
			
		||||
Edit `usermods_list.cpp`:
 | 
			
		||||
1. Open `wled00/usermods_list.cpp`
 | 
			
		||||
2. add `#include "../usermods/Animated_Staircase/Animated_Staircase.h"` to the top of the file
 | 
			
		||||
3. add `UsermodManager::add(new Animated_Staircase());` to the end of the `void registerUsermods()` function.
 | 
			
		||||
 | 
			
		||||
You can configure usermod using the Usermods settings page.
 | 
			
		||||
Please enter GPIO pins for PIR or ultrasonic sensors (trigger and echo).
 | 
			
		||||
@@ -28,10 +26,10 @@ If you use PIR sensor enter -1 for echo pin.
 | 
			
		||||
Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below).
 | 
			
		||||
 | 
			
		||||
## Hardware installation
 | 
			
		||||
 | 
			
		||||
1. Attach the LED strip to each step of the stairs.
 | 
			
		||||
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step.
 | 
			
		||||
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the next step, creating one large virtual LED strip.
 | 
			
		||||
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the 
 | 
			
		||||
   next step, creating one large virtual LED strip.
 | 
			
		||||
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
 | 
			
		||||
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
 | 
			
		||||
   step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
 | 
			
		||||
@@ -40,19 +38,20 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for
 | 
			
		||||
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
 | 
			
		||||
 | 
			
		||||
## WLED configuration
 | 
			
		||||
 | 
			
		||||
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the lowest segment id.
 | 
			
		||||
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the 
 | 
			
		||||
   lowest segment id. 
 | 
			
		||||
2. Save your segments into a preset. 
 | 
			
		||||
3. Ideally, add the preset in the config > LED setup menu to the "apply preset **n** at boot" setting.
 | 
			
		||||
3. Ideally, add the preset in the config > LED setup menu to the "apply 
 | 
			
		||||
   preset **n** at boot" setting.
 | 
			
		||||
 | 
			
		||||
## Changing behavior through API
 | 
			
		||||
 | 
			
		||||
The Staircase settings can be changed through the WLED JSON api.
 | 
			
		||||
 | 
			
		||||
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
 | 
			
		||||
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
 | 
			
		||||
or remove them and put everything on one line.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| Setting          | Description                                                   | Default |
 | 
			
		||||
|------------------|---------------------------------------------------------------|---------|
 | 
			
		||||
| enabled          | Enable or disable the usermod                                 | true    |
 | 
			
		||||
@@ -76,7 +75,6 @@ The staircase settings and sensor states are inside the WLED "state" element:
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Enable/disable the usermod
 | 
			
		||||
 | 
			
		||||
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
 | 
			
		||||
activity. This enables you to play with the lights without the usermod switching them on or off.
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +91,6 @@ To enable the usermod again, use `"enabled":true`.
 | 
			
		||||
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
 | 
			
		||||
 | 
			
		||||
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
 | 
			
		||||
 | 
			
		||||
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
 | 
			
		||||
 | 
			
		||||
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
 | 
			
		||||
@@ -103,7 +100,6 @@ distances creates delays in the WLED software, _might_ introduce timing hiccups
 | 
			
		||||
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
 | 
			
		||||
 | 
			
		||||
### Animation triggering through the API
 | 
			
		||||
 | 
			
		||||
In addition to activation by one of the stair sensors, you can also trigger the animation manually
 | 
			
		||||
via the API. To simulate triggering the bottom sensor, use:
 | 
			
		||||
 | 
			
		||||
@@ -120,19 +116,15 @@ curl -X POST -H "Content-Type: application/json" \
 | 
			
		||||
     -d '{"staircase":{"top-sensor":true}}' \
 | 
			
		||||
     xxx.xxx.xxx.xxx/json/state
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**MQTT**
 | 
			
		||||
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
 | 
			
		||||
You can also use `on` or `off` for enabling or disabling the usermod.
 | 
			
		||||
 | 
			
		||||
Have fun with this usermod
 | 
			
		||||
 | 
			
		||||
`www.rolfje.com`
 | 
			
		||||
Have fun with this usermod.<br/>
 | 
			
		||||
www.rolfje.com
 | 
			
		||||
 | 
			
		||||
Modifications @blazoncek
 | 
			
		||||
 | 
			
		||||
## Change log
 | 
			
		||||
 | 
			
		||||
2021-04
 | 
			
		||||
 | 
			
		||||
- Adaptation for runtime configuration.
 | 
			
		||||
* Adaptation for runtime configuration.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Animated_Staircase",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,186 +0,0 @@
 | 
			
		||||
// force the compiler to show a warning to confirm that this file is included
 | 
			
		||||
#warning **** Included USERMOD_BH1750 ****
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include "BH1750_v2.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
 | 
			
		||||
{
 | 
			
		||||
  return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usermod_BH1750::_mqttInitialize()
 | 
			
		||||
{
 | 
			
		||||
  mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
 | 
			
		||||
 | 
			
		||||
  if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
 | 
			
		||||
void Usermod_BH1750::_createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
 | 
			
		||||
{
 | 
			
		||||
  String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
 | 
			
		||||
  
 | 
			
		||||
  StaticJsonDocument<600> doc;
 | 
			
		||||
  
 | 
			
		||||
  doc[F("name")] = String(serverDescription) + " " + name;
 | 
			
		||||
  doc[F("state_topic")] = topic;
 | 
			
		||||
  doc[F("unique_id")] = String(mqttClientID) + name;
 | 
			
		||||
  if (unitOfMeasurement != "")
 | 
			
		||||
    doc[F("unit_of_measurement")] = unitOfMeasurement;
 | 
			
		||||
  if (deviceClass != "")
 | 
			
		||||
    doc[F("device_class")] = deviceClass;
 | 
			
		||||
  doc[F("expire_after")] = 1800;
 | 
			
		||||
 | 
			
		||||
  JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
 | 
			
		||||
  device[F("name")] = serverDescription;
 | 
			
		||||
  device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
 | 
			
		||||
  device[F("manufacturer")] = F(WLED_BRAND);
 | 
			
		||||
  device[F("model")] = F(WLED_PRODUCT_NAME);
 | 
			
		||||
  device[F("sw_version")] = versionString;
 | 
			
		||||
 | 
			
		||||
  String temp;
 | 
			
		||||
  serializeJson(doc, temp);
 | 
			
		||||
  DEBUG_PRINTLN(t);
 | 
			
		||||
  DEBUG_PRINTLN(temp);
 | 
			
		||||
 | 
			
		||||
  mqtt->publish(t.c_str(), 0, true, temp.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usermod_BH1750::setup()
 | 
			
		||||
{
 | 
			
		||||
  if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
 | 
			
		||||
  sensorFound = lightMeter.begin();
 | 
			
		||||
  initDone = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usermod_BH1750::loop()
 | 
			
		||||
{
 | 
			
		||||
  if ((!enabled) || strip.isUpdating())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  unsigned long now = millis();
 | 
			
		||||
 | 
			
		||||
  // check to see if we are due for taking a measurement
 | 
			
		||||
  // lastMeasurement will not be updated until the conversion
 | 
			
		||||
  // is complete the the reading is finished
 | 
			
		||||
  if (now - lastMeasurement < minReadingInterval)
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool shouldUpdate = now - lastSend > maxReadingInterval;
 | 
			
		||||
 | 
			
		||||
  float lux = lightMeter.readLightLevel();
 | 
			
		||||
  lastMeasurement = millis();
 | 
			
		||||
  getLuminanceComplete = true;
 | 
			
		||||
 | 
			
		||||
  if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
 | 
			
		||||
  {
 | 
			
		||||
    lastLux = lux;
 | 
			
		||||
    lastSend = millis();
 | 
			
		||||
 | 
			
		||||
    if (WLED_MQTT_CONNECTED)
 | 
			
		||||
    {
 | 
			
		||||
      if (!mqttInitialized)
 | 
			
		||||
        {
 | 
			
		||||
          _mqttInitialize();
 | 
			
		||||
          mqttInitialized = true;
 | 
			
		||||
        }
 | 
			
		||||
      mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
 | 
			
		||||
      DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Usermod_BH1750::addToJsonInfo(JsonObject &root)
 | 
			
		||||
{
 | 
			
		||||
  JsonObject user = root[F("u")];
 | 
			
		||||
  if (user.isNull())
 | 
			
		||||
    user = root.createNestedObject(F("u"));
 | 
			
		||||
 | 
			
		||||
  JsonArray lux_json = user.createNestedArray(F("Luminance"));
 | 
			
		||||
  if (!enabled) {
 | 
			
		||||
    lux_json.add(F("disabled"));
 | 
			
		||||
  } else if (!sensorFound) {
 | 
			
		||||
      // if no sensor 
 | 
			
		||||
      lux_json.add(F("BH1750 "));
 | 
			
		||||
      lux_json.add(F("Not Found"));
 | 
			
		||||
  } else if (!getLuminanceComplete) {
 | 
			
		||||
    // if we haven't read the sensor yet, let the user know
 | 
			
		||||
      // that we are still waiting for the first measurement
 | 
			
		||||
      lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
 | 
			
		||||
      lux_json.add(F(" sec until read"));
 | 
			
		||||
      return;
 | 
			
		||||
  } else {
 | 
			
		||||
    lux_json.add(lastLux);
 | 
			
		||||
    lux_json.add(F(" lx"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// (called from set.cpp) stores persistent properties to cfg.json
 | 
			
		||||
void Usermod_BH1750::addToConfig(JsonObject &root)
 | 
			
		||||
{
 | 
			
		||||
  // we add JSON object.
 | 
			
		||||
  JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
 | 
			
		||||
  top[FPSTR(_enabled)] = enabled;
 | 
			
		||||
  top[FPSTR(_maxReadInterval)] = maxReadingInterval;
 | 
			
		||||
  top[FPSTR(_minReadInterval)] = minReadingInterval;
 | 
			
		||||
  top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
 | 
			
		||||
  top[FPSTR(_offset)] = offset;
 | 
			
		||||
 | 
			
		||||
  DEBUG_PRINTLN(F("BH1750 config saved."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// called before setup() to populate properties from values stored in cfg.json
 | 
			
		||||
bool Usermod_BH1750::readFromConfig(JsonObject &root)
 | 
			
		||||
{
 | 
			
		||||
  // we look for JSON object.
 | 
			
		||||
  JsonObject top = root[FPSTR(_name)];
 | 
			
		||||
  if (top.isNull())
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_PRINT(FPSTR(_name));
 | 
			
		||||
    DEBUG_PRINT(F("BH1750"));
 | 
			
		||||
    DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  bool configComplete = !top.isNull();
 | 
			
		||||
 | 
			
		||||
  configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
 | 
			
		||||
  configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms
 | 
			
		||||
  configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
 | 
			
		||||
  configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
 | 
			
		||||
  configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
 | 
			
		||||
 | 
			
		||||
  DEBUG_PRINT(FPSTR(_name));
 | 
			
		||||
  if (!initDone) {
 | 
			
		||||
    DEBUG_PRINTLN(F(" config loaded."));
 | 
			
		||||
  } else {
 | 
			
		||||
    DEBUG_PRINTLN(F(" config (re)loaded."));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return configComplete;
 | 
			
		||||
  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// strings to reduce flash memory usage (used more than twice)
 | 
			
		||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
 | 
			
		||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
 | 
			
		||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
 | 
			
		||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
 | 
			
		||||
const char Usermod_BH1750::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux";
 | 
			
		||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static Usermod_BH1750 bh1750_v2;
 | 
			
		||||
REGISTER_USERMOD(bh1750_v2);
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <BH1750.h>
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// the max frequency to check photoresistor, 10 seconds
 | 
			
		||||
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
 | 
			
		||||
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// the min frequency to check photoresistor, 500 ms
 | 
			
		||||
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
 | 
			
		||||
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// how many seconds after boot to take first measurement, 10 seconds
 | 
			
		||||
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
 | 
			
		||||
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// only report if difference grater than offset value
 | 
			
		||||
#ifndef USERMOD_BH1750_OFFSET_VALUE
 | 
			
		||||
#define USERMOD_BH1750_OFFSET_VALUE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class Usermod_BH1750 : public Usermod
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
 | 
			
		||||
 | 
			
		||||
  unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
 | 
			
		||||
  unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
 | 
			
		||||
  unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
 | 
			
		||||
  unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
 | 
			
		||||
  // flag to indicate we have finished the first readLightLevel call
 | 
			
		||||
  // allows this library to report to the user how long until the first
 | 
			
		||||
  // measurement
 | 
			
		||||
  bool getLuminanceComplete = false;
 | 
			
		||||
 | 
			
		||||
  // flag set at startup
 | 
			
		||||
  bool enabled = true;
 | 
			
		||||
 | 
			
		||||
  // strings to reduce flash memory usage (used more than twice)
 | 
			
		||||
  static const char _name[];
 | 
			
		||||
  static const char _enabled[];
 | 
			
		||||
  static const char _maxReadInterval[];
 | 
			
		||||
  static const char _minReadInterval[];
 | 
			
		||||
  static const char _offset[];
 | 
			
		||||
  static const char _HomeAssistantDiscovery[];
 | 
			
		||||
 | 
			
		||||
  bool initDone = false;
 | 
			
		||||
  bool sensorFound = false;
 | 
			
		||||
 | 
			
		||||
  // Home Assistant and MQTT  
 | 
			
		||||
  String mqttLuminanceTopic;
 | 
			
		||||
  bool mqttInitialized = false;
 | 
			
		||||
  bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
 | 
			
		||||
 | 
			
		||||
  BH1750 lightMeter;
 | 
			
		||||
  float lastLux = -1000;
 | 
			
		||||
 | 
			
		||||
  // set up Home Assistant discovery entries
 | 
			
		||||
  void _mqttInitialize();
 | 
			
		||||
 | 
			
		||||
  // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
 | 
			
		||||
  void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  void setup();
 | 
			
		||||
  void loop();
 | 
			
		||||
  inline float getIlluminance()  {
 | 
			
		||||
    return (float)lastLux;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void addToJsonInfo(JsonObject &root);
 | 
			
		||||
 | 
			
		||||
  // (called from set.cpp) stores persistent properties to cfg.json
 | 
			
		||||
  void addToConfig(JsonObject &root);
 | 
			
		||||
 | 
			
		||||
  // called before setup() to populate properties from values stored in cfg.json
 | 
			
		||||
  bool readFromConfig(JsonObject &root);
 | 
			
		||||
 | 
			
		||||
  inline uint16_t getId()
 | 
			
		||||
  {
 | 
			
		||||
    return USERMOD_ID_BH1750;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "BH1750_v2",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "claws/BH1750":"^1.2.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,40 +4,43 @@ This usermod will read from an ambient light sensor like the BH1750.
 | 
			
		||||
The luminance is displayed in both the Info section of the web UI, as well as published to the `/luminance` MQTT topic if enabled.
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
 | 
			
		||||
- Libraries
 | 
			
		||||
  - `claws/BH1750 @^1.2.0`
 | 
			
		||||
  - This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
 | 
			
		||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
 | 
			
		||||
 | 
			
		||||
## Compilation
 | 
			
		||||
 | 
			
		||||
To enable, compile with `BH1750` in `custom_usermods` (e.g. in `platformio_override.ini`)
 | 
			
		||||
To enable, compile with `USERMOD_BH1750` defined  (e.g. in `platformio_override.ini`)
 | 
			
		||||
```ini
 | 
			
		||||
[env:usermod_BH1750_d1_mini]
 | 
			
		||||
extends = env:d1_mini
 | 
			
		||||
build_flags =
 | 
			
		||||
    ${common.build_flags_esp8266}
 | 
			
		||||
    -D USERMOD_BH1750
 | 
			
		||||
lib_deps = 
 | 
			
		||||
    ${esp8266.lib_deps}
 | 
			
		||||
    claws/BH1750 @ ^1.2.0
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Configuration Options
 | 
			
		||||
 | 
			
		||||
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
 | 
			
		||||
 | 
			
		||||
- `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
 | 
			
		||||
- `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
 | 
			
		||||
- `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
 | 
			
		||||
- `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
 | 
			
		||||
*   `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
 | 
			
		||||
*   `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
 | 
			
		||||
*   `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
 | 
			
		||||
*   `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
 | 
			
		||||
 | 
			
		||||
In addition, the Usermod screen allows you to:
 | 
			
		||||
 | 
			
		||||
- enable/disable the usermod
 | 
			
		||||
- Enable Home Assistant Discovery of usermod
 | 
			
		||||
- Configure the SCL/SDA pins
 | 
			
		||||
 | 
			
		||||
## API
 | 
			
		||||
 | 
			
		||||
The following method is available to interact with the usermod from other code modules:
 | 
			
		||||
 | 
			
		||||
- `getIlluminance` read the brightness from the sensor
 | 
			
		||||
 | 
			
		||||
## Change Log
 | 
			
		||||
 | 
			
		||||
Jul 2022
 | 
			
		||||
 | 
			
		||||
- Added Home Assistant Discovery
 | 
			
		||||
- Implemented PinManager to register pins
 | 
			
		||||
- Made pins configurable in usermod menu
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										252
									
								
								usermods/BH1750_v2/usermod_bh1750.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								usermods/BH1750_v2/usermod_bh1750.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
// force the compiler to show a warning to confirm that this file is included
 | 
			
		||||
#warning **** Included USERMOD_BH1750 ****
 | 
			
		||||
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <BH1750.h>
 | 
			
		||||
 | 
			
		||||
// the max frequency to check photoresistor, 10 seconds
 | 
			
		||||
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
 | 
			
		||||
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// the min frequency to check photoresistor, 500 ms
 | 
			
		||||
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
 | 
			
		||||
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// how many seconds after boot to take first measurement, 10 seconds
 | 
			
		||||
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
 | 
			
		||||
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// only report if difference grater than offset value
 | 
			
		||||
#ifndef USERMOD_BH1750_OFFSET_VALUE
 | 
			
		||||
#define USERMOD_BH1750_OFFSET_VALUE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class Usermod_BH1750 : public Usermod
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
 | 
			
		||||
 | 
			
		||||
  unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
 | 
			
		||||
  unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
 | 
			
		||||
  unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
 | 
			
		||||
  unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
 | 
			
		||||
  // flag to indicate we have finished the first readLightLevel call
 | 
			
		||||
  // allows this library to report to the user how long until the first
 | 
			
		||||
  // measurement
 | 
			
		||||
  bool getLuminanceComplete = false;
 | 
			
		||||
 | 
			
		||||
  // flag set at startup
 | 
			
		||||
  bool enabled = true;
 | 
			
		||||
 | 
			
		||||
  // strings to reduce flash memory usage (used more than twice)
 | 
			
		||||
  static const char _name[];
 | 
			
		||||
  static const char _enabled[];
 | 
			
		||||
  static const char _maxReadInterval[];
 | 
			
		||||
  static const char _minReadInterval[];
 | 
			
		||||
  static const char _offset[];
 | 
			
		||||
  static const char _HomeAssistantDiscovery[];
 | 
			
		||||
 | 
			
		||||
  bool initDone = false;
 | 
			
		||||
  bool sensorFound = false;
 | 
			
		||||
 | 
			
		||||
  // Home Assistant and MQTT  
 | 
			
		||||
  String mqttLuminanceTopic;
 | 
			
		||||
  bool mqttInitialized = false;
 | 
			
		||||
  bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
 | 
			
		||||
 | 
			
		||||
  BH1750 lightMeter;
 | 
			
		||||
  float lastLux = -1000;
 | 
			
		||||
 | 
			
		||||
  bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
 | 
			
		||||
  {
 | 
			
		||||
    return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // set up Home Assistant discovery entries
 | 
			
		||||
  void _mqttInitialize()
 | 
			
		||||
  {
 | 
			
		||||
    mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
 | 
			
		||||
 | 
			
		||||
    if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
 | 
			
		||||
  void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
 | 
			
		||||
  {
 | 
			
		||||
    String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
 | 
			
		||||
    
 | 
			
		||||
    StaticJsonDocument<600> doc;
 | 
			
		||||
    
 | 
			
		||||
    doc[F("name")] = String(serverDescription) + " " + name;
 | 
			
		||||
    doc[F("state_topic")] = topic;
 | 
			
		||||
    doc[F("unique_id")] = String(mqttClientID) + name;
 | 
			
		||||
    if (unitOfMeasurement != "")
 | 
			
		||||
      doc[F("unit_of_measurement")] = unitOfMeasurement;
 | 
			
		||||
    if (deviceClass != "")
 | 
			
		||||
      doc[F("device_class")] = deviceClass;
 | 
			
		||||
    doc[F("expire_after")] = 1800;
 | 
			
		||||
 | 
			
		||||
    JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
 | 
			
		||||
    device[F("name")] = serverDescription;
 | 
			
		||||
    device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
 | 
			
		||||
    device[F("manufacturer")] = F(WLED_BRAND);
 | 
			
		||||
    device[F("model")] = F(WLED_PRODUCT_NAME);
 | 
			
		||||
    device[F("sw_version")] = versionString;
 | 
			
		||||
 | 
			
		||||
    String temp;
 | 
			
		||||
    serializeJson(doc, temp);
 | 
			
		||||
    DEBUG_PRINTLN(t);
 | 
			
		||||
    DEBUG_PRINTLN(temp);
 | 
			
		||||
 | 
			
		||||
    mqtt->publish(t.c_str(), 0, true, temp.c_str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  void setup()
 | 
			
		||||
  {
 | 
			
		||||
    if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
 | 
			
		||||
    sensorFound = lightMeter.begin();
 | 
			
		||||
    initDone = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void loop()
 | 
			
		||||
  {
 | 
			
		||||
    if ((!enabled) || strip.isUpdating())
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    unsigned long now = millis();
 | 
			
		||||
 | 
			
		||||
    // check to see if we are due for taking a measurement
 | 
			
		||||
    // lastMeasurement will not be updated until the conversion
 | 
			
		||||
    // is complete the the reading is finished
 | 
			
		||||
    if (now - lastMeasurement < minReadingInterval)
 | 
			
		||||
    {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool shouldUpdate = now - lastSend > maxReadingInterval;
 | 
			
		||||
 | 
			
		||||
    float lux = lightMeter.readLightLevel();
 | 
			
		||||
    lastMeasurement = millis();
 | 
			
		||||
    getLuminanceComplete = true;
 | 
			
		||||
 | 
			
		||||
    if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
 | 
			
		||||
    {
 | 
			
		||||
      lastLux = lux;
 | 
			
		||||
      lastSend = millis();
 | 
			
		||||
#ifndef WLED_DISABLE_MQTT
 | 
			
		||||
      if (WLED_MQTT_CONNECTED)
 | 
			
		||||
      {
 | 
			
		||||
        if (!mqttInitialized)
 | 
			
		||||
          {
 | 
			
		||||
            _mqttInitialize();
 | 
			
		||||
            mqttInitialized = true;
 | 
			
		||||
          }
 | 
			
		||||
        mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
 | 
			
		||||
        DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline float getIlluminance() {
 | 
			
		||||
    return (float)lastLux;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void addToJsonInfo(JsonObject &root)
 | 
			
		||||
  {
 | 
			
		||||
    JsonObject user = root[F("u")];
 | 
			
		||||
    if (user.isNull())
 | 
			
		||||
      user = root.createNestedObject(F("u"));
 | 
			
		||||
 | 
			
		||||
    JsonArray lux_json = user.createNestedArray(F("Luminance"));
 | 
			
		||||
    if (!enabled) {
 | 
			
		||||
      lux_json.add(F("disabled"));
 | 
			
		||||
    } else if (!sensorFound) {
 | 
			
		||||
        // if no sensor 
 | 
			
		||||
        lux_json.add(F("BH1750 "));
 | 
			
		||||
        lux_json.add(F("Not Found"));
 | 
			
		||||
    } else if (!getLuminanceComplete) {
 | 
			
		||||
      // if we haven't read the sensor yet, let the user know
 | 
			
		||||
        // that we are still waiting for the first measurement
 | 
			
		||||
        lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
 | 
			
		||||
        lux_json.add(F(" sec until read"));
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
      lux_json.add(lastLux);
 | 
			
		||||
      lux_json.add(F(" lx"));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // (called from set.cpp) stores persistent properties to cfg.json
 | 
			
		||||
  void addToConfig(JsonObject &root)
 | 
			
		||||
  {
 | 
			
		||||
    // we add JSON object.
 | 
			
		||||
    JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
 | 
			
		||||
    top[FPSTR(_enabled)] = enabled;
 | 
			
		||||
    top[FPSTR(_maxReadInterval)] = maxReadingInterval;
 | 
			
		||||
    top[FPSTR(_minReadInterval)] = minReadingInterval;
 | 
			
		||||
    top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
 | 
			
		||||
    top[FPSTR(_offset)] = offset;
 | 
			
		||||
 | 
			
		||||
    DEBUG_PRINTLN(F("BH1750 config saved."));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // called before setup() to populate properties from values stored in cfg.json
 | 
			
		||||
  bool readFromConfig(JsonObject &root)
 | 
			
		||||
  {
 | 
			
		||||
    // we look for JSON object.
 | 
			
		||||
    JsonObject top = root[FPSTR(_name)];
 | 
			
		||||
    if (top.isNull())
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_PRINT(FPSTR(_name));
 | 
			
		||||
      DEBUG_PRINT(F("BH1750"));
 | 
			
		||||
      DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    bool configComplete = !top.isNull();
 | 
			
		||||
 | 
			
		||||
    configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
 | 
			
		||||
    configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms
 | 
			
		||||
    configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
 | 
			
		||||
    configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
 | 
			
		||||
    configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
 | 
			
		||||
 | 
			
		||||
    DEBUG_PRINT(FPSTR(_name));
 | 
			
		||||
    if (!initDone) {
 | 
			
		||||
      DEBUG_PRINTLN(F(" config loaded."));
 | 
			
		||||
    } else {
 | 
			
		||||
      DEBUG_PRINTLN(F(" config (re)loaded."));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return configComplete;
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t getId()
 | 
			
		||||
  {
 | 
			
		||||
    return USERMOD_ID_BH1750;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// strings to reduce flash memory usage (used more than twice)
 | 
			
		||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
 | 
			
		||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
 | 
			
		||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
 | 
			
		||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
 | 
			
		||||
const char Usermod_BH1750::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux";
 | 
			
		||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
 | 
			
		||||
@@ -22,6 +22,7 @@ Dependencies
 | 
			
		||||
- Libraries
 | 
			
		||||
  - `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280))
 | 
			
		||||
  - `Wire`
 | 
			
		||||
  - These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
 | 
			
		||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
 | 
			
		||||
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else is listening to the serial TX pin or your board will get confused by log messages!
 | 
			
		||||
 | 
			
		||||
@@ -39,11 +40,17 @@ Methods also exist to read the read/calculated values from other WLED modules th
 | 
			
		||||
 | 
			
		||||
# Compiling
 | 
			
		||||
 | 
			
		||||
To enable, add `BME280_v2` to your `custom_usermods`  (e.g. in `platformio_override.ini`)
 | 
			
		||||
To enable, compile with `USERMOD_BME280` defined  (e.g. in `platformio_override.ini`)
 | 
			
		||||
```ini
 | 
			
		||||
[env:usermod_bme280_d1_mini]
 | 
			
		||||
extends = env:d1_mini
 | 
			
		||||
custom_usermods = ${env:d1_mini.custom_usermods} BME280_v2
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${common.build_flags_esp8266}
 | 
			
		||||
  -D USERMOD_BME280
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp8266.lib_deps}
 | 
			
		||||
  BME280@~3.0.0
 | 
			
		||||
  Wire
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "BME280_v2",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "finitespace/BME280":"~3.0.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +1,17 @@
 | 
			
		||||
// force the compiler to show a warning to confirm that this file is included
 | 
			
		||||
#warning **** Included USERMOD_BME280 version 2.0 ****
 | 
			
		||||
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <BME280I2C.h>               // BME280 sensor
 | 
			
		||||
#include <EnvironmentCalculations.h> // BME280 extended measurements
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class UsermodBME280 : public Usermod
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
@@ -239,7 +241,7 @@ public:
 | 
			
		||||
        // from the UI and values read from sensor, then publish to broker
 | 
			
		||||
        if (temperature != lastTemperature || PublishAlways)
 | 
			
		||||
        {
 | 
			
		||||
          publishMqtt("temperature", String(temperature, (unsigned) TemperatureDecimals).c_str());
 | 
			
		||||
          publishMqtt("temperature", String(temperature, TemperatureDecimals).c_str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lastTemperature = temperature; // Update last sensor temperature for next loop
 | 
			
		||||
@@ -252,17 +254,17 @@ public:
 | 
			
		||||
 | 
			
		||||
          if (humidity != lastHumidity || PublishAlways)
 | 
			
		||||
          {
 | 
			
		||||
            publishMqtt("humidity", String(humidity, (unsigned) HumidityDecimals).c_str());
 | 
			
		||||
            publishMqtt("humidity", String(humidity, HumidityDecimals).c_str());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (heatIndex != lastHeatIndex || PublishAlways)
 | 
			
		||||
          {
 | 
			
		||||
            publishMqtt("heat_index", String(heatIndex, (unsigned) TemperatureDecimals).c_str());
 | 
			
		||||
            publishMqtt("heat_index", String(heatIndex, TemperatureDecimals).c_str());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (dewPoint != lastDewPoint || PublishAlways)
 | 
			
		||||
          {
 | 
			
		||||
            publishMqtt("dew_point", String(dewPoint, (unsigned) TemperatureDecimals).c_str());
 | 
			
		||||
            publishMqtt("dew_point", String(dewPoint, TemperatureDecimals).c_str());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          lastHumidity = humidity;
 | 
			
		||||
@@ -279,7 +281,7 @@ public:
 | 
			
		||||
 | 
			
		||||
        if (pressure != lastPressure || PublishAlways)
 | 
			
		||||
        {
 | 
			
		||||
          publishMqtt("pressure", String(pressure, (unsigned) PressureDecimals).c_str());
 | 
			
		||||
          publishMqtt("pressure", String(pressure, PressureDecimals).c_str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lastPressure = pressure;
 | 
			
		||||
@@ -442,6 +444,7 @@ public:
 | 
			
		||||
    configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
 | 
			
		||||
    configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
 | 
			
		||||
    configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
 | 
			
		||||
    tempScale = UseCelsius ? "°C" : "°F";
 | 
			
		||||
 | 
			
		||||
    DEBUG_PRINT(FPSTR(_name));
 | 
			
		||||
    if (!initDone) {
 | 
			
		||||
@@ -477,7 +480,3 @@ public:
 | 
			
		||||
 | 
			
		||||
const char UsermodBME280::_name[]                      PROGMEM = "BME280/BMP280";
 | 
			
		||||
const char UsermodBME280::_enabled[]                   PROGMEM = "enabled";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static UsermodBME280 bme280_v2;
 | 
			
		||||
REGISTER_USERMOD(bme280_v2);
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,58 +1,53 @@
 | 
			
		||||
# Usermod BME68X
 | 
			
		||||
 | 
			
		||||
This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page. 
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic1.png" style="width:60%;"></p>
 | 
			
		||||
 | 
			
		||||
In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings.
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic2.png"></p>
 | 
			
		||||
 | 
			
		||||
If you use HomeAssistance discovery, the device tree for HomeAssistance is created.  This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT.
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic3.png"></p>
 | 
			
		||||
 | 
			
		||||
A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant.
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic4.png" style="width:60%;"></p>
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
Raw sensor types
 | 
			
		||||
 | 
			
		||||
Sensor		Accuracy	Scale		Range
 | 
			
		||||
-----------------------------
 | 
			
		||||
	Sensor		Accuracy	Scale		Range
 | 
			
		||||
 	--------------------------------------------------------------------------------------------------
 | 
			
		||||
	Temperature	+/- 1.0		°C/°F		-40 to 85 °C
 | 
			
		||||
	Humidity	+/- 3 		%		0 to 100 %
 | 
			
		||||
	Pressure	+/- 1 		hPa		300 to 1100 hPa
 | 
			
		||||
	Gas Resistance			Ohm
 | 
			
		||||
 | 
			
		||||
Temperature	+/- 1.0		°C/°F		-40 to 85 °C
 | 
			
		||||
Humidity	+/- 3 		%		0 to 100 %
 | 
			
		||||
Pressure	+/- 1 		hPa		300 to 1100 hPa
 | 
			
		||||
Gas Resistance			Ohm
 | 
			
		||||
The BSEC Library calculates the following values via the gas resistance
 | 
			
		||||
 | 
			
		||||
Sensor		Accuracy	Scale		Range
 | 
			
		||||
-----------------------------
 | 
			
		||||
	Sensor		Accuracy	Scale		Range
 | 
			
		||||
 	--------------------------------------------------------------------------------------------------
 | 
			
		||||
	IAQ 						value between 0 and 500
 | 
			
		||||
	Static IAQ 					same as IAQ but for permanently installed devices
 | 
			
		||||
	CO2 				PPM
 | 
			
		||||
	VOC 				PPM
 | 
			
		||||
	Gas-Percentage 			%
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IAQ 						value between 0 and 500
 | 
			
		||||
Static IAQ 					same as IAQ but for permanently installed devices
 | 
			
		||||
CO2 				PPM
 | 
			
		||||
VOC 				PPM
 | 
			
		||||
Gas-Percentage 			%
 | 
			
		||||
In addition the usermod calculates
 | 
			
		||||
 | 
			
		||||
Sensor		Accuracy	Scale		Range
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
Absolute humidity	 	g/m³
 | 
			
		||||
Dew point 			°C/°F
 | 
			
		||||
	Sensor		Accuracy	Scale		Range
 | 
			
		||||
 	--------------------------------------------------------------------------------------------------
 | 
			
		||||
	Absolute humidity	 	g/m³
 | 
			
		||||
	Dew point 			°C/°F
 | 
			
		||||
 | 
			
		||||
### IAQ (Indoor Air Quality)
 | 
			
		||||
 | 
			
		||||
The IAQ is divided into the following value groups. 
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic5.png"></p>
 | 
			
		||||
 | 
			
		||||
For more detailed information, please consult the enclosed Bosch product description (BME680.pdf).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Calibration of the device
 | 
			
		||||
 | 
			
		||||
The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration. 
 | 
			
		||||
@@ -72,10 +67,10 @@ The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to t
 | 
			
		||||
 | 
			
		||||
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Settings
 | 
			
		||||
 | 
			
		||||
The settings of the usermods are set in the usermod section of wled. 
 | 
			
		||||
 | 
			
		||||
<p align="center"><img src="pics/pic6.png"></p>
 | 
			
		||||
 | 
			
		||||
The possible settings are
 | 
			
		||||
@@ -93,7 +88,6 @@ The possible settings are
 | 
			
		||||
- **Del Calibration Hist:**	If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved. 
 | 
			
		||||
 | 
			
		||||
### Sensors
 | 
			
		||||
 | 
			
		||||
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form. 
 | 
			
		||||
 | 
			
		||||
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
 | 
			
		||||
@@ -105,7 +99,6 @@ Data is published over MQTT - make sure you've enabled the MQTT sync interface.
 | 
			
		||||
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
 | 
			
		||||
 | 
			
		||||
Methods also exist to read the read/calculated values from other WLED modules through code.
 | 
			
		||||
 | 
			
		||||
- getTemperature();	The scale °C/°F is depended to the settings
 | 
			
		||||
- getHumidity();	
 | 
			
		||||
- getPressure();
 | 
			
		||||
@@ -125,36 +118,32 @@ Methods also exist to read the read/calculated values from other WLED modules th
 | 
			
		||||
- getStabStatus();
 | 
			
		||||
- getRunInStatus();
 | 
			
		||||
 | 
			
		||||
## Compilation
 | 
			
		||||
 | 
			
		||||
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
 | 
			
		||||
## Compiling
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
To enable, compile with `USERMOD_BME68X` defined (e.g. in `platformio_override.ini`) and add the `BSEC Software Library` to the lib_deps.
 | 
			
		||||
 | 
			
		||||
```[env:esp32_mySpecial]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} BME68X
 | 
			
		||||
```
 | 
			
		||||
[env:esp32-BME680]
 | 
			
		||||
board = 		esp32dev
 | 
			
		||||
platform = 		${esp32.platform}
 | 
			
		||||
platform_packages = 	${esp32.platform_packages}
 | 
			
		||||
lib_deps = 		${esp32.lib_deps}
 | 
			
		||||
           		boschsensortec/BSEC Software Library @ ^1.8.1492      	; USERMOD: BME680                                          
 | 
			
		||||
build_unflags = 	${common.build_unflags}
 | 
			
		||||
build_flags = 		${common.build_flags_esp32} 
 | 
			
		||||
              		-D USERMOD_BME68X                      			; USERMOD: BME680
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Revision History
 | 
			
		||||
 | 
			
		||||
### Version 1.0.0
 | 
			
		||||
 | 
			
		||||
- First version of the BME68X_v user module
 | 
			
		||||
 | 
			
		||||
### Version 1.0.1
 | 
			
		||||
 | 
			
		||||
- Rebased to WELD Version 0.15
 | 
			
		||||
- Reworked some default settings
 | 
			
		||||
- A problem with the default settings has been fixed
 | 
			
		||||
 | 
			
		||||
### Version 1.0.2
 | 
			
		||||
 | 
			
		||||
* Rebased to WELD Version 0.16
 | 
			
		||||
* Fixed: Solved compilation problems related to some macro naming interferences.
 | 
			
		||||
 | 
			
		||||
## Known problems
 | 
			
		||||
 | 
			
		||||
- MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core.
 | 
			
		||||
- If you save the settings often, WLED can get stuck.
 | 
			
		||||
- If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "BME68X",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "boschsensortec/BSEC Software Library":"^1.8.1492"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1114
									
								
								usermods/BME68X_v2/usermod_bme68x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1114
									
								
								usermods/BME68X_v2/usermod_bme68x.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Battery",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,9 @@ Enables battery level monitoring of your project.
 | 
			
		||||
 | 
			
		||||
## 🎈 Installation
 | 
			
		||||
 | 
			
		||||
In `platformio_override.ini` (or `platformio.ini`)<br>Under: `custom_usermods =`, add the line: `Battery`<br><br>[Example: platformio_override.ini](assets/installation_platformio_override_ini.png) |
 | 
			
		||||
| **Option 1** | **Option 2** |
 | 
			
		||||
|--------------|--------------|
 | 
			
		||||
| In `wled00/my_config.h`<br>Add the line: `#define USERMOD_BATTERY`<br><br>[Example: my_config.h](assets/installation_my_config_h.png) | In `platformio_override.ini` (or `platformio.ini`)<br>Under: `build_flags =`, add the line: `-D USERMOD_BATTERY`<br><br>[Example: platformio_override.ini](assets/installation_platformio_override_ini.png) |
 | 
			
		||||
 | 
			
		||||
<br><br>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include "battery_defaults.h"
 | 
			
		||||
#include "UMBattery.h"
 | 
			
		||||
@@ -855,7 +857,3 @@ const char UsermodBattery::_preset[]        PROGMEM = "preset";
 | 
			
		||||
const char UsermodBattery::_duration[]      PROGMEM = "duration";
 | 
			
		||||
const char UsermodBattery::_init[]          PROGMEM = "init";
 | 
			
		||||
const char UsermodBattery::_haDiscovery[]   PROGMEM = "HA-discovery";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static UsermodBattery battery;
 | 
			
		||||
REGISTER_USERMOD(battery);
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Cronixie",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,5 +4,5 @@ This usermod supports driving the Cronixie M and L clock kits by Diamex.
 | 
			
		||||
 | 
			
		||||
## Installation 
 | 
			
		||||
 | 
			
		||||
Compile and upload after adding `Cronixie` to `custom_usermods` of your PlatformIO environment.  
 | 
			
		||||
Compile and upload after adding `-D USERMOD_CRONIXIE` to `build_flags` of your PlatformIO environment.  
 | 
			
		||||
Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs.
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
class UsermodCronixie : public Usermod {
 | 
			
		||||
@@ -247,7 +249,7 @@ class UsermodCronixie : public Usermod {
 | 
			
		||||
        
 | 
			
		||||
        if (backlight && _digitOut[i] <11)
 | 
			
		||||
        {
 | 
			
		||||
          uint32_t col = strip.getSegment(0).colors[1];
 | 
			
		||||
          uint32_t col = gamma32(strip.getSegment(0).colors[1]);
 | 
			
		||||
          for (uint16_t j=o; j< o+10; j++) {
 | 
			
		||||
            if (j != excl) strip.setPixelColor(j, col);
 | 
			
		||||
          }
 | 
			
		||||
@@ -298,6 +300,3 @@ class UsermodCronixie : public Usermod {
 | 
			
		||||
      return USERMOD_ID_CRONIXIE;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static UsermodCronixie cronixie;
 | 
			
		||||
REGISTER_USERMOD(cronixie);
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "DHT",
 | 
			
		||||
  "build": { "libArchive": false},
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
; Options
 | 
			
		||||
; -------
 | 
			
		||||
; USERMOD_DHT                      - define this to have this user mod included wled00\usermods_list.cpp
 | 
			
		||||
; USERMOD_DHT_DHTTYPE              - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
 | 
			
		||||
; USERMOD_DHT_PIN                  - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
 | 
			
		||||
; USERMOD_DHT_CELSIUS              - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
 | 
			
		||||
@@ -10,11 +11,13 @@
 | 
			
		||||
 | 
			
		||||
[env:d1_mini_usermod_dht_C]
 | 
			
		||||
extends = env:d1_mini
 | 
			
		||||
custom_usermods = ${env:d1_mini.custom_usermods} DHT
 | 
			
		||||
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT_CELSIUS
 | 
			
		||||
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
 | 
			
		||||
lib_deps = ${env:d1_mini.lib_deps}
 | 
			
		||||
    https://github.com/alwynallan/DHT_nonblocking
 | 
			
		||||
 | 
			
		||||
[env:custom32_LEDPIN_16_usermod_dht_C]
 | 
			
		||||
extends = env:custom32_LEDPIN_16
 | 
			
		||||
custom_usermods = ${env:custom32_LEDPIN_16.custom_usermods} DHT
 | 
			
		||||
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
 | 
			
		||||
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
 | 
			
		||||
lib_deps = ${env.lib_deps}
 | 
			
		||||
    https://github.com/alwynallan/DHT_nonblocking
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ Copy the example `platformio_override.ini` to the root directory.  This file sho
 | 
			
		||||
 | 
			
		||||
### Define Your Options
 | 
			
		||||
 | 
			
		||||
* `USERMOD_DHT`                      - define this to include this user mod wled00\usermods_list.cpp
 | 
			
		||||
* `USERMOD_DHT_DHTTYPE`              - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
 | 
			
		||||
* `USERMOD_DHT_PIN`                  - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
 | 
			
		||||
* `USERMOD_DHT_CELSIUS`              - define this to report temperatures in degrees Celsius, otherwise Fahrenheit will be reported
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -243,7 +245,3 @@ class UsermodDHT : public Usermod {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static UsermodDHT dht;
 | 
			
		||||
REGISTER_USERMOD(dht);
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "EXAMPLE",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {}
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ In this usermod file you can find the documentation on how to take advantage of
 | 
			
		||||
 | 
			
		||||
## Installation 
 | 
			
		||||
 | 
			
		||||
Add `EXAMPLE` to `custom_usermods` in your PlatformIO environment and compile!
 | 
			
		||||
Copy `usermod_v2_example.h` to the wled00 directory.  
 | 
			
		||||
Uncomment the corresponding lines in `usermods_list.cpp` and compile!  
 | 
			
		||||
_(You shouldn't need to actually install this, it does nothing useful)_
 | 
			
		||||
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Usermods allow you to add own functionality to WLED more easily
 | 
			
		||||
 * See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
 | 
			
		||||
 * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
 | 
			
		||||
 * 
 | 
			
		||||
 * This is an example for a v2 usermod.
 | 
			
		||||
 * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
 | 
			
		||||
@@ -402,6 +404,3 @@ void MyExampleUsermod::publishMqtt(const char* state, bool retain)
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MyExampleUsermod example_usermod;
 | 
			
		||||
REGISTER_USERMOD(example_usermod);
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name:": "EleksTube_IPS",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "TFT_eSPI" : "2.5.33"
 | 
			
		||||
  } 
 | 
			
		||||
}
 | 
			
		||||
# Seems to add 300kb to the RAM requirement???
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "TFTs.h"
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +156,3 @@ class ElekstubeIPSUsermod : public Usermod {
 | 
			
		||||
const char ElekstubeIPSUsermod::_name[]         PROGMEM = "EleksTubeIPS";
 | 
			
		||||
const char ElekstubeIPSUsermod::_tubeSeg[]      PROGMEM = "tubeSegment";
 | 
			
		||||
const char ElekstubeIPSUsermod::_digitOffset[]  PROGMEM = "digitOffset";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static ElekstubeIPSUsermod elekstube_ips;
 | 
			
		||||
REGISTER_USERMOD(elekstube_ips);
 | 
			
		||||
@@ -1,12 +1,11 @@
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
 | 
			
		||||
#include <DallasTemperature.h> //Dallastemperature sensor
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//The SCL and SDA pins are defined here. 
 | 
			
		||||
//Lolin32 boards use SCL=5 SDA=4 
 | 
			
		||||
#define U8X8_PIN_SCL 5
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <BME280I2C.h> //BME280 sensor
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void UpdateBME280Data();
 | 
			
		||||
 | 
			
		||||
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit 
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Fix_unreachable_netservices_v2",
 | 
			
		||||
  "platforms": ["espressif8266"]
 | 
			
		||||
}
 | 
			
		||||
@@ -30,6 +30,41 @@ The usermod supports the following state changes:
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
1. Add `Fix_unreachable_netservices` to `custom_usermods` in your PlatformIO environment.
 | 
			
		||||
1. Copy the file `usermod_Fix_unreachable_netservices.h` to the `wled00` directory.
 | 
			
		||||
2. Register the usermod by adding `#include "usermod_Fix_unreachable_netservices.h"` in the top and `registerUsermod(new FixUnreachableNetServices());` in the bottom of `usermods_list.cpp`.
 | 
			
		||||
 | 
			
		||||
Example **usermods_list.cpp**:
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
/*
 | 
			
		||||
 * Register your v2 usermods here!
 | 
			
		||||
 *   (for v1 usermods using just usermod.cpp, you can ignore this file)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Add/uncomment your usermod filename here (and once more below)
 | 
			
		||||
 * || || ||
 | 
			
		||||
 * \/ \/ \/
 | 
			
		||||
 */
 | 
			
		||||
//#include "usermod_v2_example.h"
 | 
			
		||||
//#include "usermod_temperature.h"
 | 
			
		||||
//#include "usermod_v2_empty.h"
 | 
			
		||||
#include  "usermod_Fix_unreachable_netservices.h"
 | 
			
		||||
 | 
			
		||||
void registerUsermods()
 | 
			
		||||
{
 | 
			
		||||
  /*
 | 
			
		||||
   * Add your usermod class name here
 | 
			
		||||
   * || || ||
 | 
			
		||||
   * \/ \/ \/
 | 
			
		||||
   */
 | 
			
		||||
  //UsermodManager::add(new MyExampleUsermod());
 | 
			
		||||
  //UsermodManager::add(new UsermodTemperature());
 | 
			
		||||
  //UsermodManager::add(new UsermodRenameMe());
 | 
			
		||||
  UsermodManager::add(new FixUnreachableNetServices());
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Hopefully I can help someone with that - @gegu
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#if defined(ESP32)
 | 
			
		||||
#warning "Usermod FixUnreachableNetServices works only with ESP8266 builds"
 | 
			
		||||
class FixUnreachableNetServices : public Usermod
 | 
			
		||||
{
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(ESP8266)
 | 
			
		||||
#include <ping.h>
 | 
			
		||||
@@ -8,7 +16,7 @@
 | 
			
		||||
 * By this procedure the net services of WLED remains accessible in some problematic WLAN environments.
 | 
			
		||||
 * 
 | 
			
		||||
 * Usermods allow you to add own functionality to WLED more easily
 | 
			
		||||
 * See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
 | 
			
		||||
 * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
 | 
			
		||||
 * 
 | 
			
		||||
 * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
 | 
			
		||||
 * Multiple v2 usermods can be added to one compilation easily.
 | 
			
		||||
@@ -160,11 +168,4 @@ Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
 | 
			
		||||
    return USERMOD_ID_FIXNETSERVICES;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static FixUnreachableNetServices fix_unreachable_net_services;
 | 
			
		||||
REGISTER_USERMOD(fix_unreachable_net_services);
 | 
			
		||||
 | 
			
		||||
#else /* !ESP8266 */
 | 
			
		||||
#warning "Usermod FixUnreachableNetServices works only with ESP8266 builds"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -22,6 +22,13 @@ The following settings can be configured in the Usermod Menu:
 | 
			
		||||
- **MqttPublishAlways**: Publish always, regardless if there is a change.
 | 
			
		||||
- **MqttHomeAssistantDiscovery**: Enable Home Assistant discovery.
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
 | 
			
		||||
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
 | 
			
		||||
 | 
			
		||||
- Libraries
 | 
			
		||||
  - `wollewald/INA226_WE@~1.2.9` (by [wollewald](https://registry.platformio.org/libraries/wollewald/INA226_WE))
 | 
			
		||||
  - `Wire`
 | 
			
		||||
 | 
			
		||||
## Understanding Samples and Conversion Times
 | 
			
		||||
 | 
			
		||||
@@ -55,12 +62,16 @@ For detailed programming information and register configurations, refer to the [
 | 
			
		||||
 | 
			
		||||
## Compiling
 | 
			
		||||
 | 
			
		||||
To enable, compile with `INA226` in `custom_usermods` (e.g. in `platformio_override.ini`).
 | 
			
		||||
To enable, compile with `USERMOD_INA226` defined (e.g. in `platformio_override.ini`).
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
[env:ina226_example]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} INA226
 | 
			
		||||
build_flags = ${env:esp32dev.build_flags}
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D USERMOD_INA226
 | 
			
		||||
  ; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp32.lib_deps}
 | 
			
		||||
  wollewald/INA226_WE@~1.2.9
 | 
			
		||||
```
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "INA226_v2",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "wollewald/INA226_WE":"~1.2.9"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
[env:ina226_example]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${env:esp32dev.build_flags}
 | 
			
		||||
  ${common.build_flags} ${esp32.build_flags}
 | 
			
		||||
  -D USERMOD_INA226
 | 
			
		||||
  ; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp32.lib_deps}
 | 
			
		||||
  wollewald/INA226_WE@~1.2.9
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <INA226_WE.h>
 | 
			
		||||
 | 
			
		||||
@@ -208,6 +210,12 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~UsermodINA226()
 | 
			
		||||
    {
 | 
			
		||||
        delete _ina226;
 | 
			
		||||
        _ina226 = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef WLED_DISABLE_MQTT
 | 
			
		||||
    void mqttInitialize()
 | 
			
		||||
    {
 | 
			
		||||
@@ -543,17 +551,6 @@ public:
 | 
			
		||||
        _initDone = true;
 | 
			
		||||
        return configComplete;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~UsermodINA226()
 | 
			
		||||
    {
 | 
			
		||||
        delete _ina226;
 | 
			
		||||
        _ina226 = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char UsermodINA226::_name[] PROGMEM = "INA226";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static UsermodINA226 ina226_v2;
 | 
			
		||||
REGISTER_USERMOD(ina226_v2);
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Internal_Temperature_v2",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,8 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
- Add `Internal_Temperature` to `custom_usermods` in your `platformio.ini` (or `platformio_override.ini`).
 | 
			
		||||
- Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 📝 Change Log
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
class InternalTemperatureUsermod : public Usermod
 | 
			
		||||
@@ -48,7 +50,7 @@ public:
 | 
			
		||||
#else                                    // ESP32 ESP32S3 and ESP32C3
 | 
			
		||||
    temperature = roundf(temperatureRead() * 10) / 10;
 | 
			
		||||
#endif
 | 
			
		||||
 if(presetToActivate != 0){
 | 
			
		||||
 | 
			
		||||
    // Check if temperature has exceeded the activation threshold
 | 
			
		||||
    if (temperature >= activationThreshold) {
 | 
			
		||||
      // Update the state flag if not already set
 | 
			
		||||
@@ -56,7 +58,7 @@ public:
 | 
			
		||||
        isAboveThreshold = true;
 | 
			
		||||
        }
 | 
			
		||||
      // Check if a 'high temperature' preset is configured and it's not already active
 | 
			
		||||
      if (currentPreset != presetToActivate) {
 | 
			
		||||
      if (presetToActivate != 0 && currentPreset != presetToActivate) {
 | 
			
		||||
        // If a playlist is active, store it for reactivation later
 | 
			
		||||
        if (currentPlaylist > 0) {
 | 
			
		||||
          previousPlaylist = currentPlaylist;
 | 
			
		||||
@@ -99,7 +101,6 @@ public:
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
#ifndef WLED_DISABLE_MQTT
 | 
			
		||||
    if (WLED_MQTT_CONNECTED)
 | 
			
		||||
@@ -192,6 +193,3 @@ void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain)
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static InternalTemperatureUsermod internal_temperature_v2;
 | 
			
		||||
REGISTER_USERMOD(internal_temperature_v2);
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "LD2410_v2",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "ncmreynolds/ld2410":"^0.1.3"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,15 +10,21 @@ The movement and presence state are displayed in both the Info section of the we
 | 
			
		||||
## Dependencies
 | 
			
		||||
- Libraries
 | 
			
		||||
  - `ncmreynolds/ld2410@^0.1.3`
 | 
			
		||||
  - This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
 | 
			
		||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
 | 
			
		||||
 | 
			
		||||
## Compilation
 | 
			
		||||
 | 
			
		||||
To enable, compile with `LD2140` in `custom_usermods` (e.g. in `platformio_override.ini`)
 | 
			
		||||
To enable, compile with `USERMOD_LD2410` defined  (e.g. in `platformio_override.ini`)
 | 
			
		||||
```ini
 | 
			
		||||
[env:usermod_USERMOD_LD2410_esp32dev]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} LD2140_v2
 | 
			
		||||
build_flags =
 | 
			
		||||
    ${common.build_flags_esp32}
 | 
			
		||||
    -D USERMOD_LD2410
 | 
			
		||||
lib_deps = 
 | 
			
		||||
    ${esp32.lib_deps}
 | 
			
		||||
    ncmreynolds/ld2410@^0.1.3
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Configuration Options
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,14 @@
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <ld2410.h>
 | 
			
		||||
#warning **** Included USERMOD_LD2410 ****
 | 
			
		||||
 | 
			
		||||
#ifdef WLED_DISABLE_MQTT
 | 
			
		||||
#ifndef WLED_ENABLE_MQTT
 | 
			
		||||
#error "This user mod requires MQTT to be enabled."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include <ld2410.h>
 | 
			
		||||
 | 
			
		||||
class LD2410Usermod : public Usermod {
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
@@ -231,7 +235,3 @@ void LD2410Usermod::publishMqtt(const char* topic, const char* state, bool retai
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static LD2410Usermod ld2410_v2;
 | 
			
		||||
REGISTER_USERMOD(ld2410_v2);
 | 
			
		||||
@@ -2,14 +2,13 @@
 | 
			
		||||
This usermod will obtain readings from a Light Dependent Resistor (LDR) and will turn on/off specific presets based on those readings. This is useful for exterior lighting situations where you want the lights to only be on when it is dark out.
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
Add "LDR_Dusk_Dawn" to your platformio.ini environment's custom_usermods and build.
 | 
			
		||||
Add "-D USERMOD_LDR_DUSK_DAWN" to your platformio.ini [common] build_flags and build.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
```
 | 
			
		||||
[env:usermod_LDR_Dusk_Dawn_esp32dev]
 | 
			
		||||
extends = env:esp32dev
 | 
			
		||||
custom_usermods = ${env:esp32dev.custom_usermods} 
 | 
			
		||||
  LDR_Dusk_Dawn   # Enable LDR Dusk Dawn Usermod
 | 
			
		||||
[common]
 | 
			
		||||
build_flags =
 | 
			
		||||
  -D USERMOD_LDR_DUSK_DAWN   # Enable LDR Dusk Dawn Usermod
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Usermod Settings
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "LDR_Dusk_Dawn_v2",
 | 
			
		||||
  "build": { "libArchive": false }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
#ifndef ARDUINO_ARCH_ESP32
 | 
			
		||||
@@ -150,7 +151,3 @@ class LDR_Dusk_Dawn_v2 : public Usermod {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char LDR_Dusk_Dawn_v2::_name[]    PROGMEM = "LDR_Dusk_Dawn_v2";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static LDR_Dusk_Dawn_v2 ldr_dusk_dawn_v2;
 | 
			
		||||
REGISTER_USERMOD(ldr_dusk_dawn_v2);
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "MAX17048_v2",
 | 
			
		||||
  "build": { "libArchive": false},
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "Adafruit_MAX1704X":"https://github.com/adafruit/Adafruit_MAX1704X#1.0.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,16 +5,26 @@ This usermod reads information from an Adafruit MAX17048  and outputs the follow
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
Libraries:
 | 
			
		||||
- `Adafruit_BusIO@~1.14.5` (by [adafruit](https://github.com/adafruit/Adafruit_BusIO))
 | 
			
		||||
- `Adafruit_MAX1704X@~1.0.2` (by [adafruit](https://github.com/adafruit/Adafruit_MAX1704X))
 | 
			
		||||
 | 
			
		||||
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
 | 
			
		||||
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
 | 
			
		||||
 | 
			
		||||
## Compilation
 | 
			
		||||
 | 
			
		||||
Add "MAX17048_v2" to your platformio.ini environment's custom_usermods and build.
 | 
			
		||||
To enable, compile with `USERMOD_MAX17048` define in the build_flags (e.g. in `platformio.ini` or `platformio_override.ini`) such as in the example below:
 | 
			
		||||
```ini
 | 
			
		||||
[env:usermod_max17048_d1_mini]
 | 
			
		||||
extends = env:d1_mini
 | 
			
		||||
custom_usermods = ${env:d1_mini.custom_usermods} MAX17048_v2
 | 
			
		||||
build_flags =
 | 
			
		||||
  ${common.build_flags_esp8266}
 | 
			
		||||
  -D USERMOD_MAX17048
 | 
			
		||||
lib_deps = 
 | 
			
		||||
  ${esp8266.lib_deps}
 | 
			
		||||
  https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
 | 
			
		||||
  https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Configuration Options
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
// force the compiler to show a warning to confirm that this file is included
 | 
			
		||||
#warning **** Included USERMOD_MAX17048 V2.0 ****
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include "Adafruit_MAX1704X.h"
 | 
			
		||||
 | 
			
		||||
@@ -35,8 +37,8 @@ class  Usermod_MAX17048 : public Usermod {
 | 
			
		||||
    unsigned long lastSend = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    unsigned VoltageDecimals = 3;  // Number of decimal places in published voltage values
 | 
			
		||||
    unsigned PercentDecimals = 1;  // Number of decimal places in published percent values
 | 
			
		||||
    uint8_t  VoltageDecimals = 3;  // Number of decimal places in published voltage values
 | 
			
		||||
    uint8_t  PercentDecimals = 1;    // Number of decimal places in published percent values
 | 
			
		||||
 | 
			
		||||
    // string that are used multiple time (this will save some flash memory)
 | 
			
		||||
    static const char _name[];
 | 
			
		||||
@@ -277,7 +279,3 @@ const char Usermod_MAX17048::_enabled[] PROGMEM = "enabled";
 | 
			
		||||
const char Usermod_MAX17048::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
 | 
			
		||||
const char Usermod_MAX17048::_minReadInterval[] PROGMEM = "min-read-interval-ms";
 | 
			
		||||
const char Usermod_MAX17048::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscovery";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static Usermod_MAX17048 max17048_v2;
 | 
			
		||||
REGISTER_USERMOD(max17048_v2);
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "MY9291",
 | 
			
		||||
  "build": { "libArchive": false },
 | 
			
		||||
  "platforms": ["espressif8266"]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
#include "MY92xx.h"
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +43,3 @@ class MY9291Usermod : public Usermod {
 | 
			
		||||
      return USERMOD_ID_MY9291;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static MY9291Usermod my9291;
 | 
			
		||||
REGISTER_USERMOD(my9291);
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user