Compare commits

..

No commits in common. "main" and "v0.15.0-b1" have entirely different histories.

360 changed files with 16838 additions and 31266 deletions

View File

@ -2,7 +2,12 @@
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6 # [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
ARG VARIANT="3" 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. # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
# COPY requirements.txt /tmp/pip-tmp/ # COPY requirements.txt /tmp/pip-tmp/

View File

@ -5,54 +5,41 @@
"context": "..", "context": "..",
"args": { "args": {
// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9 // 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/*"
} }
}, },
// To give the container access to a device serial port, you can uncomment one of the following lines. // Set *default* container specific settings.json values on container create.
// Note: If running on Windows, you will have to do some additional steps: "settings": {
// https://stackoverflow.com/questions/68527888/how-can-i-use-a-usb-com-port-inside-of-a-vscode-development-container "terminal.integrated.shell.linux": "/bin/bash",
// "python.pythonPath": "/usr/local/bin/python",
// You can explicitly just forward the port you want to connect to. Replace `/dev/ttyACM0` with the serial port for "python.linting.enabled": true,
// your device. This will only work if the device is plugged in from the start without reconnecting. Adding the "python.linting.pylintEnabled": true,
// `dialout` group is needed if read/write permisions for the port are limitted to the dialout user. "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
// "runArgs": ["--device=/dev/ttyACM0", "--group-add", "dialout"], "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
// "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
// Alternatively, you can give more comprehensive access to the host system. This will expose all the host devices to "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
// the container. Adding the `dialout` group is needed if read/write permisions for the port are limitted to the "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
// dialout user. This could allow the container to modify unrelated serial devices, which would be a similar level of "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
// risk to running the build directly on the host. "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"], "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"python.pythonPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
},
"extensions": [
"ms-python.python",
"platformio.platformio-ide"
]
}
}, },
// 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. // Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [], // "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created. // 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. // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode" "remoteUser": "vscode"

View File

@ -80,7 +80,7 @@ body:
id: terms id: terms
attributes: attributes:
label: Code of Conduct 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: options:
- label: I agree to follow this project's Code of Conduct - label: I agree to follow this project's Code of Conduct
required: true required: true

20
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,20 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 120
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- keep
- enhancement
- confirmed
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Hey! This issue has been open for quite some time without any new comments now.
It will be closed automatically in a week if no further activity occurs.
Thank you for using WLED!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -1,82 +0,0 @@
name: WLED Build
# Only included into other workflows
on:
workflow_call:
jobs:
get_default_envs:
name: Gather Environments
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 "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Environments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
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: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- run: npm test

View File

@ -1,47 +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
- 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 }}

View File

@ -1,33 +0,0 @@
name: Notify Discord on PR Merge
on:
workflow_dispatch:
pull_request:
types: [closed]
jobs:
notify:
runs-on: ubuntu-latest
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: Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # This is dangerous without the first access check
- name: Send Discord notification
# if: github.event.pull_request.merged == true
run: |
curl -H "Content-Type: application/json" -d '{"content": "Pull Request ${{ github.event.pull_request.number }} merged by ${{ github.actor }}"}' ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}

View File

@ -1,35 +0,0 @@
name: WLED Release CI
on:
push:
tags:
- '*'
jobs:
wled_build:
uses: ./.github/workflows/build.yml
release:
name: Create Release
runs-on: ubuntu-latest
needs: wled_build
steps:
- 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
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.changelog }}
draft: True
files: |
*.bin
*.bin.gz

View File

@ -1,30 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '0 12 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
days-before-stale: 120
days-before-close: 7
stale-issue-label: 'stale'
stale-pr-label: 'stale'
exempt-issue-labels: 'pinned,keep,enhancement,confirmed'
exempt-pr-labels: 'pinned,keep,enhancement,confirmed'
exempt-all-milestones: true
operations-per-run: 1000
stale-issue-message: >
Hey! This issue has been open for quite some time without any new comments now.
It will be closed automatically in a week if no further activity occurs.
Thank you for using WLED! ✨
stale-pr-message: >
Hey! This pull request has been open for quite some time without any new comments now.
It will be closed automatically in a week if no further activity occurs.
Thank you for contributing to WLED! ❤️

View File

@ -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 }}

View File

@ -1,71 +0,0 @@
name: Usermod CI
on:
push:
paths:
- usermods/**
- .github/workflows/usermods.yml
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 }}

View File

@ -1,11 +1,94 @@
name: WLED CI name: WLED CI
on: on: [push, pull_request]
push:
branches:
- '*'
pull_request:
jobs: jobs:
wled_build:
uses: ./.github/workflows/build.yml get_default_envs:
name: Gather Environments
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 "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
- run: npm install
- 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: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02.bin.gz
release:
name: Create Release
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test

3
.gitignore vendored
View File

@ -15,11 +15,10 @@ wled-update.sh
/build_output/ /build_output/
/node_modules/ /node_modules/
/logs/
/wled00/extLibs /wled00/extLibs
/wled00/LittleFS /wled00/LittleFS
/wled00/my_config.h /wled00/my_config.h
/wled00/Release /wled00/Release
/wled00/wled00.ino.cpp /wled00/wled00.ino.cpp
/wled00/html_*.h /wled00/html_*.h

1
.nvmrc
View File

@ -1 +0,0 @@
20.18

View File

@ -1,186 +1,5 @@
## WLED changelog ## WLED changelog
#### Build 2410270
- WLED 0.15.0-b7 release
- Re-license the WLED project from MIT to EUPL (#4194 by @Aircoookie)
- Fix alexa devices invisible/uncontrollable (#4214 by @Svennte)
- Add visual expand button on hover (#4172)
- Usermod: Audioreactive tuning and performance enhancements (by @softhack007)
- `/json/live` (JSON live data/peek) only enabled when WebSockets are disabled
- Various bugfixes and optimisations: #4179, #4215, #4219, #4222, #4223, #4224, #4228, #4230
#### Build 2410140
- WLED 0.15.0-b6 release
- Added BRT timezone (#4188 by @LuisFadini)
- Fixed the positioning of the "Download the latest binary" button (#4184 by @maxi4329)
- Add WLED_AUTOSEGMENTS compile flag (#4183 by @PaoloTK)
- New 512kB FS parition map for 4MB devices
- Internal API change: Static PinManager & UsermodManager
- Change in Improv chip ID and version generation
- Various optimisations, bugfixes and enhancements (#4005, #4174 & #4175 by @Xevel, #4180, #4168, #4154, #4189 by @dosipod)
#### Build 2409170
- UI: Introduce common.js in settings pages (size optimisation)
- Add the ability to toggle the reception of palette synchronizations (#4137 by @felddy)
- Usermod/FX: Temperature usermod added Temperature effect (example usermod effect by @blazoncek)
- Fix AsyncWebServer version pin
#### Build 2409140
- Configure different kinds of busses at compile (#4107 by @PaoloTK)
- BREAKING: removes LEDPIN and DEFAULT_LED_TYPE compile overrides
- Fetch LED types from Bus classes (dynamic UI) (#4129 by @netmindz, @blazoncek, @dedehai)
- Temperature usermod: update OneWire to 2.3.8 (#4131 by @iammattcoleman)
#### Build 2409100
- WLED 0.15.0-b5 release
- Audioreactive usermod included by default in all compatible builds (including ESP8266)
- Demystified some byte definitions of WiZmote ESP-NOW message (#4114 by @ChuckMash)
- Update usermod "Battery" improved MQTT support (#4110 by @itCarl)
- Added a usermod for interacting with BLE Pixels Dice (#4093 by @axlan)
- Allow lower values for touch threshold (#4081 by @RobinMeis)
- Added POV image effect usermod (#3539 by @Liliputech)
- Remove repeating code to fetch audio data (#4103 by @netmindz)
- Loxone JSON parser doesn't handle lx=0 correctly (#4104 by @FreakyJ, fixes #3809)
- Rename wled00.ino to wled_main.cpp (#4090 by @willmmiles)
- SM16825 chip support including WW & CW channel swap (#4092)
- Add stress testing scripts (#4088 by @willmmiles)
- Improve jsonBufferLock management (#4089 by @willmmiles)
- Fix incorrect PWM bit depth on Esp32 with XTAL clock (#4082 by @PaoloTK)
- Devcontainer args (#4073 by @axlan)
- Effect: Fire2012 optional blur amount (#4078 by @apanteleev)
- Effect: GEQ fix bands (#4077 by @adrianschroeter)
- Boot delay option (#4060 by @DedeHai)
- ESP8266 Audioreactive sync (#3962 by @gaaat98, @netmindz, @softhack007)
- ESP8266 PWM crash fix (#4035 by @willmmiles)
- Usermod: Battery fix (#4051 by @Nickbert7)
- Usermod: Mpu6050 usermod crash fix (#4048 by @willmmiles)
- Usermod: Internal Temperature V2 (#4033 by @adamsthws)
- Various fixes and improvements (including build environments to emulate 0.14.0 for ESP8266)
#### Build 2407070
- Various fixes and improvements (mainly LED settings fix)
#### Build 2406290
- WLED 0.15.0-b4 release
- LED settings bus management update (WARNING: only allows available outputs)
- Add ETH support for LILYGO-POE-Pro (#4030 by @rorosaurus)
- Update usermod_sn_photoresistor (#4017 by @xkvmoto)
- Several internal fixes and optimisations
- move LED_BUILTIN handling to BusManager class
- reduce max panels (web server limitation)
- edit WiFi TX power (ESP32)
- keep current ledmap ID in UI
- limit outputs in UI based on length
- wifi.ap addition to JSON Info (JSON API)
- relay pin init bugfix
- file editor button in UI
- ESP8266: update was restarting device on some occasions
- a bit of throttling in UI (for ESP8266)
#### Build 2406120
- Update NeoPixelBus to v2.8.0
- Increased LED outputs one ESP32 using parallel I2S (up to 17)
- use single/mono I2S + 4x RMT for 5 outputs or less
- use parallel x8 I2S + 8x RMT for >5 outputs (limit of 300 LEDs per output)
- Fixed code of Smartnest and updated documentation (#4001 by @DevilPro1)
- ESP32-S3 WiFi fix (#4010 by @cstruck)
- TetrisAI usermod fix (#3897 by @muebau)
- ESP-NOW usermod hook
- Update wled.h regarding OTA Password (#3993 by @gsieben)
- Usermod BME68X Sensor Implementation (#3994 by @gsieben)
- Add a usermod for AHT10, AHT15 and AHT20 temperature/humidity sensors (#3977 by @LordMike)
- Update Battery usermod documentation (#3968 by @adamsthws)
- Add INA226 usermod for reading current and power over i2c (#3986 by @LordMike)
- Bugfixes: #3991
- Several internal fixes and optimisations (WARNING: some effects may be broken that rely on overflow/narrow width)
- replace uint8_t and uint16_t with unsigned
- replace in8_t and int16_t with int
- reduces code by 1kB
#### Build 2405180
- WLED 0.14.4 release
- Fix for #3978
- Official 0.15.0-b3 release
- Merge 0.14.3 fixes into 0_15
- Added Pinwheel Expand 1D->2D effect mapping mode (#3961 by @Brandon502)
- Add changeable i2c address to BME280 usermod (#3966 by @LordMike)
- Effect: Firenoise - add palette selection
- Experimental parallel I2S support for ESP32 (compile time option)
- increased outputs to 17
- increased max possible color order overrides
- use WLED_USE_PARALLEL_I2S during compile
WARNING: Do not set up more than 256 LEDs per output when using parallel I2S with NeoPixelBus less than 2.9.0
- Update Usermod: Battery (#3964 by @adamsthws)
- Update Usermod: BME280 (#3965 by @LordMike)
- TM1914 chip support (#3913)
- Ignore brightness in Peek
- Antialiased line & circle drawing functions
- Enabled some audioreactive effects for single pixel strips/segments (#3942 by @gaaat98)
- Usermod Battery: Added Support for different battery types, Optimized file structure (#3003 by @itCarl)
- Skip playlist entry API (#3946 by @freakintoddles2)
- various optimisations and bugfixes (#3987, #3978)
#### Build 2405030
- Using brightness in analog clock overlay (#3944 by @paspiz85)
- Add Webpage shortcuts (#3945 by @w00000dy)
- ArtNet Poll reply (#3892 by @askask)
- Improved brightness change via long button presses (#3933 by @gaaat98)
- Relay open drain output (#3920 by @Suxsem)
- NEW JSON API: release info (update page, `info.release`)
- update esp32 platform to arduino-esp32 v2.0.9 (#3902)
- various optimisations and bugfixes (#3952, #3922, #3878, #3926, #3919, #3904 @DedeHai)
#### Build 2404120
- v0.15.0-b3
- fix for #3896 & WS2815 current saving
- conditional compile for AA setPixelColor()
#### Build 2404100
- Internals: #3859, #3862, #3873, #3875
- Prefer I2S1 over RMT on ESP32
- usermod for Adafruit MAX17048 (#3667 by @ccruz09)
- Runtime detection of ESP32 PICO, general PSRAM support
- Extend JSON API "info" object
- add "clock" - CPU clock in MHz
- add "flash" - flash size in MB
- Fix for #3879
- Analog PWM fix for ESP8266 (#3887 by @gaaat98)
- Fix for #3870 (#3880 by @DedeHai)
- ESP32 S3/S2 touch fix (#3798 by @DedeHai)
- PIO env. PSRAM fix for S3 & S3 with 4M flash
- audioreactive always included for S3 & S2
- Fix for #3889
- BREAKING: Effect: modified KITT (Scanner) (#3763)
#### Build 2404040
- WLED 0.14.3 release
- Fix for transition 0 (#3854, #3832, #3720)
- Fix for #3855 via #3873 (by @willmmiles)
#### Build 2403280
- Individual color channel control for JSON API (fixes #3860)
- "col":[int|string|object|array, int|string|object|array, int|string|object|array]
int = Kelvin temperature or 0 for black
string = hex representation of [WW]RRGGBB
object = individual channel control {"r":0,"g":127,"b":255,"w":255}, each being optional (valid to send {})
array = direct channel values [r,g,b,w] (w element being optional)
- runtime selection for CCT IC (Athom 15W bulb)
- #3850 (by @w00000dy)
- Rotary encoder palette count bugfix
- bugfixes and optimisations
#### Build 2403240
- v0.15.0-b2
- WS2805 support (RGB + WW + CW, 600kbps)
- Unified PSRAM use
- NeoPixelBus v2.7.9 (for future WS2805 support)
- 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"}]}`)
- FW1906 Support (#3810 by @deece and @Robert-github-com)
- ESPAsyncWebServer 2.2.0 (#3828 by @willmmiles)
- Bugfixes: #3843, #3844
#### Build 2403190 #### Build 2403190
- limit max PWM frequency (fix incorrect PWM resolution) - limit max PWM frequency (fix incorrect PWM resolution)
- Segment UI bugfix - Segment UI bugfix
@ -233,7 +52,7 @@
#### Build 2309120 till build 2402010 #### Build 2309120 till build 2402010
- WLED version 0.15.0-a0 - WLED version 0.15.0-a0
- Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to (with help from @JPZV) - Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to
- Temporary AP. Use your WLED in public with temporary AP. - Temporary AP. Use your WLED in public with temporary AP.
- Github CI build system enhancements (#3718 by @WoodyLetsCode) - Github CI build system enhancements (#3718 by @WoodyLetsCode)
- Accessibility: Node list ( #3715 by @WoodyLetsCode) - Accessibility: Node list ( #3715 by @WoodyLetsCode)

View File

@ -14,21 +14,7 @@ A good description helps us to review and understand your proposed changes. For
### Target branch for pull requests ### 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.
You can simply update your own branch, and push changes in response to reviewer recommendations.
Github will pick up the changes so your PR stays up-to-date.
> [!CAUTION]
> Do not use "force-push" while your PR is open!
> It has many subtle and unexpected consequences on our github reposistory.
> 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
### Code style ### Code style
@ -42,7 +28,7 @@ You are all set if you have enabled `Editor: Detect Indentation` in VS Code.
#### Blocks #### Blocks
Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block brackets is acceptable. Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block braches is acceptable.
Good: Good:
```cpp ```cpp
@ -51,11 +37,6 @@ if (a == b) {
} }
``` ```
```cpp
if (a == b) doStuff(a);
```
Acceptable - however the first variant is usually easier to read:
```cpp ```cpp
if (a == b) if (a == b)
{ {
@ -63,9 +44,12 @@ if (a == b)
} }
``` ```
```cpp
if (a == b) doStuff(a);
```
There should always be a space between a keyword and its condition and between the condition and brace. There should always be a space between a keyword and its condition and between the condition and brace.
Within the condition, no space should be between the parenthesis and variables. Within the condition, no space should be between the paranthesis and variables.
Spaces between variables and operators are up to the authors discretion. Spaces between variables and operators are up to the authors discretion.
There should be no space between function names and their argument parenthesis. There should be no space between function names and their argument parenthesis.
@ -105,4 +89,4 @@ Good:
There is no hard character limit for a comment within a line, There is no hard character limit for a comment within a line,
though as a rule of thumb consider wrapping after 120 characters. though as a rule of thumb consider wrapping after 120 characters.
Inline comments are OK if they describe that line only and are not exceedingly wide. Inline comments are OK if they describe that line only and are not exceedingly wide.

315
LICENSE
View File

@ -1,294 +1,21 @@
Copyright (c) 2016-present Christian Schwinne and individual WLED contributors MIT License
Licensed under the EUPL v. 1.2 or later
Copyright (c) 2016 Christian Schwinne
EUROPEAN UNION PUBLIC LICENCE v. 1.2
EUPL © the European Union 2007, 2016 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
This European Union Public Licence (the EUPL) applies to the Work (as in the Software without restriction, including without limitation the rights
defined below) which is provided under the terms of this Licence. Any use of to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
the Work, other than as authorised under this Licence is prohibited (to the copies of the Software, and to permit persons to whom the Software is
extent such use is covered by a right of the copyright holder of the Work). furnished to do so, subject to the following conditions:
The Work is provided under the terms of this Licence when the Licensor (as The above copyright notice and this permission notice shall be included in all
defined below) has placed the following notice immediately following the copies or substantial portions of the Software.
copyright notice for the Work:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Licensed under the EUPL IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
or has expressed by any other means his willingness to license under the EUPL. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1. Definitions OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
In this Licence, the following terms have the following meaning:
- The Licence: this Licence.
- The Original Work: the work or software distributed or communicated by the
Licensor under this Licence, available as Source Code and also as Executable
Code as the case may be.
- Derivative Works: the works or software that could be created by the
Licensee, based upon the Original Work or modifications thereof. This
Licence does not define the extent of modification or dependence on the
Original Work required in order to classify a work as a Derivative Work;
this extent is determined by copyright law applicable in the country
mentioned in Article 15.
- The Work: the Original Work or its Derivative Works.
- The Source Code: the human-readable form of the Work which is the most
convenient for people to study and modify.
- The Executable Code: any code which has generally been compiled and which
is meant to be interpreted by a computer as a program.
- The Licensor: the natural or legal person that distributes or communicates
the Work under the Licence.
- Contributor(s): any natural or legal person who modifies the Work under
the Licence, or otherwise contributes to the creation of a Derivative Work.
- The Licensee or You: any natural or legal person who makes any usage of
the Work under the terms of the Licence.
- Distribution or Communication: any act of selling, giving, lending,
renting, distributing, communicating, transmitting, or otherwise making
available, online or offline, copies of the Work or providing access to its
essential functionalities at the disposal of any other natural or legal
person.
2. Scope of the rights granted by the Licence
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright
vested in the Original Work:
- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Work, and make Derivative Works based upon the Work,
- communicate to the public, including the right to make available or display
the Work or copies thereof to the public and perform publicly, as the case
may be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sublicense rights in the Work or copies thereof.
Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.
In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make
effective the licence of the economic rights here above listed.
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights
to any patents held by the Licensor, to the extent necessary to make use of
the rights granted on the Work under this Licence.
3. Communication of the Source Code
The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates,
in a notice following the copyright notice attached to the Work, a repository
where the Source Code is easily and freely accessible for as long as the
Licensor continues to distribute or communicate the Work.
4. Limitations on copyright
Nothing in this Licence is intended to deprive the Licensee of the benefits
from any exception or limitation to the exclusive rights of the rights owners
in the Work, of the exhaustion of those rights or of other applicable
limitations thereto.
5. Obligations of the Licensee
The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:
Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and
a copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.
Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of
the Licence — for example by communicating EUPL v. 1.2 only. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions
on the Work or Derivative Work that alter or restrict the terms of the
Licence.
Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed
under a Compatible Licence, this Distribution or Communication can be done
under the terms of this Compatible Licence. For the sake of this clause,
Compatible Licence refers to the licences listed in the appendix attached to
this Licence. Should the Licensee's obligations under the Compatible Licence
conflict with his/her obligations under this Licence, the obligations of the
Compatible Licence shall prevail.
Provision of Source Code: When distributing or communicating copies of the
Work, the Licensee will provide a machine-readable copy of the Source Code or
indicate a repository where this Source will be easily and freely available
for as long as the Licensee continues to distribute or communicate the Work.
Legal Protection: This Licence does not grant permission to use the trade
names, trademarks, service marks, or names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.
6. Chain of Authorship
The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.
Each Contributor warrants that the copyright in the modifications he/she
brings to the Work are owned by him/her or licensed to him/her and that he/she
has the power and authority to grant the Licence.
Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.
7. Disclaimer of Warranty
The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
bugs inherent to this type of development.
For the above reason, the Work is provided under the Licence on an as is
basis and without warranties of any kind concerning the Work, including
without limitation merchantability, fitness for a particular purpose, absence
of defects or errors, accuracy, non-infringement of intellectual property
rights other than copyright as stated in Article 6 of this Licence.
This disclaimer of warranty is an essential part of the Licence and a
condition for the grant of any rights to the Work.
8. Disclaimer of Liability
Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the
use of the Work, including without limitation, damages for loss of goodwill,
work stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such
damage. However, the Licensor will be liable under statutory product liability
laws as far such laws apply to the Work.
9. Additional agreements
While distributing the Work, You may choose to conclude an additional
agreement, defining obligations or services consistent with this Licence.
However, if accepting obligations, You may act only on your own behalf and on
your sole responsibility, not on behalf of the original Licensor or any other
Contributor, and only if You agree to indemnify, defend, and hold each
Contributor harmless for any liability incurred by, or claims asserted against
such Contributor by the fact You have accepted any warranty or additional
liability.
10. Acceptance of the Licence
The provisions of this Licence can be accepted by clicking on an icon I
agree placed under the bottom of a window displaying the text of this Licence
or by affirming consent in any other similar way, in accordance with the rules
of applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.
Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this
Licence, such as the use of the Work, the creation by You of a Derivative Work
or the Distribution or Communication by You of the Work or copies thereof.
11. Information to the public
In case of any Distribution or Communication of the Work by means of
electronic communication by You (for example, by offering to download the Work
from a remote location) the distribution channel or media (for example, a
website) must at least provide to the public the information requested by the
applicable law regarding the Licensor, the Licence and the way it may be
accessible, concluded, stored and reproduced by the Licensee.
12. Termination of the Licence
The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.
Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.
13. Miscellaneous
Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.
If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.
The European Commission may publish other linguistic versions or new versions
of this Licence or updated versions of the Appendix, so far this is required
and reasonable, without reducing the scope of the rights granted by the
Licence. New versions of the Licence will be published with a unique version
number.
All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.
14. Jurisdiction
Without prejudice to specific agreement between parties,
- any litigation resulting from the interpretation of this License, arising
between the European Union institutions, bodies, offices or agencies, as a
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
of Justice of the European Union, as laid down in article 272 of the Treaty
on the Functioning of the European Union,
- any litigation arising between other parties and resulting from the
interpretation of this License, will be subject to the exclusive
jurisdiction of the competent court where the Licensor resides or conducts
its primary business.
15. Applicable Law
Without prejudice to specific agreement between parties,
- this Licence shall be governed by the law of the European Union Member State
where the Licensor has his seat, resides or has his registered office,
- this licence shall be governed by Belgian law if the Licensor has no seat,
residence or registered office inside a European Union Member State.
Appendix
Compatible Licences according to Article 5 EUPL are:
- GNU General Public License (GPL) v. 2, v. 3
- GNU Affero General Public License (AGPL) v. 3
- Open Software License (OSL) v. 2.1, v. 3.0
- Eclipse Public License (EPL) v. 1.0
- CeCILL v. 2.0, v. 2.1
- Mozilla Public Licence (MPL) v. 2
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
works other than software
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
Reciprocity (LiLiQ-R+).
The European Commission may update this Appendix to later versions of the
above licences without producing a new version of the EUPL, as long as they
provide the rights granted in Article 2 of this Licence and protect the
covered Source Code from exclusive appropriation.
All other changes or additions to this Appendix require the production of a
new EUPL version.

View File

@ -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"
}

View File

@ -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;
}

2058
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.16.0-alpha", "version": "0.15.0-b1",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {
@ -14,21 +14,19 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/wled-dev/WLED.git" "url": "git+https://github.com/Aircoookie/WLED.git"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"bugs": { "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": { "dependencies": {
"clean-css": "^5.3.3", "clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0", "html-minifier-terser": "^7.2.0",
"web-resource-inliner": "^7.0.0", "inliner": "^1.13.1",
"nodemon": "^3.1.9" "nodemon": "^3.0.2",
}, "zlib": "^1.0.5"
"engines": {
"node": ">=20.0.0"
} }
} }

View File

@ -1,21 +1,3 @@
Import("env") Import('env')
import shutil
node_ex = shutil.which("node") env.Execute("npm run build")
# 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)

View File

@ -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")

View File

@ -1,24 +1,9 @@
# Little convenience script to get an object dump # Little convenience script to get an object dump
# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ")
# to get source code intermixed with disassembly (SLOW !)
Import('env') Import('env')
def obj_dump_after_elf(source, target, env): def obj_dump_after_elf(source, target, env):
platform = env.PioPlatform()
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
print("Create firmware.asm") print("Create firmware.asm")
if mcu == "esp8266": env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm")
env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32":
env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32s2":
env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32s3":
env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32c3":
env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf]) env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])

View File

@ -4,7 +4,6 @@ import shutil
import gzip import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep) OUTPUT_DIR = "build_output{}".format(os.path.sep)
#OUTPUT_DIR = os.path.join("build_output")
def _get_cpp_define_value(env, define): def _get_cpp_define_value(env, define):
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define] define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
@ -14,53 +13,63 @@ def _get_cpp_define_value(env, define):
return None return None
def _create_dirs(dirs=["map", "release", "firmware"]): def _create_dirs(dirs=["firmware", "map"]):
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in dirs: for d in dirs:
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True) if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
def create_release(source): def create_release(source):
release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME") release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name_def: if release_name:
release_name = release_name_def.replace("\\\"", "") _create_dirs(["release"])
version = _get_cpp_define_value(env, "WLED_VERSION") version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin") # get file extension of source file (.bin or .bin.gz)
release_gz_file = release_file + ".gz" ext = source.split(".", 1)[1]
print(f"Copying {source} to {release_file}") release_file = "{}release{}WLED_{}_{}.{}".format(OUTPUT_DIR, os.path.sep, version, release_name, ext)
shutil.copy(source, release_file) shutil.copy(source, release_file)
bin_gzip(release_file, release_gz_file)
else:
variant = env["PIOENV"]
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
print(f"Copying {source} to {bin_file}")
shutil.copy(source, bin_file)
def bin_rename_copy(source, target, env): def bin_rename_copy(source, target, env):
_create_dirs() _create_dirs()
variant = env["PIOENV"] variant = env["PIOENV"]
builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant)
source_map = os.path.join(builddir, env["PROGNAME"] + ".map")
# create string with location and file names based on variant # create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant) map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
create_release(str(target[0])) # check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
create_release(bin_file)
# copy firmware.map to map/<variant>.map # copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"): if os.path.isfile("firmware.map"):
print("Found linker mapfile firmware.map") shutil.move("firmware.map", map_file)
shutil.copy("firmware.map", map_file)
if os.path.isfile(source_map):
print(f"Found linker mapfile {source_map}")
shutil.copy(source_map, map_file)
def bin_gzip(source, target): def bin_gzip(source, target, env):
# only create gzip for esp8266 _create_dirs()
if not env["PIOPLATFORM"] == "espressif8266": variant = env["PIOENV"]
return
# create string with location and file names based on variant
print(f"Creating gzip file {target} from {source}") bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
with open(source,"rb") as fp: gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
with gzip.open(target, "wb", compresslevel = 9) as f:
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f) shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_rename_copy) create_release(gzip_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])

View File

@ -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'))

View File

@ -10,7 +10,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# CI/release binaries # 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, esp32dev_V4, 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, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32_wrover
src_dir = ./wled00 src_dir = ./wled00
data_dir = ./wled00/data data_dir = ./wled00/data
@ -41,13 +41,14 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/
platform_wled_default = ${common.arduino_core_3_1_2} platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 platform_packages = platformio/framework-arduinoespressif8266
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0 platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0 platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one ## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x ## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_2_0, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_0_2} ;; platform_wled_default = ${common.arduino_core_3_2_0}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 ;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502 ;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0 ;; platformio/tool-esptool @ ~1.413.0
@ -86,6 +87,7 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). # This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
build_flags = build_flags =
-Wno-attributes
-DMQTT_MAX_PACKET_SIZE=1024 -DMQTT_MAX_PACKET_SIZE=1024
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
-DBEARSSL_SSL_BASIC -DBEARSSL_SSL_BASIC
@ -103,6 +105,10 @@ build_flags =
build_unflags = build_unflags =
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
build_flags_esp32_V4= ${common.build_flags} ${esp32_idf_V4.build_flags}
ldscript_1m128k = eagle.flash.1m128.ld ldscript_1m128k = eagle.flash.1m128.ld
ldscript_2m512k = eagle.flash.2m512.ld ldscript_2m512k = eagle.flash.2m512.ld
ldscript_2m1m = eagle.flash.2m1m.ld ldscript_2m1m = eagle.flash.2m1m.ld
@ -114,10 +120,7 @@ extra_scripts =
post:pio-scripts/output_bins.py post:pio-scripts/output_bins.py
post:pio-scripts/strip-floats.py post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py pre:pio-scripts/user_config_copy.py
pre:pio-scripts/load_usermods.py
pre:pio-scripts/build_ui.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)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# COMMON SETTINGS: # COMMON SETTINGS:
@ -140,9 +143,8 @@ lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.6.0 fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.8.3 makuna/NeoPixelBus @ 2.7.5
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.1.0
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
# for I2C interface # for I2C interface
;Wire ;Wire
# ESP-NOW library # ESP-NOW library
@ -159,18 +161,22 @@ lib_deps =
;adafruit/Adafruit BMP280 Library @ 2.1.0 ;adafruit/Adafruit BMP280 Library @ 2.1.0
;adafruit/Adafruit CCS811 Library @ 1.0.4 ;adafruit/Adafruit CCS811 Library @ 1.0.4
;adafruit/Adafruit Si7021 Library @ 1.4.0 ;adafruit/Adafruit Si7021 Library @ 1.4.0
#For MAX1704x Lipo Monitor / Fuel Gauge uncomment following #For ADS1115 sensor uncomment following
; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5 ;adafruit/Adafruit BusIO @ 1.13.2
; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2 ;adafruit/Adafruit ADS1X15 @ 2.4.0
#For MPU6050 IMU uncomment follwoing #For MPU6050 IMU uncomment follwoing
;electroniccats/MPU6050 @1.0.1 ;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 # SHT85
;robtillaart/SHT85@~0.3.3 ;robtillaart/SHT85@~0.3.3
# Audioreactive usermod
;kosme/arduinoFFT @ 2.0.0
extra_scripts = ${scripts_defaults.extra_scripts} extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266] [esp8266]
build_unflags = ${common.build_unflags}
build_flags = build_flags =
-DESP8266 -DESP8266
-DFP_IN_IROM -DFP_IN_IROM
@ -192,83 +198,32 @@ build_flags =
; decrease code cache size and increase IRAM to fit all pixel functions ; decrease code cache size and increase IRAM to fit all pixel functions
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
-D NON32XFER_HANDLER ;; ask forgiveness for PROGMEM misuse
lib_deps = lib_deps =
#https://github.com/lorol/LITTLEFS.git #https://github.com/lorol/LITTLEFS.git
ESPAsyncTCP @ 1.2.2 ESPAsyncTCP @ 1.2.2
ESPAsyncUDP ESPAsyncUDP
ESP8266PWM
${env.lib_deps} ${env.lib_deps}
;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
build_flags_compat =
-DESP8266
-DFP_IN_IROM
;;-Wno-deprecated-declarations
-Wno-misleading-indentation
;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
-DVTABLES_IN_FLASH
-DMIMETYPE_MINIMAL
-DWLED_SAVE_IRAM ;; needed to prevent linker error
;; this platform version was used for WLED 0.14.0
platform_compat = espressif8266@4.2.0
platform_packages_compat =
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
;; experimental - for using older NeoPixelBus 2.7.9
lib_deps_compat =
ESPAsyncTCP @ 1.2.2
ESPAsyncUDP
ESP8266PWM
fastled/FastLED @ 3.6.0
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
[esp32_all_variants]
lib_deps =
willmmiles/AsyncTCP @ 1.3.1
bitbank2/AnimatedGIF@^1.4.7
https://github.com/Aircoookie/GifDecoder#bc3af18
build_flags =
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D WLED_ENABLE_GIF
[esp32] [esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip #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 = espressif32@3.5.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4 platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
build_unflags = ${common.build_unflags}
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2 #-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 #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS -D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
${esp32_all_variants.build_flags}
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.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
lib_deps = lib_deps =
https://github.com/lorol/LITTLEFS.git https://github.com/lorol/LITTLEFS.git
${esp32_all_variants.lib_deps} https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs # additional build flags for audioreactive
# additional build flags for audioreactive - must be applied globally AR_build_flags = -D USERMOD_AUDIOREACTIVE
AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) AR_lib_deps = kosme/arduinoFFT @ 2.0.0
AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
[esp32_idf_V4] [esp32_idf_V4]
;; experimental 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
@ -276,78 +231,73 @@ AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
;; ;;
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; 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. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@5.3.0
;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) platform_packages =
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}
build_flags = -g build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32 -DARDUINO_ARCH_ESP32 -DESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
-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 -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
${esp32_all_variants.build_flags} default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
-D WLED_ENABLE_DMX_INPUT
lib_deps = lib_deps =
${esp32_all_variants.lib_deps} https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
https://github.com/someweisguy/esp_dmx.git#47db25d
${env.lib_deps} ${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32s2] [esp32s2]
;; generic definitions for all ESP32-S2 boards ;; generic definitions for all ESP32-S2 boards
platform = ${esp32_idf_V4.platform} platform = espressif32@5.3.0
build_unflags = ${common.build_unflags} platform_packages =
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2 -DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2=1 -DCONFIG_IDF_TARGET_ESP32S2=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DCO -DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! -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: ;; 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 ;; ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
lib_deps = lib_deps =
${esp32_all_variants.lib_deps} https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32c3] [esp32c3]
;; generic definitions for all ESP32-C3 boards ;; generic definitions for all ESP32-C3 boards
platform = ${esp32_idf_V4.platform} platform = espressif32@5.3.0
build_unflags = ${common.build_unflags} platform_packages =
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3 -DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3=1 -DCONFIG_IDF_TARGET_ESP32C3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO -DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 -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: ;; 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 ;; ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
lib_deps = lib_deps =
${esp32_all_variants.lib_deps} https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
board_build.flash_mode = qio
[esp32s3] [esp32s3]
;; generic definitions for all ESP32-S3 boards ;; generic definitions for all ESP32-S3 boards
platform = ${esp32_idf_V4.platform} platform = espressif32@5.3.0
build_unflags = ${common.build_unflags} platform_packages =
build_flags = -g build_flags = -g
-DESP32 -DESP32
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S3 -DARDUINO_ARCH_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3=1 -DCONFIG_IDF_TARGET_ESP32S3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
-DCO -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: ;; 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 ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
${esp32_all_variants.build_flags}
lib_deps = lib_deps =
${esp32_all_variants.lib_deps} https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -360,26 +310,14 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
-D WLED_DISABLE_PARTICLESYSTEM2D
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder monitor_filters = esp8266_exception_decoder
[env:nodemcuv2_compat]
extends = env:nodemcuv2
;; using platform version and build options from WLED 0.14.0
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] [env:nodemcuv2_160]
extends = env:nodemcuv2 extends = env:nodemcuv2
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
-D WLED_DISABLE_PARTICLESYSTEM2D
custom_usermods = audioreactive
[env:esp8266_2m] [env:esp8266_2m]
board = esp_wroom_02 board = esp_wroom_02
@ -387,27 +325,13 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\" build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
-D WLED_DISABLE_PARTICLESYSTEM2D
-D WLED_DISABLE_PARTICLESYSTEM1D
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m_compat]
extends = env:esp8266_2m
;; using platform version and build options from WLED 0.14.0
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] [env:esp8266_2m_160]
extends = env:esp8266_2m extends = env:esp8266_2m
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\" build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160
-D WLED_DISABLE_PARTICLESYSTEM1D
-D WLED_DISABLE_PARTICLESYSTEM2D
custom_usermods = audioreactive
[env:esp01_1m_full] [env:esp01_1m_full]
board = esp01_1m board = esp01_1m
@ -415,126 +339,72 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA build_flags = ${common.build_flags_esp8266} -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_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
-D WLED_DISABLE_PARTICLESYSTEM1D
-D WLED_DISABLE_PARTICLESYSTEM2D
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full_compat]
extends = env:esp01_1m_full
;; using platform version and build options from WLED 0.14.0
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] [env:esp01_1m_full_160]
extends = env:esp01_1m_full extends = env:esp01_1m_full
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -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_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
-D WLED_DISABLE_PARTICLESYSTEM1D
-D WLED_DISABLE_PARTICLESYSTEM2D
custom_usermods = audioreactive
[env:esp32dev] [env:esp32dev]
board = esp32dev board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
[env:esp32dev_V4] [env:esp32dev_audioreactive]
board = esp32dev board = esp32dev
platform = ${esp32_idf_V4.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
custom_usermods = audioreactive build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET ${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps} lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = dio
[env:esp32dev_8M]
board = esp32dev
platform = ${esp32_idf_V4.platform}
custom_usermods = audioreactive
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
lib_deps = ${esp32_idf_V4.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.f_flash = 80000000L
; board_build.flash_mode = qio ; board_build.flash_mode = dio
[env:esp32dev_16M]
board = esp32dev
platform = ${esp32_idf_V4.platform}
custom_usermods = audioreactive
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
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
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}
;custom_usermods = audioreactive
;build_unflags = ${common.build_unflags}
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-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 = dio
[env:esp32_eth] [env:esp32_eth]
board = esp32-poe board = esp32-poe
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
upload_speed = 921600 upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags} 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 build_flags = ${common.build_flags_esp32} -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 -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
[env:esp32_wrover] [env:esp32_wrover]
extends = esp32_idf_V4 platform = ${esp32.platform}
platform = ${esp32_idf_V4.platform}
board = ttgo-t7-v14-mini32 board = ttgo-t7-v14-mini32
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
board_build.partitions = ${esp32.extended_partitions} board_build.partitions = ${esp32.default_partitions}
custom_usermods = audioreactive
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\" build_flags = ${common.build_flags_esp32} -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 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
-D DATA_PINS=25 -D WLED_USE_PSRAM
lib_deps = ${esp32_idf_V4.lib_deps} -D LEDPIN=25
lib_deps = ${esp32.lib_deps}
[env:esp32c3dev] [env:esp32c3dev]
extends = esp32c3 extends = esp32c3
platform = ${esp32c3.platform} platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
framework = arduino framework = arduino
board = esp32-c3-devkitm-1 board = esp32-c3-devkitm-1
board_build.partitions = ${esp32.default_partitions} board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\" build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this -DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
@ -543,105 +413,63 @@ upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps} lib_deps = ${esp32c3.lib_deps}
[env:esp32s3dev_16MB_opi] [env:esp32s3dev_8MB]
;; ESP32-S3 development board, with 16MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) ;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio)
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support board = esp32-s3-devkitc-1
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
platform = ${esp32s3.platform} platform = ${esp32s3.platform}
upload_speed = 921600 platform_packages = ${esp32s3.platform_packages}
custom_usermods = audioreactive upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\" build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -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=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") ;-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 ;-D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.extreme_partitions} board_build.partitions = tools/WLED_ESP32_8MB.csv
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
; board_build.flash_mode = dio ;; try this if you have problems at startup
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_opi] [env:esp32s3dev_8MB_PSRAM_opi]
;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) ;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support 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 board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
platform = ${esp32s3.platform} platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 upload_speed = 921600
custom_usermods = audioreactive
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\" build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_PSRAM_opi
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -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=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") -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 ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.large_partitions} board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:esp32S3_wroom2]
;; 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}
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
-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
-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
-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}
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
monitor_filters = esp32_exception_decoder
[env:esp32s3_4M_qspi]
;; 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}
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
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:lolin_s2_mini] [env:lolin_s2_mini]
platform = ${esp32s2.platform} platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini board = lolin_s2_mini
board_build.partitions = ${esp32.default_partitions} board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio ;board_build.flash_mode = qio
board_build.f_flash = 80000000L ;board_build.f_flash = 80000000L
custom_usermods = audioreactive
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\" build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this -DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_USE_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-D DATA_PINS=16 -D CONFIG_ASYNC_TCP_USE_WDT=0
-D LEDPIN=16
-D HW_PIN_SCL=35 -D HW_PIN_SCL=35
-D HW_PIN_SDA=33 -D HW_PIN_SDA=33
-D HW_PIN_CLOCKSPI=7 -D HW_PIN_CLOCKSPI=7
@ -649,16 +477,3 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
-D HW_PIN_MISOSPI=9 -D HW_PIN_MISOSPI=9
; -D STATUSLED=15 ; -D STATUSLED=15
lib_deps = ${esp32s2.lib_deps} 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

View File

@ -5,12 +5,12 @@
# Please visit documentation: https://docs.platformio.org/page/projectconf.html # Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio] [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 # SAMPLE
#---------- #----------
[env:WLED_generic8266_1M] [env:WLED_tasmota_1M]
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options) extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
; board = esp01_1m # uncomment when ou need different board ; board = esp01_1m # uncomment when ou need different board
; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform ; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform
@ -26,19 +26,14 @@ lib_deps = ${esp8266.lib_deps}
; adafruit/Adafruit BME280 Library@^2.2.2 ; adafruit/Adafruit BME280 Library@^2.2.2
; Wire ; Wire
; robtillaart/SHT85@~0.3.3 ; robtillaart/SHT85@~0.3.3
; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug ; gmag11/QuickESPNow ;@ 0.6.2
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library ; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following ; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash
; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE ; build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
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\"
;
; disable specific features ; disable specific features
; -D WLED_DISABLE_OTA ; -D WLED_DISABLE_OTA
; -D WLED_DISABLE_ALEXA ; -D WLED_DISABLE_ALEXA
@ -53,26 +48,18 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D WLED_DISABLE_ESPNOW ; -D WLED_DISABLE_ESPNOW
; -D WLED_DISABLE_BROWNOUT_DET ; -D WLED_DISABLE_BROWNOUT_DET
; ;
; enable optional built-in features
; -D WLED_ENABLE_PIXART
; -D WLED_ENABLE_USERMOD_PAGE # if created
; -D WLED_ENABLE_DMX
;
; PIN defines - uncomment and change, if needed: ; PIN defines - uncomment and change, if needed:
; -D DATA_PINS=2 ; -D LEDPIN=2
; or use this for multiple outputs ; or use this for multiple outputs
; -D DATA_PINS=1,3 ; -D DATA_PINS=1,3
; -D BTNPIN=0 ; -D BTNPIN=0
; -D IRPIN=4 ; -D IRPIN=4
; -D RLYPIN=12 ; -D RLYPIN=12
; -D RLYMDE=1 ; -D RLYMDE=1
; -D RLYODRAIN=0
; -D LED_BUILTIN=2 # GPIO of built-in LED ; -D LED_BUILTIN=2 # GPIO of built-in LED
; ;
; Limit max buses ; Limit max buses
; -D WLED_MAX_BUSSES=2 ; -D WLED_MAX_BUSSES=2
; -D WLED_MAX_ANALOG_CHANNELS=3 # only 3 PWM HW pins available
; -D WLED_MAX_DIGITAL_CHANNELS=2 # only 2 HW accelerated pins available
; ;
; Configure default WiFi ; Configure default WiFi
; -D CLIENT_SSID='"MyNetwork"' ; -D CLIENT_SSID='"MyNetwork"'
@ -103,14 +90,9 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D USERMOD_AUTO_SAVE ; -D USERMOD_AUTO_SAVE
; -D AUTOSAVE_AFTER_SEC=90 ; -D AUTOSAVE_AFTER_SEC=90
; ;
; Use AHT10/AHT15/AHT20 usermod
; -D USERMOD_AHT10
;
; Use INA226 usermod
; -D USERMOD_INA226
;
; Use 4 Line Display usermod with SPI display ; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY ; -D USERMOD_FOUR_LINE_DISPLAY
; -D USE_ALT_DISPlAY # mandatory
; -DFLD_SPI_DEFAULT ; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64 ; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14 ; -D FLD_PIN_CLOCKSPI=14
@ -136,13 +118,12 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; ;
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s ; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
; -D USERMOD_PIRSWITCH ; -D USERMOD_PIRSWITCH
; -D PIR_SENSOR_PIN=4 # use -1 to disable usermod ; -D PIR_SENSOR_PIN=4
; -D PIR_SENSOR_OFF_SEC=60 ; -D PIR_SENSOR_OFF_SEC=60
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
; ;
; Use Audioreactive usermod and configure I2S microphone ; Use Audioreactive usermod and configure I2S microphone
; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT ; -D USERMOD_AUDIOREACTIVE
; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps} ; -D UM_AUDIOREACTIVE_USE_NEW_FFT
; -D AUDIOPIN=-1 ; -D AUDIOPIN=-1
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM ; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
; -D I2S_SDPIN=36 ; -D I2S_SDPIN=36
@ -154,36 +135,29 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D TACHO_PIN=33 ; -D TACHO_PIN=33
; -D PWM_PIN=32 ; -D PWM_PIN=32
; ;
; Use POV Display usermod
; -D USERMOD_POV_DISPLAY
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16) ; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16 ; -D STATUSLED=16
; ;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name ; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\"" ; -D SERVERNAME="\"WLED\""
; ;
; set the number of LEDs ; set the number of LEDs
; -D PIXEL_COUNTS=30 ; -D DEFAULT_LED_COUNT=30
; or this for multiple outputs ; or this for multiple outputs
; -D PIXEL_COUNTS=30,30 ; -D PIXEL_COUNTS=30,30
;
; set milliampere limit when using ESP pin to power leds
; -D ABL_MILLIAMPS_DEFAULT=850
; ;
; set the default LED type ; enable IR by setting remote type
; -D LED_TYPES=22 # see const.h (TYPE_xxxx) ; -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
; or this for multiple outputs ;
; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
;
; set default color order of your led strip ; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB ; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
; ;
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs ; use PSRAM if a device (ESP) has one
; -D ABL_MILLIAMPS_DEFAULT=850 ; -DBOARD_HAS_PSRAM
; -D LED_MILLIAMPS_DEFAULT=55 ; -D WLED_USE_PSRAM
;
; 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
;
; 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
; ;
; configure I2C and SPI interface (for various hardware) ; configure I2C and SPI interface (for various hardware)
; -D I2CSDAPIN=33 # initialise interface ; -D I2CSDAPIN=33 # initialise interface
@ -206,7 +180,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:d1_mini] [env:d1_mini]
@ -216,7 +190,7 @@ platform_packages = ${common.platform_packages}
upload_speed = 921600 upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder monitor_filters = esp8266_exception_decoder
@ -226,7 +200,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:h803wf] [env:h803wf]
@ -235,17 +209,18 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp32dev_qio80] [env:esp32dev_qio80]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
board = esp32dev board = esp32dev
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET platform = ${esp32.platform}
${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
@ -253,26 +228,27 @@ board_build.flash_mode = qio
;; experimental ESP32 env using ESP-IDF V4.4.x ;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!! ;; Warning: this build environment is not stable!!
;; please erase your device before installing. ;; please erase your device before installing.
extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment
board = esp32dev board = esp32dev
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET platform = ${esp32_idf_V4.platform}
${esp32.AR_build_flags} ;; includes USERMOD_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_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32_idf_V4.lib_deps} lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder 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.f_flash = 80000000L
board_build.flash_mode = dio board_build.flash_mode = dio
[env:esp32s2_saola] [env:esp32s2_saola]
extends = esp32s2
board = esp32-s2-saola-1 board = esp32-s2-saola-1
platform = ${esp32s2.platform} platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
platform_packages = ${esp32s2.platform_packages} platform_packages =
framework = arduino framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio board_build.flash_mode = qio
upload_speed = 460800 upload_speed = 460800
build_flags = ${common.build_flags} ${esp32s2.build_flags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
@ -280,7 +256,7 @@ lib_deps = ${esp32s2.lib_deps}
[env:esp32s3dev_8MB_PSRAM_qspi] [env:esp32s3dev_8MB_PSRAM_qspi]
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi) ;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
extends = env:esp32s3dev_8MB_PSRAM_opi 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 = 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 board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
@ -290,7 +266,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp8285_H801] [env:esp8285_H801]
@ -299,7 +275,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:d1_mini_5CH_Shojo_PCB] [env:d1_mini_5CH_Shojo_PCB]
@ -308,7 +284,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} 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} -D WLED_USE_SHOJO_PCB
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:d1_mini_debug] [env:d1_mini_debug]
@ -318,7 +294,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags} build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:d1_mini_ota] [env:d1_mini_ota]
@ -330,7 +306,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:anavi_miracle_controller] [env:anavi_miracle_controller]
@ -339,7 +315,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2 build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp32c3dev_2MB] [env:esp32c3dev_2MB]
@ -349,7 +325,7 @@ extends = esp32c3
platform = ${esp32c3.platform} platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages} platform_packages = ${esp32c3.platform_packages}
board = esp32-c3-devkitm-1 board = esp32-c3-devkitm-1
build_flags = ${common.build_flags} ${esp32c3.build_flags} build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-D WLED_DISABLE_OTA -D WLED_DISABLE_OTA
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
@ -359,65 +335,51 @@ upload_speed = 115200
lib_deps = ${esp32c3.lib_deps} lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio board_build.flash_mode = dio
board_upload.flash_size = 2MB
board_upload.maximum_size = 2097152
[env:wemos_shield_esp32] [env:wemos_shield_esp32]
extends = esp32 ;; use default esp32 platform
board = esp32dev board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800 upload_speed = 460800
build_flags = ${common.build_flags} ${esp32.build_flags} build_unflags = ${common.build_unflags}
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\" build_flags = ${common.build_flags_esp32}
-D DATA_PINS=16 -D LEDPIN=16
-D RLYPIN=19 -D RLYPIN=19
-D BTNPIN=17 -D BTNPIN=17
-D IRPIN=18 -D IRPIN=18
-UWLED_USE_MY_CONFIG -D UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE -D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY -D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23 -D TEMPERATURE_PIN=23
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE -D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
-D USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY olikraus/U8g2 @ ^2.28.8
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE https://github.com/blazoncek/arduinoFFT.git
board_build.partitions = ${esp32.default_partitions} 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
${esp32.AR_build_flags} ;; include USERMOD_AUDIOREACTIVE
-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}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
[env:m5atom] [env:m5atom]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options) board = esp32dev
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39 build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=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] [env:sp501e]
board = esp_wroom_02 board = esp_wroom_02
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1 build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:sp511e] [env:sp511e]
board = esp_wroom_02 board = esp_wroom_02
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs [env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
@ -426,8 +388,8 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} 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 build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -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} lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb [env:Athom_15w_RGBCW] ;15w bulb
@ -436,8 +398,8 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} 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 build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -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} lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data [env:Athom_3Pin_Controller] ;small controller with only data
@ -446,7 +408,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:Athom_4Pin_Controller] ; With clock and data interface [env:Athom_4Pin_Controller] ; With clock and data interface
@ -455,7 +417,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:Athom_5Pin_Controller] ;Analog light strip controller [env:Athom_5Pin_Controller] ;Analog light strip controller
@ -464,7 +426,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:MY9291] [env:MY9291]
@ -473,7 +435,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291 build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D USERMOD_MY9291
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -487,7 +449,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0_6-rev2] [env:codm-controller-0_6-rev2]
@ -496,22 +458,24 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# EleksTube-IPS # EleksTube-IPS
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[env:elekstube_ips] [env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600 upload_speed = 921600
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED -D USERMOD_RTC
-D DATA_PINS=12 -D USERMOD_ELEKSTUBE_IPS
-D LEDPIN=12
-D RLYPIN=27 -D RLYPIN=27
-D BTNPIN=34 -D BTNPIN=34
-D PIXEL_COUNTS=6 -D DEFAULT_LED_COUNT=6
# Display config # Display config
-D ST7789_DRIVER -D ST7789_DRIVER
-D TFT_WIDTH=135 -D TFT_WIDTH=135
@ -525,14 +489,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
-D SPI_FREQUENCY=40000000 -D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED -D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
lib_deps =
# ------------------------------------------------------------------------------ ${esp32.lib_deps}
# Usermod examples TFT_eSPI @ ^2.3.70
# ------------------------------------------------------------------------------ board_build.partitions = ${esp32.default_partitions}
# 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

View File

@ -1,18 +1,18 @@
<p align="center"> <p align="center">
<img src="/images/wled_logo_akemi.png"> <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://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/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://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://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://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://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://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> </p>
# Welcome to my project 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! 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 ## ⚙️ Features
- WS2812FX library with more than 100 special effects - WS2812FX library with more than 100 special effects
@ -21,7 +21,7 @@ A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to cont
- Segments to set different effects and colors to user defined parts of the LED string - Segments to set different effects and colors to user defined parts of the LED string
- Settings page - configuration via the network - Settings page - configuration via the network
- Access Point and station mode - automatic failsafe AP - 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 - Support for RGBW strips
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them. - 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 - Presets can be used to automatically execute API calls
@ -61,7 +61,7 @@ See [here](https://kno.wled.ge/basics/compatible-hardware)!
## ✌️ Other ## ✌️ Other
Licensed under the EUPL v1.2 license Licensed under the MIT license
Credits [here](https://kno.wled.ge/about/contributors/)! Credits [here](https://kno.wled.ge/about/contributors/)!
Join the Discord server to discuss everything about WLED! Join the Discord server to discuss everything about WLED!
@ -80,5 +80,5 @@ If WLED really brightens up your day, you can [![](https://img.shields.io/badge/
If you are prone to photosensitive epilepsy, we recommended you do **not** use this software. If you are prone to photosensitive epilepsy, we recommended you do **not** use this software.
If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings. If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
As per the EUPL license, I assume no liability for any damage to you or any other person or equipment. As per the MIT license, I assume no liability for any damage to you or any other person or equipment.

View File

@ -1 +1 @@
platformio>=6.1.17 platformio

View File

@ -1,58 +1,60 @@
# #
# 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: # by the following command:
# #
# pip-compile requirements.in # pip-compile
# #
aiofiles==22.1.0
# via platformio
ajsonrpc==1.2.0 ajsonrpc==1.2.0
# via platformio # via platformio
anyio==4.8.0 anyio==3.6.2
# via starlette # via starlette
bottle==0.13.2 bottle==0.12.25
# via platformio # via platformio
certifi==2025.1.31 certifi==2023.7.22
# via requests # via requests
charset-normalizer==3.4.1 charset-normalizer==3.1.0
# via requests # via requests
click==8.1.8 click==8.1.3
# via # via
# platformio # platformio
# uvicorn # uvicorn
colorama==0.4.6 colorama==0.4.6
# via platformio # via
h11==0.16.0 # click
# platformio
h11==0.14.0
# via # via
# uvicorn # uvicorn
# wsproto # wsproto
idna==3.10 idna==3.4
# via # via
# anyio # anyio
# requests # requests
marshmallow==3.26.1 marshmallow==3.19.0
# via platformio # via platformio
packaging==24.2 packaging==23.1
# via marshmallow # via marshmallow
platformio==6.1.17 platformio==6.1.6
# via -r requirements.in # via -r requirements.in
pyelftools==0.32 pyelftools==0.29
# via platformio # via platformio
pyserial==3.5 pyserial==3.5
# via platformio # via platformio
requests==2.32.4 requests==2.31.0
# via platformio # via platformio
semantic-version==2.10.0 semantic-version==2.10.0
# via platformio # via platformio
sniffio==1.3.1 sniffio==1.3.0
# via anyio # via anyio
starlette==0.45.3 starlette==0.23.1
# via platformio # via platformio
tabulate==0.9.0 tabulate==0.9.0
# via platformio # via platformio
typing-extensions==4.12.2 urllib3==1.26.18
# via anyio
urllib3==2.5.0
# via requests # via requests
uvicorn==0.34.0 uvicorn==0.20.0
# via platformio # via platformio
wsproto==1.2.0 wsproto==1.2.0
# via platformio # via platformio

Binary file not shown.

View File

@ -1,6 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000, nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000, otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1A0000, app0, app, ota_0, 0x10000, 0x180000,
app1, app, ota_1, 0x1B0000,0x1A0000, app1, app, ota_1, 0x190000,0x180000,
spiffs, data, spiffs, 0x350000,0xB0000, spiffs, data, spiffs, 0x310000,0xF0000,

1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x1A0000 0x180000
5 app1 app ota_1 0x1B0000 0x190000 0x1A0000 0x180000
6 spiffs data spiffs 0x350000 0x310000 0xB0000 0xF0000

View File

@ -1,7 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1D0000,
app1, app, ota_1, 0x1E0000,0x1D0000,
spiffs, data, spiffs, 0x3B0000,0x40000,
coredump, data, coredump,,64K
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000,
3 otadata, data, ota, 0xe000, 0x2000,
4 app0, app, ota_0, 0x10000, 0x1D0000,
5 app1, app, ota_1, 0x1E0000,0x1D0000,
6 spiffs, data, spiffs, 0x3B0000,0x40000,
7 coredump, data, coredump,,64K

View File

@ -1,7 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1B0000,
app1, app, ota_1, 0x1C0000,0x1B0000,
spiffs, data, spiffs, 0x370000,0x80000,
coredump, data, coredump,,64K
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000,
3 otadata, data, ota, 0xe000, 0x2000,
4 app0, app, ota_0, 0x10000, 0x1B0000,
5 app1, app, ota_1, 0x1C0000,0x1B0000,
6 spiffs, data, spiffs, 0x370000,0x80000,
7 coredump, data, coredump,,64K

View File

@ -1,6 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1A0000,
app1, app, ota_1, 0x1B0000,0x1A0000,
spiffs, data, spiffs, 0x350000,0xB0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x1A0000
5 app1 app ota_1 0x1B0000 0x1A0000
6 spiffs data spiffs 0x350000 0xB0000

View File

@ -1,17 +0,0 @@
#!/bin/bash
# Pull all settings pages for comparison
HOST=$1
TGT_PATH=$2
CURL_ARGS="--compressed"
# Replicate one target many times
function replicate() {
for i in {0..10}
do
echo -n " http://${HOST}/settings.js?p=$i -o ${TGT_PATH}/$i.xml"
done
}
read -a TARGETS <<< $(replicate)
mkdir -p ${TGT_PATH}
curl ${CURL_ARGS} ${TARGETS[@]}

View File

@ -83,7 +83,6 @@ describe('Script', () => {
// Backup files // Backup files
fs.cpSync("wled00/data", "wled00Backup", { recursive: true }); fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
fs.cpSync("tools/cdata.js", "cdata.bak.js"); fs.cpSync("tools/cdata.js", "cdata.bak.js");
fs.cpSync("package.json", "package.bak.json");
}); });
after(() => { after(() => {
// Restore backup // Restore backup
@ -91,8 +90,6 @@ describe('Script', () => {
fs.renameSync("wled00Backup", "wled00/data"); fs.renameSync("wled00Backup", "wled00/data");
fs.rmSync("tools/cdata.js"); fs.rmSync("tools/cdata.js");
fs.renameSync("cdata.bak.js", "tools/cdata.js"); fs.renameSync("cdata.bak.js", "tools/cdata.js");
fs.rmSync("package.json");
fs.renameSync("package.bak.json", "package.json");
}); });
// delete all html_*.h files // delete all html_*.h files
@ -134,7 +131,7 @@ describe('Script', () => {
// run script cdata.js again and wait for it to finish // run script cdata.js again and wait for it to finish
await execPromise('node tools/cdata.js'); await execPromise('node tools/cdata.js');
await checkIfFileWasNewlyCreated(path.join(folderPath, resultFile)); checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
} }
describe('should build if', () => { describe('should build if', () => {
@ -185,10 +182,6 @@ describe('Script', () => {
it('cdata.js changes', async () => { it('cdata.js changes', async () => {
await testFileModification('tools/cdata.js', 'html_ui.h'); await testFileModification('tools/cdata.js', 'html_ui.h');
}); });
it('package.json changes', async () => {
await testFileModification('package.json', 'html_ui.h');
});
}); });
describe('should not build if', () => { describe('should not build if', () => {

View File

@ -2,7 +2,7 @@
* Writes compressed C arrays of data files (web interface) * Writes compressed C arrays of data files (web interface)
* How to use it? * How to use it?
* *
* 1) Install Node 20+ and npm * 1) Install Node 11+ and npm
* 2) npm install * 2) npm install
* 3) npm run build * 3) npm run build
* *
@ -15,10 +15,10 @@
* It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page. * It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page.
*/ */
const fs = require("node:fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const inline = require("web-resource-inliner"); const inliner = require("inliner");
const zlib = require("node:zlib"); const zlib = require("zlib");
const CleanCSS = require("clean-css"); const CleanCSS = require("clean-css");
const minifyHtml = require("html-minifier-terser").minify; const minifyHtml = require("html-minifier-terser").minify;
const packageJson = require("../package.json"); const packageJson = require("../package.json");
@ -89,7 +89,7 @@ function adoptVersionAndRepo(html) {
repoUrl = repoUrl.replace(/^git\+/, ""); repoUrl = repoUrl.replace(/^git\+/, "");
repoUrl = repoUrl.replace(/\.git$/, ""); repoUrl = repoUrl.replace(/\.git$/, "");
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl); 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; let version = packageJson.version;
if (version) { if (version) {
@ -101,7 +101,6 @@ function adoptVersionAndRepo(html) {
async function minify(str, type = "plain") { async function minify(str, type = "plain") {
const options = { const options = {
collapseWhitespace: true, collapseWhitespace: true,
conservativeCollapse: true, // preserve spaces in text
collapseBooleanAttributes: true, collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true, collapseInlineTagWhitespace: true,
minifyCSS: true, minifyCSS: true,
@ -117,8 +116,7 @@ async function minify(str, type = "plain") {
} else if (type == "css-minify") { } else if (type == "css-minify") {
return new CleanCSS({}).minify(str).styles; return new CleanCSS({}).minify(str).styles;
} else if (type == "js-minify") { } else if (type == "js-minify") {
let js = await minifyHtml('<script>' + str + '</script>', options); return await minifyHtml('<script>' + str + '</script>', options).replace(/<[\/]*script>/g, '');
return js.replace(/<[\/]*script>/g, '');
} else if (type == "html-minify") { } else if (type == "html-minify") {
return await minifyHtml(str, options); return await minifyHtml(str, options);
} }
@ -128,26 +126,21 @@ async function minify(str, type = "plain") {
async function writeHtmlGzipped(sourceFile, resultFile, page) { async function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Reading " + sourceFile); console.info("Reading " + sourceFile);
inline.html({ new inliner(sourceFile, async function (error, html) {
fileContent: fs.readFileSync(sourceFile, "utf8"), if (error) throw error;
relativeTo: path.dirname(sourceFile),
strict: true,
},
async function (error, html) {
if (error) throw error;
html = adoptVersionAndRepo(html); html = adoptVersionAndRepo(html);
const originalLength = html.length; const originalLength = html.length;
html = await minify(html, "html-minify"); html = await minify(html, "html-minify");
const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION }); const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION });
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes"); console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
const array = hexdump(result); const array = hexdump(result);
let src = singleHeader; let src = singleHeader;
src += `const uint16_t PAGE_${page}_L = ${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`; src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
console.info("Writing " + resultFile); console.info("Writing " + resultFile);
fs.writeFileSync(resultFile, src); fs.writeFileSync(resultFile, src);
}); });
} }
async function specToChunk(srcDir, s) { async function specToChunk(srcDir, s) {
@ -214,7 +207,7 @@ function isAnyFileInFolderNewerThan(folderPath, time) {
} }
// Check if the web UI is already built // Check if the web UI is already built
function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") { function isAlreadyBuilt(folderPath) {
let lastBuildTime = Infinity; let lastBuildTime = Infinity;
for (const file of output) { for (const file of output) {
@ -227,7 +220,7 @@ function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") {
} }
} }
return !isAnyFileInFolderNewerThan(webUIPath, lastBuildTime) && !isFileNewerThan(packageJsonPath, lastBuildTime) && !isFileNewerThan(__filename, lastBuildTime); return !isAnyFileInFolderNewerThan(folderPath, lastBuildTime) && !isFileNewerThan("tools/cdata.js", lastBuildTime);
} }
// Don't run this script if we're in a test environment // Don't run this script if we're in a test environment
@ -259,12 +252,6 @@ writeChunks(
str str
.replace("%%", "%") .replace("%%", "%")
}, },
{
file: "common.js",
name: "JS_common",
method: "gzip",
filter: "js-minify",
},
{ {
file: "settings.htm", file: "settings.htm",
name: "PAGE_settings", name: "PAGE_settings",

View File

@ -1,38 +0,0 @@
#!/bin/bash
# Some web server stress tests
#
# Perform a large number of parallel requests, stress testing the web server
# TODO: some kind of performance metrics
# Accepts three command line arguments:
# - first argument - mandatory - IP or hostname of target server
# - second argument - target type (optional)
# - third argument - xfer count (for replicated targets) (optional)
HOST=$1
declare -n TARGET_STR="${2:-JSON_LARGER}_TARGETS"
REPLICATE_COUNT=$(("${3:-10}"))
PARALLEL_MAX=${PARALLEL_MAX:-50}
CURL_ARGS="--compressed --parallel --parallel-immediate --parallel-max ${PARALLEL_MAX}"
CURL_PRINT_RESPONSE_ARGS="-w %{http_code}\n"
JSON_TARGETS=('json/state' 'json/info' 'json/si', 'json/palettes' 'json/fxdata' 'settings/s.js?p=2')
FILE_TARGETS=('' 'iro.js' 'rangetouch.js' 'settings' 'settings/wifi')
# Replicate one target many times
function replicate() {
printf "${1}?%d " $(seq 1 ${REPLICATE_COUNT})
}
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[@]})
#echo "${TARGETS[@]}"
FULL_TGT_OPTIONS=$(printf "http://${HOST}/%s -o /dev/null " "${TARGETS[@]}")
#echo ${FULL_TGT_OPTIONS}
time curl ${CURL_ARGS} ${FULL_TGT_OPTIONS}

View File

@ -1,46 +0,0 @@
import numpy as np
import socket
class WledRealtimeClient:
def __init__(self, wled_controller_ip, num_pixels, udp_port=21324, max_pixels_per_packet=126):
self.wled_controller_ip = wled_controller_ip
self.num_pixels = num_pixels
self.udp_port = udp_port
self.max_pixels_per_packet = max_pixels_per_packet
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._prev_pixels = np.full((3, self.num_pixels), 253, dtype=np.uint8)
self.pixels = np.full((3, self.num_pixels), 1, dtype=np.uint8)
def update(self):
# Truncate values and cast to integer
self.pixels = np.clip(self.pixels, 0, 255).astype(np.uint8)
p = np.copy(self.pixels)
idx = np.where(~np.all(p == self._prev_pixels, axis=0))[0]
num_pixels = len(idx)
n_packets = (num_pixels + self.max_pixels_per_packet - 1) // self.max_pixels_per_packet
idx_split = np.array_split(idx, n_packets)
header = bytes([1, 2]) # WARLS protocol header
for packet_indices in idx_split:
data = bytearray(header)
for i in packet_indices:
data.extend([i, *p[:, i]]) # Index and RGB values
self._sock.sendto(bytes(data), (self.wled_controller_ip, self.udp_port))
self._prev_pixels = np.copy(p)
################################## LED blink test ##################################
if __name__ == "__main__":
WLED_CONTROLLER_IP = "192.168.1.153"
NUM_PIXELS = 255 # Amount of LEDs on your strip
import time
wled = WledRealtimeClient(WLED_CONTROLLER_IP, NUM_PIXELS)
print('Starting LED blink test')
while True:
for i in range(NUM_PIXELS):
wled.pixels[1, i] = 255 if wled.pixels[1, i] == 0 else 0
wled.update()
time.sleep(.01)

View File

@ -1,286 +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
}
# Generic curl handler function
curl_handler() {
local command="$1"
local hostname="$2"
response=$($command -w "%{http_code}" -o /dev/null)
curl_exit_code=$?
if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then
return 0
elif [ $curl_exit_code -ne 0 ]; then
log "ERROR" "$RED" "Connection error during request to $hostname (curl exit code: $curl_exit_code)."
return 1
elif [ "$response" -ge 400 ]; then
log "ERROR" "$RED" "Server error during request to $hostname (HTTP status code: $response)."
return 2
else
log "ERROR" "$RED" "Unexpected response from $hostname (HTTP status code: $response)."
return 3
fi
}
# 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: $hostname ($address:$port)"
mkdir -p "$backup_dir"
local cfg_url="http://$address:$port/cfg.json"
local presets_url="http://$address:$port/presets.json"
local cfg_dest="${backup_dir}/${hostname}.cfg.json"
local presets_dest="${backup_dir}/${hostname}.presets.json"
# Write to ".tmp" files first, then move when success, to ensure we don't write partial files
local curl_command_cfg="curl -s "$cfg_url" -o "$cfg_dest.tmp""
local curl_command_presets="curl -s "$presets_url" -o "$presets_dest.tmp""
if ! curl_handler "$curl_command_cfg" "$hostname"; then
log "ERROR" "$RED" "Failed to backup configuration for $hostname"
rm -f "$cfg_dest.tmp"
return 1
fi
if ! curl_handler "$curl_command_presets" "$hostname"; then
log "ERROR" "$RED" "Failed to backup presets for $hostname"
rm -f "$presets_dest.tmp"
return 1
fi
mv "$cfg_dest.tmp" "$cfg_dest"
mv "$presets_dest.tmp" "$presets_dest"
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"
local curl_command="curl -s -X POST -F "file=@$firmware" "$url""
if ! curl_handler "$curl_command" "$hostname"; 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

View File

@ -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"
}
}

View File

@ -6,5 +6,5 @@ Configuration is performed via the Usermod menu. There are no parameters to set
## Installation ## 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`

View File

@ -1,3 +1,5 @@
#pragma once
#include "wled.h" #include "wled.h"
#include <Adafruit_ADS1X15.h> #include <Adafruit_ADS1X15.h>
#include <math.h> #include <math.h>
@ -250,7 +252,4 @@ class ADS1115Usermod : public Usermod {
int16_t results = ads.getLastConversionResults(); int16_t results = ads.getLastConversionResults();
readings[activeChannel] = ads.computeVolts(results); readings[activeChannel] = ads.computeVolts(results);
} }
}; };
static ADS1115Usermod ads1115_v2;
REGISTER_USERMOD(ads1115_v2);

View File

@ -1,328 +0,0 @@
#include "wled.h"
#include <AHT10.h>
#define AHT10_SUCCESS 1
class UsermodAHT10 : public Usermod
{
private:
static const char _name[];
unsigned long _lastLoopCheck = 0;
bool _settingEnabled : 1; // Enable the usermod
bool _mqttPublish : 1; // Publish mqtt values
bool _mqttPublishAlways : 1; // Publish always, regardless if there is a change
bool _mqttHomeAssistant : 1; // Enable Home Assistant docs
bool _initDone : 1; // Initialization is done
// Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime
uint8_t _i2cAddress = AHT10_ADDRESS_0X38;
ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR;
uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds
float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
uint8_t _lastStatus = 0;
float _lastHumidity = 0;
float _lastTemperature = 0;
#ifndef WLED_MQTT_DISABLE
float _lastHumiditySent = 0;
float _lastTemperatureSent = 0;
#endif
AHT10 *_aht = nullptr;
float truncateDecimals(float val)
{
return roundf(val * _decimalFactor) / _decimalFactor;
}
void initializeAht()
{
if (_aht != nullptr)
{
delete _aht;
}
_aht = new AHT10(_i2cAddress, _ahtType);
_lastStatus = 0;
_lastHumidity = 0;
_lastTemperature = 0;
}
#ifndef WLED_DISABLE_MQTT
void mqttInitialize()
{
// This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt
if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_mqttHomeAssistant)
return;
char topic[128];
snprintf_P(topic, 127, "%s/temperature", mqttDeviceTopic);
mqttCreateHassSensor(F("Temperature"), topic, F("temperature"), F("°C"));
snprintf_P(topic, 127, "%s/humidity", mqttDeviceTopic);
mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%"));
}
void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange)
{
// Check if MQTT Connected, otherwise it will crash the 8266
// Only report if the change is larger than the required diff
if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange))
{
char subuf[128];
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
mqtt->publish(subuf, 0, false, String(state).c_str());
lastState = state;
}
}
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
{
String t = String(F("homeassistant/sensor/")) + mqttClientID + "/" + name + F("/config");
StaticJsonDocument<600> doc;
doc[F("name")] = 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());
}
#endif
public:
void setup()
{
initializeAht();
}
void loop()
{
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!_settingEnabled || strip.isUpdating())
return;
// do your magic here
unsigned long currentTime = millis();
if (currentTime - _lastLoopCheck < _checkInterval)
return;
_lastLoopCheck = currentTime;
_lastStatus = _aht->readRawData();
if (_lastStatus == AHT10_ERROR)
{
// Perform softReset and retry
DEBUG_PRINTLN(F("AHTxx returned error, doing softReset"));
if (!_aht->softReset())
{
DEBUG_PRINTLN(F("softReset failed"));
return;
}
_lastStatus = _aht->readRawData();
}
if (_lastStatus == AHT10_SUCCESS)
{
float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA));
float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA));
#ifndef WLED_DISABLE_MQTT
// Push to MQTT
// We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided.
// The AHT10/15/20 has an accuracy of 0.3C in the temperature readings
mqttPublishIfChanged(F("temperature"), _lastTemperatureSent, temperature, 0.1f);
// The AHT10/15/20 has an accuracy in the humidity sensor of 2%
mqttPublishIfChanged(F("humidity"), _lastHumiditySent, humidity, 0.5f);
#endif
// Store
_lastTemperature = temperature;
_lastHumidity = humidity;
}
}
#ifndef WLED_DISABLE_MQTT
void onMqttConnect(bool sessionPresent)
{
mqttInitialize();
}
#endif
uint16_t getId()
{
return USERMOD_ID_AHT10;
}
void addToJsonInfo(JsonObject &root) override
{
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
#ifdef USERMOD_AHT10_DEBUG
JsonArray temp = user.createNestedArray(F("AHT last loop"));
temp.add(_lastLoopCheck);
temp = user.createNestedArray(F("AHT last status"));
temp.add(_lastStatus);
#endif
JsonArray jsonTemp = user.createNestedArray(F("Temperature"));
JsonArray jsonHumidity = user.createNestedArray(F("Humidity"));
if (_lastLoopCheck == 0)
{
// Before first run
jsonTemp.add(F("Not read yet"));
jsonHumidity.add(F("Not read yet"));
return;
}
if (_lastStatus != AHT10_SUCCESS)
{
jsonTemp.add(F("An error occurred"));
jsonHumidity.add(F("An error occurred"));
return;
}
jsonTemp.add(_lastTemperature);
jsonTemp.add(F("°C"));
jsonHumidity.add(_lastHumidity);
jsonHumidity.add(F("%"));
}
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[F("Enabled")] = _settingEnabled;
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
top[F("SensorType")] = _ahtType;
top[F("CheckInterval")] = _checkInterval / 1000;
top[F("Decimals")] = log10f(_decimalFactor);
#ifndef WLED_DISABLE_MQTT
top[F("MqttPublish")] = _mqttPublish;
top[F("MqttPublishAlways")] = _mqttPublishAlways;
top[F("MqttHomeAssistantDiscovery")] = _mqttHomeAssistant;
#endif
DEBUG_PRINTLN(F("AHT10 config saved."));
}
bool readFromConfig(JsonObject &root) override
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
if (!configComplete)
return false;
bool tmpBool = false;
configComplete &= getJsonValue(top[F("Enabled")], tmpBool);
if (configComplete)
_settingEnabled = tmpBool;
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval);
if (configComplete)
{
if (1 <= _checkInterval && _checkInterval <= 600)
_checkInterval *= 1000;
else
// Invalid input
_checkInterval = 60000;
}
configComplete &= getJsonValue(top[F("Decimals")], _decimalFactor);
if (configComplete)
{
if (0 <= _decimalFactor && _decimalFactor <= 5)
_decimalFactor = pow10f(_decimalFactor);
else
// Invalid input
_decimalFactor = 100;
}
uint8_t tmpAhtType;
configComplete &= getJsonValue(top[F("SensorType")], tmpAhtType);
if (configComplete)
{
if (0 <= tmpAhtType && tmpAhtType <= 2)
_ahtType = static_cast<ASAIR_I2C_SENSOR>(tmpAhtType);
else
// Invalid input
_ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR;
}
#ifndef WLED_DISABLE_MQTT
configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool);
if (configComplete)
_mqttPublish = tmpBool;
configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool);
if (configComplete)
_mqttPublishAlways = tmpBool;
configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool);
if (configComplete)
_mqttHomeAssistant = tmpBool;
#endif
if (_initDone)
{
// Reloading config
initializeAht();
#ifndef WLED_DISABLE_MQTT
mqttInitialize();
#endif
}
_initDone = true;
return configComplete;
}
~UsermodAHT10()
{
delete _aht;
_aht = nullptr;
}
};
const char UsermodAHT10::_name[] PROGMEM = "AHTxx";
static UsermodAHT10 aht10_v2;
REGISTER_USERMOD(aht10_v2);

View File

@ -1,30 +0,0 @@
# Usermod AHT10
This Usermod is designed to read a `AHT10`, `AHT15` or `AHT20` sensor and output the following:
- Temperature
- Humidity
Configuration is performed via the Usermod menu. The following settings can be configured in the Usermod Menu:
- I2CAddress: The i2c address in decimal. Set it to either 56 (0x38, the default) or 57 (0x39).
- SensorType, one of:
- 0 - AHT10
- 1 - AHT15
- 2 - AHT20
- CheckInterval: Number of seconds between readings
- Decimals: Number of decimals to put in the output
Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
- Libraries
- `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10))
- `Wire`
## Author
[@LordMike](https://github.com/LordMike)
# Compiling
To enable, add 'AHT10' to `custom_usermods` in your platformio encrionment (e.g. in `platformio_override.ini`)
```ini
[env:aht10_example]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} AHT10
```

View File

@ -1,7 +0,0 @@
{
"name": "AHT10_v2",
"build": { "libArchive": false },
"dependencies": {
"enjoyneering/AHT10":"~1.1.0"
}
}

View File

@ -1,5 +0,0 @@
[env:aht10_example]
extends = env:esp32dev
build_flags =
${common.build_flags} ${esp32.build_flags}
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal

View File

@ -1,3 +1,4 @@
#pragma once
#include "wled.h" #include "wled.h"
/* /*
@ -101,10 +102,10 @@ private:
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) { void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
uint32_t ms = time.ms % 1000; uint32_t ms = time.ms % 1000;
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2; uint8_t b0 = (cos8(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; uint8_t b1 = (sin8(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) { 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) { // for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
// uint16_t trailLed = dec(secondLed, i, secondsSegment); // uint16_t trailLed = dec(secondLed, i, secondsSegment);
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1); // 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; return USERMOD_ID_ANALOG_CLOCK;
} }
}; };
static AnalogClockUsermod analog_clock;
REGISTER_USERMOD(analog_clock);

View File

@ -1,4 +0,0 @@
{
"name": "Analog_Clock",
"build": { "libArchive": false }
}

View File

@ -7,6 +7,7 @@
* *
* See the accompanying README.md file for more info. * See the accompanying README.md file for more info.
*/ */
#pragma once
#include "wled.h" #include "wled.h"
class Animated_Staircase : public Usermod { class Animated_Staircase : public Usermod {
@ -331,7 +332,7 @@ class Animated_Staircase : public Usermod {
}; };
// NOTE: this *WILL* return TRUE if all the pins are set to -1. // NOTE: this *WILL* return TRUE if all the pins are set to -1.
// this is *BY DESIGN*. // this is *BY DESIGN*.
if (!PinManager::allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) { if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
topPIRorTriggerPin = -1; topPIRorTriggerPin = -1;
topEchoPin = -1; topEchoPin = -1;
bottomPIRorTriggerPin = -1; bottomPIRorTriggerPin = -1;
@ -424,10 +425,10 @@ class Animated_Staircase : public Usermod {
} }
void appendConfigData() { void appendConfigData() {
//oappend(F("dd=addDropdown('staircase','selectfield');")); //oappend(SET_F("dd=addDropdown('staircase','selectfield');"));
//oappend(F("addOption(dd,'1st value',0);")); //oappend(SET_F("addOption(dd,'1st value',0);"));
//oappend(F("addOption(dd,'2nd value',1);")); //oappend(SET_F("addOption(dd,'2nd value',1);"));
//oappend(F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field //oappend(SET_F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field
} }
@ -512,10 +513,10 @@ class Animated_Staircase : public Usermod {
(oldBottomAPin != bottomPIRorTriggerPin) || (oldBottomAPin != bottomPIRorTriggerPin) ||
(oldBottomBPin != bottomEchoPin)) { (oldBottomBPin != bottomEchoPin)) {
changed = true; changed = true;
PinManager::deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase); pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase); pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase); pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
PinManager::deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase); pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
} }
if (changed) setup(); if (changed) setup();
} }
@ -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::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm"; const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off"; const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
static Animated_Staircase animated_staircase;
REGISTER_USERMOD(animated_staircase);

View File

@ -1,5 +1,4 @@
# Usermod Animated Staircase # Usermod Animated Staircase
This usermod makes your staircase look cool by illuminating it with an animation. It uses 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: 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. speed, on/off time and distance by sending an HTTP request, see below.
## WLED integration ## 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/). 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: Before compiling, you have to make the following modifications:
Edit your environment in `platformio_override.ini` Edit `usermods_list.cpp`:
1. Open `wled00/usermods_list.cpp`
1. Open `platformio_override.ini` 2. add `#include "../usermods/Animated_Staircase/Animated_Staircase.h"` to the top of the file
2. add `Animated_Staircase` to the `custom_usermods` line for your environment 3. add `usermods.add(new Animated_Staircase());` to the end of the `void registerUsermods()` function.
You can configure usermod using the Usermods settings page. You can configure usermod using the Usermods settings page.
Please enter GPIO pins for PIR or ultrasonic sensors (trigger and echo). 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). Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below).
## Hardware installation ## Hardware installation
1. Attach the LED strip to each step of the stairs. 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. 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. 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 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 step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
@ -40,23 +38,24 @@ 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. You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
## WLED configuration ## WLED configuration
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the lowest segment id. lowest segment id.
2. Save your segments into a preset. 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 ## Changing behavior through API
The Staircase settings can be changed through the WLED JSON 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. **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 `^` 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. or remove them and put everything on one line.
| Setting | Description | Default | | Setting | Description | Default |
|------------------|---------------------------------------------------------------|---------| |------------------|---------------------------------------------------------------|---------|
| enabled | Enable or disable the usermod | true | | enabled | Enable or disable the usermod | true |
| bottom-sensor | Manually trigger a down to up animation via API | false | | bottom-sensor | Manually trigger a down to up animation via API | false |
| top-sensor | Manually trigger an up to down animation via API | false | | top-sensor | Manually trigger an up to down animation via API | false |
@ -76,7 +75,6 @@ The staircase settings and sensor states are inside the WLED "state" element:
``` ```
### Enable/disable the usermod ### Enable/disable the usermod
By disabling the usermod you will be able to keep the LED's on, independent from the sensor 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. 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. 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 ### 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. 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. 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. a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
### Animation triggering through the API ### Animation triggering through the API
In addition to activation by one of the stair sensors, you can also trigger the animation manually 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: 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}}' \ -d '{"staircase":{"top-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state xxx.xxx.xxx.xxx/json/state
``` ```
**MQTT** **MQTT**
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation. 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. You can also use `on` or `off` for enabling or disabling the usermod.
Have fun with this usermod Have fun with this usermod.<br/>
www.rolfje.com
`www.rolfje.com`
Modifications @blazoncek Modifications @blazoncek
## Change log ## Change log
2021-04 2021-04
* Adaptation for runtime configuration.
- Adaptation for runtime configuration.

View File

@ -1,4 +0,0 @@
{
"name": "Animated_Staircase",
"build": { "libArchive": false }
}

View File

@ -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);

View File

@ -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;
}
};

View File

@ -1,7 +0,0 @@
{
"name": "BH1750_v2",
"build": { "libArchive": false },
"dependencies": {
"claws/BH1750":"^1.2.0"
}
}

View File

@ -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. 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 ## Dependencies
- Libraries - Libraries
- `claws/BH1750 @^1.2.0` - `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. - Data is published over MQTT - make sure you've enabled the MQTT sync interface.
## Compilation ## 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 ### Configuration Options
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time): 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_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_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_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_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: In addition, the Usermod screen allows you to:
- enable/disable the usermod - enable/disable the usermod
- Enable Home Assistant Discovery of usermod - Enable Home Assistant Discovery of usermod
- Configure the SCL/SDA pins - Configure the SCL/SDA pins
## API ## API
The following method is available to interact with the usermod from other code modules: The following method is available to interact with the usermod from other code modules:
- `getIlluminance` read the brightness from the sensor - `getIlluminance` read the brightness from the sensor
## Change Log ## Change Log
Jul 2022 Jul 2022
- Added Home Assistant Discovery - Added Home Assistant Discovery
- Implemented PinManager to register pins - Implemented PinManager to register pins
- Made pins configurable in usermod menu - Made pins configurable in usermod menu

View 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 = F("");
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";

View File

@ -7,7 +7,6 @@ This Usermod is designed to read a `BME280` or `BMP280` sensor and output the fo
- Dew Point (`BME280` only) - Dew Point (`BME280` only)
Configuration is performed via the Usermod menu. There are no parameters to set in code! The following settings can be configured in the Usermod Menu: Configuration is performed via the Usermod menu. There are no parameters to set in code! The following settings can be configured in the Usermod Menu:
- The i2c address in decimal. Set it to either 118 (0x76, the default) or 119 (0x77).
- Temperature Decimals (number of decimal places to output) - Temperature Decimals (number of decimal places to output)
- Humidity Decimals - Humidity Decimals
- Pressure Decimals - Pressure Decimals
@ -22,6 +21,7 @@ Dependencies
- Libraries - Libraries
- `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) - `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280))
- `Wire` - `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. - 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! - 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 +39,17 @@ Methods also exist to read the read/calculated values from other WLED modules th
# Compiling # 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 ```ini
[env:usermod_bme280_d1_mini] [env:usermod_bme280_d1_mini]
extends = env: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
``` ```

View File

@ -1,7 +0,0 @@
{
"name": "BME280_v2",
"build": { "libArchive": false },
"dependencies": {
"finitespace/BME280":"~3.0.0"
}
}

View File

@ -1,15 +1,17 @@
// force the compiler to show a warning to confirm that this file is included // force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_BME280 version 2.0 **** #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 "wled.h"
#include <Arduino.h> #include <Arduino.h>
#include <BME280I2C.h> // BME280 sensor #include <BME280I2C.h> // BME280 sensor
#include <EnvironmentCalculations.h> // BME280 extended measurements #include <EnvironmentCalculations.h> // BME280 extended measurements
#ifdef WLED_DISABLE_MQTT
#error "This user mod requires MQTT to be enabled."
#endif
class UsermodBME280 : public Usermod class UsermodBME280 : public Usermod
{ {
private: private:
@ -22,7 +24,6 @@ private:
uint8_t PressureDecimals = 0; // Number of decimal places in published pressure values uint8_t PressureDecimals = 0; // Number of decimal places in published pressure values
uint16_t TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds uint16_t TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
uint16_t PressureInterval = 300; // Interval to measure pressure in seconds uint16_t PressureInterval = 300; // Interval to measure pressure in seconds
BME280I2C::I2CAddr I2CAddress = BME280I2C::I2CAddr_0x76; // i2c address, defaults to 0x76
bool PublishAlways = false; // Publish values even when they have not changed bool PublishAlways = false; // Publish values even when they have not changed
bool UseCelsius = true; // Use Celsius for Reporting bool UseCelsius = true; // Use Celsius for Reporting
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
@ -34,7 +35,20 @@ private:
#endif #endif
bool initDone = false; bool initDone = false;
BME280I2C bme; // BME280 sensor settings
BME280I2C::Settings settings{
BME280::OSR_X16, // Temperature oversampling x16
BME280::OSR_X16, // Humidity oversampling x16
BME280::OSR_X16, // Pressure oversampling x16
// Defaults
BME280::Mode_Forced,
BME280::StandbyTime_1000ms,
BME280::Filter_Off,
BME280::SpiEnable_False,
BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76
};
BME280I2C bme{settings};
uint8_t sensorType; uint8_t sensorType;
@ -167,52 +181,34 @@ private:
} }
} }
void initializeBmeComms()
{
BME280I2C::Settings settings{
BME280::OSR_X16, // Temperature oversampling x16
BME280::OSR_X16, // Humidity oversampling x16
BME280::OSR_X16, // Pressure oversampling x16
BME280::Mode_Forced,
BME280::StandbyTime_1000ms,
BME280::Filter_Off,
BME280::SpiEnable_False,
I2CAddress
};
bme.setSettings(settings);
if (!bme.begin())
{
sensorType = 0;
DEBUG_PRINTLN(F("Could not find BME280 I2C sensor!"));
}
else
{
switch (bme.chipModel())
{
case BME280::ChipModel_BME280:
sensorType = 1;
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
break;
case BME280::ChipModel_BMP280:
sensorType = 2;
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
break;
default:
sensorType = 0;
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
}
}
}
public: public:
void setup() void setup()
{ {
if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; } if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
initializeBmeComms(); if (!bme.begin())
initDone = true; {
sensorType = 0;
DEBUG_PRINTLN(F("Could not find BME280 I2C sensor!"));
}
else
{
switch (bme.chipModel())
{
case BME280::ChipModel_BME280:
sensorType = 1;
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
break;
case BME280::ChipModel_BMP280:
sensorType = 2;
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
break;
default:
sensorType = 0;
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
}
}
initDone=true;
} }
void loop() void loop()
@ -239,7 +235,7 @@ public:
// from the UI and values read from sensor, then publish to broker // from the UI and values read from sensor, then publish to broker
if (temperature != lastTemperature || PublishAlways) 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 lastTemperature = temperature; // Update last sensor temperature for next loop
@ -252,17 +248,17 @@ public:
if (humidity != lastHumidity || PublishAlways) if (humidity != lastHumidity || PublishAlways)
{ {
publishMqtt("humidity", String(humidity, (unsigned) HumidityDecimals).c_str()); publishMqtt("humidity", String(humidity, HumidityDecimals).c_str());
} }
if (heatIndex != lastHeatIndex || PublishAlways) 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) if (dewPoint != lastDewPoint || PublishAlways)
{ {
publishMqtt("dew_point", String(dewPoint, (unsigned) TemperatureDecimals).c_str()); publishMqtt("dew_point", String(dewPoint, TemperatureDecimals).c_str());
} }
lastHumidity = humidity; lastHumidity = humidity;
@ -279,7 +275,7 @@ public:
if (pressure != lastPressure || PublishAlways) if (pressure != lastPressure || PublishAlways)
{ {
publishMqtt("pressure", String(pressure, (unsigned) PressureDecimals).c_str()); publishMqtt("pressure", String(pressure, PressureDecimals).c_str());
} }
lastPressure = pressure; lastPressure = pressure;
@ -369,11 +365,12 @@ public:
} }
else if (sensorType==2) //BMP280 else if (sensorType==2) //BMP280
{ {
JsonArray temperature_json = user.createNestedArray(F("Temperature")); JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray pressure_json = user.createNestedArray(F("Pressure")); JsonArray pressure_json = user.createNestedArray(F("Pressure"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals)); temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)));
temperature_json.add(tempScale); temperature_json.add(tempScale);
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals)); pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa")); pressure_json.add(F("hPa"));
} }
else if (sensorType==1) //BME280 else if (sensorType==1) //BME280
@ -385,9 +382,9 @@ public:
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point")); JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals)); temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
temperature_json.add(tempScale); temperature_json.add(tempScale);
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals)); humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)));
humidity_json.add(F("%")); humidity_json.add(F("%"));
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals)); pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa")); pressure_json.add(F("hPa"));
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals)); heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
heatindex_json.add(tempScale); heatindex_json.add(tempScale);
@ -402,7 +399,6 @@ public:
{ {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
top[F("I2CAddress")] = static_cast<uint8_t>(I2CAddress);
top[F("TemperatureDecimals")] = TemperatureDecimals; top[F("TemperatureDecimals")] = TemperatureDecimals;
top[F("HumidityDecimals")] = HumidityDecimals; top[F("HumidityDecimals")] = HumidityDecimals;
top[F("PressureDecimals")] = PressureDecimals; top[F("PressureDecimals")] = PressureDecimals;
@ -430,10 +426,6 @@ public:
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing // A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
uint8_t tmpI2cAddress;
configComplete &= getJsonValue(top[F("I2CAddress")], tmpI2cAddress, 0x76);
I2CAddress = static_cast<BME280I2C::I2CAddr>(tmpI2cAddress);
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1); configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0); configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0); configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
@ -448,23 +440,8 @@ public:
// first run: reading from cfg.json // first run: reading from cfg.json
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
// changing parameters from settings page
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
// Reset all known values
sensorType = 0;
sensorTemperature = 0;
sensorHumidity = 0;
sensorHeatIndex = 0;
sensorDewPoint = 0;
sensorPressure = 0;
lastTemperature = 0;
lastHumidity = 0;
lastHeatIndex = 0;
lastDewPoint = 0;
lastPressure = 0;
initializeBmeComms();
} }
return configComplete; return configComplete;
@ -477,7 +454,3 @@ public:
const char UsermodBME280::_name[] PROGMEM = "BME280/BMP280"; const char UsermodBME280::_name[] PROGMEM = "BME280/BMP280";
const char UsermodBME280::_enabled[] PROGMEM = "enabled"; const char UsermodBME280::_enabled[] PROGMEM = "enabled";
static UsermodBME280 bme280_v2;
REGISTER_USERMOD(bme280_v2);

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,163 +0,0 @@
# 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
Raw sensor types
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
The BSEC Library calculates the following values via the gas resistance
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 %
In addition the usermod calculates
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.
There is a range of additional information for this, which the driver also provides. These values can be found in HomeAssistant under Diagnostics.
- **STABILIZATION_STATUS**: Gas sensor stabilization status [boolean] Indicates initial stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1).
- **RUN_IN_STATUS**: Gas sensor run-in status [boolean] Indicates power-on stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1)
Furthermore, all GAS based values have their own accuracy value. These have the following meaning:
- **Accuracy = 0** means the sensor is being stabilized (this can take a while on the first run)
- **Accuracy = 1** means that the previous measured values show too few differences and cannot be used for calibration. If the sensor is at accuracy 1 for too long, you must ensure that the ambient air is chaning. Opening the windows is fine. Or sometimes it is sufficient to breathe on the sensor for approx. 5 minutes.
- **Accuracy = 2** means the sensor is currently calibrating.
- **Accuracy = 3** means that the sensor has been successfully calibrated. Once accuracy 3 is reached, the calibration data is automatically written to the file system. This calibration data will be used again at the next start and will speed up the calibration.
The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to the value for IAQ, BSEC also provides us with CO2 and VOC equivalent values. When using the sensor, the calibration value should also always be read out and displayed or transmitted.
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
- **Enable:** Enables / disables the usermod
- **I2C address:** I2C address of the sensor. You can choose between 0X77 & 0X76. The default is 0x77.
- **Interval:** Specifies the interval of seconds at which the usermod should be executed. The default is every second.
- **Pub Chages Only:** If this item is active, the values are only published if they have changed since the last publication.
- **Pub Accuracy:** The Accuracy values associated with the gas values are also published.
- **Pub Calib State:** If this item is active, STABILIZATION_STATUS& RUN_IN_STATUS are also published.
- **Temp Scale:** Here you can choose between °C and °F.
- **Temp Offset:** The temperature offset is always set in °C. It must be converted for Fahrenheit.
- **HA Discovery:** If this item is active, the HomeAssistant sensor tree is created.
- **Pause While WLED Active:** If WLED has many LEDs to calculate, the computing power may no longer be sufficient to calculate the LEDs and read the sensor data. The LEDs then hang for a few microseconds, which can be seen. If this point is active, no sensor data is fetched as long as WLED is running.
- **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.
## Output
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();
- getGasResistance();
- getAbsoluteHumidity();
- getDewPoint(); The scale °C/°F is depended to the settings
- getIaq();
- getStaticIaq();
- getCo2();
- getVoc();
- getGasPerc();
- getIaqAccuracy();
- getStaticIaqAccuracy();
- getCo2Accuracy();
- getVocAccuracy();
- getGasPercAccuracy();
- getStabStatus();
- getRunInStatus();
## Compilation
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
Example:
```[env:esp32_mySpecial]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} BME68X
```
## 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.
<div><img src="pics/GeoGab.svg" width="20%"/> </div>
Gabriel Sieben (gsieben@geogab.net)

View File

@ -1,7 +0,0 @@
{
"name": "BME68X",
"build": { "libArchive": false },
"dependencies": {
"boschsensortec/BSEC Software Library":"^1.8.1492"
}
}

View File

@ -1,76 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="enable-background:new 0 0 595.28 127.56;"
viewBox="0 0 600 135"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata2372"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs2370"><linearGradient
osb:paint="solid"
id="linearGradient3877"><stop
id="stop3875"
offset="0"
style="stop-color:#808285;stop-opacity:1;" /></linearGradient><clipPath
id="clipPath2379"
clipPathUnits="userSpaceOnUse"><g
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
id="use2381"><path
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
class="st0"
d="m 588.34,94.29 c 0,13.28 -10.77,24.04 -24.04,24.04 H 31.38 C 18.1,118.33 7.34,107.57 7.34,94.29 V 32.38 C 7.34,19.1 18.1,8.34 31.38,8.34 h 532.91 c 13.28,0 24.04,10.76 24.04,24.04 V 94.29 Z M 159.44,69.66 c 0,17.15 11.68,19.07 21.89,19.07 h 14.23 c 14.6,0 23.44,-3.2 23.44,-13.87 v -0.91 h -7.84 c -0.45,7.11 -4.47,8.39 -14.32,8.39 h -10.76 c -13.68,0 -18.79,-2.74 -18.79,-13.32 V 66.65 H 219 V 60.9 C 219,44.3 208.88,40.74 194.83,40.74 h -12.59 c -11.12,0 -22.8,1.92 -22.8,18.79 z m 51.72,-9.95 h -43.87 c 0.73,-10.58 3.65,-12.59 16.05,-12.59 h 11.49 c 12.77,0 16.33,4.74 16.33,9.49 z m 18.62,10.13 c 0,15.33 10.86,18.88 25.73,18.88 h 11.22 c 14.87,0 25.72,-3.56 25.72,-18.88 V 59.62 c 0,-15.32 -10.85,-18.88 -25.72,-18.88 h -11.22 c -14.87,0 -25.73,3.56 -25.73,18.88 z m 54.82,-0.46 c 0,10.86 -6.38,12.95 -15.51,12.95 h -15.96 c -9.12,0 -15.51,-2.09 -15.51,-12.95 v -9.31 c 0,-10.85 6.39,-12.95 15.51,-12.95 h 15.96 c 9.12,0 15.51,2.1 15.51,12.95 z m 173.79,18.61 v -31.2 c 0,-13.87 -9.85,-16.06 -24.45,-16.06 h -12.41 c -14.05,0 -19.43,3.47 -19.43,11.95 v 2.28 h 7.84 v -1.64 c 0,-3.83 1.92,-6.2 11.77,-6.2 h 11.49 c 12.04,0 17.33,1.09 17.33,9.49 v 8.76 h -0.18 c -2.74,-4.47 -6.39,-5.56 -16.6,-5.56 H 421.16 C 407.48,59.81 400,61.09 400,71.67 v 3.1 c 0,8.76 3.1,13.96 14.96,13.96 h 18.79 z m -7.84,-12.5 c 0,5.84 -5.47,6.84 -19.7,6.84 h -10.4 c -10.76,0 -12.59,-2.19 -12.59,-7.39 v -1.46 c 0,-5.84 2.83,-7.3 12.95,-7.3 h 12.04 c 12.04,0 17.7,0.82 17.7,7.2 z M 73.6,66.69 h 10.88 l 5.03,0.01 h 48.67 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 H 88.83 c -8.58,0 -15.51,-2.55 -15.51,-14.05 v -0.35 -0.26 z m -9.16,-0.01 c 0,0.06 0,0.14 0,0.2 0.67,14.68 6.68,21.76 23.47,21.76 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 H 89.51 L 85.39,59.39 H 73.32 v -0.01 -16.9 c 0,-11.49 6.93,-14.05 15.51,-14.05 H 112 c 22.07,0 25.81,0.91 25.81,13.23 h 8.39 v -2.37 c 0,-15.23 -12.68,-18.15 -24.54,-18.15 H 87.92 c -17.88,0 -23.53,8.03 -23.53,24.72 V 59.39 L 64.38,59.4 H 44.6 c -11.86,0 -24.54,2.92 -24.54,18.15 v 2.37 5.02 2.37 c 0,15.24 12.68,18.16 24.54,18.16 h 40.79 432.59 34.03 c 17.88,0 23.54,-8.03 23.54,-24.72 V 45.88 c 0,-16.69 -5.65,-24.72 -23.54,-24.72 l -0.16,-0.02 h -223 c -17.88,0 -23.53,8.03 -23.53,24.72 v 18.06 c 0,16.69 5.65,24.72 23.53,24.72 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 h -42.78 v 7.3 h 33.84 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 h -34.94 c -8.58,0 -15.5,-2.55 -15.5,-14.05 V 42.48 c 0,-11.49 6.93,-14.05 15.5,-14.05 l 142.5,0.02 v 61.24 l 24.35,0.73 h 10.95 c 14.23,0 24.18,-3.56 24.18,-19.61 V 62.6 c 0,-14.87 -5.75,-20.16 -24.63,-20.16 h -11.95 -14.88 -0.17 l -0.02,-14.01 h 71.75 l -0.75,0.02 c 8.58,0 15.51,2.55 15.51,14.04 v 41.62 c 0,11.49 -6.93,14.05 -15.51,14.05 H 518.97 86.87 54.27 c -22.08,0 -25.81,-0.91 -25.81,-13.23 v -5.02 c 0,-12.31 3.74,-13.23 25.81,-13.23 h 9.84 z m 459.45,4.04 c 0,9.03 -2.83,13.32 -15.6,13.32 h -12.77 c -11.67,0 -15.42,-4.93 -15.42,-13.41 v -8.85 c 0,-11.13 6.48,-12.95 17.06,-12.95 h 10.58 c 10.76,0 16.14,2.01 16.14,12.77 v 9.12 z"
id="path3935" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
class="st0"
d="m 611.07375,-48.387188 c 0,13.28 -10.77,24.039999 -24.04,24.039999 H 54.113747 c -13.27999,0 -24.04,-10.76 -24.04,-24.039999 v -61.910002 c 0,-13.28 10.76001,-24.04 24.04,-24.04 H 587.02375 c 13.28,0 24.04,10.76 24.04,24.04 v 61.910002 z"
id="path3937" /></g></clipPath><clipPath
id="clipPath2398"
clipPathUnits="userSpaceOnUse"><g
id="use2400"
style="fill:#268298;fill-opacity:1"><g
id="g3959"
clip-path="url(#clipPath2407)"
style="fill:#268298;fill-opacity:1"><g
id="g3957"
style="fill:#268298;fill-opacity:1"><rect
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="rect3955"
width="350.98587"
height="147.57361"
x="95.224861"
y="-97.290329" /></g></g></g></clipPath><clipPath
id="clipPath2407"
clipPathUnits="userSpaceOnUse"><g
id="use2409"
style="fill:#268298;fill-opacity:1"><rect
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="rect3963"
width="350.98587"
height="147.57361"
x="95.224861"
y="-97.290329" /></g></clipPath></defs>
<style
id="style2363"
type="text/css">
.st0{fill:#808285;}
</style>
<path
style="opacity:1;fill:#ffffff;fill-opacity:0.9859813;fill-rule:nonzero;stroke:#5c7823;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
class="st0"
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826163 C 11.111876,127.56 5.1635108e-7,116.79902 5.1635108e-7,103.51782 V 41.602186 C 5.1635108e-7,28.320979 11.111876,17.560001 24.826163,17.560001 H 575.16351 c 13.71429,0 24.82616,10.760978 24.82616,24.042185 v 61.915634 z"
id="path2365-0" /><path
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
class="st0"
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826162 C 11.111877,127.56 5.1635111e-7,116.79902 5.1635111e-7,103.51782 V 41.602187 C 5.1635111e-7,28.320977 11.111876,17.559997 24.826162,17.559997 H 575.16351 c 13.71429,0 24.82616,10.76098 24.82616,24.04219 V 103.51782 Z M 157.07401,78.885567 c 0,17.15156 12.06196,19.07173 22.60585,19.07173 h 14.69535 c 15.07745,0 24.20654,-3.20029 24.20654,-13.87125 v -0.91009 h -8.09638 c -0.46472,7.11065 -4.61618,8.39076 -14.7883,8.39076 h -11.11188 c -14.12736,0 -19.40447,-2.74024 -19.40447,-13.3212 v -2.37022 h 53.40103 v -5.75052 c 0,-16.60151 -10.45094,-20.16183 -24.96041,-20.16183 h -13.00172 c -11.48365,0 -23.54561,1.92017 -23.54561,18.79171 z m 53.41136,-9.9509 h -45.30465 c 0.75387,-10.58096 3.76936,-12.59114 16.57487,-12.59114 h 11.86575 c 13.18761,0 16.86403,4.74043 16.86403,9.49086 z m 19.22891,10.13092 c 0,15.33139 11.21515,18.88171 26.57143,18.88171 h 11.58692 c 15.35629,0 26.56111,-3.56032 26.56111,-18.88171 v -10.22093 c 0,-15.32139 -11.20482,-18.88171 -26.56111,-18.88171 h -11.58692 c -15.35628,0 -26.57143,3.56032 -26.57143,18.88171 z m 56.61275,-0.46004 c 0,10.86098 -6.58865,12.95117 -16.01722,12.95117 h -16.48193 c -9.41825,0 -16.01721,-2.09019 -16.01721,-12.95117 v -9.31085 c 0,-10.85098 6.59896,-12.95117 16.01721,-12.95117 h 16.48193 c 9.41824,0 16.01722,2.10019 16.01722,12.95117 z m 179.47332,18.61169 v -31.20283 c 0,-13.87126 -10.17212,-16.06146 -25.24957,-16.06146 h -12.81584 c -14.50946,0 -20.0654,3.47031 -20.0654,11.95108 v 2.28021 h 8.09638 v -1.64015 c 0,-3.83035 1.98279,-6.20056 12.15491,-6.20056 h 11.86575 c 12.43373,0 17.89673,1.0901 17.89673,9.49086 v 8.7608 h -0.18589 c -2.8296,-4.47041 -6.59897,-5.56051 -17.14286,-5.56051 h -13.00172 c -14.12736,0 -21.85198,1.28012 -21.85198,11.86108 v 3.10028 c 0,8.76079 3.20138,13.96126 15.44923,13.96126 h 19.40447 z m -8.09639,-12.50114 c 0,5.84053 -5.64888,6.84062 -20.34423,6.84062 h -10.74011 c -11.11187,0 -13.00172,-2.19019 -13.00172,-7.39067 v -1.46013 c 0,-5.84053 2.92255,-7.30066 13.3735,-7.30066 h 12.43373 c 12.43374,0 18.27883,0.82007 18.27883,7.20065 z m -389.27711,-8.8008 h 11.2358 l 5.194492,0.01 h 50.261618 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88124,12.23111 H 84.154905 c -8.860585,0 -16.017212,-2.55023 -16.017212,-14.05127 v -0.35003 -0.26003 z m -9.459552,-0.01 c 0,0.06 0,0.14002 0,0.20002 0.69191,14.68133 6.898451,21.76198 24.237521,21.76198 h 38.055071 c 17.04992,0 23.0809,-6.39058 23.0809,-24.36221 v -4.88045 H 84.857142 l -4.254733,-0.01 H 68.137693 v -0.01 -16.90153 c 0,-11.49104 7.156627,-14.05129 16.017212,-14.05129 h 23.927705 c 22.79174,0 26.65405,0.91009 26.65405,13.23121 h 8.66437 v -2.37021 c 0,-15.23139 -13.09466,-18.15166 -25.34251,-18.15166 H 83.215146 c -18.464717,0 -24.299483,8.03073 -24.299483,24.72226 v 13.53122 l -0.01033,0.01 h -20.42685 c -12.247849,0 -25.342513,2.92027 -25.342513,18.15165 v 2.37022 5.02045 2.37022 c 0,15.241393 13.094664,18.161653 25.342513,18.161653 h 42.123923 446.736664 35.14286 c 18.46471,0 24.30981,-8.03073 24.30981,-24.722253 v -34.87316 c 0,-16.69153 -5.83477,-24.72226 -24.30981,-24.72226 l -0.16523,-0.02 H 332.0241 c -18.46471,0 -24.29948,8.03073 -24.29948,24.72226 v 18.06163 c 0,16.69152 5.83477,24.72225 24.29948,24.72225 h 38.05508 c 17.04991,0 23.08089,-6.39058 23.08089,-24.36221 v -4.88045 h -44.179 v 7.30067 h 34.94664 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88123,12.23111 h -36.08262 c -8.86059,0 -16.00688,-2.55023 -16.00688,-14.05127 v -24.81225 c 0,-11.49104 7.15662,-14.05129 16.00688,-14.05129 l 147.16007,0.02 v 61.24556 l 25.14629,0.73007 h 11.30809 c 14.69536,0 24.97074,-3.56033 24.97074,-19.61178 v -8.21075 c 0,-14.87135 -5.93803,-20.16183 -25.43545,-20.16183 h -12.34079 -15.36661 -0.17556 l -0.0207,-14.01128 h 74.09639 l -0.77453,0.02 c 8.86059,0 16.01721,2.55024 16.01721,14.04128 v 41.62378 c 0,11.491053 -7.15662,14.051283 -16.01721,14.051283 H 528.36145 82.130808 48.464716 c -22.802065,0 -26.654044,-0.91008 -26.654044,-13.231213 v -5.02046 c 0,-12.31111 3.862306,-13.2312 26.654044,-13.2312 h 10.161789 z m 474.475042,4.04037 c 0,9.03082 -2.92255,13.32121 -16.11015,13.32121 h -13.18761 c -12.05164,0 -15.92427,-4.93045 -15.92427,-13.41122 v -8.8508 c 0,-11.13101 6.69191,-12.95118 17.6179,-12.95118 h 10.92599 c 11.11188,0 16.66781,2.01019 16.66781,12.77116 v 9.12083 z"
id="path2365" />
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@ -1,160 +0,0 @@
#ifndef UMBBattery_h
#define UMBBattery_h
#include "battery_defaults.h"
/**
* Battery base class
* all other battery classes should inherit from this
*/
class UMBattery
{
private:
protected:
float minVoltage;
float maxVoltage;
float voltage;
int8_t level = 100;
float calibration; // offset or calibration value to fine tune the calculated voltage
float voltageMultiplier; // ratio for the voltage divider
float linearMapping(float v, float min, float max, float oMin = 0.0f, float oMax = 100.0f)
{
return (v-min) * (oMax-oMin) / (max-min) + oMin;
}
public:
UMBattery()
{
this->setVoltageMultiplier(USERMOD_BATTERY_VOLTAGE_MULTIPLIER);
this->setCalibration(USERMOD_BATTERY_CALIBRATION);
}
virtual void update(batteryConfig cfg)
{
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage);
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage);
if(cfg.level) this->setLevel(cfg.level);
if(cfg.calibration) this->setCalibration(cfg.calibration);
if(cfg.voltageMultiplier) this->setVoltageMultiplier(cfg.voltageMultiplier);
}
/**
* Corresponding battery curves
* calculates the level in % (0-100) with given voltage and possible voltage range
*/
virtual float mapVoltage(float v, float min, float max) = 0;
// {
// example implementation, linear mapping
// return (v-min) * 100 / (max-min);
// };
virtual void calculateAndSetLevel(float voltage) = 0;
/*
*
* Getter and Setter
*
*/
/*
* Get lowest configured battery voltage
*/
virtual float getMinVoltage()
{
return this->minVoltage;
}
/*
* Set lowest battery voltage
* can't be below 0 volt
*/
virtual void setMinVoltage(float voltage)
{
this->minVoltage = max(0.0f, voltage);
}
/*
* Get highest configured battery voltage
*/
virtual float getMaxVoltage()
{
return this->maxVoltage;
}
/*
* Set highest battery voltage
* can't be below minVoltage
*/
virtual void setMaxVoltage(float voltage)
{
this->maxVoltage = max(getMinVoltage()+.5f, voltage);
}
float getVoltage()
{
return this->voltage;
}
/**
* check if voltage is within specified voltage range, allow 10% over/under voltage
*/
void setVoltage(float voltage)
{
// this->voltage = ( (voltage < this->getMinVoltage() * 0.85f) || (voltage > this->getMaxVoltage() * 1.1f) )
// ? -1.0f
// : voltage;
this->voltage = voltage;
}
float getLevel()
{
return this->level;
}
void setLevel(float level)
{
this->level = constrain(level, 0.0f, 110.0f);
}
/*
* Get the configured calibration value
* a offset value to fine-tune the calculated voltage.
*/
virtual float getCalibration()
{
return calibration;
}
/*
* Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage.
*/
virtual void setCalibration(float offset)
{
calibration = offset;
}
/*
* Get the configured calibration value
* a value to set the voltage divider ratio
*/
virtual float getVoltageMultiplier()
{
return voltageMultiplier;
}
/*
* Set the voltage multiplier value
* a value to set the voltage divider ratio.
*/
virtual void setVoltageMultiplier(float multiplier)
{
voltageMultiplier = multiplier;
}
};
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@ -1,8 +1,3 @@
#ifndef UMBDefaults_h
#define UMBDefaults_h
#include "wled.h"
// pin defaults // pin defaults
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39 // for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
@ -14,66 +9,24 @@
#endif #endif
#endif #endif
// The initial delay before the first battery voltage reading after power-on.
// This allows the voltage to stabilize before readings are taken, improving accuracy of initial reading.
#ifndef USERMOD_BATTERY_INITIAL_DELAY
#define USERMOD_BATTERY_INITIAL_DELAY 10000 // (milliseconds)
#endif
// the frequency to check the battery, 30 sec // the frequency to check the battery, 30 sec
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL #ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000 #define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
#endif #endif
// default for 18650 battery
/* Default Battery Type // https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
* 0 = unkown // Discharge voltage: 2.5 volt + .1 for personal safety
* 1 = Lipo #ifndef USERMOD_BATTERY_MIN_VOLTAGE
* 2 = Lion #ifdef USERMOD_BATTERY_USE_LIPO
*/ // LiPo "1S" Batteries should not be dischared below 3V !!
#ifndef USERMOD_BATTERY_DEFAULT_TYPE #define USERMOD_BATTERY_MIN_VOLTAGE 3.2f
#define USERMOD_BATTERY_DEFAULT_TYPE 0 #else
#endif #define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
/* #endif
*
* Unkown 'Battery' defaults
*
*/
#ifndef USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE
// Extra save defaults
#define USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE 3.3f
#endif
#ifndef USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE
#define USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE 4.2f
#endif #endif
/* //the default ratio for the voltage divider
*
* Lithium polymer (Li-Po) defaults
*
*/
#ifndef USERMOD_BATTERY_LIPO_MIN_VOLTAGE
// LiPo "1S" Batteries should not be dischared below 3V !!
#define USERMOD_BATTERY_LIPO_MIN_VOLTAGE 3.2f
#endif
#ifndef USERMOD_BATTERY_LIPO_MAX_VOLTAGE
#define USERMOD_BATTERY_LIPO_MAX_VOLTAGE 4.2f
#endif
/*
*
* Lithium-ion (Li-Ion) defaults
*
*/
#ifndef USERMOD_BATTERY_LION_MIN_VOLTAGE
// default for 18650 battery
#define USERMOD_BATTERY_LION_MIN_VOLTAGE 2.6f
#endif
#ifndef USERMOD_BATTERY_LION_MAX_VOLTAGE
#define USERMOD_BATTERY_LION_MAX_VOLTAGE 4.2f
#endif
// the default ratio for the voltage divider
#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER #ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
@ -82,8 +35,13 @@
#endif #endif
#endif #endif
#ifndef USERMOD_BATTERY_AVERAGING_ALPHA #ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_AVERAGING_ALPHA 0.1f #define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif
// a common capacity for single 18650 battery cells is between 2500 and 3600 mAh
#ifndef USERMOD_BATTERY_TOTAL_CAPACITY
#define USERMOD_BATTERY_TOTAL_CAPACITY 3100
#endif #endif
// offset or calibration value to fine tune the calculated voltage // offset or calibration value to fine tune the calculated voltage
@ -91,6 +49,11 @@
#define USERMOD_BATTERY_CALIBRATION 0 #define USERMOD_BATTERY_CALIBRATION 0
#endif #endif
// calculate remaining time / the time that is left before the battery runs out of power
// #ifndef USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED
// #define USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED false
// #endif
// auto-off feature // auto-off feature
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED #ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED
#define USERMOD_BATTERY_AUTO_OFF_ENABLED true #define USERMOD_BATTERY_AUTO_OFF_ENABLED true
@ -115,26 +78,4 @@
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION #ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5 #define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5
#endif
// battery types
typedef enum
{
unknown=0,
lipo=1,
lion=2
} batteryType;
// used for initial configuration after boot
typedef struct bconfig_t
{
batteryType type;
float minVoltage;
float maxVoltage;
float voltage; // current voltage
int8_t level; // current level
float calibration; // offset or calibration value to fine tune the calculated voltage
float voltageMultiplier;
} batteryConfig;
#endif #endif

View File

@ -1,4 +0,0 @@
{
"name": "Battery",
"build": { "libArchive": false }
}

View File

@ -6,169 +6,105 @@
Enables battery level monitoring of your project. Enables battery level monitoring of your project.
<p align="left"> For this to work, the positive side of the (18650) battery must be connected to pin `A0` of the d1 mini/esp8266 with a 100k Ohm resistor (see [Useful Links](#useful-links)).
<img width="700" src="assets/battery_info_screen.png">
</p>
<br> If you have an ESP32 board, connect the positive side of the battery to ADC1 (GPIO32 - GPIO39)
<p align="center">
<img width="500" src="assets/battery_info_screen.png">
</p>
## ⚙️ Features ## ⚙️ Features
- 💯 Displays current battery voltage - 💯 Displays current battery voltage
- 🚥 Displays battery level - 🚥 Displays battery level
- 🚫 Auto-off with configurable threshold - 🚫 Auto-off with configurable Threshold
- 🚨 Low power indicator with many configuration possibilities - 🚨 Low power indicator with many configuration possibilities
<br><br>
## 🎈 Installation ## 🎈 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) | define `USERMOD_BATTERY` in `wled00/my_config.h`
<br><br> ### Example wiring
## 🔌 Example wiring <p align="center">
<img width="300" src="assets/battery_connection_schematic_01.png">
</p>
- (see [Useful Links](#useful-links)). ### Define Your Options
<table style="width: 100%; table-layout: fixed;">
<tr>
<!-- Column for the first image -->
<td style="width: 50%; vertical-align: bottom;">
<img width="300" src="assets/battery_connection_schematic_01.png" style="display: block;">
<p><strong>ESP8266</strong><br>
With a 100k Ohm resistor, connect the positive<br>
side of the battery to pin `A0`.</p>
</td>
<!-- Column for the second image -->
<td style="width: 50%; vertical-align: bottom;">
<img width="250" src="assets/battery_connection_schematic_esp32.png" style="display: block;">
<p><strong>ESP32</strong> (+S2, S3, C3 etc...)<br>
Use a voltage divider (two resistors of equal value).<br>
Connect to ADC1 (GPIO32 - GPIO39). GPIO35 is Default.</p>
</td>
</tr>
</table>
<br><br>
## Define Your Options
| Name | Unit | Description | | Name | Unit | Description |
| ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- | | ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- |
| `USERMOD_BATTERY` | | Define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp | | `USERMOD_BATTERY` | | define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp |
| `USERMOD_BATTERY_MEASUREMENT_PIN` | | Defaults to A0 on ESP8266 and GPIO35 on ESP32 | | `USERMOD_BATTERY_USE_LIPO` | | define this (in `my_config.h`) if you use LiPo rechargeables (1S) |
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | Battery check interval. defaults to 30 seconds | | `USERMOD_BATTERY_MEASUREMENT_PIN` | | defaults to A0 on ESP8266 and GPIO35 on ESP32 |
| `USERMOD_BATTERY_INITIAL_DELAY` | ms | Delay before initial reading. defaults to 10 seconds to allow voltage stabilization | | `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
| `USERMOD_BATTERY_{TYPE}_MIN_VOLTAGE` | v | Minimum battery voltage. default is 2.6 (18650 battery standard) | | `USERMOD_BATTERY_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
| `USERMOD_BATTERY_{TYPE}_MAX_VOLTAGE` | v | Maximum battery voltage. default is 4.2 (18650 battery standard) | | `USERMOD_BATTERY_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
| `USERMOD_BATTERY_{TYPE}_TOTAL_CAPACITY` | mAh | The capacity of all cells in parallel summed up | | `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
| `USERMOD_BATTERY_{TYPE}_CALIBRATION` | | Offset / calibration number, fine tune the measured voltage by the microcontroller | | `USERMOD_BATTERY_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
| Auto-Off | --- | --- | | Auto-Off | --- | --- |
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | Enables auto-off | | `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off |
| `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | When this threshold is reached master power turns off | | `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | when this threshold is reached master power turns off |
| Low-Power-Indicator | --- | --- | | Low-Power-Indicator | --- | --- |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED` | true/false | Enables low power indication | | `USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED` | true/false | enables low power indication |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET` | preset id | When low power is detected then use this preset to indicate low power | | `USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET` | preset id | when low power is detected then use this preset to indicate low power |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD` | % (0-100) | When this threshold is reached low power gets indicated | | `USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD` | % (0-100) | when this threshold is reached low power gets indicated |
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION` | seconds | For this long the configured preset is played | | `USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION` | seconds | for this long the configured preset is played |
All parameters can be configured at runtime via the Usermods settings page. All parameters can be configured at runtime via the Usermods settings page.
<br>
**NOTICE:** Each Battery type can be pre-configured individualy (in `my_config.h`)
| Name | Alias | `my_config.h` example |
| --------------- | ------------- | ------------------------------------- |
| Lithium Polymer | lipo (Li-Po) | `USERMOD_BATTERY_lipo_MIN_VOLTAGE` |
| Lithium Ionen | lion (Li-Ion) | `USERMOD_BATTERY_lion_TOTAL_CAPACITY` |
<br><br>
## 🔧 Calibration
The calibration number is a value that is added to the final computed voltage after it has been scaled by the voltage multiplier.
It fine-tunes the voltage reading so that it more closely matches the actual battery voltage, compensating for inaccuracies inherent in the voltage divider resistors or the ESP's ADC measurements.
Set calibration either in the Usermods settings page or at compile time in `my_config.h` or `platformio_override.ini`.
It can be either a positive or negative number.
<br><br>
## ⚠️ Important ## ⚠️ Important
Make sure you know your battery specifications! All batteries are **NOT** the same! - Make sure you know your battery specifications! All batteries are **NOT** the same!
- Example:
Example: | Your battery specification table | | Options you can define |
| :-------------------------------- |:--------------- | :---------------------------- |
| Your battery specification table | | Options you can define | | Capacity | 3500mAh 12,5 Wh | |
| --------------------------------- | --------------- | ----------------------------- | | Minimum capacity | 3350mAh 11,9 Wh | |
| Capacity | 3500mAh 12.5Wh | |
| Minimum capacity | 3350mAh 11.9Wh | |
| Rated voltage | 3.6V - 3.7V | | | Rated voltage | 3.6V - 3.7V | |
| **Charging end voltage** | **4.2V ± 0.05** | `USERMOD_BATTERY_MAX_VOLTAGE` | | **Charging end voltage** | **4,2V ± 0,05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
| **Discharge voltage** | **2.5V** | `USERMOD_BATTERY_MIN_VOLTAGE` | | **Discharge voltage** | **2,5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
| Max. discharge current (constant) | 10A (10000mA) | | | Max. discharge current (constant) | 10A (10000mA) | |
| max. charging current | 1.7A (1700mA) | | | max. charging current | 1.7A (1700mA) | |
| ... | ... | ... | | ... | ... | ... |
| .. | .. | .. | | .. | .. | .. |
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833) Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
<br><br>
## 🌐 Useful Links ## 🌐 Useful Links
- https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start - https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start
- https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/ - https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/
<br><br>
## 📝 Change Log ## 📝 Change Log
2024-08-19
- Improved MQTT support
- Added battery percentage & battery voltage as MQTT topic
2024-05-11
- Documentation updated
2024-04-30
- Integrate factory pattern to make it easier to add other / custom battery types
- Update readme
- Improved initial reading accuracy by delaying initial measurement to allow voltage to stabilize at power-on
2023-01-04 2023-01-04
- Basic support for LiPo rechargeable batteries (`-D USERMOD_BATTERY_USE_LIPO`) - basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`)
- Improved support for ESP32 (read calibrated voltage) - improved support for esp32 (read calibrated voltage)
- Corrected config saving (measurement pin, and battery min/max were lost) - corrected config saving (measurement pin, and battery min/max were lost)
- Various bugfixes - various bugfixes
2022-12-25 2022-12-25
- Added "auto-off" feature - added "auto-off" feature
- Added "low-power-indication" feature - added "low-power-indication" feature
- Added "calibration/offset" field to configuration page - added "calibration/offset" field to configuration page
- Added getter and setter, so that user usermods could interact with this one - added getter and setter, so that user usermods could interact with this one
- Update readme (added new options, made it markdownlint compliant) - update readme (added new options, made it markdownlint compliant)
2021-09-02 2021-09-02
- Added "Battery voltage" to info - added "Battery voltage" to info
- Added circuit diagram to readme - added circuit diagram to readme
- Added MQTT support, sending battery voltage - added MQTT support, sending battery voltage
- Minor fixes - minor fixes
2021-08-15 2021-08-15
- Changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries - changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
- Updated readme, added specification table - Updated readme, added specification table
2021-08-10 2021-08-10

View File

@ -1,38 +0,0 @@
#ifndef UMBLion_h
#define UMBLion_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* LiOn Battery
*
*/
class LionUMBattery : public UMBattery
{
private:
public:
LionUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_LION_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_LION_MAX_VOLTAGE);
}
float mapVoltage(float v, float min, float max) override
{
return this->linearMapping(v, min, max); // basic mapping
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
virtual void setMaxVoltage(float voltage) override
{
this->maxVoltage = max(getMinVoltage()+1.0f, voltage);
}
};
#endif

View File

@ -1,54 +0,0 @@
#ifndef UMBLipo_h
#define UMBLipo_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* LiPo Battery
*
*/
class LipoUMBattery : public UMBattery
{
private:
public:
LipoUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_LIPO_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_LIPO_MAX_VOLTAGE);
}
/**
* LiPo batteries have a differnt discharge curve, see
* https://blog.ampow.com/lipo-voltage-chart/
*/
float mapVoltage(float v, float min, float max) override
{
float lvl = 0.0f;
lvl = this->linearMapping(v, min, max); // basic mapping
if (lvl < 40.0f)
lvl = this->linearMapping(lvl, 0, 40, 0, 12); // last 45% -> drops very quickly
else {
if (lvl < 90.0f)
lvl = this->linearMapping(lvl, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
else // level > 90%
lvl = this->linearMapping(lvl, 90, 105, 95, 100); // highest 15% -> drop slowly
}
return lvl;
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
virtual void setMaxVoltage(float voltage) override
{
this->maxVoltage = max(getMinVoltage()+0.7f, voltage);
}
};
#endif

View File

@ -1,39 +0,0 @@
#ifndef UMBUnkown_h
#define UMBUnkown_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* Unkown / Default Battery
*
*/
class UnkownUMBattery : public UMBattery
{
private:
public:
UnkownUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
}
void update(batteryConfig cfg)
{
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage); else this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage); else this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
}
float mapVoltage(float v, float min, float max) override
{
return this->linearMapping(v, min, max); // basic mapping
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
};
#endif

View File

@ -1,14 +1,13 @@
#pragma once
#include "wled.h" #include "wled.h"
#include "battery_defaults.h" #include "battery_defaults.h"
#include "UMBattery.h"
#include "types/UnkownUMBattery.h"
#include "types/LionUMBattery.h"
#include "types/LipoUMBattery.h"
/* /*
* Usermod by Maximilian Mewes * Usermod by Maximilian Mewes
* E-mail: mewes.maximilian@gmx.de * Mail: mewes.maximilian@gmx.de
* Created at: 25.12.2022 * GitHub: itCarl
* Date: 25.12.2022
* If you have any questions, please feel free to contact me. * If you have any questions, please feel free to contact me.
*/ */
class UsermodBattery : public Usermod class UsermodBattery : public Usermod
@ -16,39 +15,49 @@ class UsermodBattery : public Usermod
private: private:
// battery pin can be defined in my_config.h // battery pin can be defined in my_config.h
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN; int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
UMBattery* bat = new UnkownUMBattery();
batteryConfig cfg;
// Initial delay before first reading to allow voltage stabilization
unsigned long initialDelay = USERMOD_BATTERY_INITIAL_DELAY;
bool initialDelayComplete = false;
bool isFirstVoltageReading = true;
// how often to read the battery voltage // how often to read the battery voltage
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL; unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0; unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0; unsigned long lastReadTime = 0;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
// all battery cells summed up
unsigned int totalBatteryCapacity = USERMOD_BATTERY_TOTAL_CAPACITY;
// raw analog reading
float rawValue = 0.0f;
// calculated voltage
float voltage = maxBatteryVoltage;
// between 0 and 1, to control strength of voltage smoothing filter // between 0 and 1, to control strength of voltage smoothing filter
float alpha = USERMOD_BATTERY_AVERAGING_ALPHA; float alpha = 0.05f;
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
// mapped battery level based on voltage
int8_t batteryLevel = 100;
// offset or calibration value to fine tune the calculated voltage
float calibration = USERMOD_BATTERY_CALIBRATION;
// time left estimation feature
// bool calculateTimeLeftEnabled = USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED;
// float estimatedTimeLeft = 0.0;
// auto shutdown/shutoff/master off feature // auto shutdown/shutoff/master off feature
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED; bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED;
uint8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD; int8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD;
// low power indicator feature // low power indicator feature
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED; bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED;
uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET; int8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD; int8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10; int8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
uint8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION; int8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
bool lowPowerIndicationDone = false; bool lowPowerIndicationDone = false;
unsigned long lowPowerActivationTime = 0; // used temporary during active time unsigned long lowPowerActivationTime = 0; // used temporary during active time
uint8_t lastPreset = 0; int8_t lastPreset = 0;
//
bool initDone = false; bool initDone = false;
bool initializing = true; bool initializing = true;
bool HomeAssistantDiscovery = false;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -58,29 +67,22 @@ class UsermodBattery : public Usermod
static const char _preset[]; static const char _preset[];
static const char _duration[]; static const char _duration[];
static const char _init[]; static const char _init[];
static const char _haDiscovery[];
// custom map function
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/**
* Helper for rounding floating point values
*/
float dot2round(float x) float dot2round(float x)
{ {
float nx = (int)(x * 100 + .5); float nx = (int)(x * 100 + .5);
return (float)(nx / 100); return (float)(nx / 100);
} }
/** /*
* Helper for converting a string to lowercase
*/
String stringToLower(String str)
{
for(int i = 0; i < str.length(); i++)
if(str[i] >= 'A' && str[i] <= 'Z')
str[i] += 32;
return str;
}
/**
* Turn off all leds * Turn off all leds
*/ */
void turnOff() void turnOff()
@ -89,15 +91,15 @@ class UsermodBattery : public Usermod
stateUpdated(CALL_MODE_DIRECT_CHANGE); stateUpdated(CALL_MODE_DIRECT_CHANGE);
} }
/** /*
* Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously * Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously
*/ */
void lowPowerIndicator() void lowPowerIndicator()
{ {
if (!lowPowerIndicatorEnabled) return; if (!lowPowerIndicatorEnabled) return;
if (batteryPin < 0) return; // no measurement if (batteryPin < 0) return; // no measurement
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= bat->getLevel()) lowPowerIndicationDone = false; if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= batteryLevel) lowPowerIndicationDone = false;
if (lowPowerIndicatorThreshold <= bat->getLevel()) return; if (lowPowerIndicatorThreshold <= batteryLevel) return;
if (lowPowerIndicationDone) return; if (lowPowerIndicationDone) return;
if (lowPowerActivationTime <= 1) { if (lowPowerActivationTime <= 1) {
lowPowerActivationTime = millis(); lowPowerActivationTime = millis();
@ -112,95 +114,34 @@ class UsermodBattery : public Usermod
} }
} }
/**
* read the battery voltage in different ways depending on the architecture
*/
float readVoltage() float readVoltage()
{ {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
return (analogReadMilliVolts(batteryPin) / 1000.0f) * bat->getVoltageMultiplier() + bat->getCalibration(); return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration;
#else #else
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value // use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
return (analogRead(batteryPin) / 1023.0f) * bat->getVoltageMultiplier() + bat->getCalibration(); return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration;
#endif #endif
} }
#ifndef WLED_DISABLE_MQTT
void addMqttSensor(const String &name, const String &type, const String &topic, const String &deviceClass, const String &unitOfMeasurement = "", const bool &isDiagnostic = false)
{
StaticJsonDocument<600> doc;
char uid[128], json_str[1024], buf[128];
doc[F("name")] = name;
doc[F("stat_t")] = topic;
sprintf_P(uid, PSTR("%s_%s_%s"), escapedMac.c_str(), stringToLower(name).c_str(), type);
doc[F("uniq_id")] = uid;
doc[F("dev_cla")] = deviceClass;
doc[F("exp_aft")] = 1800;
if(type == "binary_sensor") {
doc[F("pl_on")] = "on";
doc[F("pl_off")] = "off";
}
if(unitOfMeasurement != "")
doc[F("unit_of_measurement")] = unitOfMeasurement;
if(isDiagnostic)
doc[F("entity_category")] = "diagnostic";
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("ids")] = String(F("wled-sensor-")) + mqttClientID;
device[F("mf")] = F(WLED_BRAND);
device[F("mdl")] = F(WLED_PRODUCT_NAME);
device[F("sw")] = versionString;
sprintf_P(buf, PSTR("homeassistant/%s/%s/%s/config"), type, mqttClientID, uid);
DEBUG_PRINTLN(buf);
size_t payload_size = serializeJson(doc, json_str);
DEBUG_PRINTLN(json_str);
mqtt->publish(buf, 0, true, json_str, payload_size);
}
void publishMqtt(const char* topic, const char* state)
{
if (WLED_MQTT_CONNECTED) {
char buf[128];
snprintf_P(buf, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
mqtt->publish(buf, 0, false, state);
}
}
#endif
public: public:
//Functions called by WLED //Functions called by WLED
/** /*
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void setup()
{ {
// plug in the right battery type
if(cfg.type == (batteryType)lipo) {
bat = new LipoUMBattery();
} else if(cfg.type == (batteryType)lion) {
bat = new LionUMBattery();
}
// update the choosen battery type with configured values
bat->update(cfg);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
bool success = false; bool success = false;
DEBUG_PRINTLN(F("Allocating battery pin...")); DEBUG_PRINTLN(F("Allocating battery pin..."));
if (batteryPin >= 0 && digitalPinToAnalogChannel(batteryPin) >= 0) if (batteryPin >= 0 && digitalPinToAnalogChannel(batteryPin) >= 0)
if (PinManager::allocatePin(batteryPin, false, PinOwner::UM_Battery)) { if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
success = true; success = true;
voltage = readVoltage();
} }
if (!success) { if (!success) {
@ -211,17 +152,17 @@ class UsermodBattery : public Usermod
} }
#else //ESP8266 boards have only one analog input pin A0 #else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT); pinMode(batteryPin, INPUT);
voltage = readVoltage();
#endif #endif
// First voltage reading is delayed to allow voltage stabilization after powering up nextReadTime = millis() + readingInterval;
nextReadTime = millis() + initialDelay;
lastReadTime = millis(); lastReadTime = millis();
initDone = true; initDone = true;
} }
/** /*
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
@ -241,25 +182,6 @@ class UsermodBattery : public Usermod
lowPowerIndicator(); lowPowerIndicator();
// Handling the initial delay
if (!initialDelayComplete && millis() < nextReadTime)
return; // Continue to return until the initial delay is over
// Once the initial delay is over, set it as complete
if (!initialDelayComplete)
{
initialDelayComplete = true;
// Set the regular interval after initial delay
nextReadTime = millis() + readingInterval;
}
// Make the first voltage reading after the initial delay has elapsed
if (isFirstVoltageReading)
{
bat->setVoltage(readVoltage());
isFirstVoltageReading = false;
}
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms) // check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if (millis() < nextReadTime) return; if (millis() < nextReadTime) return;
@ -269,27 +191,58 @@ class UsermodBattery : public Usermod
if (batteryPin < 0) return; // nothing to read if (batteryPin < 0) return; // nothing to read
initializing = false; initializing = false;
float rawValue = readVoltage();
rawValue = readVoltage();
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
float filteredVoltage = bat->getVoltage() + alpha * (rawValue - bat->getVoltage()); voltage = voltage + alpha * (rawValue - voltage);
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
bat->setVoltage(filteredVoltage);
// translate battery voltage into percentage // translate battery voltage into percentage
bat->calculateAndSetLevel(filteredVoltage); /*
the standard "map" function doesn't work
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
*/
#ifdef USERMOD_BATTERY_USE_LIPO
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100); // basic mapping
// LiPo batteries have a differnt dischargin curve, see
// https://blog.ampow.com/lipo-voltage-chart/
if (batteryLevel < 40.0f)
batteryLevel = mapf(batteryLevel, 0, 40, 0, 12); // last 45% -> drops very quickly
else {
if (batteryLevel < 90.0f)
batteryLevel = mapf(batteryLevel, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
else // level > 90%
batteryLevel = mapf(batteryLevel, 90, 105, 95, 100); // highest 15% -> drop slowly
}
#else
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
#endif
if (voltage > -1.0f) batteryLevel = constrain(batteryLevel, 0.0f, 110.0f);
// if (calculateTimeLeftEnabled) {
// float currentBatteryCapacity = totalBatteryCapacity;
// estimatedTimeLeft = (currentBatteryCapacity/strip.currentMilliamps)*60;
// }
// Auto off -- Master power off // Auto off -- Master power off
if (autoOffEnabled && (autoOffThreshold >= bat->getLevel())) if (autoOffEnabled && (autoOffThreshold >= batteryLevel))
turnOff(); turnOff();
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
publishMqtt("battery", String(bat->getLevel(), 0).c_str()); // SmartHome stuff
publishMqtt("voltage", String(bat->getVoltage()).c_str()); // still don't know much about MQTT and/or HA
if (WLED_MQTT_CONNECTED) {
char buf[64]; // buffer for snprintf()
snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String(voltage).c_str());
}
#endif #endif
} }
/** /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor * Below it is shown how this could be used for e.g. a light sensor
@ -309,6 +262,16 @@ class UsermodBattery : public Usermod
// info modal display names // info modal display names
JsonArray infoPercentage = user.createNestedArray(F("Battery level")); JsonArray infoPercentage = user.createNestedArray(F("Battery level"));
JsonArray infoVoltage = user.createNestedArray(F("Battery voltage")); JsonArray infoVoltage = user.createNestedArray(F("Battery voltage"));
// if (calculateTimeLeftEnabled)
// {
// JsonArray infoEstimatedTimeLeft = user.createNestedArray(F("Estimated time left"));
// if (initializing) {
// infoEstimatedTimeLeft.add(FPSTR(_init));
// } else {
// infoEstimatedTimeLeft.add(estimatedTimeLeft);
// infoEstimatedTimeLeft.add(F(" min"));
// }
// }
JsonArray infoNextUpdate = user.createNestedArray(F("Next update")); JsonArray infoNextUpdate = user.createNestedArray(F("Next update"));
infoNextUpdate.add((nextReadTime - millis()) / 1000); infoNextUpdate.add((nextReadTime - millis()) / 1000);
@ -320,105 +283,46 @@ class UsermodBattery : public Usermod
return; return;
} }
if (bat->getLevel() < 0) { if (batteryLevel < 0) {
infoPercentage.add(F("invalid")); infoPercentage.add(F("invalid"));
} else { } else {
infoPercentage.add(bat->getLevel()); infoPercentage.add(batteryLevel);
} }
infoPercentage.add(F(" %")); infoPercentage.add(F(" %"));
if (bat->getVoltage() < 0) { if (voltage < 0) {
infoVoltage.add(F("invalid")); infoVoltage.add(F("invalid"));
} else { } else {
infoVoltage.add(dot2round(bat->getVoltage())); infoVoltage.add(dot2round(voltage));
} }
infoVoltage.add(F(" V")); infoVoltage.add(F(" V"));
} }
void addBatteryToJsonObject(JsonObject& battery, bool forJsonState)
{
if(forJsonState) { battery[F("type")] = cfg.type; } else {battery[F("type")] = (String)cfg.type; } // has to be a String otherwise it won't get converted to a Dropdown
battery[F("min-voltage")] = bat->getMinVoltage();
battery[F("max-voltage")] = bat->getMaxVoltage();
battery[F("calibration")] = bat->getCalibration();
battery[F("voltage-multiplier")] = bat->getVoltageMultiplier();
battery[FPSTR(_readInterval)] = readingInterval;
battery[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section /*
ao[FPSTR(_enabled)] = autoOffEnabled;
ao[FPSTR(_threshold)] = autoOffThreshold;
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
}
void getUsermodConfigFromJsonObject(JsonObject& battery)
{
getJsonValue(battery[F("type")], cfg.type);
getJsonValue(battery[F("min-voltage")], cfg.minVoltage);
getJsonValue(battery[F("max-voltage")], cfg.maxVoltage);
getJsonValue(battery[F("calibration")], cfg.calibration);
getJsonValue(battery[F("voltage-multiplier")], cfg.voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
setHomeAssistantDiscovery(battery[FPSTR(_haDiscovery)] | HomeAssistantDiscovery);
JsonObject ao = battery[F("auto-off")];
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
JsonObject lp = battery[F("indicator")];
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset);
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
if(initDone)
bat->update(cfg);
}
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/*
void addToJsonState(JsonObject& root) void addToJsonState(JsonObject& root)
{ {
JsonObject battery = root.createNestedObject(FPSTR(_name));
if (battery.isNull())
battery = root.createNestedObject(FPSTR(_name));
addBatteryToJsonObject(battery, true);
DEBUG_PRINTLN(F("Battery state exposed in JSON API."));
} }
*/
/** /*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void readFromJsonState(JsonObject& root) void readFromJsonState(JsonObject& root)
{ {
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject battery = root[FPSTR(_name)];
if (!battery.isNull()) {
getUsermodConfigFromJsonObject(battery);
DEBUG_PRINTLN(F("Battery state read from JSON API."));
}
} }
*/ */
/** /*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved) * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop(). * If you want to force saving the current state, use serializeConfig() in your loop().
@ -455,55 +359,60 @@ class UsermodBattery : public Usermod
*/ */
void addToConfig(JsonObject& root) void addToConfig(JsonObject& root)
{ {
JsonObject battery = root.createNestedObject(FPSTR(_name)); JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
if (battery.isNull()) {
battery = root.createNestedObject(FPSTR(_name));
}
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
battery[F("pin")] = batteryPin; battery[F("pin")] = batteryPin;
#endif #endif
// battery[F("time-left")] = calculateTimeLeftEnabled;
battery[F("min-voltage")] = minBatteryVoltage;
battery[F("max-voltage")] = maxBatteryVoltage;
battery[F("capacity")] = totalBatteryCapacity;
battery[F("calibration")] = calibration;
battery[F("voltage-multiplier")] = voltageMultiplier;
battery[FPSTR(_readInterval)] = readingInterval;
addBatteryToJsonObject(battery, false); JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
ao[FPSTR(_enabled)] = autoOffEnabled;
ao[FPSTR(_threshold)] = autoOffThreshold;
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
// read voltage in case calibration or voltage multiplier changed to see immediate effect // read voltage in case calibration or voltage multiplier changed to see immediate effect
bat->setVoltage(readVoltage()); voltage = readVoltage();
DEBUG_PRINTLN(F("Battery config saved.")); DEBUG_PRINTLN(F("Battery config saved."));
} }
void appendConfigData() void appendConfigData()
{ {
// Total: 462 Bytes oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');"));
oappend(F("td=addDropdown('Battery','type');")); // 34 Bytes oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');"));
oappend(F("addOption(td,'Unkown','0');")); // 28 Bytes oappend(SET_F("addInfo('Battery:capacity', 1, 'mAh');"));
oappend(F("addOption(td,'LiPo','1');")); // 26 Bytes oappend(SET_F("addInfo('Battery:interval', 1, 'ms');"));
oappend(F("addOption(td,'LiOn','2');")); // 26 Bytes oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');"));
oappend(F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');"));
oappend(F("addInfo('Battery:min-voltage',1,'v');")); // 38 Bytes oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');"));
oappend(F("addInfo('Battery:max-voltage',1,'v');")); // 38 Bytes
oappend(F("addInfo('Battery:interval',1,'ms');")); // 36 Bytes
oappend(F("addInfo('Battery:HA-discovery',1,'');")); // 38 Bytes
oappend(F("addInfo('Battery:auto-off:threshold',1,'%');")); // 45 Bytes
oappend(F("addInfo('Battery:indicator:threshold',1,'%');")); // 46 Bytes
oappend(F("addInfo('Battery:indicator:duration',1,'s');")); // 45 Bytes
// this option list would exeed the oappend() buffer // cannot quite get this mf to work. its exeeding some buffer limit i think
// a list of all presets to select one from // what i wanted is a list of all presets to select one from
// oappend(F("bd=addDropdown('Battery:low-power-indicator', 'preset');")); // oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
// the loop generates: oappend(F("addOption(bd, 'preset name', preset id);")); // the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
// for(int8_t i=1; i < 42; i++) { // for(int8_t i=1; i < 42; i++) {
// oappend(F("addOption(bd, 'Preset#")); // oappend(SET_F("addOption(bd, 'Preset#"));
// oappendi(i); // oappendi(i);
// oappend(F("',")); // oappend(SET_F("',"));
// oappendi(i); // oappendi(i);
// oappend(F(");")); // oappend(SET_F(");"));
// } // }
} }
/** /*
* readFromConfig() can be used to read back the custom settings you added with addToConfig(). * readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page) * This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
* *
@ -535,14 +444,26 @@ class UsermodBattery : public Usermod
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
newBatteryPin = battery[F("pin")] | newBatteryPin; newBatteryPin = battery[F("pin")] | newBatteryPin;
#endif #endif
setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage()); // calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage()); setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage);
setCalibration(battery[F("calibration")] | bat->getCalibration()); setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier()); setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
setCalibration(battery[F("calibration")] | calibration);
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
setHomeAssistantDiscovery(battery[FPSTR(_haDiscovery)] | HomeAssistantDiscovery);
getUsermodConfigFromJsonObject(battery); JsonObject ao = battery[F("auto-off")];
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
JsonObject lp = battery[F("indicator")];
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset); // dropdown trickery (int)lp["preset"]
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
DEBUG_PRINT(FPSTR(_name));
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
if (!initDone) if (!initDone)
@ -559,7 +480,7 @@ class UsermodBattery : public Usermod
if (newBatteryPin != batteryPin) if (newBatteryPin != batteryPin)
{ {
// deallocate pin // deallocate pin
PinManager::deallocatePin(batteryPin, PinOwner::UM_Battery); pinManager.deallocatePin(batteryPin, PinOwner::UM_Battery);
batteryPin = newBatteryPin; batteryPin = newBatteryPin;
// initialise // initialise
setup(); setup();
@ -570,24 +491,37 @@ class UsermodBattery : public Usermod
return !battery[FPSTR(_readInterval)].isNull(); return !battery[FPSTR(_readInterval)].isNull();
} }
#ifndef WLED_DISABLE_MQTT /*
void onMqttConnect(bool sessionPresent) * Generate a preset sample for low power indication
*/
void generateExamplePreset()
{ {
// Home Assistant Autodiscovery // StaticJsonDocument<300> j;
if (!HomeAssistantDiscovery) // JsonObject preset = j.createNestedObject();
return; // preset["mainseg"] = 0;
// JsonArray seg = preset.createNestedArray("seg");
// JsonObject seg0 = seg.createNestedObject();
// seg0["id"] = 0;
// seg0["start"] = 0;
// seg0["stop"] = 60;
// seg0["grp"] = 0;
// seg0["spc"] = 0;
// seg0["on"] = true;
// seg0["bri"] = 255;
// battery percentage // JsonArray col0 = seg0.createNestedArray("col");
char mqttBatteryTopic[128]; // JsonArray col00 = col0.createNestedArray();
snprintf_P(mqttBatteryTopic, 127, PSTR("%s/battery"), mqttDeviceTopic); // col00.add(255);
this->addMqttSensor(F("Battery"), "sensor", mqttBatteryTopic, "battery", "%", true); // col00.add(0);
// col00.add(0);
// voltage // seg0["fx"] = 1;
char mqttVoltageTopic[128]; // seg0["sx"] = 128;
snprintf_P(mqttVoltageTopic, 127, PSTR("%s/voltage"), mqttDeviceTopic); // seg0["ix"] = 128;
this->addMqttSensor(F("Voltage"), "sensor", mqttVoltageTopic, "voltage", "V", true);
// savePreset(199, "Low power Indicator", preset);
} }
#endif
/* /*
* *
@ -595,7 +529,7 @@ class UsermodBattery : public Usermod
* *
*/ */
/** /*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed. * This could be used in the future for the system to determine whether your usermod is installed.
*/ */
@ -604,23 +538,13 @@ class UsermodBattery : public Usermod
return USERMOD_ID_BATTERY; return USERMOD_ID_BATTERY;
} }
/**
* get currently active battery type
*/
batteryType getBatteryType()
{
return cfg.type;
}
/**
*
*/
unsigned long getReadingInterval() unsigned long getReadingInterval()
{ {
return readingInterval; return readingInterval;
} }
/** /*
* minimum repetition is 3000ms (3s) * minimum repetition is 3000ms (3s)
*/ */
void setReadingInterval(unsigned long newReadingInterval) void setReadingInterval(unsigned long newReadingInterval)
@ -628,84 +552,105 @@ class UsermodBattery : public Usermod
readingInterval = max((unsigned long)3000, newReadingInterval); readingInterval = max((unsigned long)3000, newReadingInterval);
} }
/**
/*
* Get lowest configured battery voltage * Get lowest configured battery voltage
*/ */
float getMinBatteryVoltage() float getMinBatteryVoltage()
{ {
return bat->getMinVoltage(); return minBatteryVoltage;
} }
/** /*
* Set lowest battery voltage * Set lowest battery voltage
* can't be below 0 volt * can't be below 0 volt
*/ */
void setMinBatteryVoltage(float voltage) void setMinBatteryVoltage(float voltage)
{ {
bat->setMinVoltage(voltage); minBatteryVoltage = max(0.0f, voltage);
} }
/** /*
* Get highest configured battery voltage * Get highest configured battery voltage
*/ */
float getMaxBatteryVoltage() float getMaxBatteryVoltage()
{ {
return bat->getMaxVoltage(); return maxBatteryVoltage;
} }
/** /*
* Set highest battery voltage * Set highest battery voltage
* can't be below minBatteryVoltage * can't be below minBatteryVoltage
*/ */
void setMaxBatteryVoltage(float voltage) void setMaxBatteryVoltage(float voltage)
{ {
bat->setMaxVoltage(voltage); #ifdef USERMOD_BATTERY_USE_LIPO
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
#else
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
#endif
} }
/** /*
* Get the capacity of all cells in parralel sumed up
* unit: mAh
*/
unsigned int getTotalBatteryCapacity()
{
return totalBatteryCapacity;
}
void setTotalBatteryCapacity(unsigned int capacity)
{
totalBatteryCapacity = capacity;
}
/*
* Get the calculated voltage * Get the calculated voltage
* formula: (adc pin value / adc precision * max voltage) + calibration * formula: (adc pin value / adc precision * max voltage) + calibration
*/ */
float getVoltage() float getVoltage()
{ {
return bat->getVoltage(); return voltage;
} }
/** /*
* Get the mapped battery level (0 - 100) based on voltage * Get the mapped battery level (0 - 100) based on voltage
* important: voltage can drop when a load is applied, so its only an estimate * important: voltage can drop when a load is applied, so its only an estimate
*/ */
int8_t getBatteryLevel() int8_t getBatteryLevel()
{ {
return bat->getLevel(); return batteryLevel;
} }
/** /*
* Get the configured calibration value * Get the configured calibration value
* a offset value to fine-tune the calculated voltage. * a offset value to fine-tune the calculated voltage.
*/ */
float getCalibration() float getCalibration()
{ {
return bat->getCalibration(); return calibration;
} }
/** /*
* Set the voltage calibration offset value * Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage. * a offset value to fine-tune the calculated voltage.
*/ */
void setCalibration(float offset) void setCalibration(float offset)
{ {
bat->setCalibration(offset); calibration = offset;
} }
/** /*
* Set the voltage multiplier value * Set the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups * A multiplier that may need adjusting for different voltage divider setups
*/ */
void setVoltageMultiplier(float multiplier) void setVoltageMultiplier(float multiplier)
{ {
bat->setVoltageMultiplier(multiplier); voltageMultiplier = multiplier;
} }
/* /*
@ -714,10 +659,10 @@ class UsermodBattery : public Usermod
*/ */
float getVoltageMultiplier() float getVoltageMultiplier()
{ {
return bat->getVoltageMultiplier(); return voltageMultiplier;
} }
/** /*
* Get auto-off feature enabled status * Get auto-off feature enabled status
* is auto-off enabled, true/false * is auto-off enabled, true/false
*/ */
@ -726,7 +671,7 @@ class UsermodBattery : public Usermod
return autoOffEnabled; return autoOffEnabled;
} }
/** /*
* Set auto-off feature status * Set auto-off feature status
*/ */
void setAutoOffEnabled(bool enabled) void setAutoOffEnabled(bool enabled)
@ -734,7 +679,7 @@ class UsermodBattery : public Usermod
autoOffEnabled = enabled; autoOffEnabled = enabled;
} }
/** /*
* Get auto-off threshold in percent (0-100) * Get auto-off threshold in percent (0-100)
*/ */
int8_t getAutoOffThreshold() int8_t getAutoOffThreshold()
@ -742,7 +687,7 @@ class UsermodBattery : public Usermod
return autoOffThreshold; return autoOffThreshold;
} }
/** /*
* Set auto-off threshold in percent (0-100) * Set auto-off threshold in percent (0-100)
*/ */
void setAutoOffThreshold(int8_t threshold) void setAutoOffThreshold(int8_t threshold)
@ -752,7 +697,8 @@ class UsermodBattery : public Usermod
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold; autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold;
} }
/**
/*
* Get low-power-indicator feature enabled status * Get low-power-indicator feature enabled status
* is the low-power-indicator enabled, true/false * is the low-power-indicator enabled, true/false
*/ */
@ -761,7 +707,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorEnabled; return lowPowerIndicatorEnabled;
} }
/** /*
* Set low-power-indicator feature status * Set low-power-indicator feature status
*/ */
void setLowPowerIndicatorEnabled(bool enabled) void setLowPowerIndicatorEnabled(bool enabled)
@ -769,7 +715,7 @@ class UsermodBattery : public Usermod
lowPowerIndicatorEnabled = enabled; lowPowerIndicatorEnabled = enabled;
} }
/** /*
* Get low-power-indicator preset to activate when low power is detected * Get low-power-indicator preset to activate when low power is detected
*/ */
int8_t getLowPowerIndicatorPreset() int8_t getLowPowerIndicatorPreset()
@ -777,7 +723,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorPreset; return lowPowerIndicatorPreset;
} }
/** /*
* Set low-power-indicator preset to activate when low power is detected * Set low-power-indicator preset to activate when low power is detected
*/ */
void setLowPowerIndicatorPreset(int8_t presetId) void setLowPowerIndicatorPreset(int8_t presetId)
@ -795,7 +741,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorThreshold; return lowPowerIndicatorThreshold;
} }
/** /*
* Set low-power-indicator threshold in percent (0-100) * Set low-power-indicator threshold in percent (0-100)
*/ */
void setLowPowerIndicatorThreshold(int8_t threshold) void setLowPowerIndicatorThreshold(int8_t threshold)
@ -805,7 +751,7 @@ class UsermodBattery : public Usermod
lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold); lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
} }
/** /*
* Get low-power-indicator duration in seconds * Get low-power-indicator duration in seconds
*/ */
int8_t getLowPowerIndicatorDuration() int8_t getLowPowerIndicatorDuration()
@ -813,7 +759,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorDuration; return lowPowerIndicatorDuration;
} }
/** /*
* Set low-power-indicator duration in seconds * Set low-power-indicator duration in seconds
*/ */
void setLowPowerIndicatorDuration(int8_t duration) void setLowPowerIndicatorDuration(int8_t duration)
@ -821,29 +767,14 @@ class UsermodBattery : public Usermod
lowPowerIndicatorDuration = duration; lowPowerIndicatorDuration = duration;
} }
/**
* Get low-power-indicator status when the indication is done this returns true /*
* Get low-power-indicator status when the indication is done thsi returns true
*/ */
bool getLowPowerIndicatorDone() bool getLowPowerIndicatorDone()
{ {
return lowPowerIndicationDone; return lowPowerIndicationDone;
} }
/**
* Set Home Assistant auto discovery
*/
void setHomeAssistantDiscovery(bool enable)
{
HomeAssistantDiscovery = enable;
}
/**
* Get Home Assistant auto discovery
*/
bool getHomeAssistantDiscovery()
{
return HomeAssistantDiscovery;
}
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
@ -854,8 +785,3 @@ const char UsermodBattery::_threshold[] PROGMEM = "threshold";
const char UsermodBattery::_preset[] PROGMEM = "preset"; const char UsermodBattery::_preset[] PROGMEM = "preset";
const char UsermodBattery::_duration[] PROGMEM = "duration"; const char UsermodBattery::_duration[] PROGMEM = "duration";
const char UsermodBattery::_init[] PROGMEM = "init"; const char UsermodBattery::_init[] PROGMEM = "init";
const char UsermodBattery::_haDiscovery[] PROGMEM = "HA-discovery";
static UsermodBattery battery;
REGISTER_USERMOD(battery);

View File

@ -1,4 +0,0 @@
{
"name": "Cronixie",
"build": { "libArchive": false }
}

View File

@ -4,5 +4,5 @@ This usermod supports driving the Cronixie M and L clock kits by Diamex.
## Installation ## 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. Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs.

View File

@ -1,3 +1,5 @@
#pragma once
#include "wled.h" #include "wled.h"
class UsermodCronixie : public Usermod { class UsermodCronixie : public Usermod {
@ -247,7 +249,7 @@ class UsermodCronixie : public Usermod {
if (backlight && _digitOut[i] <11) 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++) { for (uint16_t j=o; j< o+10; j++) {
if (j != excl) strip.setPixelColor(j, col); if (j != excl) strip.setPixelColor(j, col);
} }
@ -297,7 +299,4 @@ class UsermodCronixie : public Usermod {
{ {
return USERMOD_ID_CRONIXIE; return USERMOD_ID_CRONIXIE;
} }
}; };
static UsermodCronixie cronixie;
REGISTER_USERMOD(cronixie);

View File

@ -1,7 +0,0 @@
{
"name": "DHT",
"build": { "libArchive": false},
"dependencies": {
"DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking"
}
}

View File

@ -1,5 +1,6 @@
; Options ; 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_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_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 ; 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] [env:d1_mini_usermod_dht_C]
extends = env:d1_mini extends = env:d1_mini
custom_usermods = ${env:d1_mini.custom_usermods} DHT build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT_CELSIUS lib_deps = ${env:d1_mini.lib_deps}
https://github.com/alwynallan/DHT_nonblocking
[env:custom32_LEDPIN_16_usermod_dht_C] [env:custom32_LEDPIN_16_usermod_dht_C]
extends = env:custom32_LEDPIN_16 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 -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS lib_deps = ${env.lib_deps}
https://github.com/alwynallan/DHT_nonblocking

View File

@ -15,6 +15,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options ### 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_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_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 * `USERMOD_DHT_CELSIUS` - define this to report temperatures in degrees Celsius, otherwise Fahrenheit will be reported

View File

@ -1,5 +1,7 @@
#pragma once
#include "wled.h" #include "wled.h"
#ifdef WLED_DISABLE_MQTT #ifndef WLED_ENABLE_MQTT
#error "This user mod requires MQTT to be enabled." #error "This user mod requires MQTT to be enabled."
#endif #endif
@ -243,7 +245,3 @@ class UsermodDHT : public Usermod {
} }
}; };
static UsermodDHT dht;
REGISTER_USERMOD(dht);

View File

@ -1,5 +0,0 @@
{
"name": "EXAMPLE",
"build": { "libArchive": false },
"dependencies": {}
}

View File

@ -4,6 +4,7 @@ In this usermod file you can find the documentation on how to take advantage of
## Installation ## 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)_ _(You shouldn't need to actually install this, it does nothing useful)_

View File

@ -1,8 +1,10 @@
#pragma once
#include "wled.h" #include "wled.h"
/* /*
* Usermods allow you to add own functionality to WLED more easily * 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. * 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. * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
@ -69,7 +71,7 @@ class MyExampleUsermod : public Usermod {
// #endif // #endif
// in setup() // in setup()
// #ifdef USERMOD_EXAMPLE // #ifdef USERMOD_EXAMPLE
// UM = (MyExampleUsermod*) UsermodManager::lookup(USERMOD_ID_EXAMPLE); // UM = (MyExampleUsermod*) usermods.lookup(USERMOD_ID_EXAMPLE);
// #endif // #endif
// somewhere in loop() or other member method // somewhere in loop() or other member method
// #ifdef USERMOD_EXAMPLE // #ifdef USERMOD_EXAMPLE
@ -285,11 +287,11 @@ class MyExampleUsermod : public Usermod {
*/ */
void appendConfigData() override void appendConfigData() override
{ {
oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":great")); oappend(F("',1,'<i>(this is a great config value)</i>');")); oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":great")); oappend(SET_F("',1,'<i>(this is a great config value)</i>');"));
oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":testString")); oappend(F("',1,'enter any string you want');")); oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":testString")); oappend(SET_F("',1,'enter any string you want');"));
oappend(F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(F("','testInt');")); oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','testInt');"));
oappend(F("addOption(dd,'Nothing',0);")); oappend(SET_F("addOption(dd,'Nothing',0);"));
oappend(F("addOption(dd,'Everything',42);")); oappend(SET_F("addOption(dd,'Everything',42);"));
} }
@ -402,6 +404,3 @@ void MyExampleUsermod::publishMqtt(const char* state, bool retain)
} }
#endif #endif
} }
static MyExampleUsermod example_usermod;
REGISTER_USERMOD(example_usermod);

View File

@ -1,8 +0,0 @@
{
"name:": "EleksTube_IPS",
"build": { "libArchive": false },
"dependencies": {
"TFT_eSPI" : "2.5.33"
}
}
# Seems to add 300kb to the RAM requirement???

View File

@ -1,3 +1,4 @@
#pragma once
#include "TFTs.h" #include "TFTs.h"
#include "wled.h" #include "wled.h"
@ -155,7 +156,3 @@ class ElekstubeIPSUsermod : public Usermod {
const char ElekstubeIPSUsermod::_name[] PROGMEM = "EleksTubeIPS"; const char ElekstubeIPSUsermod::_name[] PROGMEM = "EleksTubeIPS";
const char ElekstubeIPSUsermod::_tubeSeg[] PROGMEM = "tubeSegment"; const char ElekstubeIPSUsermod::_tubeSeg[] PROGMEM = "tubeSegment";
const char ElekstubeIPSUsermod::_digitOffset[] PROGMEM = "digitOffset"; const char ElekstubeIPSUsermod::_digitOffset[] PROGMEM = "digitOffset";
static ElekstubeIPSUsermod elekstube_ips;
REGISTER_USERMOD(elekstube_ips);

View File

@ -1,12 +1,11 @@
#ifndef WLED_ENABLE_MQTT
#error "This user mod requires MQTT to be enabled."
#endif
#include "wled.h" #include "wled.h"
#include <Arduino.h> #include <Arduino.h>
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
#include <DallasTemperature.h> //Dallastemperature sensor #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. //The SCL and SDA pins are defined here.
//Lolin32 boards use SCL=5 SDA=4 //Lolin32 boards use SCL=5 SDA=4
#define U8X8_PIN_SCL 5 #define U8X8_PIN_SCL 5

View File

@ -1,13 +1,13 @@
#ifndef WLED_ENABLE_MQTT
#error "This user mod requires MQTT to be enabled."
#endif
#include "wled.h" #include "wled.h"
#include <Arduino.h> #include <Arduino.h>
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
#include <Wire.h> #include <Wire.h>
#include <BME280I2C.h> //BME280 sensor #include <BME280I2C.h> //BME280 sensor
#ifdef WLED_DISABLE_MQTT
#error "This user mod requires MQTT to be enabled."
#endif
void UpdateBME280Data(); void UpdateBME280Data();
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit #define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit

View File

@ -1,4 +0,0 @@
{
"name": "Fix_unreachable_netservices_v2",
"platforms": ["espressif8266"]
}

Some files were not shown because too many files have changed in this diff Show More