mirror of
https://github.com/wled/WLED.git
synced 2025-07-15 06:46:34 +00:00
Merge branch 'main' into usermod-libs-matrix
This commit is contained in:
commit
7835550f1a
24
.github/workflows/pr-merge.yaml
vendored
24
.github/workflows/pr-merge.yaml
vendored
@ -1,5 +1,6 @@
|
|||||||
name: Notify Discord on PR Merge
|
name: Notify Discord on PR Merge
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
|
|
||||||
@ -7,10 +8,29 @@
|
|||||||
notify:
|
notify:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Get User Permission
|
||||||
|
id: checkAccess
|
||||||
|
uses: actions-cool/check-user-permission@v2
|
||||||
|
with:
|
||||||
|
require: write
|
||||||
|
username: ${{ github.triggering_actor }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Check User Permission
|
||||||
|
if: steps.checkAccess.outputs.require-result == 'false'
|
||||||
|
run: |
|
||||||
|
echo "${{ github.triggering_actor }} does not have permissions on this repo."
|
||||||
|
echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}"
|
||||||
|
echo "Job originally triggered by ${{ github.actor }}"
|
||||||
|
exit 1
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }} # This is dangerous without the first access check
|
||||||
- name: Send Discord notification
|
- name: Send Discord notification
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
DISCORD_WEBHOOK_BETA_TESTERS: ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
DISCORD_WEBHOOK_BETA_TESTERS: ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
||||||
if: github.event.pull_request.merged == true
|
# if: github.event.pull_request.merged == true
|
||||||
run: |
|
run: |
|
||||||
curl -H "Content-Type: application/json" -d '{"content": "Pull Request #{{ github.event.pull_request.number }} merged by {{ github.actor }}"}' $DISCORD_WEBHOOK_BETA_TESTERS
|
curl -H "Content-Type: application/json" -d '{"content": "Pull Request ${{ github.event.pull_request.number }} merged by ${{ github.actor }}"}' $DISCORD_WEBHOOK_BETA_TESTERS
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -129,9 +129,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
Import('env')
|
Import('env')
|
||||||
import os.path
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from pathlib import Path # For OS-agnostic path manipulation
|
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"
|
usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
|
||||||
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
|
|
||||||
|
|
||||||
if env['PIOENV'] == "usermods":
|
# Utility functions
|
||||||
# Add all usermods
|
def find_usermod(mod: str) -> Path:
|
||||||
env.GetProjectConfig().set(f"env:usermods", 'custom_usermods', " ".join([f.name for f in all_usermods]))
|
|
||||||
|
|
||||||
def find_usermod(mod: str):
|
|
||||||
"""Locate this library in the usermods folder.
|
"""Locate this library in the usermods folder.
|
||||||
We do this to avoid needing to rename a bunch of folders;
|
We do this to avoid needing to rename a bunch of folders;
|
||||||
this could be removed later
|
this could be removed later
|
||||||
@ -22,51 +19,36 @@ def find_usermod(mod: str):
|
|||||||
return mp
|
return mp
|
||||||
mp = usermod_dir / f"{mod}_v2"
|
mp = usermod_dir / f"{mod}_v2"
|
||||||
if mp.exists():
|
if mp.exists():
|
||||||
return mp
|
return mp
|
||||||
mp = usermod_dir / f"usermod_v2_{mod}"
|
mp = usermod_dir / f"usermod_v2_{mod}"
|
||||||
if mp.exists():
|
if mp.exists():
|
||||||
return mp
|
return mp
|
||||||
raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
|
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","")
|
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:
|
if usermods:
|
||||||
# Inject usermods in to project lib_deps
|
# Inject usermods in to project lib_deps
|
||||||
proj = env.GetProjectConfig()
|
symlinks = [f"symlink://{find_usermod(mod).resolve()}" for mod in usermods]
|
||||||
deps = env.GetProjectOption('lib_deps')
|
env.GetProjectConfig().set("env:" + env['PIOENV'], 'lib_deps', env.GetProjectOption('lib_deps') + symlinks)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
# Utility function for assembling usermod include paths
|
# Utility function for assembling usermod include paths
|
||||||
def cached_add_includes(dep, dep_cache: set, includes: deque):
|
def cached_add_includes(dep, dep_cache: set, includes: deque):
|
||||||
""" Add dep's include paths to includes if it's not in the cache """
|
""" Add dep's include paths to includes if it's not in the cache """
|
||||||
if dep not in dep_cache:
|
if dep not in dep_cache:
|
||||||
dep_cache.add(dep)
|
dep_cache.add(dep)
|
||||||
for include in dep.get_include_dirs():
|
for include in dep.get_include_dirs():
|
||||||
if include not in includes:
|
if include not in includes:
|
||||||
@ -82,13 +64,6 @@ old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
|
|||||||
|
|
||||||
# Our new wrapper
|
# Our new wrapper
|
||||||
def wrapped_ConfigureProjectLibBuilder(xenv):
|
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
|
# Call the wrapped function
|
||||||
result = old_ConfigureProjectLibBuilder.clone(xenv)()
|
result = old_ConfigureProjectLibBuilder.clone(xenv)()
|
||||||
|
|
||||||
@ -102,12 +77,25 @@ def wrapped_ConfigureProjectLibBuilder(xenv):
|
|||||||
for dep in result.depbuilders:
|
for dep in result.depbuilders:
|
||||||
cached_add_includes(dep, processed_deps, extra_include_dirs)
|
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]:
|
broken_usermods = []
|
||||||
# Add the wled folder to the include path
|
for dep in result.depbuilders:
|
||||||
um.env.PrependUnique(CPPPATH=wled_dir)
|
if is_wled_module(dep):
|
||||||
# Add WLED's own dependencies
|
# Add the wled folder to the include path
|
||||||
for dir in extra_include_dirs:
|
dep.env.PrependUnique(CPPPATH=str(wled_dir))
|
||||||
um.env.PrependUnique(CPPPATH=dir)
|
# Add WLED's own dependencies
|
||||||
|
for dir in extra_include_dirs:
|
||||||
|
dep.env.PrependUnique(CPPPATH=str(dir))
|
||||||
|
# Enforce that libArchive is not set; we must link them directly to the executable
|
||||||
|
if dep.lib_archive:
|
||||||
|
broken_usermods.append(dep)
|
||||||
|
|
||||||
|
if broken_usermods:
|
||||||
|
broken_usermods = [usermod.name for usermod in broken_usermods]
|
||||||
|
secho(
|
||||||
|
f"ERROR: libArchive=false is missing on usermod(s) {' '.join(broken_usermods)} -- modules will not compile in correctly",
|
||||||
|
fg="red",
|
||||||
|
err=True)
|
||||||
|
Exit(1)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
92
pio-scripts/validate_modules.py
Normal file
92
pio-scripts/validate_modules.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
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 source directories
|
||||||
|
module_lib_builders = [builder for builder in env.GetLibBuilders() if is_wled_module(env, builder)]
|
||||||
|
|
||||||
|
if env.GetProjectOption("custom_usermods","") == "*":
|
||||||
|
# All usermods build; filter non-platform-OK modules
|
||||||
|
module_lib_builders = [builder for builder in module_lib_builders if env.IsCompatibleLibBuilder(builder)]
|
||||||
|
else:
|
||||||
|
incompatible_builders = [builder for builder in module_lib_builders if not env.IsCompatibleLibBuilder(builder)]
|
||||||
|
if incompatible_builders:
|
||||||
|
secho(
|
||||||
|
f"ERROR: Modules {[b.name for b in incompatible_builders]} are not compatible with this platform!",
|
||||||
|
fg="red",
|
||||||
|
err=True)
|
||||||
|
Exit(1)
|
||||||
|
|
||||||
|
# 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/user_config_copy.py
|
||||||
pre:pio-scripts/load_usermods.py
|
pre:pio-scripts/load_usermods.py
|
||||||
pre:pio-scripts/build_ui.py
|
pre:pio-scripts/build_ui.py
|
||||||
|
post:pio-scripts/validate_modules.py ;; double-check the build output usermods
|
||||||
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
|
; 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}
|
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
board_build.flash_mode = dio
|
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
|
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
|
||||||
|
@ -38,7 +38,7 @@ pyelftools==0.32
|
|||||||
# via platformio
|
# via platformio
|
||||||
pyserial==3.5
|
pyserial==3.5
|
||||||
# via platformio
|
# via platformio
|
||||||
requests==2.32.3
|
requests==2.32.4
|
||||||
# via platformio
|
# via platformio
|
||||||
semantic-version==2.10.0
|
semantic-version==2.10.0
|
||||||
# via platformio
|
# via platformio
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "ADS1115_v2",
|
"name": "ADS1115_v2",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
|
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
|
||||||
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"
|
"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": {
|
"dependencies": {
|
||||||
"enjoyneering/AHT10":"~1.1.0"
|
"enjoyneering/AHT10":"~1.1.0"
|
||||||
}
|
}
|
||||||
|
@ -102,9 +102,9 @@ private:
|
|||||||
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
|
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
|
||||||
uint32_t ms = time.ms % 1000;
|
uint32_t ms = time.ms % 1000;
|
||||||
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
|
uint8_t b0 = (cos8_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;
|
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) {
|
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) {
|
// for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
|
||||||
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
|
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
|
||||||
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
|
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
|
||||||
// setPixelColor(trailLed, 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
|
# Usermod Animated Staircase
|
||||||
|
|
||||||
This usermod makes your staircase look cool by illuminating it with an animation. It uses
|
This usermod makes your staircase look cool by illuminating it with an animation. It uses
|
||||||
PIR or ultrasonic sensors at the top and bottom of your stairs to:
|
PIR or ultrasonic sensors at the top and bottom of your stairs to:
|
||||||
|
|
||||||
@ -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.
|
speed, on/off time and distance by sending an HTTP request, see below.
|
||||||
|
|
||||||
## WLED integration
|
## WLED integration
|
||||||
|
|
||||||
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
|
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
|
||||||
|
|
||||||
Before compiling, you have to make the following modifications:
|
Before compiling, you have to make the following modifications:
|
||||||
|
|
||||||
Edit your environment in `platformio_override.ini`
|
Edit your environment in `platformio_override.ini`
|
||||||
|
|
||||||
1. Open `platformio_override.ini`
|
1. Open `platformio_override.ini`
|
||||||
2. add `Animated_Staircase` to the `custom_usermods` line for your environment
|
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).
|
Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below).
|
||||||
|
|
||||||
## Hardware installation
|
## Hardware installation
|
||||||
|
|
||||||
1. Attach the LED strip to each step of the stairs.
|
1. Attach the LED strip to each step of the stairs.
|
||||||
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step.
|
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step.
|
||||||
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the
|
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.
|
||||||
next step, creating one large virtual LED strip.
|
|
||||||
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
|
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
|
||||||
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
|
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
|
||||||
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
|
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
|
||||||
@ -37,24 +40,23 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for
|
|||||||
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
|
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
|
||||||
|
|
||||||
## WLED configuration
|
## WLED configuration
|
||||||
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
|
|
||||||
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.
|
2. Save your segments into a preset.
|
||||||
3. Ideally, add the preset in the config > LED setup menu to the "apply
|
3. Ideally, add the preset in the config > LED setup menu to the "apply preset **n** at boot" setting.
|
||||||
preset **n** at boot" setting.
|
|
||||||
|
|
||||||
## Changing behavior through API
|
## Changing behavior through API
|
||||||
|
|
||||||
The Staircase settings can be changed through the WLED JSON api.
|
The Staircase settings can be changed through the WLED JSON api.
|
||||||
|
|
||||||
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
|
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
|
||||||
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
|
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
|
||||||
or remove them and put everything on one line.
|
or remove them and put everything on one line.
|
||||||
|
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting | Description | Default |
|
||||||
|------------------|---------------------------------------------------------------|---------|
|
|------------------|---------------------------------------------------------------|---------|
|
||||||
| enabled | Enable or disable the usermod | true |
|
| enabled | Enable or disable the usermod | true |
|
||||||
| bottom-sensor | Manually trigger a down to up animation via API | false |
|
| bottom-sensor | Manually trigger a down to up animation via API | false |
|
||||||
| top-sensor | Manually trigger an up to down animation via API | false |
|
| top-sensor | Manually trigger an up to down animation via API | false |
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +76,7 @@ The staircase settings and sensor states are inside the WLED "state" element:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Enable/disable the usermod
|
### Enable/disable the usermod
|
||||||
|
|
||||||
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
|
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
|
||||||
activity. This enables you to play with the lights without the usermod switching them on or off.
|
activity. This enables you to play with the lights without the usermod switching them on or off.
|
||||||
|
|
||||||
@ -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.
|
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
|
||||||
|
|
||||||
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
|
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
|
||||||
|
|
||||||
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
|
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
|
||||||
|
|
||||||
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
|
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
|
||||||
@ -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.
|
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
|
||||||
|
|
||||||
### Animation triggering through the API
|
### Animation triggering through the API
|
||||||
|
|
||||||
In addition to activation by one of the stair sensors, you can also trigger the animation manually
|
In addition to activation by one of the stair sensors, you can also trigger the animation manually
|
||||||
via the API. To simulate triggering the bottom sensor, use:
|
via the API. To simulate triggering the bottom sensor, use:
|
||||||
|
|
||||||
@ -115,15 +120,19 @@ curl -X POST -H "Content-Type: application/json" \
|
|||||||
-d '{"staircase":{"top-sensor":true}}' \
|
-d '{"staircase":{"top-sensor":true}}' \
|
||||||
xxx.xxx.xxx.xxx/json/state
|
xxx.xxx.xxx.xxx/json/state
|
||||||
```
|
```
|
||||||
|
|
||||||
**MQTT**
|
**MQTT**
|
||||||
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
|
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
|
||||||
You can also use `on` or `off` for enabling or disabling the usermod.
|
You can also use `on` or `off` for enabling or disabling the usermod.
|
||||||
|
|
||||||
Have fun with this usermod.<br/>
|
Have fun with this usermod
|
||||||
www.rolfje.com
|
|
||||||
|
`www.rolfje.com`
|
||||||
|
|
||||||
Modifications @blazoncek
|
Modifications @blazoncek
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
2021-04
|
2021-04
|
||||||
* Adaptation for runtime configuration.
|
|
||||||
|
- Adaptation for runtime configuration.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name:": "Animated_Staircase"
|
"name": "Animated_Staircase",
|
||||||
|
"build": { "libArchive": false }
|
||||||
}
|
}
|
@ -2,244 +2,176 @@
|
|||||||
#warning **** Included USERMOD_BH1750 ****
|
#warning **** Included USERMOD_BH1750 ****
|
||||||
|
|
||||||
#include "wled.h"
|
#include "wled.h"
|
||||||
#include <BH1750.h>
|
#include "BH1750_v2.h"
|
||||||
|
|
||||||
#ifdef WLED_DISABLE_MQTT
|
#ifdef WLED_DISABLE_MQTT
|
||||||
#error "This user mod requires MQTT to be enabled."
|
#error "This user mod requires MQTT to be enabled."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// the max frequency to check photoresistor, 10 seconds
|
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||||
#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:
|
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||||
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
|
}
|
||||||
|
|
||||||
unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
|
void Usermod_BH1750::_mqttInitialize()
|
||||||
unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
|
{
|
||||||
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
||||||
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
|
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
||||||
bool enabled = true;
|
}
|
||||||
|
|
||||||
// strings to reduce flash memory usage (used more than twice)
|
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||||
static const char _name[];
|
void Usermod_BH1750::_createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||||
static const char _enabled[];
|
{
|
||||||
static const char _maxReadInterval[];
|
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||||
static const char _minReadInterval[];
|
|
||||||
static const char _offset[];
|
|
||||||
static const char _HomeAssistantDiscovery[];
|
|
||||||
|
|
||||||
bool initDone = false;
|
|
||||||
bool sensorFound = false;
|
|
||||||
|
|
||||||
// Home Assistant and MQTT
|
|
||||||
String mqttLuminanceTopic;
|
|
||||||
bool mqttInitialized = false;
|
|
||||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
|
||||||
|
|
||||||
BH1750 lightMeter;
|
|
||||||
float lastLux = -1000;
|
|
||||||
|
|
||||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
|
||||||
{
|
|
||||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up Home Assistant discovery entries
|
StaticJsonDocument<600> doc;
|
||||||
void _mqttInitialize()
|
|
||||||
{
|
doc[F("name")] = String(serverDescription) + " " + name;
|
||||||
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
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;
|
||||||
|
|
||||||
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||||
|
device[F("name")] = serverDescription;
|
||||||
|
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||||
|
device[F("manufacturer")] = F(WLED_BRAND);
|
||||||
|
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||||
|
device[F("sw_version")] = versionString;
|
||||||
|
|
||||||
|
String temp;
|
||||||
|
serializeJson(doc, temp);
|
||||||
|
DEBUG_PRINTLN(t);
|
||||||
|
DEBUG_PRINTLN(temp);
|
||||||
|
|
||||||
|
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Usermod_BH1750::setup()
|
||||||
|
{
|
||||||
|
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
|
||||||
|
sensorFound = lightMeter.begin();
|
||||||
|
initDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Usermod_BH1750::loop()
|
||||||
|
{
|
||||||
|
if ((!enabled) || strip.isUpdating())
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
// check to see if we are due for taking a measurement
|
||||||
|
// lastMeasurement will not be updated until the conversion
|
||||||
|
// is complete the the reading is finished
|
||||||
|
if (now - lastMeasurement < minReadingInterval)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
|
||||||
|
float lux = lightMeter.readLightLevel();
|
||||||
|
lastMeasurement = millis();
|
||||||
|
getLuminanceComplete = true;
|
||||||
|
|
||||||
|
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||||
{
|
{
|
||||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
lastLux = lux;
|
||||||
|
lastSend = millis();
|
||||||
StaticJsonDocument<600> doc;
|
|
||||||
|
|
||||||
doc[F("name")] = String(serverDescription) + " " + name;
|
|
||||||
doc[F("state_topic")] = topic;
|
|
||||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
|
||||||
if (unitOfMeasurement != "")
|
|
||||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
|
||||||
if (deviceClass != "")
|
|
||||||
doc[F("device_class")] = deviceClass;
|
|
||||||
doc[F("expire_after")] = 1800;
|
|
||||||
|
|
||||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
if (WLED_MQTT_CONNECTED)
|
||||||
device[F("name")] = serverDescription;
|
{
|
||||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
if (!mqttInitialized)
|
||||||
device[F("manufacturer")] = F(WLED_BRAND);
|
{
|
||||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
_mqttInitialize();
|
||||||
device[F("sw_version")] = versionString;
|
mqttInitialized = true;
|
||||||
|
}
|
||||||
String temp;
|
mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
|
||||||
serializeJson(doc, temp);
|
DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
|
||||||
DEBUG_PRINTLN(t);
|
}
|
||||||
DEBUG_PRINTLN(temp);
|
else
|
||||||
|
{
|
||||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
|
|
||||||
sensorFound = lightMeter.begin();
|
|
||||||
initDone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
void Usermod_BH1750::addToJsonInfo(JsonObject &root)
|
||||||
{
|
{
|
||||||
if ((!enabled) || strip.isUpdating())
|
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;
|
return;
|
||||||
|
} else {
|
||||||
unsigned long now = millis();
|
lux_json.add(lastLux);
|
||||||
|
lux_json.add(F(" lx"));
|
||||||
// check to see if we are due for taking a measurement
|
|
||||||
// lastMeasurement will not be updated until the conversion
|
|
||||||
// is complete the the reading is finished
|
|
||||||
if (now - lastMeasurement < minReadingInterval)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
|
||||||
|
|
||||||
float lux = lightMeter.readLightLevel();
|
|
||||||
lastMeasurement = millis();
|
|
||||||
getLuminanceComplete = true;
|
|
||||||
|
|
||||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
|
||||||
{
|
|
||||||
lastLux = lux;
|
|
||||||
lastSend = millis();
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
|
||||||
if (WLED_MQTT_CONNECTED)
|
|
||||||
{
|
|
||||||
if (!mqttInitialized)
|
|
||||||
{
|
|
||||||
_mqttInitialize();
|
|
||||||
mqttInitialized = true;
|
|
||||||
}
|
|
||||||
mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
|
|
||||||
DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline float getIlluminance() {
|
// (called from set.cpp) stores persistent properties to cfg.json
|
||||||
return (float)lastLux;
|
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));
|
DEBUG_PRINT(FPSTR(_name));
|
||||||
if (!initDone) {
|
DEBUG_PRINT(F("BH1750"));
|
||||||
DEBUG_PRINTLN(F(" config loaded."));
|
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||||
} else {
|
return false;
|
||||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
}
|
||||||
}
|
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 configComplete;
|
||||||
{
|
|
||||||
return USERMOD_ID_BH1750;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// strings to reduce flash memory usage (used more than twice)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
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": {
|
"dependencies": {
|
||||||
"claws/BH1750":"^1.2.0"
|
"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.
|
The luminance is displayed in both the Info section of the web UI, as well as published to the `/luminance` MQTT topic if enabled.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- Libraries
|
- Libraries
|
||||||
- `claws/BH1750 @^1.2.0`
|
- `claws/BH1750 @^1.2.0`
|
||||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||||
@ -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`)
|
To enable, compile with `BH1750` in `custom_usermods` (e.g. in `platformio_override.ini`)
|
||||||
|
|
||||||
### Configuration Options
|
### Configuration Options
|
||||||
|
|
||||||
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
|
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
|
||||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
|
||||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
- `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
- `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
|
- `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:
|
In addition, the Usermod screen allows you to:
|
||||||
|
|
||||||
- enable/disable the usermod
|
- enable/disable the usermod
|
||||||
- Enable Home Assistant Discovery of usermod
|
- Enable Home Assistant Discovery of usermod
|
||||||
- Configure the SCL/SDA pins
|
- Configure the SCL/SDA pins
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
The following method is available to interact with the usermod from other code modules:
|
The following method is available to interact with the usermod from other code modules:
|
||||||
|
|
||||||
- `getIlluminance` read the brightness from the sensor
|
- `getIlluminance` read the brightness from the sensor
|
||||||
|
|
||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
Jul 2022
|
Jul 2022
|
||||||
|
|
||||||
- Added Home Assistant Discovery
|
- Added Home Assistant Discovery
|
||||||
- Implemented PinManager to register pins
|
- Implemented PinManager to register pins
|
||||||
- Made pins configurable in usermod menu
|
- Made pins configurable in usermod menu
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "BME280_v2",
|
"name": "BME280_v2",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"finitespace/BME280":"~3.0.0"
|
"finitespace/BME280":"~3.0.0"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @file usermod_BMW68X.h
|
* @file usermod_BMW68X.cpp
|
||||||
* @author Gabriel A. Sieben (GeoGab)
|
* @author Gabriel A. Sieben (GeoGab)
|
||||||
* @brief Usermod for WLED to implement the BME680/BME688 sensor
|
* @brief Usermod for WLED to implement the BME680/BME688 sensor
|
||||||
* @version 1.0.2
|
* @version 1.0.2
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "BME68X",
|
"name": "BME68X",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boschsensortec/BSEC Software Library":"^1.8.1492"
|
"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)
|
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++) {
|
for (uint16_t j=o; j< o+10; j++) {
|
||||||
if (j != excl) strip.setPixelColor(j, col);
|
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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking"
|
"DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name:": "EXAMPLE",
|
"name": "EXAMPLE",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "EleksTube_IPS",
|
"name:": "EleksTube_IPS",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"TFT_eSPI" : "2.5.33"
|
"TFT_eSPI" : "2.5.33"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name:": "Fix_unreachable_netservices_v2",
|
"name": "Fix_unreachable_netservices_v2",
|
||||||
"platforms": ["espressif8266"]
|
"platforms": ["espressif8266"]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "INA226_v2",
|
"name": "INA226_v2",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wollewald/INA226_WE":"~1.2.9"
|
"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": {
|
"dependencies": {
|
||||||
"ncmreynolds/ld2410":"^0.1.3"
|
"ncmreynolds/ld2410":"^0.1.3"
|
||||||
}
|
}
|
||||||
|
@ -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);
|
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
|
unsigned VoltageDecimals = 3; // Number of decimal places in published voltage values
|
||||||
uint8_t PercentDecimals = 1; // Number of decimal places in published percent values
|
unsigned PercentDecimals = 1; // Number of decimal places in published percent values
|
||||||
|
|
||||||
// string that are used multiple time (this will save some flash memory)
|
// string that are used multiple time (this will save some flash memory)
|
||||||
static const char _name[];
|
static const char _name[];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name:": "MAX17048_v2",
|
"name": "MAX17048_v2",
|
||||||
"build": { "libArchive": false},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Adafruit_MAX1704X":"https://github.com/adafruit/Adafruit_MAX1704X#1.0.2"
|
"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
|
## 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.
|
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||||
|
|
||||||
## Compilation
|
## 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:
|
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
|
```ini
|
||||||
[env:usermod_max17048_d1_mini]
|
[env:usermod_max17048_d1_mini]
|
||||||
extends = env:d1_mini
|
extends = env:d1_mini
|
||||||
build_flags =
|
custom_usermods = ${env:d1_mini.custom_usermods} MAX17048_v2
|
||||||
${common.build_flags_esp8266}
|
|
||||||
-D USERMOD_MAX17048
|
|
||||||
lib_deps =
|
|
||||||
${esp8266.lib_deps}
|
|
||||||
https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
|
|
||||||
https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration Options
|
### Configuration Options
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name:": "MY9291",
|
"name": "MY9291",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"platforms": ["espressif8266"]
|
"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`.
|
**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.
|
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
|
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).
|
(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
|
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 :**
|
**Example usermod.h :**
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "wled.h"
|
#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
|
* `override` - override PIR input when WLED state is changed using UI
|
||||||
* `domoticz-idx` - Domoticz virtual switch ID (used with MQTT `domoticz/in`)
|
* `domoticz-idx` - Domoticz virtual switch ID (used with MQTT `domoticz/in`)
|
||||||
|
|
||||||
|
|
||||||
Have fun - @gegu & @blazoncek
|
Have fun - @gegu & @blazoncek
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
2021-04
|
2021-04
|
||||||
|
|
||||||
* Adaptation for runtime configuration.
|
* Adaptation for runtime configuration.
|
||||||
|
|
||||||
2021-11
|
2021-11
|
||||||
|
|
||||||
* Added information about dynamic configuration options
|
* Added information about dynamic configuration options
|
||||||
* Added option to temporary enable/disable usermod from WLED UI (Info dialog)
|
* Added option to temporary enable/disable usermod from WLED UI (Info dialog)
|
||||||
|
|
||||||
2022-11
|
2022-11
|
||||||
|
|
||||||
* Added compile time option for off timer.
|
* Added compile time option for off timer.
|
||||||
* Added Home Assistant autodiscovery MQTT broadcast.
|
* Added Home Assistant autodiscovery MQTT broadcast.
|
||||||
* Updated info on compiling.
|
* Updated info on compiling.
|
||||||
|
|
||||||
2023-??
|
2023-??
|
||||||
|
|
||||||
* Override option
|
* Override option
|
||||||
* Domoticz virtual switch ID (used with MQTT `domoticz/in`)
|
* Domoticz virtual switch ID (used with MQTT `domoticz/in`)
|
||||||
|
|
||||||
2024-02
|
2024-02
|
||||||
* Added compile time option to expand number of PIR sensors (they are logically ORed) `-D PIR_SENSOR_MAX_SENSORS=3`
|
|
||||||
|
* 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": {
|
"build": {
|
||||||
|
"libArchive": false,
|
||||||
"extraScript": "setup_deps.py"
|
"extraScript": "setup_deps.py"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -40,6 +40,9 @@ If the fan speed is unlocked, it will revert to temperature controlled speed on
|
|||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
2021-10
|
2021-10
|
||||||
|
|
||||||
* First public release
|
* First public release
|
||||||
|
|
||||||
2022-05
|
2022-05
|
||||||
|
|
||||||
* Added JSON API call to allow changing of speed
|
* Added JSON API call to allow changing of speed
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
|
from platformio.package.meta import PackageSpec
|
||||||
Import('env')
|
Import('env')
|
||||||
|
|
||||||
|
|
||||||
usermods = env.GetProjectOption("custom_usermods","").split()
|
libs = [PackageSpec(lib).name for lib in env.GetProjectOption("lib_deps",[])]
|
||||||
# Check for dependencies
|
# Check for dependencies
|
||||||
if "Temperature" in usermods:
|
if "Temperature" in libs:
|
||||||
env.Append(CPPDEFINES=[("USERMOD_DALLASTEMPERATURE")])
|
env.Append(CPPDEFINES=[("USERMOD_DALLASTEMPERATURE")])
|
||||||
elif "sht" in usermods:
|
elif "sht" in libs:
|
||||||
env.Append(CPPDEFINES=[("USERMOD_SHT")])
|
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")
|
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 "wled.h"
|
||||||
|
#include "SN_Photoresistor.h"
|
||||||
|
|
||||||
//Pin defaults for QuinLed Dig-Uno (A0)
|
//Pin defaults for QuinLed Dig-Uno (A0)
|
||||||
#ifndef PHOTORESISTOR_PIN
|
#ifndef PHOTORESISTOR_PIN
|
||||||
#define PHOTORESISTOR_PIN A0
|
#define PHOTORESISTOR_PIN A0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// the frequency to check photoresistor, 10 seconds
|
static bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||||
#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:
|
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
|
||||||
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;
|
uint16_t Usermod_SN_Photoresistor::getLuminance()
|
||||||
// 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);
|
// http://forum.arduino.cc/index.php?topic=37555.0
|
||||||
// flag to indicate we have finished the first getTemperature call
|
// https://forum.arduino.cc/index.php?topic=185158.0
|
||||||
// allows this library to report to the user how long until the first
|
float volts = analogRead(PHOTORESISTOR_PIN) * (referenceVoltage / adcPrecision);
|
||||||
// measurement
|
float amps = volts / resistorValue;
|
||||||
bool getLuminanceComplete = false;
|
float lux = amps * 1000000 * 2.0;
|
||||||
uint16_t lastLDRValue = -1000;
|
|
||||||
|
|
||||||
// flag set at startup
|
lastMeasurement = millis();
|
||||||
bool disabled = false;
|
getLuminanceComplete = true;
|
||||||
|
return uint16_t(lux);
|
||||||
|
}
|
||||||
|
|
||||||
// strings to reduce flash memory usage (used more than twice)
|
void Usermod_SN_Photoresistor::setup()
|
||||||
static const char _name[];
|
{
|
||||||
static const char _enabled[];
|
// set pinmode
|
||||||
static const char _readInterval[];
|
pinMode(PHOTORESISTOR_PIN, INPUT);
|
||||||
static const char _referenceVoltage[];
|
}
|
||||||
static const char _resistorValue[];
|
|
||||||
static const char _adcPrecision[];
|
|
||||||
static const char _offset[];
|
|
||||||
|
|
||||||
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
|
lastLDRValue = currentLDRValue;
|
||||||
// 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;
|
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
if (WLED_MQTT_CONNECTED)
|
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 we haven't read the sensor yet, let the user know
|
char subuf[45];
|
||||||
// that we are still waiting for the first measurement
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
strcat_P(subuf, PSTR("/luminance"));
|
||||||
lux.add(F(" sec until read"));
|
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
lux.add(lastLDRValue);
|
{
|
||||||
lux.add(F(" lux"));
|
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
|
||||||
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
|
void Usermod_SN_Photoresistor::addToJsonInfo(JsonObject &root)
|
||||||
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
|
{
|
||||||
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
|
JsonObject user = root[F("u")];
|
||||||
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
|
if (user.isNull())
|
||||||
offset = top[FPSTR(_offset)] | offset;
|
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_PRINT(FPSTR(_name));
|
||||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||||
|
return false;
|
||||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
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)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
const char Usermod_SN_Photoresistor::_name[] PROGMEM = "Photoresistor";
|
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::_adcPrecision[] PROGMEM = "adc-precision";
|
||||||
const char Usermod_SN_Photoresistor::_offset[] PROGMEM = "offset";
|
const char Usermod_SN_Photoresistor::_offset[] PROGMEM = "offset";
|
||||||
|
|
||||||
|
|
||||||
static Usermod_SN_Photoresistor sn_photoresistor;
|
static Usermod_SN_Photoresistor sn_photoresistor;
|
||||||
REGISTER_USERMOD(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,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "Si7021_MQTT_HA",
|
"name": "Si7021_MQTT_HA",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"finitespace/BME280":"3.0.0",
|
"finitespace/BME280":"3.0.0",
|
||||||
"adafruit/Adafruit Si7021 Library" : "1.5.3"
|
"adafruit/Adafruit Si7021 Library" : "1.5.3"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name:": "Temperature",
|
"name": "Temperature",
|
||||||
"build": { "libArchive": false},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"paulstoffregen/OneWire":"~2.3.8"
|
"paulstoffregen/OneWire":"~2.3.8"
|
||||||
|
@ -35,19 +35,23 @@ All parameters can be configured at runtime via the Usermods settings page, incl
|
|||||||
|
|
||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
2020-09-12
|
2020-09-12
|
||||||
|
|
||||||
* Changed to use async non-blocking implementation
|
* Changed to use async non-blocking implementation
|
||||||
* Do not report erroneous low temperatures to MQTT
|
* Do not report erroneous low temperatures to MQTT
|
||||||
* Disable plugin if temperature sensor not detected
|
* Disable plugin if temperature sensor not detected
|
||||||
* Report the number of seconds until the first read in the info screen instead of sensor error
|
* Report the number of seconds until the first read in the info screen instead of sensor error
|
||||||
|
|
||||||
2021-04
|
2021-04
|
||||||
|
|
||||||
* Adaptation for runtime configuration.
|
* Adaptation for runtime configuration.
|
||||||
|
|
||||||
2023-05
|
2023-05
|
||||||
|
|
||||||
* Rewrite to conform to newer recommendations.
|
* Rewrite to conform to newer recommendations.
|
||||||
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error
|
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error
|
||||||
|
|
||||||
2024-09
|
2024-09
|
||||||
|
|
||||||
* Update OneWire to version 2.3.8, which includes stickbreaker's and garyd9's ESP32 fixes:
|
* Update OneWire to version 2.3.8, which includes stickbreaker's and garyd9's ESP32 fixes:
|
||||||
blazoncek's fork is no longer needed
|
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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pololu/VL53L0X" : "^1.3.0"
|
"pololu/VL53L0X" : "^1.3.0"
|
||||||
|
@ -1736,7 +1736,7 @@ class AudioReactive : public Usermod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onStateChange(uint8_t callMode) override {
|
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
|
// if palettes were removed during JSON call re-add them
|
||||||
createAudioPalettes();
|
createAudioPalettes();
|
||||||
}
|
}
|
||||||
@ -1966,20 +1966,20 @@ class AudioReactive : public Usermod {
|
|||||||
void AudioReactive::removeAudioPalettes(void) {
|
void AudioReactive::removeAudioPalettes(void) {
|
||||||
DEBUG_PRINTLN(F("Removing audio palettes."));
|
DEBUG_PRINTLN(F("Removing audio palettes."));
|
||||||
while (palettes>0) {
|
while (palettes>0) {
|
||||||
strip.customPalettes.pop_back();
|
customPalettes.pop_back();
|
||||||
DEBUG_PRINTLN(palettes);
|
DEBUG_PRINTLN(palettes);
|
||||||
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) {
|
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;
|
if (palettes) return;
|
||||||
DEBUG_PRINTLN(F("Adding audio palettes."));
|
DEBUG_PRINTLN(F("Adding audio palettes."));
|
||||||
for (int i=0; i<MAX_PALETTES; i++)
|
for (int i=0; i<MAX_PALETTES; i++)
|
||||||
if (strip.customPalettes.size() < 10) {
|
if (customPalettes.size() < 10) {
|
||||||
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
|
customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
|
||||||
palettes++;
|
palettes++;
|
||||||
DEBUG_PRINTLN(palettes);
|
DEBUG_PRINTLN(palettes);
|
||||||
} else break;
|
} else break;
|
||||||
@ -2016,7 +2016,7 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
|
|||||||
|
|
||||||
void AudioReactive::fillAudioPalettes() {
|
void AudioReactive::fillAudioPalettes() {
|
||||||
if (!palettes) return;
|
if (!palettes) return;
|
||||||
size_t lastCustPalette = strip.customPalettes.size();
|
size_t lastCustPalette = customPalettes.size();
|
||||||
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
|
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
|
||||||
for (int pal=0; pal<palettes; pal++) {
|
for (int pal=0; pal<palettes; pal++) {
|
||||||
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
||||||
@ -2045,7 +2045,7 @@ void AudioReactive::fillAudioPalettes() {
|
|||||||
tcp[14] = rgb.g;
|
tcp[14] = rgb.g;
|
||||||
tcp[15] = rgb.b;
|
tcp[15] = rgb.b;
|
||||||
|
|
||||||
strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
|
customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,24 +8,27 @@ Does audio processing and provides data structure that specially written effects
|
|||||||
**does not** provide effects or draw anything to an LED strip/matrix.
|
**does not** provide effects or draw anything to an LED strip/matrix.
|
||||||
|
|
||||||
## Additional Documentation
|
## 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):
|
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)
|
* [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.
|
* [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)
|
* [Digital Audio](https://github.com/atuline/WLED/wiki/Digital-Microphone-Hookup)
|
||||||
* [Analog Audio](https://github.com/atuline/WLED/wiki/Analog-Audio-Input-Options)
|
* [Analog Audio](https://github.com/atuline/WLED/wiki/Analog-Audio-Input-Options)
|
||||||
* [UDP Sound sync](https://github.com/atuline/WLED/wiki/UDP-Sound-Sync)
|
* [UDP Sound sync](https://github.com/atuline/WLED/wiki/UDP-Sound-Sync)
|
||||||
|
|
||||||
|
|
||||||
## Supported MCUs
|
## 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.
|
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.
|
||||||
|
|
||||||
Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3.
|
Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3.
|
||||||
|
|
||||||
Currently ESP8266 is not supported, due to low speed and small RAM of this chip.
|
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.
|
There are however plans to create a lightweight audioreactive for the 8266, with reduced features.
|
||||||
## Installation
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
Add 'ADS1115_v2' to `custom_usermods` in your platformio environment.
|
Add 'ADS1115_v2' to `custom_usermods` in your platformio environment.
|
||||||
|
|
||||||
@ -35,29 +38,31 @@ 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):
|
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 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 AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
|
||||||
- `-D I2S_SDPIN=x` : GPIO for SD pin on digital microphone (32)
|
* `-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_WSPIN=x` : GPIO for WS pin on digital microphone (15)
|
||||||
- `-D I2S_CKPIN=x` : GPIO for SCK pin on digital microphone (14)
|
* `-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 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_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
|
||||||
- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
* `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
||||||
|
|
||||||
Other options:
|
Other options:
|
||||||
|
|
||||||
- `-D UM_AUDIOREACTIVE_ENABLE` : makes usermod default enabled (not the same as include into build option!)
|
* `-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_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.
|
**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
|
### Advanced Compile-Time Options
|
||||||
|
|
||||||
You can use the following additional flags in your `build_flags`
|
You can use the following additional flags in your `build_flags`
|
||||||
|
|
||||||
* `-D SR_SQUELCH=x` : Default "squelch" setting (10)
|
* `-D SR_SQUELCH=x` : Default "squelch" setting (10)
|
||||||
* `-D SR_GAIN=x` : Default "gain" setting (60)
|
* `-D SR_GAIN=x` : Default "gain" setting (60)
|
||||||
* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this).
|
* `-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_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_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 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.
|
* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB.
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name:": "boblight"
|
"name": "boblight",
|
||||||
|
"build": { "libArchive": false }
|
||||||
}
|
}
|
@ -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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electroniccats/MPU6050":"1.0.1"
|
"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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arduino-pixels-dice":"https://github.com/axlan/arduino-pixels-dice.git",
|
"arduino-pixels-dice":"https://github.com/axlan/arduino-pixels-dice.git",
|
||||||
|
@ -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.
|
Turn your WLED lights into a rev light and shift indicator for Project Cars.
|
||||||
It's easy to use.
|
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 |
|
| Number | Updates/Second |
|
||||||
| ------ | -------------- |
|
| ------ | -------------- |
|
||||||
@ -19,5 +19,5 @@ It's easy to use.
|
|||||||
| 8 | 05 |
|
| 8 | 05 |
|
||||||
| 9 | 1 |
|
| 9 | 1 |
|
||||||
|
|
||||||
3. Once you enter a race, WLED should automatically shift to PCARS mode.
|
_3._ Once you enter a race, WLED should automatically shift to PCARS mode.
|
||||||
4. Done.
|
_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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"olikraus/U8g2":"~2.28.8",
|
"olikraus/U8g2":"~2.28.8",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
### Usermods
|
# Usermods
|
||||||
|
|
||||||
This folder serves as a repository for usermods (custom `usermod.cpp` files)!
|
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:
|
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`)
|
* Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`)
|
||||||
- Include your custom files
|
* 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
|
* 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!
|
* Create a pull request!
|
||||||
- If your feature is useful for the majority of WLED users, I will consider adding it to the base code!
|
* 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.
|
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.
|
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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lennarthennigs/ESP Rotary":"^2.1.1"
|
"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},
|
"build": { "libArchive": false},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adafruit/Adafruit BMP280 Library":"2.6.8",
|
"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"
|
#include "wled.h"
|
||||||
|
#ifdef USERMOD_SN_PHOTORESISTOR
|
||||||
|
#include "SN_Photoresistor.h"
|
||||||
|
#endif
|
||||||
|
#ifdef USERMOD_BH1750
|
||||||
|
#include "BH1750_v2.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WLED_DISABLE_MQTT
|
#ifdef WLED_DISABLE_MQTT
|
||||||
#error "This user mod requires MQTT to be enabled."
|
#error "This user mod requires MQTT to be enabled."
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "sht",
|
"name": "sht",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"robtillaart/SHT85": "~0.3.3"
|
"robtillaart/SHT85": "~0.3.3"
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,43 @@
|
|||||||
# SHT
|
# SHT
|
||||||
|
|
||||||
Usermod to support various SHT i2c sensors like the SHT30, SHT31, SHT35 and SHT85
|
Usermod to support various SHT i2c sensors like the SHT30, SHT31, SHT35 and SHT85
|
||||||
|
|
||||||
## Requirements
|
## 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
|
## 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`.
|
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:
|
ESP32:
|
||||||
```
|
|
||||||
|
```ini
|
||||||
[env:custom_esp32dev_usermod_sht]
|
[env:custom_esp32dev_usermod_sht]
|
||||||
extends = env:esp32dev
|
extends = env:esp32dev
|
||||||
custom_usermods = ${env:esp32dev.custom_usermods} sht
|
custom_usermods = ${env:esp32dev.custom_usermods} sht
|
||||||
```
|
```
|
||||||
|
|
||||||
ESP8266:
|
ESP8266:
|
||||||
```
|
|
||||||
|
```ini
|
||||||
[env:custom_d1_mini_usermod_sht]
|
[env:custom_d1_mini_usermod_sht]
|
||||||
extends = env:d1_mini
|
extends = env:d1_mini
|
||||||
custom_usermods = ${env:d1_mini.custom_usermods} sht
|
custom_usermods = ${env:d1_mini.custom_usermods} sht
|
||||||
```
|
```
|
||||||
|
|
||||||
## MQTT Discovery for Home Assistant
|
## 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.
|
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
|
### 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.
|
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
|
## 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:
|
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:
|
* SHT-Type:
|
||||||
* What it does: Select the SHT sensor type you want to use
|
* What it does: Select the SHT sensor type you want to use
|
||||||
* Possible values: SHT30, SHT31, SHT35, SHT85
|
* 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
|
* Default: Disabled
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
2022-12
|
2022-12
|
||||||
|
|
||||||
* First implementation.
|
* First implementation.
|
||||||
|
|
||||||
## Credits
|
## 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
|
## Features
|
||||||
|
|
||||||
* Configure the URL endpoint (only support HTTP for now, no HTTPS) and polling interval via the WLED user interface.
|
* 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.
|
* 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.
|
* 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.
|
* 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.
|
* Unique ID generation based on the device's MAC address and a configurable salt value, appended to the request URL for identification.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
* Enable the `usermod_v2_HttpPullLightControl` via the WLED user interface.
|
* Enable the `usermod_v2_HttpPullLightControl` via the WLED user interface.
|
||||||
* Specify the URL endpoint and polling interval.
|
* Specify the URL endpoint and polling interval.
|
||||||
|
|
||||||
## JSON Format and examples
|
## 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.
|
* 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.
|
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.
|
* 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.
|
* 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":
|
"seg":
|
||||||
{
|
{
|
||||||
"frz": false,
|
"frz": false,
|
||||||
"fx": 28,
|
"fx": 28,
|
||||||
"sx": 200,
|
"sx": 200,
|
||||||
"ix": 128,
|
"ix": 128,
|
||||||
"col": [
|
"col": [
|
||||||
"FF0000",
|
"FF0000",
|
||||||
"000000",
|
"000000",
|
||||||
"FFFFFF"
|
"FFFFFF"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Add `usermod_v2_HttpPullLightControl` to your WLED project following the instructions provided in the WLED documentation.
|
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!
|
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:
|
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
|
* -DUSERMOD_HTTP_PULL_LIGHT_CONTROL ;To Enable the usermod
|
||||||
- -DWLED_AP_PASS="\"christmas\""
|
* -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
|
||||||
- -DWLED_OTA_PASS="\"otapw-secret\""
|
* -DHTTP_PULL_LIGHT_CONTROL_SALT="\"my_very-S3cret_C0de\"" ; A secret SALT which will help by making the ID more safe
|
||||||
- -DMDNS_NAME="\"christmascard\""
|
* -DHTTP_PULL_LIGHT_CONTROL_INTERVAL=30 ; The interval at which the URL is requested in seconds
|
||||||
- -DSERVERNAME="\"CHRISTMASCARD\""
|
* -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 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)
|
* -DWLED_AP_SSID="\"Christmas Card\"" ; These flags are not just for my Usermod but you probably want to set them
|
||||||
- -DDEBUG_LEVEL=5
|
* -DWLED_AP_PASS="\"christmas\""
|
||||||
- -DWLED_DEBUG
|
* -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
|
## Use Case: Interactive Christmas Cards
|
||||||
|
|
||||||
@ -107,4 +112,4 @@ Imagine distributing interactive Christmas cards embedded with a tiny ESP32 and
|
|||||||
|
|
||||||
Your server keeps track of how many cards are active at any given time. If all 20 cards are active, your server instructs each card to light up all of its LEDs. However, if only 4 cards are active, your server instructs each card to light up only 4 LEDs. This creates a real-time interactive experience, symbolizing the collective spirit of the holiday season. Each lit LED represents a friend who's thinking about the others, and the visual feedback creates a sense of connection among the group, despite the physical distance.
|
Your server keeps track of how many cards are active at any given time. If all 20 cards are active, your server instructs each card to light up all of its LEDs. However, if only 4 cards are active, your server instructs each card to light up only 4 LEDs. This creates a real-time interactive experience, symbolizing the collective spirit of the holiday season. Each lit LED represents a friend who's thinking about the others, and the visual feedback creates a sense of connection among the group, despite the physical distance.
|
||||||
|
|
||||||
This setup demonstrates a unique way to blend traditional holiday sentiments with modern technology, offering an engaging and memorable experience.
|
This setup demonstrates a unique way to blend traditional holiday sentiments with modern technology, offering an engaging and memorable experience.
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl";
|
const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl";
|
||||||
const char HttpPullLightControl::_enabled[] PROGMEM = "Enable";
|
const char HttpPullLightControl::_enabled[] PROGMEM = "Enable";
|
||||||
|
|
||||||
|
static HttpPullLightControl http_pull_usermod;
|
||||||
|
REGISTER_USERMOD(http_pull_usermod);
|
||||||
|
|
||||||
void HttpPullLightControl::setup() {
|
void HttpPullLightControl::setup() {
|
||||||
//Serial.begin(115200);
|
//Serial.begin(115200);
|
||||||
|
|
||||||
@ -297,10 +300,10 @@ void HttpPullLightControl::handleResponse(String& responseStr) {
|
|||||||
// Check for valid JSON, otherwise we brick the program runtime
|
// Check for valid JSON, otherwise we brick the program runtime
|
||||||
if (jsonStr[0] == '{' || jsonStr[0] == '[') {
|
if (jsonStr[0] == '{' || jsonStr[0] == '[') {
|
||||||
// Attempt to deserialize the JSON response
|
// Attempt to deserialize the JSON response
|
||||||
DeserializationError error = deserializeJson(doc, jsonStr);
|
DeserializationError error = deserializeJson(*pDoc, jsonStr);
|
||||||
if (error == DeserializationError::Ok) {
|
if (error == DeserializationError::Ok) {
|
||||||
// Get JSON object from th doc
|
// 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)
|
// Parse the object throuhg deserializeState (use CALL_MODE_NO_NOTIFY or OR CALL_MODE_DIRECT_CHANGE)
|
||||||
deserializeState(obj, CALL_MODE_NO_NOTIFY);
|
deserializeState(obj, CALL_MODE_NO_NOTIFY);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name:": "usermod_v2_RF433",
|
"name": "usermod_v2_RF433",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sui77/rc-switch":"2.6.4"
|
"sui77/rc-switch":"2.6.4"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "animartrix",
|
"name": "animartrix",
|
||||||
|
"build": { "libArchive": false },
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Animartrix": "https://github.com/netmindz/animartrix.git#b172586"
|
"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
|
v2 Usermod to automatically save settings
|
||||||
to preset number AUTOSAVE_PRESET_NUM after a change to any of:
|
to preset number AUTOSAVE_PRESET_NUM after a change to any of:
|
||||||
|
|
||||||
* brightness
|
* brightness
|
||||||
* effect speed
|
* effect speed
|
||||||
* effect intensity
|
* effect intensity
|
||||||
@ -19,7 +20,7 @@ Note: WLED doesn't respect the brightness of the preset being auto loaded, so th
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Copy and update the example `platformio_override.ini.sample`
|
Copy and update the example `platformio_override.ini.sample`
|
||||||
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
|
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
|
||||||
This file should be placed in the same directory as `platformio.ini`.
|
This file should be placed in the same directory as `platformio.ini`.
|
||||||
|
|
||||||
@ -50,6 +51,9 @@ Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
|||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
2021-02
|
2021-02
|
||||||
|
|
||||||
* First public release
|
* First public release
|
||||||
|
|
||||||
2021-04
|
2021-04
|
||||||
|
|
||||||
* Adaptation for runtime configuration.
|
* 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
|
or add `-D USERMOD_BRIGHTNESS_FOLLOW_SUN` to `build_flags` in platformio_override.ini
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
Open Usermod Settings in WLED to change settings:
|
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`.
|
`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.
|
`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
|
### PlatformIO requirements
|
||||||
|
|
||||||
No special requirements.
|
No special requirements.
|
||||||
|
|
||||||
## Change Log
|
### Change Log
|
||||||
|
|
||||||
2025-01-02
|
2025-01-02
|
||||||
|
|
||||||
* init
|
* 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"
|
#include "wled.h"
|
||||||
|
|
||||||
//v2 usermod that allows to change brightness and color using a rotary encoder,
|
//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::_min_bri[] PROGMEM = "Min Brightness";
|
||||||
const char UsermodBrightnessFollowSun::_max_bri[] PROGMEM = "Max Brightness";
|
const char UsermodBrightnessFollowSun::_max_bri[] PROGMEM = "Max Brightness";
|
||||||
const char UsermodBrightnessFollowSun::_relax_hour[] PROGMEM = "Relax Hour";
|
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)
|
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
|
||||||
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Fontname: wled_logo_akemi_4x4
|
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]
|
[platformio]
|
||||||
default_envs = esp32dev
|
default_envs = esp32dev_fld
|
||||||
|
|
||||||
[env:esp32dev]
|
[env:esp32dev_fld]
|
||||||
board = esp32dev
|
extends = env:esp32dev_V4
|
||||||
platform = ${esp32.platform}
|
custom_usermods = ${env:esp32dev_V4.custom_usermods} four_line_display_ALT
|
||||||
build_unflags = ${common.build_unflags}
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags_esp32}
|
${env:esp32dev_V4.build_flags}
|
||||||
-D USERMOD_FOUR_LINE_DISPLAY
|
|
||||||
-D FLD_TYPE=SH1106
|
-D FLD_TYPE=SH1106
|
||||||
-D I2CSCLPIN=27
|
-D I2CSCLPIN=27
|
||||||
-D I2CSDAPIN=26
|
-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
|
## Functionalities
|
||||||
|
|
||||||
Press the encoder to cycle through the options:
|
Press the encoder to cycle through the options:
|
||||||
|
|
||||||
* Brightness
|
* Brightness
|
||||||
* Speed
|
* Speed
|
||||||
* Intensity
|
* Intensity
|
||||||
@ -35,15 +36,15 @@ These options are configurable in Config > Usermods
|
|||||||
|
|
||||||
* `enabled` - enable/disable usermod
|
* `enabled` - enable/disable usermod
|
||||||
* `type` - display type in numeric format
|
* `type` - display type in numeric format
|
||||||
* 1 = I2C SSD1306 128x32
|
* 1 = I2C SSD1306 128x32
|
||||||
* 2 = I2C SH1106 128x32
|
* 2 = I2C SH1106 128x32
|
||||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||||
* 4 = I2C SSD1305 128x32
|
* 4 = I2C SSD1305 128x32
|
||||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||||
* 6 = SPI SSD1306 128x32
|
* 6 = SPI SSD1306 128x32
|
||||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||||
* 8 = SPI SSD1309 128x64 (4 double-height lines)
|
* 8 = SPI SSD1309 128x64 (4 double-height lines)
|
||||||
* 9 = I2C 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
|
* `pin` - GPIO pins used for display; SPI displays can use SCK, MOSI, CS, DC & RST
|
||||||
* `flip` - flip/rotate display 180°
|
* `flip` - flip/rotate display 180°
|
||||||
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
|
* `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
|
* `showSeconds` - Show seconds on the clock display
|
||||||
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
|
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
|
||||||
|
|
||||||
|
|
||||||
### PlatformIO requirements
|
### PlatformIO requirements
|
||||||
|
|
||||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
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
|
## Change Log
|
||||||
|
|
||||||
2021-10
|
2021-10
|
||||||
|
|
||||||
* First public release
|
* First public release
|
||||||
|
@ -0,0 +1,315 @@
|
|||||||
|
#include "wled.h"
|
||||||
|
#undef U8X8_NO_HW_I2C // borrowed from WLEDMM: we do want I2C hardware drivers - if possible
|
||||||
|
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef FLD_ESP32_NO_THREADS
|
||||||
|
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FLD_PIN_CS
|
||||||
|
#define FLD_PIN_CS 15
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#ifndef FLD_PIN_DC
|
||||||
|
#define FLD_PIN_DC 19
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_RESET
|
||||||
|
#define FLD_PIN_RESET 26
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef FLD_PIN_DC
|
||||||
|
#define FLD_PIN_DC 12
|
||||||
|
#endif
|
||||||
|
#ifndef FLD_PIN_RESET
|
||||||
|
#define FLD_PIN_RESET 16
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FLD_TYPE
|
||||||
|
#ifndef FLD_SPI_DEFAULT
|
||||||
|
#define FLD_TYPE SSD1306
|
||||||
|
#else
|
||||||
|
#define FLD_TYPE SSD1306_SPI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// When to time out to the clock or blank the screen
|
||||||
|
// if SLEEP_MODE_ENABLED.
|
||||||
|
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
|
||||||
|
|
||||||
|
// Minimum time between redrawing screen in ms
|
||||||
|
#define REFRESH_RATE_MS 1000
|
||||||
|
|
||||||
|
// Extra char (+1) for null
|
||||||
|
#define LINE_BUFFER_SIZE 16+1
|
||||||
|
#define MAX_JSON_CHARS 19+1
|
||||||
|
#define MAX_MODE_LINE_SPACE 13+1
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NONE = 0,
|
||||||
|
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
|
||||||
|
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
|
||||||
|
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
|
||||||
|
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
|
||||||
|
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||||
|
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||||
|
SSD1309_SPI64, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
|
||||||
|
SSD1309_64 // U8X8_SSD1309_128X64_NONAME0_HW_I2C
|
||||||
|
} DisplayType;
|
||||||
|
|
||||||
|
class FourLineDisplayUsermod : public Usermod {
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
|
||||||
|
public:
|
||||||
|
FourLineDisplayUsermod() { if (!instance) instance = this; }
|
||||||
|
static FourLineDisplayUsermod* getInstance(void) { return instance; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static FourLineDisplayUsermod *instance;
|
||||||
|
bool initDone = false;
|
||||||
|
volatile bool drawing = false;
|
||||||
|
volatile bool lockRedraw = false;
|
||||||
|
|
||||||
|
// HW interface & configuration
|
||||||
|
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
|
||||||
|
|
||||||
|
#ifndef FLD_SPI_DEFAULT
|
||||||
|
int8_t ioPin[3] = {-1, -1, -1}; // I2C pins: SCL, SDA
|
||||||
|
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
||||||
|
#else
|
||||||
|
int8_t ioPin[3] = {FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // custom SPI pins: CS, DC, RST
|
||||||
|
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DisplayType type = FLD_TYPE; // display type
|
||||||
|
bool flip = false; // flip display 180°
|
||||||
|
uint8_t contrast = 10; // screen contrast
|
||||||
|
uint8_t lineHeight = 1; // 1 row or 2 rows
|
||||||
|
uint16_t refreshRate = REFRESH_RATE_MS; // in ms
|
||||||
|
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
|
||||||
|
bool sleepMode = true; // allow screen sleep?
|
||||||
|
bool clockMode = false; // display clock
|
||||||
|
bool showSeconds = true; // display clock with seconds
|
||||||
|
bool enabled = true;
|
||||||
|
bool contrastFix = false;
|
||||||
|
|
||||||
|
// Next variables hold the previous known values to determine if redraw is
|
||||||
|
// required.
|
||||||
|
String knownSsid = apSSID;
|
||||||
|
IPAddress knownIp = IPAddress(4, 3, 2, 1);
|
||||||
|
uint8_t knownBrightness = 0;
|
||||||
|
uint8_t knownEffectSpeed = 0;
|
||||||
|
uint8_t knownEffectIntensity = 0;
|
||||||
|
uint8_t knownMode = 0;
|
||||||
|
uint8_t knownPalette = 0;
|
||||||
|
uint8_t knownMinute = 99;
|
||||||
|
uint8_t knownHour = 99;
|
||||||
|
byte brightness100;
|
||||||
|
byte fxspeed100;
|
||||||
|
byte fxintensity100;
|
||||||
|
bool knownnightlight = nightlightActive;
|
||||||
|
bool wificonnected = interfacesInited;
|
||||||
|
bool powerON = true;
|
||||||
|
|
||||||
|
bool displayTurnedOff = false;
|
||||||
|
unsigned long nextUpdate = 0;
|
||||||
|
unsigned long lastRedraw = 0;
|
||||||
|
unsigned long overlayUntil = 0;
|
||||||
|
|
||||||
|
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||||
|
byte markLineNum = 255;
|
||||||
|
byte markColNum = 255;
|
||||||
|
|
||||||
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
|
static const char _name[];
|
||||||
|
static const char _enabled[];
|
||||||
|
static const char _contrast[];
|
||||||
|
static const char _refreshRate[];
|
||||||
|
static const char _screenTimeOut[];
|
||||||
|
static const char _flip[];
|
||||||
|
static const char _sleepMode[];
|
||||||
|
static const char _clockMode[];
|
||||||
|
static const char _showSeconds[];
|
||||||
|
static const char _busClkFrequency[];
|
||||||
|
static const char _contrastFix[];
|
||||||
|
|
||||||
|
// If display does not work or looks corrupted check the
|
||||||
|
// constructor reference:
|
||||||
|
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||||
|
// or check the gallery:
|
||||||
|
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||||
|
|
||||||
|
// some displays need this to properly apply contrast
|
||||||
|
void setVcomh(bool highContrast);
|
||||||
|
void startDisplay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrappers for screen drawing
|
||||||
|
*/
|
||||||
|
void setFlipMode(uint8_t mode);
|
||||||
|
void setContrast(uint8_t contrast);
|
||||||
|
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false);
|
||||||
|
void draw2x2String(uint8_t col, uint8_t row, const char *string);
|
||||||
|
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false);
|
||||||
|
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font);
|
||||||
|
void draw2x2GlyphIcons();
|
||||||
|
uint8_t getCols();
|
||||||
|
void clear();
|
||||||
|
void setPowerSave(uint8_t save);
|
||||||
|
void center(String &line, uint8_t width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the current date and time in large characters
|
||||||
|
* on the middle rows. Based 24 or 12 hour depending on
|
||||||
|
* the useAMPM configuration.
|
||||||
|
*/
|
||||||
|
void showTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable sleep (turn the display off) or clock mode.
|
||||||
|
*/
|
||||||
|
void sleepOrClock(bool enabled);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// gets called once at boot. Do all initialization that doesn't depend on
|
||||||
|
// network here
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||||
|
// interfaces here
|
||||||
|
void connected() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Da loop.
|
||||||
|
*/
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
//function to update lastredraw
|
||||||
|
inline void updateRedrawTime() { lastRedraw = millis(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redraw the screen (but only if things have changed
|
||||||
|
* or if forceRedraw).
|
||||||
|
*/
|
||||||
|
void redraw(bool forceRedraw);
|
||||||
|
|
||||||
|
void updateBrightness();
|
||||||
|
void updateSpeed();
|
||||||
|
void updateIntensity();
|
||||||
|
void drawStatusIcons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marks the position of the arrow showing
|
||||||
|
* the current setting being changed
|
||||||
|
* pass line and colum info
|
||||||
|
*/
|
||||||
|
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
|
||||||
|
|
||||||
|
//Draw the arrow for the current setting being changed
|
||||||
|
void drawArrow();
|
||||||
|
|
||||||
|
//Display the current effect or palette (desiredEntry)
|
||||||
|
// on the appropriate line (row).
|
||||||
|
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there screen is off or in clock is displayed,
|
||||||
|
* this will return true. This allows us to throw away
|
||||||
|
* the first input from the rotary encoder but
|
||||||
|
* to wake up the screen.
|
||||||
|
*/
|
||||||
|
bool wakeDisplay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to show one line and a glyph as overlay for a period of time.
|
||||||
|
* Clears the screen and prints.
|
||||||
|
* Used in Rotary Encoder usermod.
|
||||||
|
*/
|
||||||
|
void overlay(const char* line1, long showHowLong, byte glyphType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to show Akemi WLED logo overlay for a period of time.
|
||||||
|
* Clears the screen and prints.
|
||||||
|
*/
|
||||||
|
void overlayLogo(long showHowLong);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to show two lines as overlay for a period of time.
|
||||||
|
* Clears the screen and prints.
|
||||||
|
* Used in Auto Save usermod
|
||||||
|
*/
|
||||||
|
void overlay(const char* line1, const char* line2, long showHowLong);
|
||||||
|
|
||||||
|
void networkOverlay(const char* line1, long showHowLong);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handleButton() can be used to override default button behaviour. Returning true
|
||||||
|
* will prevent button working in a default way.
|
||||||
|
* Replicating button.cpp
|
||||||
|
*/
|
||||||
|
bool handleButton(uint8_t b);
|
||||||
|
|
||||||
|
void onUpdateBegin(bool init) override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||||
|
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||||
|
* Below it is shown how this could be used for e.g. a light sensor
|
||||||
|
*/
|
||||||
|
//void addToJsonInfo(JsonObject& root) override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||||
|
* Values in the state object may be modified by connected clients
|
||||||
|
*/
|
||||||
|
//void addToJsonState(JsonObject& root) override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||||
|
* Values in the state object may be modified by connected clients
|
||||||
|
*/
|
||||||
|
//void readFromJsonState(JsonObject& root) override;
|
||||||
|
|
||||||
|
void appendConfigData() override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||||
|
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||||
|
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||||
|
*
|
||||||
|
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||||
|
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||||
|
* Use it sparingly and always in the loop, never in network callbacks!
|
||||||
|
*
|
||||||
|
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||||
|
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||||
|
*
|
||||||
|
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||||
|
*/
|
||||||
|
void addToConfig(JsonObject& root) override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||||
|
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||||
|
*
|
||||||
|
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||||
|
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||||
|
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||||
|
*/
|
||||||
|
bool readFromConfig(JsonObject& root) override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||||
|
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||||
|
*/
|
||||||
|
uint16_t getId() override {
|
||||||
|
return USERMOD_ID_FOUR_LINE_DISP;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,11 +1,5 @@
|
|||||||
#include "wled.h"
|
#include "usermod_v2_four_line_display.h"
|
||||||
#undef U8X8_NO_HW_I2C // borrowed from WLEDMM: we do want I2C hardware drivers - if possible
|
#include "4LD_wled_fonts.h"
|
||||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
|
||||||
#include "4LD_wled_fonts.c"
|
|
||||||
|
|
||||||
#ifndef FLD_ESP32_NO_THREADS
|
|
||||||
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Inspired by the usermod_v2_four_line_display
|
// Inspired by the usermod_v2_four_line_display
|
||||||
@ -20,330 +14,18 @@
|
|||||||
//
|
//
|
||||||
// Make sure to enable NTP and set your time zone in WLED Config | Time.
|
// Make sure to enable NTP and set your time zone in WLED Config | Time.
|
||||||
//
|
//
|
||||||
// REQUIREMENT: You must add the following requirements to
|
|
||||||
// REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini
|
|
||||||
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine)
|
|
||||||
// REQUIREMENT: * Wire
|
|
||||||
//
|
|
||||||
// If display does not work or looks corrupted check the
|
// If display does not work or looks corrupted check the
|
||||||
// constructor reference:
|
// constructor reference:
|
||||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||||
// or check the gallery:
|
// or check the gallery:
|
||||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||||
|
|
||||||
#ifndef FLD_PIN_CS
|
|
||||||
#define FLD_PIN_CS 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
#ifndef FLD_PIN_DC
|
|
||||||
#define FLD_PIN_DC 19
|
|
||||||
#endif
|
|
||||||
#ifndef FLD_PIN_RESET
|
|
||||||
#define FLD_PIN_RESET 26
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifndef FLD_PIN_DC
|
|
||||||
#define FLD_PIN_DC 12
|
|
||||||
#endif
|
|
||||||
#ifndef FLD_PIN_RESET
|
|
||||||
#define FLD_PIN_RESET 16
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FLD_TYPE
|
|
||||||
#ifndef FLD_SPI_DEFAULT
|
|
||||||
#define FLD_TYPE SSD1306
|
|
||||||
#else
|
|
||||||
#define FLD_TYPE SSD1306_SPI
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// When to time out to the clock or blank the screen
|
|
||||||
// if SLEEP_MODE_ENABLED.
|
|
||||||
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
|
|
||||||
|
|
||||||
// Minimum time between redrawing screen in ms
|
|
||||||
#define REFRESH_RATE_MS 1000
|
|
||||||
|
|
||||||
// Extra char (+1) for null
|
|
||||||
#define LINE_BUFFER_SIZE 16+1
|
|
||||||
#define MAX_JSON_CHARS 19+1
|
|
||||||
#define MAX_MODE_LINE_SPACE 13+1
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
static TaskHandle_t Display_Task = nullptr;
|
static TaskHandle_t Display_Task = nullptr;
|
||||||
void DisplayTaskCode(void * parameter);
|
void DisplayTaskCode(void * parameter);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NONE = 0,
|
|
||||||
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
|
|
||||||
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
|
|
||||||
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
|
||||||
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
|
|
||||||
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
|
|
||||||
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
|
||||||
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
|
||||||
SSD1309_SPI64, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
|
|
||||||
SSD1309_64 // U8X8_SSD1309_128X64_NONAME0_HW_I2C
|
|
||||||
} DisplayType;
|
|
||||||
|
|
||||||
|
|
||||||
class FourLineDisplayUsermod : public Usermod {
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
|
|
||||||
public:
|
|
||||||
FourLineDisplayUsermod() { if (!instance) instance = this; }
|
|
||||||
static FourLineDisplayUsermod* getInstance(void) { return instance; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static FourLineDisplayUsermod *instance;
|
|
||||||
bool initDone = false;
|
|
||||||
volatile bool drawing = false;
|
|
||||||
volatile bool lockRedraw = false;
|
|
||||||
|
|
||||||
// HW interface & configuration
|
|
||||||
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
|
|
||||||
|
|
||||||
#ifndef FLD_SPI_DEFAULT
|
|
||||||
int8_t ioPin[3] = {-1, -1, -1}; // I2C pins: SCL, SDA
|
|
||||||
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
|
||||||
#else
|
|
||||||
int8_t ioPin[3] = {FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // custom SPI pins: CS, DC, RST
|
|
||||||
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DisplayType type = FLD_TYPE; // display type
|
|
||||||
bool flip = false; // flip display 180°
|
|
||||||
uint8_t contrast = 10; // screen contrast
|
|
||||||
uint8_t lineHeight = 1; // 1 row or 2 rows
|
|
||||||
uint16_t refreshRate = REFRESH_RATE_MS; // in ms
|
|
||||||
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
|
|
||||||
bool sleepMode = true; // allow screen sleep?
|
|
||||||
bool clockMode = false; // display clock
|
|
||||||
bool showSeconds = true; // display clock with seconds
|
|
||||||
bool enabled = true;
|
|
||||||
bool contrastFix = false;
|
|
||||||
|
|
||||||
// Next variables hold the previous known values to determine if redraw is
|
|
||||||
// required.
|
|
||||||
String knownSsid = apSSID;
|
|
||||||
IPAddress knownIp = IPAddress(4, 3, 2, 1);
|
|
||||||
uint8_t knownBrightness = 0;
|
|
||||||
uint8_t knownEffectSpeed = 0;
|
|
||||||
uint8_t knownEffectIntensity = 0;
|
|
||||||
uint8_t knownMode = 0;
|
|
||||||
uint8_t knownPalette = 0;
|
|
||||||
uint8_t knownMinute = 99;
|
|
||||||
uint8_t knownHour = 99;
|
|
||||||
byte brightness100;
|
|
||||||
byte fxspeed100;
|
|
||||||
byte fxintensity100;
|
|
||||||
bool knownnightlight = nightlightActive;
|
|
||||||
bool wificonnected = interfacesInited;
|
|
||||||
bool powerON = true;
|
|
||||||
|
|
||||||
bool displayTurnedOff = false;
|
|
||||||
unsigned long nextUpdate = 0;
|
|
||||||
unsigned long lastRedraw = 0;
|
|
||||||
unsigned long overlayUntil = 0;
|
|
||||||
|
|
||||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
|
||||||
byte markLineNum = 255;
|
|
||||||
byte markColNum = 255;
|
|
||||||
|
|
||||||
// strings to reduce flash memory usage (used more than twice)
|
|
||||||
static const char _name[];
|
|
||||||
static const char _enabled[];
|
|
||||||
static const char _contrast[];
|
|
||||||
static const char _refreshRate[];
|
|
||||||
static const char _screenTimeOut[];
|
|
||||||
static const char _flip[];
|
|
||||||
static const char _sleepMode[];
|
|
||||||
static const char _clockMode[];
|
|
||||||
static const char _showSeconds[];
|
|
||||||
static const char _busClkFrequency[];
|
|
||||||
static const char _contrastFix[];
|
|
||||||
|
|
||||||
// If display does not work or looks corrupted check the
|
|
||||||
// constructor reference:
|
|
||||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
|
||||||
// or check the gallery:
|
|
||||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
|
||||||
|
|
||||||
// some displays need this to properly apply contrast
|
|
||||||
void setVcomh(bool highContrast);
|
|
||||||
void startDisplay();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrappers for screen drawing
|
|
||||||
*/
|
|
||||||
void setFlipMode(uint8_t mode);
|
|
||||||
void setContrast(uint8_t contrast);
|
|
||||||
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false);
|
|
||||||
void draw2x2String(uint8_t col, uint8_t row, const char *string);
|
|
||||||
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false);
|
|
||||||
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font);
|
|
||||||
void draw2x2GlyphIcons();
|
|
||||||
uint8_t getCols();
|
|
||||||
void clear();
|
|
||||||
void setPowerSave(uint8_t save);
|
|
||||||
void center(String &line, uint8_t width);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the current date and time in large characters
|
|
||||||
* on the middle rows. Based 24 or 12 hour depending on
|
|
||||||
* the useAMPM configuration.
|
|
||||||
*/
|
|
||||||
void showTime();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable sleep (turn the display off) or clock mode.
|
|
||||||
*/
|
|
||||||
void sleepOrClock(bool enabled);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// gets called once at boot. Do all initialization that doesn't depend on
|
|
||||||
// network here
|
|
||||||
void setup() override;
|
|
||||||
|
|
||||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
|
||||||
// interfaces here
|
|
||||||
void connected() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Da loop.
|
|
||||||
*/
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
//function to update lastredraw
|
|
||||||
inline void updateRedrawTime() { lastRedraw = millis(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redraw the screen (but only if things have changed
|
|
||||||
* or if forceRedraw).
|
|
||||||
*/
|
|
||||||
void redraw(bool forceRedraw);
|
|
||||||
|
|
||||||
void updateBrightness();
|
|
||||||
void updateSpeed();
|
|
||||||
void updateIntensity();
|
|
||||||
void drawStatusIcons();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* marks the position of the arrow showing
|
|
||||||
* the current setting being changed
|
|
||||||
* pass line and colum info
|
|
||||||
*/
|
|
||||||
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
|
|
||||||
|
|
||||||
//Draw the arrow for the current setting being changed
|
|
||||||
void drawArrow();
|
|
||||||
|
|
||||||
//Display the current effect or palette (desiredEntry)
|
|
||||||
// on the appropriate line (row).
|
|
||||||
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there screen is off or in clock is displayed,
|
|
||||||
* this will return true. This allows us to throw away
|
|
||||||
* the first input from the rotary encoder but
|
|
||||||
* to wake up the screen.
|
|
||||||
*/
|
|
||||||
bool wakeDisplay();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to show one line and a glyph as overlay for a period of time.
|
|
||||||
* Clears the screen and prints.
|
|
||||||
* Used in Rotary Encoder usermod.
|
|
||||||
*/
|
|
||||||
void overlay(const char* line1, long showHowLong, byte glyphType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to show Akemi WLED logo overlay for a period of time.
|
|
||||||
* Clears the screen and prints.
|
|
||||||
*/
|
|
||||||
void overlayLogo(long showHowLong);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows you to show two lines as overlay for a period of time.
|
|
||||||
* Clears the screen and prints.
|
|
||||||
* Used in Auto Save usermod
|
|
||||||
*/
|
|
||||||
void overlay(const char* line1, const char* line2, long showHowLong);
|
|
||||||
|
|
||||||
void networkOverlay(const char* line1, long showHowLong);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handleButton() can be used to override default button behaviour. Returning true
|
|
||||||
* will prevent button working in a default way.
|
|
||||||
* Replicating button.cpp
|
|
||||||
*/
|
|
||||||
bool handleButton(uint8_t b);
|
|
||||||
|
|
||||||
void onUpdateBegin(bool init) override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
|
||||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
|
||||||
* Below it is shown how this could be used for e.g. a light sensor
|
|
||||||
*/
|
|
||||||
//void addToJsonInfo(JsonObject& root) override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
|
||||||
* Values in the state object may be modified by connected clients
|
|
||||||
*/
|
|
||||||
//void addToJsonState(JsonObject& root) override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
|
||||||
* Values in the state object may be modified by connected clients
|
|
||||||
*/
|
|
||||||
//void readFromJsonState(JsonObject& root) override;
|
|
||||||
|
|
||||||
void appendConfigData() override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
|
||||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
|
||||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
|
||||||
*
|
|
||||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
|
||||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
|
||||||
* Use it sparingly and always in the loop, never in network callbacks!
|
|
||||||
*
|
|
||||||
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
|
||||||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
|
||||||
*
|
|
||||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
|
||||||
*/
|
|
||||||
void addToConfig(JsonObject& root) override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
|
||||||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
|
||||||
*
|
|
||||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
|
||||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
|
||||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
|
||||||
*/
|
|
||||||
bool readFromConfig(JsonObject& root) override;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
|
||||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
|
||||||
*/
|
|
||||||
uint16_t getId() override {
|
|
||||||
return USERMOD_ID_FOUR_LINE_DISP;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// strings to reduce flash memory usage (used more than twice)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
|
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
|
||||||
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
|
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
|
||||||
@ -1387,4 +1069,4 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
|
|||||||
|
|
||||||
|
|
||||||
static FourLineDisplayUsermod usermod_v2_four_line_display_alt;
|
static FourLineDisplayUsermod usermod_v2_four_line_display_alt;
|
||||||
REGISTER_USERMOD(usermod_v2_four_line_display_alt);
|
REGISTER_USERMOD(usermod_v2_four_line_display_alt);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name:": "usermod_v2_klipper_percentage"
|
"name": "usermod_v2_klipper_percentage",
|
||||||
|
"build": { "libArchive": false }
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"name:": "usermod_v2_ping_pong_clock"
|
"name": "usermod_v2_ping_pong_clock",
|
||||||
|
"build": { "libArchive": false }
|
||||||
}
|
}
|
@ -1,3 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name:": "usermod_v2_rotary_encoder_ui_ALT"
|
"name": "rotary_encoder_ui_ALT",
|
||||||
|
"build": {
|
||||||
|
"libArchive": false,
|
||||||
|
"extraScript": "setup_deps.py"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,13 +1,11 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
default_envs = esp32dev
|
default_envs = esp32dev_re
|
||||||
|
|
||||||
[env:esp32dev]
|
[env:esp32dev_re]
|
||||||
board = esp32dev
|
extends = env:esp32dev_V4
|
||||||
platform = ${esp32.platform}
|
custom_usermods = ${env:esp32dev_V4.custom_usermods} rotary_encoder_ui_ALT
|
||||||
build_unflags = ${common.build_unflags}
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags_esp32}
|
${env:esp32dev_V4.build_flags}
|
||||||
-D USERMOD_ROTARY_ENCODER_UI
|
|
||||||
-D USERMOD_ROTARY_ENCODER_GPIO=INPUT
|
-D USERMOD_ROTARY_ENCODER_GPIO=INPUT
|
||||||
-D ENCODER_DT_PIN=21
|
-D ENCODER_DT_PIN=21
|
||||||
-D ENCODER_CLK_PIN=23
|
-D ENCODER_CLK_PIN=23
|
||||||
|
@ -5,6 +5,7 @@ This usermod supports the UI of the `usermod_v2_rotary_encoder_ui_ALT`.
|
|||||||
## Functionalities
|
## Functionalities
|
||||||
|
|
||||||
Press the encoder to cycle through the options:
|
Press the encoder to cycle through the options:
|
||||||
|
|
||||||
* Brightness
|
* Brightness
|
||||||
* Speed
|
* Speed
|
||||||
* Intensity
|
* Intensity
|
||||||
@ -25,10 +26,6 @@ Copy the example `platformio_override.sample.ini` to the root directory of your
|
|||||||
|
|
||||||
### Define Your Options
|
### Define Your Options
|
||||||
|
|
||||||
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
|
|
||||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
|
|
||||||
also tells this usermod that the display is available
|
|
||||||
(see the Four Line Display usermod `readme.md` for more details)
|
|
||||||
* `ENCODER_DT_PIN` - defaults to 18
|
* `ENCODER_DT_PIN` - defaults to 18
|
||||||
* `ENCODER_CLK_PIN` - defaults to 5
|
* `ENCODER_CLK_PIN` - defaults to 5
|
||||||
* `ENCODER_SW_PIN` - defaults to 19
|
* `ENCODER_SW_PIN` - defaults to 19
|
||||||
@ -43,4 +40,5 @@ No special requirements.
|
|||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
2021-10
|
2021-10
|
||||||
|
|
||||||
* First public release
|
* 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