mirror of
https://github.com/wled/WLED.git
synced 2025-07-28 13:16:34 +00:00
Merge branch 'wled:main' into main
This commit is contained in:
commit
806163f1ed
27
.github/workflows/pr-merge.yaml
vendored
27
.github/workflows/pr-merge.yaml
vendored
@ -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
71
.github/workflows/usermods.yml
vendored
Normal 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
6
package-lock.json
generated
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
80
pio-scripts/validate_modules.py
Normal file
80
pio-scripts/validate_modules.py
Normal 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'))
|
@ -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
|
||||
|
@ -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
286
tools/wled-tools
Executable 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
|
@ -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"
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "AHT10_v2",
|
||||
"name": "AHT10_v2",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"enjoyneering/AHT10":"~1.1.0"
|
||||
}
|
||||
|
@ -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));
|
||||
// }
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "Analog_Clock"
|
||||
"name": "Analog_Clock",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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.
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "Animated_Staircase"
|
||||
"name": "Animated_Staircase",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -2,244 +2,176 @@
|
||||
#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
|
||||
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
{
|
||||
private:
|
||||
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
}
|
||||
|
||||
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;
|
||||
void Usermod_BH1750::_mqttInitialize()
|
||||
{
|
||||
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
||||
|
||||
// flag set at startup
|
||||
bool enabled = true;
|
||||
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
||||
}
|
||||
|
||||
// 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[];
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void Usermod_BH1750::_createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
bool initDone = false;
|
||||
bool sensorFound = false;
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
// Home Assistant and MQTT
|
||||
String mqttLuminanceTopic;
|
||||
bool mqttInitialized = false;
|
||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
||||
doc[F("name")] = String(serverDescription) + " " + name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
BH1750 lightMeter;
|
||||
float lastLux = -1000;
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
void Usermod_BH1750::setup()
|
||||
{
|
||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
|
||||
sensorFound = lightMeter.begin();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void Usermod_BH1750::loop()
|
||||
{
|
||||
if ((!enabled) || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < minReadingInterval)
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// set up Home Assistant discovery entries
|
||||
void _mqttInitialize()
|
||||
{
|
||||
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
lastLux = lux;
|
||||
lastSend = millis();
|
||||
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
if (!mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
|
||||
DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
void Usermod_BH1750::addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
doc[F("name")] = String(serverDescription) + " " + name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
|
||||
sensorFound = lightMeter.begin();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if ((!enabled) || strip.isUpdating())
|
||||
JsonArray lux_json = user.createNestedArray(F("Luminance"));
|
||||
if (!enabled) {
|
||||
lux_json.add(F("disabled"));
|
||||
} else if (!sensorFound) {
|
||||
// if no sensor
|
||||
lux_json.add(F("BH1750 "));
|
||||
lux_json.add(F("Not Found"));
|
||||
} else if (!getLuminanceComplete) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux_json.add(F(" sec until read"));
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < minReadingInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
lastLux = lux;
|
||||
lastSend = millis();
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
if (!mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
|
||||
DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
}
|
||||
|
||||
inline float getIlluminance() {
|
||||
return (float)lastLux;
|
||||
}
|
||||
// (called from set.cpp) stores persistent properties to cfg.json
|
||||
void Usermod_BH1750::addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
DEBUG_PRINTLN(F("BH1750 config saved."));
|
||||
}
|
||||
|
||||
// called before setup() to populate properties from values stored in cfg.json
|
||||
bool Usermod_BH1750::readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull())
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux_json = user.createNestedArray(F("Luminance"));
|
||||
if (!enabled) {
|
||||
lux_json.add(F("disabled"));
|
||||
} else if (!sensorFound) {
|
||||
// if no sensor
|
||||
lux_json.add(F("BH1750 "));
|
||||
lux_json.add(F("Not Found"));
|
||||
} else if (!getLuminanceComplete) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux_json.add(F(" sec until read"));
|
||||
return;
|
||||
} else {
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
}
|
||||
|
||||
// (called from set.cpp) stores persistent properties to cfg.json
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
DEBUG_PRINTLN(F("BH1750 config saved."));
|
||||
}
|
||||
|
||||
// called before setup() to populate properties from values stored in cfg.json
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull())
|
||||
{
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINT(F("BH1750"));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
DEBUG_PRINT(F("BH1750"));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
return configComplete;
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
return configComplete;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
||||
|
92
usermods/BH1750_v2/BH1750_v2.h
Normal file
92
usermods/BH1750_v2/BH1750_v2.h
Normal 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;
|
||||
}
|
||||
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "BH1750_v2",
|
||||
"name": "BH1750_v2",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"claws/BH1750":"^1.2.0"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "BME280_v2",
|
||||
"name": "BME280_v2",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"finitespace/BME280":"~3.0.0"
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
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 %
|
||||
|
||||
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
|
||||
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.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name:": "BME68X_v2",
|
||||
"build": { "libArchive": false},
|
||||
"name": "BME68X",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"boschsensortec/BSEC Software Library":"^1.8.1492"
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "Battery"
|
||||
"name": "Battery",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "Cronixie"
|
||||
"name": "Cronixie",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "DHT",
|
||||
"name": "DHT",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking"
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"name:": "EXAMPLE",
|
||||
"name": "EXAMPLE",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "EleksTube_IPS",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"TFT_eSPI" : "2.5.33"
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"name:": "Fix_unreachable_netservices_v2",
|
||||
"name": "Fix_unreachable_netservices_v2",
|
||||
"platforms": ["espressif8266"]
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "INA226_v2",
|
||||
"name": "INA226_v2",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"wollewald/INA226_WE":"~1.2.9"
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "Internal_Temperature_v2"
|
||||
"name": "Internal_Temperature_v2",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "LD2410_v2",
|
||||
"name": "LD2410_v2",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"ncmreynolds/ld2410":"^0.1.3"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "LDR_Dusk_Dawn_v2"
|
||||
"name": "LDR_Dusk_Dawn_v2",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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[];
|
||||
|
@ -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"
|
@ -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
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"name:": "MY9291",
|
||||
"name": "MY9291",
|
||||
"build": { "libArchive": false },
|
||||
"platforms": ["espressif8266"]
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "PIR_sensor_switch"
|
||||
"name": "PIR_sensor_switch",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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`
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name:": "PWM_fan",
|
||||
"name": "PWM_fan",
|
||||
"build": {
|
||||
"libArchive": false,
|
||||
"extraScript": "setup_deps.py"
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "RTC"
|
||||
"name": "RTC",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,204 +1,137 @@
|
||||
#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
|
||||
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
{
|
||||
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;
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
|
||||
}
|
||||
|
||||
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;
|
||||
uint16_t Usermod_SN_Photoresistor::getLuminance()
|
||||
{
|
||||
// http://forum.arduino.cc/index.php?topic=37555.0
|
||||
// https://forum.arduino.cc/index.php?topic=185158.0
|
||||
float volts = analogRead(PHOTORESISTOR_PIN) * (referenceVoltage / adcPrecision);
|
||||
float amps = volts / resistorValue;
|
||||
float lux = amps * 1000000 * 2.0;
|
||||
|
||||
// flag set at startup
|
||||
bool disabled = false;
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
return uint16_t(lux);
|
||||
}
|
||||
|
||||
// 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[];
|
||||
void Usermod_SN_Photoresistor::setup()
|
||||
{
|
||||
// set pinmode
|
||||
pinMode(PHOTORESISTOR_PIN, INPUT);
|
||||
}
|
||||
|
||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
void Usermod_SN_Photoresistor::loop()
|
||||
{
|
||||
if (disabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < readingInterval)
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t getLuminance()
|
||||
uint16_t currentLDRValue = getLuminance();
|
||||
if (checkBoundSensor(currentLDRValue, lastLDRValue, offset))
|
||||
{
|
||||
// http://forum.arduino.cc/index.php?topic=37555.0
|
||||
// https://forum.arduino.cc/index.php?topic=185158.0
|
||||
float volts = analogRead(PHOTORESISTOR_PIN) * (referenceVoltage / adcPrecision);
|
||||
float amps = volts / resistorValue;
|
||||
float lux = amps * 1000000 * 2.0;
|
||||
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
return uint16_t(lux);
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
// set pinmode
|
||||
pinMode(PHOTORESISTOR_PIN, INPUT);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (disabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < readingInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t currentLDRValue = getLuminance();
|
||||
if (checkBoundSensor(currentLDRValue, lastLDRValue, offset))
|
||||
{
|
||||
lastLDRValue = currentLDRValue;
|
||||
lastLDRValue = currentLDRValue;
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char subuf[45];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/luminance"));
|
||||
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t getLastLDRValue()
|
||||
{
|
||||
return lastLDRValue;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux = user.createNestedArray(F("Luminance"));
|
||||
|
||||
if (!getLuminanceComplete)
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux.add(F(" sec until read"));
|
||||
return;
|
||||
char subuf[45];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/luminance"));
|
||||
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
|
||||
}
|
||||
|
||||
lux.add(lastLDRValue);
|
||||
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)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = !disabled;
|
||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
||||
top[FPSTR(_referenceVoltage)] = referenceVoltage;
|
||||
top[FPSTR(_resistorValue)] = resistorValue;
|
||||
top[FPSTR(_adcPrecision)] = adcPrecision;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
DEBUG_PRINTLN(F("Photoresistor config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
||||
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
|
||||
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
|
||||
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
|
||||
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
|
||||
offset = top[FPSTR(_offset)] | offset;
|
||||
|
||||
void Usermod_SN_Photoresistor::addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux = user.createNestedArray(F("Luminance"));
|
||||
|
||||
if (!getLuminanceComplete)
|
||||
{
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux.add(F(" sec until read"));
|
||||
return;
|
||||
}
|
||||
|
||||
lux.add(lastLDRValue);
|
||||
lux.add(F(" lux"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void Usermod_SN_Photoresistor::addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = !disabled;
|
||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
||||
top[FPSTR(_referenceVoltage)] = referenceVoltage;
|
||||
top[FPSTR(_resistorValue)] = resistorValue;
|
||||
top[FPSTR(_adcPrecision)] = adcPrecision;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
DEBUG_PRINTLN(F("Photoresistor config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
bool Usermod_SN_Photoresistor::readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
||||
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
|
||||
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
|
||||
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
|
||||
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
|
||||
offset = top[FPSTR(_offset)] | offset;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// 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);
|
90
usermods/SN_Photoresistor/SN_Photoresistor.h
Normal file
90
usermods/SN_Photoresistor/SN_Photoresistor.h
Normal 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);
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "SN_Photoresistor"
|
||||
"name": "SN_Photoresistor",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "ST7789_display"
|
||||
"name:": "ST7789_display",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "Temperature",
|
||||
"name": "Temperature",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"paulstoffregen/OneWire":"~2.3.8"
|
||||
|
@ -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
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "TetrisAI_v2"
|
||||
"name": "TetrisAI_v2",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "VL53L0X_gestures",
|
||||
"name": "VL53L0X_gestures",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"pololu/VL53L0X" : "^1.3.0"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "boblight"
|
||||
"name": "boblight",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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 {
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "buzzer"
|
||||
"name": "buzzer",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "deep_sleep"
|
||||
"name": "deep_sleep",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "mpu6050_imu",
|
||||
"name": "mpu6050_imu",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"electroniccats/MPU6050":"1.0.1"
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "multi_relay"
|
||||
"name": "multi_relay",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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",
|
||||
|
31
usermods/platformio_override.usermods.ini
Normal file
31
usermods/platformio_override.usermods.ini
Normal 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
|
@ -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.
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "pwm_outputs"
|
||||
"name": "pwm_outputs",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "quinled-an-penta",
|
||||
"name": "quinled-an-penta",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"olikraus/U8g2":"~2.28.8",
|
||||
|
@ -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.
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "rgb-rotary-encoder",
|
||||
"name": "rgb-rotary-encoder",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"lennarthennigs/ESP Rotary":"^2.1.1"
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "sd_card"
|
||||
"name": "sd_card",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name:": "sensors_to_mqtt",
|
||||
"name": "sensors_to_mqtt",
|
||||
"build": { "libArchive": false},
|
||||
"dependencies": {
|
||||
"adafruit/Adafruit BMP280 Library":"2.6.8",
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "seven_segment_display"
|
||||
"name": "seven_segment_display",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
{
|
||||
"name:": "seven_segment_display_reloaded"
|
||||
"name": "seven_segment_display_reloaded",
|
||||
"build": {
|
||||
"libArchive": false,
|
||||
"extraScript": "setup_deps.py"
|
||||
}
|
||||
}
|
10
usermods/seven_segment_display_reloaded/setup_deps.py
Normal file
10
usermods/seven_segment_display_reloaded/setup_deps.py
Normal 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")])
|
@ -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."
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "sht",
|
||||
"name": "sht",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"robtillaart/SHT85": "~0.3.3"
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "smartnest"
|
||||
"name": "smartnest",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "stairway_wipe_basic"
|
||||
"name": "stairway_wipe_basic",
|
||||
"build": { "libArchive": false }
|
||||
}
|
4
usermods/user_fx/README.md
Normal file
4
usermods/user_fx/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Usermod user FX
|
||||
|
||||
This Usermod is a common place to put various user's LED effects.
|
||||
|
4
usermods/user_fx/library.json
Normal file
4
usermods/user_fx/library.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "user_fx",
|
||||
"build": { "libArchive": false }
|
||||
}
|
116
usermods/user_fx/user_fx.cpp
Normal file
116
usermods/user_fx/user_fx.cpp
Normal 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);
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name:": "usermod_rotary_brightness_color"
|
||||
"name": "usermod_rotary_brightness_color",
|
||||
"build": { "libArchive": false }
|
||||
}
|
4
usermods/usermod_v2_HttpPullLightControl/library.json
Normal file
4
usermods/usermod_v2_HttpPullLightControl/library.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "usermod_v2_HttpPullLightControl",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"name:": "usermod_v2_HttpPullLightControl"
|
||||
}
|
@ -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,48 +60,51 @@ 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":
|
||||
{
|
||||
"frz": false,
|
||||
"fx": 28,
|
||||
"sx": 200,
|
||||
"ix": 128,
|
||||
"col": [
|
||||
"FF0000",
|
||||
"000000",
|
||||
"FFFFFF"
|
||||
]
|
||||
}
|
||||
"frz": false,
|
||||
"fx": 28,
|
||||
"sx": 200,
|
||||
"ix": 128,
|
||||
"col": [
|
||||
"FF0000",
|
||||
"000000",
|
||||
"FFFFFF"
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name:": "usermod_v2_RF433",
|
||||
"name": "usermod_v2_RF433",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"sui77/rc-switch":"2.6.4"
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "animartrix",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"Animartrix": "https://github.com/netmindz/animartrix.git#b172586"
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"name": "auto_save"
|
||||
"name": "auto_save",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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.
|
||||
|
@ -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
|
||||
|
4
usermods/usermod_v2_brightness_follow_sun/library.json
Normal file
4
usermods/usermod_v2_brightness_follow_sun/library.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "brightness_follow_sun",
|
||||
"build": { "libArchive": false }
|
||||
}
|
@ -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);
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Fontname: wled_logo_akemi_4x4
|
8
usermods/usermod_v2_four_line_display_ALT/library.json
Normal file
8
usermods/usermod_v2_four_line_display_ALT/library.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "four_line_display_ALT",
|
||||
"build": { "libArchive": false },
|
||||
"dependencies": {
|
||||
"U8g2": "~2.34.4",
|
||||
"Wire": ""
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"name:": "usermod_v2_four_line_display_ALT"
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
@ -35,15 +36,15 @@ These options are configurable in Config > Usermods
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `type` - display type in numeric format
|
||||
* 1 = I2C SSD1306 128x32
|
||||
* 2 = I2C SH1106 128x32
|
||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||
* 4 = I2C SSD1305 128x32
|
||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||
* 6 = SPI SSD1306 128x32
|
||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||
* 8 = SPI SSD1309 128x64 (4 double-height lines)
|
||||
* 9 = I2C SSD1309 128x64 (4 double-height lines)
|
||||
* 1 = I2C SSD1306 128x32
|
||||
* 2 = I2C SH1106 128x32
|
||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||
* 4 = I2C SSD1305 128x32
|
||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||
* 6 = SPI SSD1306 128x32
|
||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||
* 8 = SPI SSD1309 128x64 (4 double-height lines)
|
||||
* 9 = I2C SSD1309 128x64 (4 double-height lines)
|
||||
* `pin` - GPIO pins used for display; SPI displays can use SCK, MOSI, CS, DC & RST
|
||||
* `flip` - flip/rotate display 180°
|
||||
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user