Merge branch 'wled:main' into main

This commit is contained in:
Jason Somers 2025-07-17 22:13:01 -04:00 committed by GitHub
commit 806163f1ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
163 changed files with 7404 additions and 6334 deletions

View File

@ -1,5 +1,6 @@
name: Notify Discord on PR Merge
on:
workflow_dispatch:
pull_request:
types: [closed]
@ -7,10 +8,26 @@
notify:
runs-on: ubuntu-latest
steps:
- name: Send Discord notification
shell: bash
- name: Get User Permission
id: checkAccess
uses: actions-cool/check-user-permission@v2
with:
require: write
username: ${{ github.triggering_actor }}
env:
DISCORD_WEBHOOK_BETA_TESTERS: ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
if: github.event.pull_request.merged == true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check User Permission
if: steps.checkAccess.outputs.require-result == 'false'
run: |
curl -H "Content-Type: application/json" -d '{"content": "Pull Request #{{ github.event.pull_request.number }} merged by {{ github.actor }}"}' $DISCORD_WEBHOOK_BETA_TESTERS
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 }}

71
.github/workflows/usermods.yml vendored Normal file
View File

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

6
package-lock.json generated
View File

@ -129,9 +129,9 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",

View File

@ -1,17 +1,14 @@
Import('env')
import os.path
from collections import deque
from pathlib import Path # For OS-agnostic path manipulation
from platformio.package.manager.library import LibraryPackageManager
from click import secho
from SCons.Script import Exit
from platformio.builder.tools.piolib import LibBuilderBase
usermod_dir = Path(env["PROJECT_DIR"]) / "usermods"
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
if env['PIOENV'] == "usermods":
# Add all usermods
env.GetProjectConfig().set(f"env:usermods", 'custom_usermods', " ".join([f.name for f in all_usermods]))
def find_usermod(mod: str):
# 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
@ -28,40 +25,25 @@ def find_usermod(mod: str):
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
proj = env.GetProjectConfig()
deps = env.GetProjectOption('lib_deps')
src_dir = proj.get("platformio", "src_dir")
src_dir = src_dir.replace('\\','/')
mod_paths = {mod: find_usermod(mod) for mod in usermods.split()}
usermods = [f"{mod} = symlink://{path}" for mod, path in mod_paths.items()]
proj.set("env:" + env['PIOENV'], 'lib_deps', deps + usermods)
# Force usermods to be installed in to the environment build state before the LDF runs
# Otherwise we won't be able to see them until it's too late to change their paths for LDF
# Logic is largely borrowed from PlaformIO internals
not_found_specs = []
for spec in usermods:
found = False
for storage_dir in env.GetLibSourceDirs():
#print(f"Checking {storage_dir} for {spec}")
lm = LibraryPackageManager(storage_dir)
if lm.get_package(spec):
#print("Found!")
found = True
break
if not found:
#print("Missing!")
not_found_specs.append(spec)
if not_found_specs:
lm = LibraryPackageManager(
env.subst(os.path.join("$PROJECT_LIBDEPS_DIR", "$PIOENV"))
)
for spec in not_found_specs:
#print(f"LU: forcing install of {spec}")
lm.install(spec)
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):
@ -82,13 +64,6 @@ old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
# Our new wrapper
def wrapped_ConfigureProjectLibBuilder(xenv):
# Update usermod properties
# Set libArchive before build actions are added
for um in (um for um in xenv.GetLibBuilders() if usermod_dir in Path(um.src_dir).parents):
build = um._manifest.get("build", {})
build["libArchive"] = False
um._manifest["build"] = build
# Call the wrapped function
result = old_ConfigureProjectLibBuilder.clone(xenv)()
@ -102,12 +77,29 @@ def wrapped_ConfigureProjectLibBuilder(xenv):
for dep in result.depbuilders:
cached_add_includes(dep, processed_deps, extra_include_dirs)
for um in [dep for dep in result.depbuilders if usermod_dir in Path(dep.src_dir).parents]:
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
um.env.PrependUnique(CPPPATH=wled_dir)
dep.env.PrependUnique(CPPPATH=str(wled_dir))
# Add WLED's own dependencies
for dir in extra_include_dirs:
um.env.PrependUnique(CPPPATH=dir)
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

View File

@ -0,0 +1,80 @@
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

@ -116,6 +116,7 @@ extra_scripts =
pre:pio-scripts/user_config_copy.py
pre:pio-scripts/load_usermods.py
pre:pio-scripts/build_ui.py
post:pio-scripts/validate_modules.py ;; double-check the build output usermods
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
# ------------------------------------------------------------------------------
@ -659,5 +660,5 @@ build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.flash_mode = dio
; custom_usermods = *every folder with library.json* -- injected by pio-scripts/load_usermods.py
custom_usermods = * ; Expands to all usermods in usermods folder
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat

View File

@ -20,7 +20,7 @@ click==8.1.8
# uvicorn
colorama==0.4.6
# via platformio
h11==0.14.0
h11==0.16.0
# via
# uvicorn
# wsproto
@ -38,7 +38,7 @@ pyelftools==0.32
# via platformio
pyserial==3.5
# via platformio
requests==2.32.3
requests==2.32.4
# via platformio
semantic-version==2.10.0
# via platformio
@ -50,7 +50,7 @@ tabulate==0.9.0
# via platformio
typing-extensions==4.12.2
# via anyio
urllib3==2.3.0
urllib3==2.5.0
# via requests
uvicorn==0.34.0
# via platformio

286
tools/wled-tools Executable file
View File

@ -0,0 +1,286 @@
#!/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,5 +1,6 @@
{
"name:": "ADS1115_v2",
"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

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

View File

@ -102,9 +102,9 @@ private:
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
uint32_t ms = time.ms % 1000;
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
setPixelColor(secondLed, scale32(secondColor, b0));
uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
setPixelColor(inc(secondLed, 1, secondsSegment), scale32(secondColor, b1));
}
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
@ -191,7 +191,7 @@ public:
// for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
// setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
// setPixelColor(trailLed, scale32(secondColor, trailBright));
// }
}

View File

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

View File

@ -1,4 +1,5 @@
# Usermod Animated Staircase
This usermod makes your staircase look cool by illuminating it with an animation. It uses
PIR or ultrasonic sensors at the top and bottom of your stairs to:
@ -11,11 +12,13 @@ The Animated Staircase can be controlled by the WLED API. Change settings such a
speed, on/off time and distance by sending an HTTP request, see below.
## WLED integration
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
Before compiling, you have to make the following modifications:
Edit your environment in `platformio_override.ini`
1. Open `platformio_override.ini`
2. add `Animated_Staircase` to the `custom_usermods` line for your environment
@ -25,10 +28,10 @@ If you use PIR sensor enter -1 for echo pin.
Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below).
## Hardware installation
1. Attach the LED strip to each step of the stairs.
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step.
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the
next step, creating one large virtual LED strip.
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the next step, creating one large virtual LED strip.
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
@ -37,20 +40,19 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
## WLED configuration
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
lowest segment id.
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the lowest segment id.
2. Save your segments into a preset.
3. Ideally, add the preset in the config > LED setup menu to the "apply
preset **n** at boot" setting.
3. Ideally, add the preset in the config > LED setup menu to the "apply preset **n** at boot" setting.
## Changing behavior through API
The Staircase settings can be changed through the WLED JSON api.
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
or remove them and put everything on one line.
| Setting | Description | Default |
|------------------|---------------------------------------------------------------|---------|
| enabled | Enable or disable the usermod | true |
@ -74,6 +76,7 @@ The staircase settings and sensor states are inside the WLED "state" element:
```
### Enable/disable the usermod
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
activity. This enables you to play with the lights without the usermod switching them on or off.
@ -90,6 +93,7 @@ To enable the usermod again, use `"enabled":true`.
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
@ -99,6 +103,7 @@ distances creates delays in the WLED software, _might_ introduce timing hiccups
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
### Animation triggering through the API
In addition to activation by one of the stair sensors, you can also trigger the animation manually
via the API. To simulate triggering the bottom sensor, use:
@ -115,15 +120,19 @@ curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"top-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state
```
**MQTT**
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
You can also use `on` or `off` for enabling or disabling the usermod.
Have fun with this usermod.<br/>
www.rolfje.com
Have fun with this usermod
`www.rolfje.com`
Modifications @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.
- Adaptation for runtime configuration.

View File

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

View File

@ -2,75 +2,18 @@
#warning **** Included USERMOD_BH1750 ****
#include "wled.h"
#include <BH1750.h>
#include "BH1750_v2.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;
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
static 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()
void Usermod_BH1750::_mqttInitialize()
{
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
@ -78,7 +21,7 @@ private:
}
// 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)
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");
@ -108,15 +51,14 @@ private:
mqtt->publish(t.c_str(), 0, true, temp.c_str());
}
public:
void setup()
void Usermod_BH1750::setup()
{
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
sensorFound = lightMeter.begin();
initDone = true;
}
void loop()
void Usermod_BH1750::loop()
{
if ((!enabled) || strip.isUpdating())
return;
@ -141,7 +83,7 @@ public:
{
lastLux = lux;
lastSend = millis();
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)
{
if (!mqttInitialized)
@ -156,15 +98,11 @@ public:
{
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
}
#endif
}
}
inline float getIlluminance() {
return (float)lastLux;
}
void addToJsonInfo(JsonObject &root)
void Usermod_BH1750::addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull())
@ -190,7 +128,7 @@ public:
}
// (called from set.cpp) stores persistent properties to cfg.json
void addToConfig(JsonObject &root)
void Usermod_BH1750::addToConfig(JsonObject &root)
{
// we add JSON object.
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
@ -204,7 +142,7 @@ public:
}
// called before setup() to populate properties from values stored in cfg.json
bool readFromConfig(JsonObject &root)
bool Usermod_BH1750::readFromConfig(JsonObject &root)
{
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
@ -234,12 +172,6 @@ public:
}
uint16_t getId()
{
return USERMOD_ID_BH1750;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";

View File

@ -0,0 +1,92 @@
#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,5 +1,6 @@
{
"name:": "BH1750_v2",
"name": "BH1750_v2",
"build": { "libArchive": false },
"dependencies": {
"claws/BH1750":"^1.2.0"
}

View File

@ -4,6 +4,7 @@ This usermod will read from an ambient light sensor like the BH1750.
The luminance is displayed in both the Info section of the web UI, as well as published to the `/luminance` MQTT topic if enabled.
## Dependencies
- Libraries
- `claws/BH1750 @^1.2.0`
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
@ -13,23 +14,30 @@ The luminance is displayed in both the Info section of the web UI, as well as pu
To enable, compile with `BH1750` in `custom_usermods` (e.g. in `platformio_override.ini`)
### Configuration Options
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
- `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
- `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
- `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
- `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
In addition, the Usermod screen allows you to:
- enable/disable the usermod
- Enable Home Assistant Discovery of usermod
- Configure the SCL/SDA pins
## API
The following method is available to interact with the usermod from other code modules:
- `getIlluminance` read the brightness from the sensor
## Change Log
Jul 2022
- Added Home Assistant Discovery
- Implemented PinManager to register pins
- Made pins configurable in usermod menu

View File

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

View File

@ -1,21 +1,18 @@
/**
* @file usermod_BMW68X.h
* @file usermod_BMW68X.cpp
* @author Gabriel A. Sieben (GeoGab)
* @brief Usermod for WLED to implement the BME680/BME688 sensor
* @version 1.0.0
* @date 19 Feb 2024
* @version 1.0.2
* @date 28 March 2025
*/
#warning ********************Included USERMOD_BME68X ********************
#define UMOD_DEVICE "ESP32" // NOTE - Set your hardware here
#define HARDWARE_VERSION "1.0" // NOTE - Set your hardware version here
#define UMOD_BME680X_SW_VERSION "1.0.1" // NOTE - Version of the User Mod
#define UMOD_BME680X_SW_VERSION "1.0.2" // NOTE - Version of the User Mod
#define CALIB_FILE_NAME "/BME680X-Calib.hex" // NOTE - Calibration file name
#define UMOD_NAME "BME680X" // NOTE - User module name
#define UMOD_DEBUG_NAME "UM-BME680X: " // NOTE - Debug print module name addon
/* Debug Print Text Coloring */
#define ESC "\033"
#define ESC_CSI ESC "["
#define ESC_STYLE_RESET ESC_CSI "0m"
@ -33,10 +30,10 @@
/* Debug Print Special Text */
#define INFO_COLUMN ESC_CURSOR_COLUMN(60)
#define OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
#define FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
#define WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
#define DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
#define GOGAB_OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
#define GOGAB_FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
#define GOGAB_WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
#define GOGAB_DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
#include "bsec.h" // Bosch sensor library
#include "wled.h"
@ -328,7 +325,7 @@ void UsermodBME68X::setup() {
/* Check, if i2c is activated */
if (i2c_scl < 0 || i2c_sda < 0) {
settings.enabled = false; // Disable usermod once i2c is not running
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." FAIL));
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." GOGAB_FAIL));
return;
}
@ -352,7 +349,7 @@ void UsermodBME68X::setup() {
loadState(); // Load the old calibration data
checkIaqSensorStatus(); // Check the sensor status
// HomeAssistantDiscovery();
DEBUG_PRINTLN(F(INFO_COLUMN DONE));
DEBUG_PRINTLN(F(INFO_COLUMN GOGAB_DONE));
}
/**
@ -564,7 +561,7 @@ void UsermodBME68X::HomeAssistantDiscovery() {
MQTT_PublishHASensor(_nameStabStatus, "", _unitNone, settings.publishSensorState - 1, 1);
MQTT_PublishHASensor(_nameRunInStatus, "", _unitNone, settings.publishSensorState - 1, 1);
DEBUG_PRINTLN(UMOD_DEBUG_NAME DONE);
DEBUG_PRINTLN(UMOD_DEBUG_NAME GOGAB_DONE);
}
/**
@ -750,7 +747,7 @@ void UsermodBME68X::addToConfig(JsonObject& root) {
sensors_json[FPSTR(_nameVoc)] = settings.decimals.Voc;
sensors_json[FPSTR(_nameGasPer)] = settings.decimals.gasPerc;
DEBUG_PRINTLN(F(OK));
DEBUG_PRINTLN(F(GOGAB_OK));
}
/**
@ -831,7 +828,7 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameVoc)], settings.decimals.Voc, 0 );
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameGasPer)], settings.decimals.gasPerc, 0 );
DEBUG_PRINTLN(F(OK));
DEBUG_PRINTLN(F(GOGAB_OK));
/* Set the selected temperature unit */
if (settings.tempScale) {
@ -845,10 +842,10 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Deleting Calibration File"));
flags.DeleteCaibration = false;
if (WLED_FS.remove(CALIB_FILE_NAME)) {
DEBUG_PRINTLN(F(OK));
DEBUG_PRINTLN(F(GOGAB_OK));
}
else {
DEBUG_PRINTLN(F(FAIL));
DEBUG_PRINTLN(F(GOGAB_FAIL));
}
}
@ -1026,11 +1023,11 @@ void UsermodBME68X::checkIaqSensorStatus() {
flags.InitSuccessful = false;
if (iaqSensor.bsecStatus < BSEC_OK) {
InfoPageStatusLine += " Error Code : " + String(iaqSensor.bsecStatus);
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
InfoPageStatusLine += " Warning Code : " + String(iaqSensor.bsecStatus);
DEBUG_PRINTLN(WARN);
DEBUG_PRINTLN(GOGAB_WARN);
}
}
else {
@ -1041,16 +1038,16 @@ void UsermodBME68X::checkIaqSensorStatus() {
flags.InitSuccessful = false;
if (iaqSensor.bme68xStatus < BME68X_OK) {
InfoPageStatusLine += "error code: " + String(iaqSensor.bme68xStatus);
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
InfoPageStatusLine += "warning code: " + String(iaqSensor.bme68xStatus);
DEBUG_PRINTLN(WARN);
DEBUG_PRINTLN(GOGAB_WARN);
}
}
else {
InfoPageStatusLine += F("OK");
DEBUG_PRINTLN(OK);
DEBUG_PRINTLN(GOGAB_OK);
}
}
}
@ -1063,12 +1060,12 @@ void UsermodBME68X::loadState() {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Read the calibration file: "));
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_READ);
if (!file) {
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
file.read(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
DEBUG_PRINTLN(OK);
DEBUG_PRINTLN(GOGAB_OK);
iaqSensor.setState(bsecState);
}
}
@ -1084,14 +1081,14 @@ void UsermodBME68X::saveState() {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Write the calibration file "));
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_WRITE);
if (!file) {
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
iaqSensor.getState(bsecState);
file.write(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
stateUpdateCounter++;
DEBUG_PRINTF("(saved %d times)" OK "\n", stateUpdateCounter);
DEBUG_PRINTF("(saved %d times)" GOGAB_OK "\n", stateUpdateCounter);
flags.SaveState = false; // Clear save state flag
char contbuffer[30];

View File

@ -1,53 +1,58 @@
# 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.
@ -67,10 +72,10 @@ The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to t
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
## Settings
The settings of the usermods are set in the usermod section of wled.
<p align="center"><img src="pics/pic6.png"></p>
The possible settings are
@ -88,6 +93,7 @@ The possible settings are
- **Del Calibration Hist:** If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.
### Sensors
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
@ -99,6 +105,7 @@ 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();
@ -118,15 +125,36 @@ Methods also exist to read the read/calculated values from other WLED modules th
- getStabStatus();
- getRunInStatus();
## Compilation
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
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.

View File

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

View File

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

View File

@ -247,7 +247,7 @@ class UsermodCronixie : public Usermod {
if (backlight && _digitOut[i] <11)
{
uint32_t col = gamma32(strip.getSegment(0).colors[1]);
uint32_t col = strip.getSegment(0).colors[1];
for (uint16_t j=o; j< o+10; j++) {
if (j != excl) strip.setPixelColor(j, col);
}

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
{
"name:": "EleksTube_IPS",
"build": { "libArchive": false },
"dependencies": {
"TFT_eSPI" : "2.5.33"
}

View File

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

View File

@ -1,5 +1,6 @@
{
"name:": "INA226_v2",
"name": "INA226_v2",
"build": { "libArchive": false },
"dependencies": {
"wollewald/INA226_WE":"~1.2.9"
}

View File

@ -1,3 +1,4 @@
{
"name:": "Internal_Temperature_v2"
"name": "Internal_Temperature_v2",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,6 @@
{
"name:": "LD2410_v2",
"name": "LD2410_v2",
"build": { "libArchive": false },
"dependencies": {
"ncmreynolds/ld2410":"^0.1.3"
}

View File

@ -18,7 +18,7 @@ To enable, compile with `LD2140` in `custom_usermods` (e.g. in `platformio_overr
```ini
[env:usermod_USERMOD_LD2410_esp32dev]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} LD2140
custom_usermods = ${env:esp32dev.custom_usermods} LD2140_v2
```
### Configuration Options

View File

@ -1,3 +1,4 @@
{
"name:": "LDR_Dusk_Dawn_v2"
"name": "LDR_Dusk_Dawn_v2",
"build": { "libArchive": false }
}

View File

@ -35,8 +35,8 @@ class Usermod_MAX17048 : public Usermod {
unsigned long lastSend = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
uint8_t VoltageDecimals = 3; // Number of decimal places in published voltage values
uint8_t PercentDecimals = 1; // Number of decimal places in published percent values
unsigned VoltageDecimals = 3; // Number of decimal places in published voltage values
unsigned PercentDecimals = 1; // Number of decimal places in published percent values
// string that are used multiple time (this will save some flash memory)
static const char _name[];

View File

@ -1,5 +1,5 @@
{
"name:": "MAX17048_v2",
"name": "MAX17048_v2",
"build": { "libArchive": false},
"dependencies": {
"Adafruit_MAX1704X":"https://github.com/adafruit/Adafruit_MAX1704X#1.0.2"

View File

@ -5,26 +5,16 @@ This usermod reads information from an Adafruit MAX17048 and outputs the follow
## Dependencies
Libraries:
- `Adafruit_BusIO@~1.14.5` (by [adafruit](https://github.com/adafruit/Adafruit_BusIO))
- `Adafruit_MAX1704X@~1.0.2` (by [adafruit](https://github.com/adafruit/Adafruit_MAX1704X))
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
## Compilation
Add "MAX17048_v2" to your platformio.ini environment's custom_usermods and build.
To enable, compile with `USERMOD_MAX17048` define in the build_flags (e.g. in `platformio.ini` or `platformio_override.ini`) such as in the example below:
```ini
[env:usermod_max17048_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MAX17048
lib_deps =
${esp8266.lib_deps}
https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
custom_usermods = ${env:d1_mini.custom_usermods} MAX17048_v2
```
### Configuration Options

View File

@ -1,4 +1,5 @@
{
"name:": "MY9291",
"name": "MY9291",
"build": { "libArchive": false },
"platforms": ["espressif8266"]
}

View File

@ -1,3 +1,4 @@
{
"name:": "PIR_sensor_switch"
"name": "PIR_sensor_switch",
"build": { "libArchive": false }
}

View File

@ -25,7 +25,7 @@ You can also use usermod's off timer instead of sensor's. In such case rotate th
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionally `-D PIR_SENSOR_PIN=16` to override default pin. You can also change the default off time by adding `-D PIR_SENSOR_OFF_SEC=30`.
## API to enable/disable the PIR sensor from outside. For example from another usermod:
## API to enable/disable the PIR sensor from outside. For example from another usermod
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
@ -33,15 +33,16 @@ When the PIR sensor state changes an MQTT message is broadcasted with topic `wle
Usermod can also be configured to send just the MQTT message but not change WLED state using settings page as well as responding to motion only at night
(assuming NTP and latitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance:
### There are two options to get access to the usermod instance
1. Include `usermod_PIR_sensor_switch.h` **before** you include other usermods in `usermods_list.cpp'
_1._ Include `usermod_PIR_sensor_switch.h` **before** you include other usermods in `usermods_list.cpp'
or
2. Use `#include "usermod_PIR_sensor_switch.h"` at the top of the `usermod.h` where you need it.
_2._ Use `#include "usermod_PIR_sensor_switch.h"` at the top of the `usermod.h` where you need it.
**Example usermod.h :**
```cpp
#include "wled.h"
@ -79,25 +80,30 @@ Usermod can be configured via the Usermods settings page.
* `override` - override PIR input when WLED state is changed using UI
* `domoticz-idx` - Domoticz virtual switch ID (used with MQTT `domoticz/in`)
Have fun - @gegu & @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.
2021-11
* Added information about dynamic configuration options
* Added option to temporary enable/disable usermod from WLED UI (Info dialog)
2022-11
* Added compile time option for off timer.
* Added Home Assistant autodiscovery MQTT broadcast.
* Updated info on compiling.
2023-??
* Override option
* Domoticz virtual switch ID (used with MQTT `domoticz/in`)
2024-02
* Added compile time option to expand number of PIR sensors (they are logically ORed) `-D PIR_SENSOR_MAX_SENSORS=3`

View File

@ -1,6 +1,7 @@
{
"name:": "PWM_fan",
"name": "PWM_fan",
"build": {
"libArchive": false,
"extraScript": "setup_deps.py"
}
}

View File

@ -40,6 +40,9 @@ If the fan speed is unlocked, it will revert to temperature controlled speed on
## Change Log
2021-10
* First public release
2022-05
* Added JSON API call to allow changing of speed

View File

@ -1,12 +1,12 @@
from platformio.package.meta import PackageSpec
Import('env')
usermods = env.GetProjectOption("custom_usermods","").split()
libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
# Check for dependencies
if "Temperature" in usermods:
if "Temperature" in libs:
env.Append(CPPDEFINES=[("USERMOD_DALLASTEMPERATURE")])
elif "sht" in usermods:
elif "sht" in libs:
env.Append(CPPDEFINES=[("USERMOD_SHT")])
else:
elif "PWM_fan" in libs: # The script can be run if this module was previously selected
raise RuntimeError("PWM_fan usermod requires Temperature or sht to be enabled")

View File

@ -1,3 +1,4 @@
{
"name:": "RTC"
"name": "RTC",
"build": { "libArchive": false }
}

View File

@ -1,75 +1,17 @@
#include "wled.h"
#include "SN_Photoresistor.h"
//Pin defaults for QuinLed Dig-Uno (A0)
#ifndef PHOTORESISTOR_PIN
#define PHOTORESISTOR_PIN A0
#endif
// the frequency to check photoresistor, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL
#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT
#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000
#endif
// supplied voltage
#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE
#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5
#endif
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0f
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
// only report if difference grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
class Usermod_SN_Photoresistor : public Usermod
{
private:
float referenceVoltage = USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE;
float resistorValue = USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE;
float adcPrecision = USERMOD_SN_PHOTORESISTOR_ADC_PRECISION;
int8_t offset = USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE;
unsigned long readingInterval = USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
uint16_t lastLDRValue = -1000;
// flag set at startup
bool disabled = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _readInterval[];
static const char _referenceVoltage[];
static const char _resistorValue[];
static const char _adcPrecision[];
static const char _offset[];
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
{
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
}
uint16_t getLuminance()
uint16_t Usermod_SN_Photoresistor::getLuminance()
{
// http://forum.arduino.cc/index.php?topic=37555.0
// https://forum.arduino.cc/index.php?topic=185158.0
@ -82,14 +24,13 @@ private:
return uint16_t(lux);
}
public:
void setup()
void Usermod_SN_Photoresistor::setup()
{
// set pinmode
pinMode(PHOTORESISTOR_PIN, INPUT);
}
void loop()
void Usermod_SN_Photoresistor::loop()
{
if (disabled || strip.isUpdating())
return;
@ -125,12 +66,8 @@ public:
#endif
}
uint16_t getLastLDRValue()
{
return lastLDRValue;
}
void addToJsonInfo(JsonObject &root)
void Usermod_SN_Photoresistor::addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull())
@ -151,15 +88,11 @@ public:
lux.add(F(" lux"));
}
uint16_t getId()
{
return USERMOD_ID_SN_PHOTORESISTOR;
}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root)
void Usermod_SN_Photoresistor::addToConfig(JsonObject &root)
{
// we add JSON object.
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
@ -176,7 +109,7 @@ public:
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/
bool readFromConfig(JsonObject &root)
bool Usermod_SN_Photoresistor::readFromConfig(JsonObject &root)
{
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
@ -198,7 +131,7 @@ public:
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Usermod_SN_Photoresistor::_name[] PROGMEM = "Photoresistor";
@ -209,6 +142,5 @@ const char Usermod_SN_Photoresistor::_resistorValue[] PROGMEM = "resistor-value"
const char Usermod_SN_Photoresistor::_adcPrecision[] PROGMEM = "adc-precision";
const char Usermod_SN_Photoresistor::_offset[] PROGMEM = "offset";
static Usermod_SN_Photoresistor sn_photoresistor;
REGISTER_USERMOD(sn_photoresistor);

View File

@ -0,0 +1,90 @@
#pragma once
#include "wled.h"
// the frequency to check photoresistor, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL
#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT
#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000
#endif
// supplied voltage
#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE
#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5
#endif
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0f
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
// only report if difference grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
class Usermod_SN_Photoresistor : public Usermod
{
private:
float referenceVoltage = USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE;
float resistorValue = USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE;
float adcPrecision = USERMOD_SN_PHOTORESISTOR_ADC_PRECISION;
int8_t offset = USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE;
unsigned long readingInterval = USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
uint16_t lastLDRValue = 65535;
// flag set at startup
bool disabled = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _readInterval[];
static const char _referenceVoltage[];
static const char _resistorValue[];
static const char _adcPrecision[];
static const char _offset[];
uint16_t getLuminance();
public:
void setup();
void loop();
uint16_t getLastLDRValue()
{
return lastLDRValue;
}
void addToJsonInfo(JsonObject &root);
uint16_t getId()
{
return USERMOD_ID_SN_PHOTORESISTOR;
}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root);
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/
bool readFromConfig(JsonObject &root);
};

View File

@ -1,3 +1,4 @@
{
"name:": "SN_Photoresistor"
"name": "SN_Photoresistor",
"build": { "libArchive": false }
}

View File

@ -1,14 +0,0 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
#ifdef USERMOD_SN_PHOTORESISTOR
#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h"
#endif
void registerUsermods()
{
#ifdef USERMOD_SN_PHOTORESISTOR
UsermodManager::add(new Usermod_SN_Photoresistor());
#endif
}

View File

@ -1,3 +1,4 @@
{
"name:": "ST7789_display"
"name:": "ST7789_display",
"build": { "libArchive": false }
}

View File

@ -1,7 +1,10 @@
{
"name:": "Si7021_MQTT_HA",
"name": "Si7021_MQTT_HA",
"build": { "libArchive": false },
"dependencies": {
"finitespace/BME280":"3.0.0",
"adafruit/Adafruit Si7021 Library" : "1.5.3"
"adafruit/Adafruit Si7021 Library" : "1.5.3",
"SPI":"*",
"adafruit/Adafruit BusIO": "1.17.1"
}
}

View File

@ -49,18 +49,8 @@ SDA_PIN = 4;
## Software
Add to `build_flags` in platformio.ini:
Add `Si7021_MQTT_HA` to custom_usermods
```
-D USERMOD_SI7021_MQTT_HA
```
Add to `lib_deps` in platformio.ini:
```
adafruit/Adafruit Si7021 Library @ 1.4.0
BME280@~3.0.0
```
# Credits

View File

@ -1,5 +1,5 @@
{
"name:": "Temperature",
"name": "Temperature",
"build": { "libArchive": false},
"dependencies": {
"paulstoffregen/OneWire":"~2.3.8"

View File

@ -36,18 +36,22 @@ All parameters can be configured at runtime via the Usermods settings page, incl
## Change Log
2020-09-12
* Changed to use async non-blocking implementation
* Do not report erroneous low temperatures to MQTT
* Disable plugin if temperature sensor not detected
* Report the number of seconds until the first read in the info screen instead of sensor error
2021-04
* Adaptation for runtime configuration.
2023-05
* Rewrite to conform to newer recommendations.
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error
2024-09
* Update OneWire to version 2.3.8, which includes stickbreaker's and garyd9's ESP32 fixes:
blazoncek's fork is no longer needed

View File

@ -1,3 +1,4 @@
{
"name:": "TetrisAI_v2"
"name": "TetrisAI_v2",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,5 @@
{
"name:": "VL53L0X_gestures",
"name": "VL53L0X_gestures",
"build": { "libArchive": false},
"dependencies": {
"pololu/VL53L0X" : "^1.3.0"

View File

@ -65,11 +65,14 @@ static bool udpSyncConnected = false; // UDP connection status -> true i
// audioreactive variables
#ifdef ARDUINO_ARCH_ESP32
#ifndef SR_AGC // Automatic gain control mode
#define SR_AGC 0 // default mode = off
#endif
static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point
static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier
static float sampleAvg = 0.0f; // Smoothed Average sample - sampleAvg < 1 means "quiet" (simple noise gate)
static float sampleAgc = 0.0f; // Smoothed AGC sample
static uint8_t soundAgc = 0; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value)
static uint8_t soundAgc = SR_AGC; // Automatic gain control: 0 - off, 1 - normal, 2 - vivid, 3 - lazy (config value)
#endif
//static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
@ -869,7 +872,7 @@ class AudioReactive : public Usermod {
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
#ifdef WLED_DISABLE_SOUND
micIn = inoise8(millis(), millis()); // Simulated analog read
micIn = perlin8(millis(), millis()); // Simulated analog read
micDataReal = micIn;
#else
#ifdef ARDUINO_ARCH_ESP32
@ -1736,7 +1739,7 @@ class AudioReactive : public Usermod {
}
void onStateChange(uint8_t callMode) override {
if (initDone && enabled && addPalettes && palettes==0 && strip.customPalettes.size()<10) {
if (initDone && enabled && addPalettes && palettes==0 && customPalettes.size()<10) {
// if palettes were removed during JSON call re-add them
createAudioPalettes();
}
@ -1966,20 +1969,20 @@ class AudioReactive : public Usermod {
void AudioReactive::removeAudioPalettes(void) {
DEBUG_PRINTLN(F("Removing audio palettes."));
while (palettes>0) {
strip.customPalettes.pop_back();
customPalettes.pop_back();
DEBUG_PRINTLN(palettes);
palettes--;
}
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(customPalettes.size());
}
void AudioReactive::createAudioPalettes(void) {
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(customPalettes.size());
if (palettes) return;
DEBUG_PRINTLN(F("Adding audio palettes."));
for (int i=0; i<MAX_PALETTES; i++)
if (strip.customPalettes.size() < 10) {
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
if (customPalettes.size() < 10) {
customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
palettes++;
DEBUG_PRINTLN(palettes);
} else break;
@ -2016,7 +2019,7 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
void AudioReactive::fillAudioPalettes() {
if (!palettes) return;
size_t lastCustPalette = strip.customPalettes.size();
size_t lastCustPalette = customPalettes.size();
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
for (int pal=0; pal<palettes; pal++) {
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
@ -2045,7 +2048,7 @@ void AudioReactive::fillAudioPalettes() {
tcp[14] = rgb.g;
tcp[15] = rgb.b;
strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
}
}

View File

@ -8,15 +8,17 @@ Does audio processing and provides data structure that specially written effects
**does not** provide effects or draw anything to an LED strip/matrix.
## Additional Documentation
This usermod is an evolution of [SR-WLED](https://github.com/atuline/WLED), and a lot of documentation and information can be found in the [SR-WLED wiki](https://github.com/atuline/WLED/wiki):
* [getting started with audio](https://github.com/atuline/WLED/wiki/First-Time-Setup#sound)
* [Sound settings](https://github.com/atuline/WLED/wiki/Sound-Settings) - similar to options on the usemod settings page in WLED.
* [Digital Audio](https://github.com/atuline/WLED/wiki/Digital-Microphone-Hookup)
* [Analog Audio](https://github.com/atuline/WLED/wiki/Analog-Audio-Input-Options)
* [UDP Sound sync](https://github.com/atuline/WLED/wiki/UDP-Sound-Sync)
## Supported MCUs
This audioreactive usermod works best on "classic ESP32" (dual core), and on ESP32-S3 which also has dual core and hardware floating point support.
It will compile successfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
@ -25,6 +27,7 @@ Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP
Currently ESP8266 is not supported, due to low speed and small RAM of this chip.
There are however plans to create a lightweight audioreactive for the 8266, with reduced features.
## Installation
Add 'ADS1115_v2' to `custom_usermods` in your platformio environment.
@ -35,29 +38,32 @@ All parameters are runtime configurable. Some may require a hard reset after cha
If you want to define default GPIOs during compile time, use the following (default values in parentheses):
- `-D SR_DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S (default), 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
- `-D AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
- `-D I2S_SDPIN=x` : GPIO for SD pin on digital microphone (32)
- `-D I2S_WSPIN=x` : GPIO for WS pin on digital microphone (15)
- `-D I2S_CKPIN=x` : GPIO for SCK pin on digital microphone (14)
- `-D MCLK_PIN=x` : GPIO for master clock pin on digital Line-In boards (-1)
- `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
* `-D SR_DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S (default), 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
* `-D AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
* `-D I2S_SDPIN=x` : GPIO for SD pin on digital microphone (32)
* `-D I2S_WSPIN=x` : GPIO for WS pin on digital microphone (15)
* `-D I2S_CKPIN=x` : GPIO for SCK pin on digital microphone (14)
* `-D MCLK_PIN=x` : GPIO for master clock pin on digital Line-In boards (-1)
* `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
* `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
Other options:
- `-D UM_AUDIOREACTIVE_ENABLE` : makes usermod default enabled (not the same as include into build option!)
- `-D UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF` : disables rise/fall limiter default
* `-D UM_AUDIOREACTIVE_ENABLE` : makes usermod default enabled (not the same as include into build option!)
* `-D UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF` : disables rise/fall limiter default
**NOTE** I2S is used for analog audio sampling. Hence, the analog *buttons* (i.e. potentiometers) are disabled when running this usermod with an analog microphone.
### Advanced Compile-Time Options
You can use the following additional flags in your `build_flags`
* `-D SR_SQUELCH=x` : Default "squelch" setting (10)
* `-D SR_GAIN=x` : Default "gain" setting (60)
* `-D SR_AGC=x` : (Only ESP32) Default "AGC (Automatic Gain Control)" setting (0): 0=off, 1=normal, 2=vivid, 3=lazy
* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this).
* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this).
* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM resources (not recommended unless you absolutely need this).
* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this *will* cause conflicts(lock-up) with any analogRead() call.
* `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE)
* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB.

View File

@ -1,3 +1,4 @@
{
"name:": "boblight"
"name": "boblight",
"build": { "libArchive": false }
}

View File

@ -5,16 +5,17 @@
#define USERMOD_ID_BUZZER 900
#ifndef USERMOD_BUZZER_PIN
#ifdef GPIO_NUM_32
#define USERMOD_BUZZER_PIN GPIO_NUM_32
#else
#define USERMOD_BUZZER_PIN 21
#endif
#endif
/*
* Usermods allow you to add own functionality to WLED more easily
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
*
* Using a usermod:
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
*/
class BuzzerUsermod : public Usermod {

View File

@ -1,3 +1,4 @@
{
"name:": "buzzer"
"name": "buzzer",
"build": { "libArchive": false }
}

View File

@ -1,3 +1,4 @@
{
"name:": "deep_sleep"
"name": "deep_sleep",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,5 @@
{
"name:": "mpu6050_imu",
"name": "mpu6050_imu",
"build": { "libArchive": false},
"dependencies": {
"electroniccats/MPU6050":"1.0.1"

View File

@ -1,3 +1,4 @@
{
"name:": "multi_relay"
"name": "multi_relay",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,5 @@
{
"name:": "pixels_dice_tray",
"name": "pixels_dice_tray",
"build": { "libArchive": false},
"dependencies": {
"arduino-pixels-dice":"https://github.com/axlan/arduino-pixels-dice.git",

View File

@ -0,0 +1,31 @@
[platformio]
default_envs = usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3
[env:usermods_esp32]
extends = env:esp32dev_V4
custom_usermods = ${usermods.custom_usermods}
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
[env:usermods_esp32c3]
extends = env:esp32c3dev
board = esp32-c3-devkitm-1
custom_usermods = ${usermods.custom_usermods}
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
[env:usermods_esp32s2]
extends = env:lolin_s2_mini
custom_usermods = ${usermods.custom_usermods}
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
[env:usermods_esp32s3]
extends = env:esp32s3dev_16MB_opi
custom_usermods = ${usermods.custom_usermods}
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
[usermods]
# Added in CI

View File

@ -1,11 +1,11 @@
### Shift Light for Project Cars
# Shift Light for Project Cars
Turn your WLED lights into a rev light and shift indicator for Project Cars.
It's easy to use.
1. Make sure your WLED device and your PC/console are on the same network and can talk to each other
_1._ Make sure your WLED device and your PC/console are on the same network and can talk to each other
2. Go to the gameplay settings menu in PCARS and enable UDP. There are 9 numbers you can choose from. This is the refresh rate. The lower the number, the better. However, you might run into problems at faster rates.
_2._ Go to the gameplay settings menu in PCARS and enable UDP. There are 9 numbers you can choose from. This is the refresh rate. The lower the number, the better. However, you might run into problems at faster rates.
| Number | Updates/Second |
| ------ | -------------- |
@ -19,5 +19,5 @@ It's easy to use.
| 8 | 05 |
| 9 | 1 |
3. Once you enter a race, WLED should automatically shift to PCARS mode.
4. Done.
_3._ Once you enter a race, WLED should automatically shift to PCARS mode.
_4._ Done.

View File

@ -1,3 +1,4 @@
{
"name:": "pwm_outputs"
"name": "pwm_outputs",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,5 @@
{
"name:": "quinled-an-penta",
"name": "quinled-an-penta",
"build": { "libArchive": false},
"dependencies": {
"olikraus/U8g2":"~2.28.8",

View File

@ -1,4 +1,4 @@
### Usermods
# Usermods
This folder serves as a repository for usermods (custom `usermod.cpp` files)!
@ -6,11 +6,11 @@ If you have created a usermod you believe is useful (for example to support a pa
In order for other people to be able to have fun with your usermod, please keep these points in mind:
- Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`)
- Include your custom files
- If your usermod requires changes to other WLED files, please write a `readme.md` outlining the steps one needs to take
- Create a pull request!
- If your feature is useful for the majority of WLED users, I will consider adding it to the base code!
* Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`)
* Include your custom files
* If your usermod requires changes to other WLED files, please write a `readme.md` outlining the steps one needs to take
* Create a pull request!
* If your feature is useful for the majority of WLED users, I will consider adding it to the base code!
While I do my best to not break too much, keep in mind that as WLED is updated, usermods might break.
I am not actively maintaining any usermod in this directory, that is your responsibility as the creator of the usermod.

View File

@ -1,5 +1,5 @@
{
"name:": "rgb-rotary-encoder",
"name": "rgb-rotary-encoder",
"build": { "libArchive": false},
"dependencies": {
"lennarthennigs/ESP Rotary":"^2.1.1"

View File

@ -1,3 +1,4 @@
{
"name:": "sd_card"
"name": "sd_card",
"build": { "libArchive": false }
}

View File

@ -1,5 +1,5 @@
{
"name:": "sensors_to_mqtt",
"name": "sensors_to_mqtt",
"build": { "libArchive": false},
"dependencies": {
"adafruit/Adafruit BMP280 Library":"2.6.8",

View File

@ -1,3 +1,4 @@
{
"name:": "seven_segment_display"
"name": "seven_segment_display",
"build": { "libArchive": false }
}

View File

@ -1,3 +1,7 @@
{
"name:": "seven_segment_display_reloaded"
"name": "seven_segment_display_reloaded",
"build": {
"libArchive": false,
"extraScript": "setup_deps.py"
}
}

View File

@ -0,0 +1,10 @@
from platformio.package.meta import PackageSpec
Import('env')
libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
# Check for partner usermods
if "SN_Photoresistor" in libs:
env.Append(CPPDEFINES=[("USERMOD_SN_PHOTORESISTOR")])
if any(mod in ("BH1750_v2", "BH1750") for mod in libs):
env.Append(CPPDEFINES=[("USERMOD_BH1750")])

View File

@ -1,4 +1,10 @@
#include "wled.h"
#ifdef USERMOD_SN_PHOTORESISTOR
#include "SN_Photoresistor.h"
#endif
#ifdef USERMOD_BH1750
#include "BH1750_v2.h"
#endif
#ifdef WLED_DISABLE_MQTT
#error "This user mod requires MQTT to be enabled."

View File

@ -1,5 +1,6 @@
{
"name:": "sht",
"name": "sht",
"build": { "libArchive": false },
"dependencies": {
"robtillaart/SHT85": "~0.3.3"
}

View File

@ -1,35 +1,43 @@
# SHT
Usermod to support various SHT i2c sensors like the SHT30, SHT31, SHT35 and SHT85
## Requirements
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
* "SHT85" by Rob Tillaart, v0.2 or higher: <https://github.com/RobTillaart/SHT85>
## Usermod installation
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one, add the custom_usermod `sht`.
ESP32:
```
```ini
[env:custom_esp32dev_usermod_sht]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} sht
```
ESP8266:
```
```ini
[env:custom_d1_mini_usermod_sht]
extends = env:d1_mini
custom_usermods = ${env:d1_mini.custom_usermods} sht
```
## MQTT Discovery for Home Assistant
If you're using Home Assistant and want to have the temperature and humidity available as entities in HA, you can tick the "Add-To-Home-Assistant-MQTT-Discovery" option in the usermod settings. If you have an MQTT broker configured under "Sync Settings" and it is connected, the mod will publish the auto discovery message to your broker and HA will instantly find it and create an entity each for the temperature and humidity.
### Publishing readings via MQTT
Regardless of having MQTT discovery ticked or not, the mod will always report temperature and humidity to the WLED MQTT topic of that instance, if you have a broker configured and it's connected.
## Configuration
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D USERMOD_SHT`, you will see the config for it there:
* SHT-Type:
* What it does: Select the SHT sensor type you want to use
* Possible values: SHT30, SHT31, SHT35, SHT85
@ -44,8 +52,11 @@ Navigate to the "Config" and then to the "Usermods" section. If you compiled WLE
* Default: Disabled
## Change log
2022-12
* First implementation.
## Credits
ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG
ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: <https://discord.gg/WdbAauG>

View File

@ -1,3 +1,4 @@
{
"name:": "smartnest"
"name": "smartnest",
"build": { "libArchive": false }
}

View File

@ -1,3 +1,4 @@
{
"name:": "stairway_wipe_basic"
"name": "stairway_wipe_basic",
"build": { "libArchive": false }
}

View File

@ -0,0 +1,4 @@
# Usermod user FX
This Usermod is a common place to put various user's LED effects.

View File

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

View File

@ -0,0 +1,116 @@
#include "wled.h"
// for information how FX metadata strings work see https://kno.wled.ge/interfaces/json-api/#effect-metadata
// static effect, used if an effect fails to initialize
static uint16_t mode_static(void) {
SEGMENT.fill(SEGCOLOR(0));
return strip.isOffRefreshRequired() ? FRAMETIME : 350;
}
/////////////////////////
// User FX functions //
/////////////////////////
// Diffusion Fire: fire effect intended for 2D setups smaller than 16x16
static uint16_t mode_diffusionfire(void) {
if (!strip.isMatrix || !SEGMENT.is2D())
return mode_static(); // not a 2D set-up
const int cols = SEG_W;
const int rows = SEG_H;
const auto XY = [&](int x, int y) { return x + y * cols; };
const uint8_t refresh_hz = map(SEGMENT.speed, 0, 255, 20, 80);
const unsigned refresh_ms = 1000 / refresh_hz;
const int16_t diffusion = map(SEGMENT.custom1, 0, 255, 0, 100);
const uint8_t spark_rate = SEGMENT.intensity;
const uint8_t turbulence = SEGMENT.custom2;
unsigned dataSize = SEGMENT.length(); // allocate persistent data for heat value for each pixel
if (!SEGENV.allocateData(dataSize))
return mode_static(); // allocation failed
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
SEGENV.step = 0;
}
if ((strip.now - SEGENV.step) >= refresh_ms) {
uint8_t tmp_row[cols];
SEGENV.step = strip.now;
// scroll up
for (unsigned y = 1; y < rows; y++)
for (unsigned x = 0; x < cols; x++) {
unsigned src = XY(x, y);
unsigned dst = XY(x, y - 1);
SEGMENT.data[dst] = SEGMENT.data[src];
}
if (hw_random8() > turbulence) {
// create new sparks at bottom row
for (unsigned x = 0; x < cols; x++) {
uint8_t p = hw_random8();
if (p < spark_rate) {
unsigned dst = XY(x, rows - 1);
SEGMENT.data[dst] = 255;
}
}
}
// diffuse
for (unsigned y = 0; y < rows; y++) {
for (unsigned x = 0; x < cols; x++) {
unsigned v = SEGMENT.data[XY(x, y)];
if (x > 0) {
v += SEGMENT.data[XY(x - 1, y)];
}
if (x < (cols - 1)) {
v += SEGMENT.data[XY(x + 1, y)];
}
tmp_row[x] = min(255, (int)(v * 100 / (300 + diffusion)));
}
for (unsigned x = 0; x < cols; x++) {
SEGMENT.data[XY(x, y)] = tmp_row[x];
if (SEGMENT.check1) {
uint32_t color = ColorFromPalette(SEGPALETTE, tmp_row[x], 255, LINEARBLEND_NOWRAP);
SEGMENT.setPixelColorXY(x, y, color);
} else {
uint32_t color = SEGCOLOR(0);
SEGMENT.setPixelColorXY(x, y, color_fade(color, tmp_row[x]));
}
}
}
}
return FRAMETIME;
}
static const char _data_FX_MODE_DIFFUSIONFIRE[] PROGMEM = "Diffusion Fire@!,Spark rate,Diffusion Speed,Turbulence,,Use palette;;Color;;2;pal=35";
/////////////////////
// UserMod Class //
/////////////////////
class UserFxUsermod : public Usermod {
private:
public:
void setup() override {
strip.addEffect(255, &mode_diffusionfire, _data_FX_MODE_DIFFUSIONFIRE);
////////////////////////////////////////
// add your effect function(s) here //
////////////////////////////////////////
// use id=255 for all custom user FX (the final id is assigned when adding the effect)
// strip.addEffect(255, &mode_your_effect, _data_FX_MODE_YOUR_EFFECT);
// strip.addEffect(255, &mode_your_effect2, _data_FX_MODE_YOUR_EFFECT2);
// strip.addEffect(255, &mode_your_effect3, _data_FX_MODE_YOUR_EFFECT3);
}
void loop() override {} // nothing to do in the loop
uint16_t getId() override { return USERMOD_ID_USER_FX; }
};
static UserFxUsermod user_fx;
REGISTER_USERMOD(user_fx);

View File

@ -1,3 +1,4 @@
{
"name:": "usermod_rotary_brightness_color"
"name": "usermod_rotary_brightness_color",
"build": { "libArchive": false }
}

View File

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

View File

@ -1,3 +0,0 @@
{
"name:": "usermod_v2_HttpPullLightControl"
}

View File

@ -5,7 +5,7 @@ The `usermod_v2_HttpPullLightControl` is a custom user module for WLED that enab
## Features
* Configure the URL endpoint (only support HTTP for now, no HTTPS) and polling interval via the WLED user interface.
* All options from the JSON API are supported (since v0.0.3). See: https://kno.wled.ge/interfaces/json-api/
* All options from the JSON API are supported (since v0.0.3). See: [https://kno.wled.ge/interfaces/json-api/](https://kno.wled.ge/interfaces/json-api/)
* The ability to control the brightness of all lights and the state (on/off) and color of individual lights remotely.
* Start or stop an effect and when you run the same effect when its's already running, it won't restart.
* The ability to control all these settings per segment.
@ -13,13 +13,15 @@ The `usermod_v2_HttpPullLightControl` is a custom user module for WLED that enab
* Unique ID generation based on the device's MAC address and a configurable salt value, appended to the request URL for identification.
## Configuration
* Enable the `usermod_v2_HttpPullLightControl` via the WLED user interface.
* Specify the URL endpoint and polling interval.
## JSON Format and examples
* The module sends a GET request to the configured URL, appending a unique identifier as a query parameter: `https://www.example.com/mycustompage.php?id=xxxxxxxx` where xxxxxxx is a 40 character long SHA1 hash of the MAC address combined with a given salt.
* Response Format (since v0.0.3) it is eactly the same as the WLED JSON API, see: https://kno.wled.ge/interfaces/json-api/
* Response Format (since v0.0.3) it is eactly the same as the WLED JSON API, see: [https://kno.wled.ge/interfaces/json-api/](https://kno.wled.ge/interfaces/json-api/)
After getting the URL (it can be a static file like static.json or a mylogic.php which gives a dynamic response), the response is read and parsed to WLED.
* An example of a response to set the individual lights: 0 to RED, 12 to Green and 14 to BLUE. Remember that is will SET lights, you might want to set all the others to black.
@ -58,6 +60,8 @@ After getting the URL (it can be a static file like static.json or a mylogic.php
}`
* Or use the following example to start an effect, but first we UNFREEZE (frz=false) the segment because it was frozen by individual light control in the previous examples (28=Chase effect, Speed=180m Intensity=128). The three color slots are the slots you see under the color wheel and used by the effect. RED, Black, White in this case.
```json
`{
"seg":
{
@ -72,34 +76,35 @@ After getting the URL (it can be a static file like static.json or a mylogic.php
]
}
}`
```
## Installation
1. Add `usermod_v2_HttpPullLightControl` to your WLED project following the instructions provided in the WLED documentation.
2. Compile by setting the build_flag: -D USERMOD_HTTP_PULL_LIGHT_CONTROL and upload to your ESP32/ESP8266!
3. There are several compile options which you can put in your platformio.ini or platformio_override.ini:
- -DUSERMOD_HTTP_PULL_LIGHT_CONTROL ;To Enable the usermod
- -DHTTP_PULL_LIGHT_CONTROL_URL="\"http://mydomain.com/json-response.php\"" ; The URL which will be requested all the time to set the lights/effects
- -DHTTP_PULL_LIGHT_CONTROL_SALT="\"my_very-S3cret_C0de\"" ; A secret SALT which will help by making the ID more safe
- -DHTTP_PULL_LIGHT_CONTROL_INTERVAL=30 ; The interval at which the URL is requested in seconds
- -DHTTP_PULL_LIGHT_CONTROL_HIDE_SALT ; Do you want to Hide the SALT in the User Interface? If yes, Set this flag. Note that the salt can now only be set via the above -DHTTP_PULL_LIGHT_CONTROL_SALT= setting
- -DWLED_AP_SSID="\"Christmas Card\"" ; These flags are not just for my Usermod but you probably want to set them
- -DWLED_AP_PASS="\"christmas\""
- -DWLED_OTA_PASS="\"otapw-secret\""
- -DMDNS_NAME="\"christmascard\""
- -DSERVERNAME="\"CHRISTMASCARD\""
- -D ABL_MILLIAMPS_DEFAULT=450
- -D DEFAULT_LED_COUNT=60 ; For a LED Ring of 60 LEDs
- -D BTNPIN=41 ; The M5Stack Atom S3 Lite has a button on GPIO41
- -D DATA_PINS=2 ; The M5Stack Atom S3 Lite has a Grove connector on the front, we use this GPIO2
- -D STATUSLED=35 ; The M5Stack Atom S3 Lite has a Multi-Color LED on GPIO35, although I didnt managed to control it
- -D IRPIN=4 ; The M5Stack Atom S3 Lite has a IR LED on GPIO4
* -DUSERMOD_HTTP_PULL_LIGHT_CONTROL ;To Enable the usermod
* -DHTTP_PULL_LIGHT_CONTROL_URL="\"`http://mydomain.com/json-response.php`\"" ; The URL which will be requested all the time to set the lights/effects
* -DHTTP_PULL_LIGHT_CONTROL_SALT="\"my_very-S3cret_C0de\"" ; A secret SALT which will help by making the ID more safe
* -DHTTP_PULL_LIGHT_CONTROL_INTERVAL=30 ; The interval at which the URL is requested in seconds
* -DHTTP_PULL_LIGHT_CONTROL_HIDE_SALT ; Do you want to Hide the SALT in the User Interface? If yes, Set this flag. Note that the salt can now only be set via the above -DHTTP_PULL_LIGHT_CONTROL_SALT= setting
- -D DEBUG=1 ; Set these DEBUG flags ONLY if you want to debug and read out Serial (using Visual Studio Code - Serial Monitor)
- -DDEBUG_LEVEL=5
- -DWLED_DEBUG
* -DWLED_AP_SSID="\"Christmas Card\"" ; These flags are not just for my Usermod but you probably want to set them
* -DWLED_AP_PASS="\"christmas\""
* -DWLED_OTA_PASS="\"otapw-secret\""
* -DMDNS_NAME="\"christmascard\""
* -DSERVERNAME="\"CHRISTMASCARD\""
* -D ABL_MILLIAMPS_DEFAULT=450
* -D DEFAULT_LED_COUNT=60 ; For a LED Ring of 60 LEDs
* -D BTNPIN=41 ; The M5Stack Atom S3 Lite has a button on GPIO41
* -D DATA_PINS=2 ; The M5Stack Atom S3 Lite has a Grove connector on the front, we use this GPIO2
* -D STATUSLED=35 ; The M5Stack Atom S3 Lite has a Multi-Color LED on GPIO35, although I didnt managed to control it
* -D IRPIN=4 ; The M5Stack Atom S3 Lite has a IR LED on GPIO4
* -D DEBUG=1 ; Set these DEBUG flags ONLY if you want to debug and read out Serial (using Visual Studio Code - Serial Monitor)
* -DDEBUG_LEVEL=5
* -DWLED_DEBUG
## Use Case: Interactive Christmas Cards

View File

@ -4,6 +4,9 @@
const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl";
const char HttpPullLightControl::_enabled[] PROGMEM = "Enable";
static HttpPullLightControl http_pull_usermod;
REGISTER_USERMOD(http_pull_usermod);
void HttpPullLightControl::setup() {
//Serial.begin(115200);
@ -297,10 +300,10 @@ void HttpPullLightControl::handleResponse(String& responseStr) {
// Check for valid JSON, otherwise we brick the program runtime
if (jsonStr[0] == '{' || jsonStr[0] == '[') {
// Attempt to deserialize the JSON response
DeserializationError error = deserializeJson(doc, jsonStr);
DeserializationError error = deserializeJson(*pDoc, jsonStr);
if (error == DeserializationError::Ok) {
// Get JSON object from th doc
JsonObject obj = doc.as<JsonObject>();
JsonObject obj = pDoc->as<JsonObject>();
// Parse the object throuhg deserializeState (use CALL_MODE_NO_NOTIFY or OR CALL_MODE_DIRECT_CHANGE)
deserializeState(obj, CALL_MODE_NO_NOTIFY);
} else {

View File

@ -1,5 +1,6 @@
{
"name:": "usermod_v2_RF433",
"name": "usermod_v2_RF433",
"build": { "libArchive": false },
"dependencies": {
"sui77/rc-switch":"2.6.4"
}

View File

@ -1,5 +1,6 @@
{
"name": "animartrix",
"build": { "libArchive": false },
"dependencies": {
"Animartrix": "https://github.com/netmindz/animartrix.git#b172586"
}

View File

@ -1,3 +1,4 @@
{
"name": "auto_save"
"name": "auto_save",
"build": { "libArchive": false }
}

View File

@ -2,6 +2,7 @@
v2 Usermod to automatically save settings
to preset number AUTOSAVE_PRESET_NUM after a change to any of:
* brightness
* effect speed
* effect intensity
@ -50,6 +51,9 @@ Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
2021-02
* First public release
2021-04
* Adaptation for runtime configuration.

View File

@ -10,8 +10,8 @@ define `USERMOD_BRIGHTNESS_FOLLOW_SUN` e.g. `#define USERMOD_BRIGHTNESS_FOLLOW_S
or add `-D USERMOD_BRIGHTNESS_FOLLOW_SUN` to `build_flags` in platformio_override.ini
### Options
Open Usermod Settings in WLED to change settings:
`Enable` - When checked `Enable`, turn on the `Brightness Follow Sun` Usermod, which will automatically turn on the lights, adjust the brightness, and turn off the lights. If you need to completely turn off the lights, please unchecked `Enable`.
@ -24,12 +24,12 @@ Open Usermod Settings in WLED to change settings:
`Relax Hour` - The unit is in hours, with an effective range of 0-6. According to the settings, maintain the lowest brightness for 0-6 hours before sunrise and after sunset.
### PlatformIO requirements
No special requirements.
## Change Log
### Change Log
2025-01-02
* init

View File

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

View File

@ -1,5 +1,3 @@
#pragma once
#include "wled.h"
//v2 usermod that allows to change brightness and color using a rotary encoder,
@ -128,3 +126,6 @@ const char UsermodBrightnessFollowSun::_update_interval[] PROGMEM = "Update
const char UsermodBrightnessFollowSun::_min_bri[] PROGMEM = "Min Brightness";
const char UsermodBrightnessFollowSun::_max_bri[] PROGMEM = "Max Brightness";
const char UsermodBrightnessFollowSun::_relax_hour[] PROGMEM = "Relax Hour";
static UsermodBrightnessFollowSun usermod_brightness_follow_sun;
REGISTER_USERMOD(usermod_brightness_follow_sun);

View File

@ -1,7 +1,5 @@
#pragma once
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
#pragma once
/*
Fontname: wled_logo_akemi_4x4

View File

@ -0,0 +1,8 @@
{
"name": "four_line_display_ALT",
"build": { "libArchive": false },
"dependencies": {
"U8g2": "~2.34.4",
"Wire": ""
}
}

View File

@ -1,3 +0,0 @@
{
"name:": "usermod_v2_four_line_display_ALT"
}

View File

@ -1,18 +1,11 @@
[platformio]
default_envs = esp32dev
default_envs = esp32dev_fld
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
[env:esp32dev_fld]
extends = env:esp32dev_V4
custom_usermods = ${env:esp32dev_V4.custom_usermods} four_line_display_ALT
build_flags =
${common.build_flags_esp32}
-D USERMOD_FOUR_LINE_DISPLAY
${env:esp32dev_V4.build_flags}
-D FLD_TYPE=SH1106
-D I2CSCLPIN=27
-D I2CSDAPIN=26
lib_deps =
${esp32.lib_deps}
U8g2@~2.34.4
Wire

View File

@ -5,6 +5,7 @@ This usermod could be used in compination with `usermod_v2_rotary_encoder_ui_ALT
## Functionalities
Press the encoder to cycle through the options:
* Brightness
* Speed
* Intensity
@ -53,7 +54,6 @@ These options are configurable in Config > Usermods
* `showSeconds` - Show seconds on the clock display
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
### PlatformIO requirements
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
@ -61,4 +61,5 @@ Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
2021-10
* First public release

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