Merge branch 'main' of https://github.com/aircoookie/WLED into audioreactive-prototype
This commit is contained in:
15
usermods/ADS1115_v2/ChannelSettings.h
Normal file
15
usermods/ADS1115_v2/ChannelSettings.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "wled.h"
|
||||
|
||||
namespace ADS1115
|
||||
{
|
||||
struct ChannelSettings {
|
||||
const String settingName;
|
||||
bool isEnabled;
|
||||
String name;
|
||||
String units;
|
||||
const uint16_t mux;
|
||||
float multiplier;
|
||||
float offset;
|
||||
uint8_t decimals;
|
||||
};
|
||||
}
|
||||
10
usermods/ADS1115_v2/readme.md
Normal file
10
usermods/ADS1115_v2/readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# ADS1115 16-Bit ADC with four inputs
|
||||
|
||||
This usermod will read from an ADS1115 ADC. The voltages are displayed in the Info section of the web UI.
|
||||
|
||||
Configuration is all completed via the Usermod menu. There are no settings to set in code!
|
||||
|
||||
## Installation
|
||||
|
||||
Add the build flag `-D USERMOD_ADS1115` to your platformio environment.
|
||||
Uncomment libraries with comment `#For ADS1115 sensor uncomment following`
|
||||
255
usermods/ADS1115_v2/usermod_ads1115.h
Normal file
255
usermods/ADS1115_v2/usermod_ads1115.h
Normal file
@@ -0,0 +1,255 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Adafruit_ADS1X15.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ChannelSettings.h"
|
||||
|
||||
using namespace ADS1115;
|
||||
|
||||
class ADS1115Usermod : public Usermod {
|
||||
public:
|
||||
void setup() {
|
||||
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V
|
||||
|
||||
if (!ads.begin()) {
|
||||
Serial.println("Failed to initialize ADS");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initChannel()) {
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
startReading();
|
||||
|
||||
isEnabled = true;
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (isEnabled && millis() - lastTime > loopInterval) {
|
||||
lastTime = millis();
|
||||
|
||||
// If we don't have new data, skip this iteration.
|
||||
if (!ads.conversionComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateResult();
|
||||
moveToNextChannel();
|
||||
startReading();
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) user = root.createNestedObject(F("u"));
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
|
||||
if (!settingsPtr->isEnabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonArray lightArr = user.createNestedArray(settingsPtr->name); //name
|
||||
float value = round((readings[i] + settingsPtr->offset) * settingsPtr->multiplier, settingsPtr->decimals);
|
||||
lightArr.add(value); //value
|
||||
lightArr.add(" " + settingsPtr->units); //unit
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(F("ADC ADS1115"));
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
JsonObject channel = top.createNestedObject(settingsPtr->settingName);
|
||||
channel[F("Enabled")] = settingsPtr->isEnabled;
|
||||
channel[F("Name")] = settingsPtr->name;
|
||||
channel[F("Units")] = settingsPtr->units;
|
||||
channel[F("Multiplier")] = settingsPtr->multiplier;
|
||||
channel[F("Offset")] = settingsPtr->offset;
|
||||
channel[F("Decimals")] = settingsPtr->decimals;
|
||||
}
|
||||
|
||||
top[F("Loop Interval")] = loopInterval;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root[F("ADC ADS1115")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
bool hasEnabledChannels = false;
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount && configComplete; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
JsonObject channel = top[settingsPtr->settingName];
|
||||
|
||||
configComplete &= !channel.isNull();
|
||||
|
||||
configComplete &= getJsonValue(channel[F("Enabled")], settingsPtr->isEnabled);
|
||||
configComplete &= getJsonValue(channel[F("Name")], settingsPtr->name);
|
||||
configComplete &= getJsonValue(channel[F("Units")], settingsPtr->units);
|
||||
configComplete &= getJsonValue(channel[F("Multiplier")], settingsPtr->multiplier);
|
||||
configComplete &= getJsonValue(channel[F("Offset")], settingsPtr->offset);
|
||||
configComplete &= getJsonValue(channel[F("Decimals")], settingsPtr->decimals);
|
||||
|
||||
hasEnabledChannels |= settingsPtr->isEnabled;
|
||||
}
|
||||
|
||||
configComplete &= getJsonValue(top[F("Loop Interval")], loopInterval);
|
||||
|
||||
isEnabled = isInitialized && configComplete && hasEnabledChannels;
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_ADS1115;
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint8_t channelsCount = 8;
|
||||
|
||||
ChannelSettings channelSettings[channelsCount] = {
|
||||
{
|
||||
"Differential reading from AIN0 (P) and AIN1 (N)",
|
||||
false,
|
||||
"Differential AIN0 AIN1",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_0_1,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN0 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN0 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_0_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN1 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN1 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_1_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN2 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN2 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_2_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN0",
|
||||
false,
|
||||
"Single-ended AIN0",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_0,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN1",
|
||||
false,
|
||||
"Single-ended AIN1",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_1,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN2",
|
||||
false,
|
||||
"Single-ended AIN2",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_2,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN3",
|
||||
false,
|
||||
"Single-ended AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
};
|
||||
float readings[channelsCount] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
unsigned long loopInterval = 1000;
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
Adafruit_ADS1115 ads;
|
||||
uint8_t activeChannel;
|
||||
|
||||
bool isEnabled = false;
|
||||
bool isInitialized = false;
|
||||
|
||||
static float round(float value, uint8_t decimals) {
|
||||
return roundf(value * powf(10, decimals)) / powf(10, decimals);
|
||||
}
|
||||
|
||||
bool initChannel() {
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
if (channelSettings[i].isEnabled) {
|
||||
activeChannel = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
activeChannel = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToNextChannel() {
|
||||
uint8_t oldActiveChannel = activeChannel;
|
||||
|
||||
do
|
||||
{
|
||||
if (++activeChannel >= channelsCount){
|
||||
activeChannel = 0;
|
||||
}
|
||||
}
|
||||
while (!channelSettings[activeChannel].isEnabled && oldActiveChannel != activeChannel);
|
||||
}
|
||||
|
||||
void startReading() {
|
||||
ads.startADCReading(channelSettings[activeChannel].mux, /*continuous=*/false);
|
||||
}
|
||||
|
||||
void updateResult() {
|
||||
int16_t results = ads.getLastConversionResults();
|
||||
readings[activeChannel] = ads.computeVolts(results);
|
||||
}
|
||||
};
|
||||
244
usermods/Analog_Clock/Analog_Clock.h
Normal file
244
usermods/Analog_Clock/Analog_Clock.h
Normal file
@@ -0,0 +1,244 @@
|
||||
#pragma once
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Usermod for analog clock
|
||||
*/
|
||||
extern Timezone* tz;
|
||||
|
||||
class AnalogClockUsermod : public Usermod {
|
||||
private:
|
||||
static constexpr uint32_t refreshRate = 50; // per second
|
||||
static constexpr uint32_t refreshDelay = 1000 / refreshRate;
|
||||
|
||||
struct Segment {
|
||||
// config
|
||||
int16_t firstLed = 0;
|
||||
int16_t lastLed = 59;
|
||||
int16_t centerLed = 0;
|
||||
|
||||
// runtime
|
||||
int16_t size;
|
||||
|
||||
Segment() {
|
||||
update();
|
||||
}
|
||||
|
||||
void validateAndUpdate() {
|
||||
if (firstLed < 0 || firstLed >= strip.getLengthTotal() ||
|
||||
lastLed < firstLed || lastLed >= strip.getLengthTotal()) {
|
||||
*this = {};
|
||||
return;
|
||||
}
|
||||
if (centerLed < firstLed || centerLed > lastLed) {
|
||||
centerLed = firstLed;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void update() {
|
||||
size = lastLed - firstLed + 1;
|
||||
}
|
||||
};
|
||||
|
||||
// configuration (available in API and stored in flash)
|
||||
bool enabled = false;
|
||||
Segment mainSegment;
|
||||
uint32_t hourColor = 0x0000FF;
|
||||
uint32_t minuteColor = 0x00FF00;
|
||||
bool secondsEnabled = true;
|
||||
Segment secondsSegment;
|
||||
uint32_t secondColor = 0xFF0000;
|
||||
bool blendColors = true;
|
||||
uint16_t secondsEffect = 0;
|
||||
|
||||
// runtime
|
||||
bool initDone = false;
|
||||
uint32_t lastOverlayDraw = 0;
|
||||
|
||||
void validateAndUpdate() {
|
||||
mainSegment.validateAndUpdate();
|
||||
secondsSegment.validateAndUpdate();
|
||||
if (secondsEffect < 0 || secondsEffect > 1) {
|
||||
secondsEffect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t adjustToSegment(double progress, Segment const& segment) {
|
||||
int16_t led = segment.centerLed + progress * segment.size;
|
||||
return led > segment.lastLed
|
||||
? segment.firstLed + led - segment.lastLed - 1
|
||||
: led;
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t n, uint32_t c) {
|
||||
if (!blendColors) {
|
||||
strip.setPixelColor(n, c);
|
||||
} else {
|
||||
uint32_t oldC = strip.getPixelColor(n);
|
||||
strip.setPixelColor(n, qadd32(oldC, c));
|
||||
}
|
||||
}
|
||||
|
||||
String colorToHexString(uint32_t c) {
|
||||
char buffer[9];
|
||||
sprintf(buffer, "%06X", c);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool hexStringToColor(String const& s, uint32_t& c, uint32_t def) {
|
||||
errno = 0;
|
||||
char* ep;
|
||||
unsigned long long r = strtoull(s.c_str(), &ep, 16);
|
||||
if (*ep == 0 && errno != ERANGE) {
|
||||
c = r;
|
||||
return true;
|
||||
} else {
|
||||
c = def;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
|
||||
uint32_t ms = time.ms % 1000;
|
||||
uint8_t b0 = (cos8(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
|
||||
uint8_t b1 = (sin8(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
|
||||
}
|
||||
|
||||
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
|
||||
return RGBW32(
|
||||
qadd8(R(c1), R(c2)),
|
||||
qadd8(G(c1), G(c2)),
|
||||
qadd8(B(c1), B(c2)),
|
||||
qadd8(W(c1), W(c2))
|
||||
);
|
||||
}
|
||||
|
||||
static inline uint32_t scale32(uint32_t c, fract8 scale) {
|
||||
return RGBW32(
|
||||
scale8(R(c), scale),
|
||||
scale8(G(c), scale),
|
||||
scale8(B(c), scale),
|
||||
scale8(W(c), scale)
|
||||
);
|
||||
}
|
||||
|
||||
static inline int16_t dec(int16_t n, int16_t i, Segment const& seg) {
|
||||
return n - seg.firstLed >= i
|
||||
? n - i
|
||||
: seg.lastLed - seg.firstLed - i + n + 1;
|
||||
}
|
||||
|
||||
static inline int16_t inc(int16_t n, int16_t i, Segment const& seg) {
|
||||
int16_t r = n + i;
|
||||
if (r > seg.lastLed) {
|
||||
return seg.firstLed + n - seg.lastLed;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public:
|
||||
AnalogClockUsermod() {
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
initDone = true;
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (millis() - lastOverlayDraw > refreshDelay) {
|
||||
strip.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw() override {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastOverlayDraw = millis();
|
||||
|
||||
auto time = toki.getTime();
|
||||
auto localSec = tz ? tz->toLocal(time.sec) : time.sec;
|
||||
double secondP = second(localSec) / 60.0;
|
||||
double minuteP = minute(localSec) / 60.0;
|
||||
double hourP = (hour(localSec) % 12) / 12.0 + minuteP / 12.0;
|
||||
|
||||
if (secondsEnabled) {
|
||||
int16_t secondLed = adjustToSegment(secondP, secondsSegment);
|
||||
|
||||
switch (secondsEffect) {
|
||||
case 0: // no effect
|
||||
setPixelColor(secondLed, secondColor);
|
||||
break;
|
||||
|
||||
case 1: // fading seconds
|
||||
secondsEffectSineFade(secondLed, time);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: move to secondsTrailEffect
|
||||
// for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
|
||||
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
|
||||
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
|
||||
// setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
|
||||
// }
|
||||
}
|
||||
|
||||
setPixelColor(adjustToSegment(minuteP, mainSegment), minuteColor);
|
||||
setPixelColor(adjustToSegment(hourP, mainSegment), hourColor);
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root) override {
|
||||
validateAndUpdate();
|
||||
|
||||
JsonObject top = root.createNestedObject("Analog Clock");
|
||||
top["Overlay Enabled"] = enabled;
|
||||
top["First LED (Main Ring)"] = mainSegment.firstLed;
|
||||
top["Last LED (Main Ring)"] = mainSegment.lastLed;
|
||||
top["Center/12h LED (Main Ring)"] = mainSegment.centerLed;
|
||||
top["Hour Color (RRGGBB)"] = colorToHexString(hourColor);
|
||||
top["Minute Color (RRGGBB)"] = colorToHexString(minuteColor);
|
||||
top["Show Seconds"] = secondsEnabled;
|
||||
top["First LED (Seconds Ring)"] = secondsSegment.firstLed;
|
||||
top["Last LED (Seconds Ring)"] = secondsSegment.lastLed;
|
||||
top["Center/12h LED (Seconds Ring)"] = secondsSegment.centerLed;
|
||||
top["Second Color (RRGGBB)"] = colorToHexString(secondColor);
|
||||
top["Seconds Effect (0-1)"] = secondsEffect;
|
||||
top["Blend Colors"] = blendColors;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root) override {
|
||||
JsonObject top = root["Analog Clock"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
String color;
|
||||
configComplete &= getJsonValue(top["Overlay Enabled"], enabled, false);
|
||||
configComplete &= getJsonValue(top["First LED (Main Ring)"], mainSegment.firstLed, 0);
|
||||
configComplete &= getJsonValue(top["Last LED (Main Ring)"], mainSegment.lastLed, 59);
|
||||
configComplete &= getJsonValue(top["Center/12h LED (Main Ring)"], mainSegment.centerLed, 0);
|
||||
configComplete &= getJsonValue(top["Hour Color (RRGGBB)"], color, "0000FF") && hexStringToColor(color, hourColor, 0x0000FF);
|
||||
configComplete &= getJsonValue(top["Minute Color (RRGGBB)"], color, "00FF00") && hexStringToColor(color, minuteColor, 0x00FF00);
|
||||
configComplete &= getJsonValue(top["Show Seconds"], secondsEnabled, true);
|
||||
configComplete &= getJsonValue(top["First LED (Seconds Ring)"], secondsSegment.firstLed, 0);
|
||||
configComplete &= getJsonValue(top["Last LED (Seconds Ring)"], secondsSegment.lastLed, 59);
|
||||
configComplete &= getJsonValue(top["Center/12h LED (Seconds Ring)"], secondsSegment.centerLed, 0);
|
||||
configComplete &= getJsonValue(top["Second Color (RRGGBB)"], color, "FF0000") && hexStringToColor(color, secondColor, 0xFF0000);
|
||||
configComplete &= getJsonValue(top["Seconds Effect (0-1)"], secondsEffect, 0);
|
||||
configComplete &= getJsonValue(top["Blend Colors"], blendColors, true);
|
||||
|
||||
if (initDone) {
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId() override {
|
||||
return USERMOD_ID_ANALOG_CLOCK;
|
||||
}
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_BH1750 - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; 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_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
; USERMOD_BH1750_OFFSET_VALUE - the offset value to report on, defaults to 1
|
||||
;
|
||||
[env:usermod_BH1750_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BH1750
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
claws/BH1750 @ ^1.2.0
|
||||
@@ -3,22 +3,47 @@
|
||||
This usermod will read from an ambient light sensor like the BH1750 sensor.
|
||||
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
|
||||
|
||||
## Installation
|
||||
## Dependencies
|
||||
- Libraries
|
||||
- `claws/BH1750 @^1.2.0`
|
||||
- This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
|
||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
## Compiliation
|
||||
|
||||
### Define Your Options
|
||||
To enable, compile with `USERMOD_BH1750` defined (e.g. in `platformio_override.ini`)
|
||||
```ini
|
||||
[env:usermod_BH1750_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BH1750
|
||||
lib_deps =
|
||||
${esp8266.lib_deps}
|
||||
claws/BH1750 @ ^1.2.0
|
||||
```
|
||||
|
||||
* `USERMOD_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `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_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||
### Configuration Options
|
||||
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
|
||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
|
||||
All parameters can be configured at runtime using Usermods settings page.
|
||||
In addition, on the Usermod screen allows you to:
|
||||
- enable/disable the usermod
|
||||
- Enable Home Assistant Discovery of usermod
|
||||
- Configure the SCL/SDA pins
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_BH1750_d1_mini`.
|
||||
## API
|
||||
The following method is available to interact with the usermod from other code modules:
|
||||
- `getIlluminance` read the brightness from the sensor
|
||||
|
||||
## Change Log
|
||||
Jul 2022
|
||||
- Added Home Assistant Discovery
|
||||
- Implemented PinManager to register pins
|
||||
- Made pins configurable in usermod menu
|
||||
- Added API call to read illuminance from other modules
|
||||
- Enhanced info-screen outputs
|
||||
- Updated `readme.md`
|
||||
@@ -1,3 +1,6 @@
|
||||
// force the compiler to show a warning to confirm that this file is included
|
||||
#warning **** Included USERMOD_BH1750 ****
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
@@ -47,6 +50,24 @@ private:
|
||||
static const char _maxReadInterval[];
|
||||
static const char _minReadInterval[];
|
||||
static const char _offset[];
|
||||
static const char _HomeAssistantDiscovery[];
|
||||
|
||||
// set the default pins based on the architecture, these get overridden by Usermod menu settings
|
||||
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else // ESP8266 boards
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#endif
|
||||
int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
|
||||
bool initDone = false;
|
||||
bool sensorFound = false;
|
||||
|
||||
// Home Assistant and MQTT
|
||||
String mqttLuminanceTopic = F("");
|
||||
bool mqttInitialized = false;
|
||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
||||
|
||||
BH1750 lightMeter;
|
||||
float lastLux = -1000;
|
||||
@@ -55,12 +76,59 @@ private:
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
}
|
||||
|
||||
// set up Home Assistant discovery entries
|
||||
void _mqttInitialize()
|
||||
{
|
||||
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
||||
|
||||
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = String(serverDescription) + F(" ") + name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F("WLED");
|
||||
device[F("model")] = F("FOSS");
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin();
|
||||
lightMeter.begin();
|
||||
bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
|
||||
PinOwner po = PinOwner::UM_BH1750; // defaults to being pinowner for SCL/SDA pins
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
|
||||
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, po)) return;
|
||||
|
||||
Wire.begin(ioPin[1], ioPin[0]);
|
||||
|
||||
sensorFound = lightMeter.begin();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
@@ -90,18 +158,25 @@ public:
|
||||
lastSend = millis();
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char subuf[45];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/luminance"));
|
||||
mqtt->publish(subuf, 0, true, String(lux).c_str());
|
||||
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("Missing MQTT connection. Not publishing data");
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline float getIlluminance() {
|
||||
return (float)lastLux;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
@@ -109,28 +184,23 @@ public:
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux_json = user.createNestedArray(F("Luminance"));
|
||||
|
||||
if (!getLuminanceComplete)
|
||||
{
|
||||
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;
|
||||
// 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"));
|
||||
}
|
||||
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
// (called from set.cpp) stores persistent properties to cfg.json
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
@@ -138,35 +208,68 @@ public:
|
||||
top[FPSTR(_enabled)] = !disabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
JsonArray io_pin = top.createNestedArray(F("pin"));
|
||||
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
|
||||
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
|
||||
|
||||
DEBUG_PRINTLN(F("Photoresistor config saved."));
|
||||
DEBUG_PRINTLN(F("BH1750 config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
// called before setup() to populate properties from values stored in cfg.json
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
|
||||
|
||||
// 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)], disabled, 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);
|
||||
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
|
||||
|
||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
||||
maxReadingInterval = (top[FPSTR(_maxReadInterval)] | maxReadingInterval); // ms
|
||||
minReadingInterval = (top[FPSTR(_minReadInterval)] | minReadingInterval); // ms
|
||||
offset = top[FPSTR(_offset)] | offset;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
|
||||
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
|
||||
PinOwner po = PinOwner::UM_BH1750;
|
||||
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
|
||||
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
|
||||
setup();
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[F("pin")].isNull();
|
||||
}
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
return configComplete;
|
||||
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
@@ -174,4 +277,5 @@ const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
|
||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
|
||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
|
||||
const char Usermod_BH1750::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux";
|
||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
*/
|
||||
#ifdef USERMOD_BH1750
|
||||
#include "../usermods/BH1750_v2/usermod_BH1750.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
#ifdef USERMOD_BH1750
|
||||
usermods.add(new Usermod_BH1750());
|
||||
#endif
|
||||
}
|
||||
@@ -6,12 +6,13 @@
|
||||
; USERMOD_DHT_CELSIUS - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
; USERMOD_DHT_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
; USERMOD_DHT_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
; USERMOD_DHT_MQTT - publish measurements to the MQTT broker
|
||||
; USERMOD_DHT_STATS - For debug, report delay stats
|
||||
|
||||
[env:d1_mini_usermod_dht_C]
|
||||
extends = env:d1_mini
|
||||
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
|
||||
lib_deps = ${env.lib_deps}
|
||||
lib_deps = ${env:d1_mini.lib_deps}
|
||||
https://github.com/alwynallan/DHT_nonblocking
|
||||
|
||||
[env:custom32_LEDPIN_16_usermod_dht_C]
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# DHT Temperature/Humidity sensor usermod
|
||||
|
||||
This usermod will read from an attached DHT22 or DHT11 humidity and temperature sensor.
|
||||
The sensor readings are displayed in the Info section of the web UI.
|
||||
The sensor readings are displayed in the Info section of the web UI (and optionally send to a MQTT broker).
|
||||
|
||||
If sensor is not detected after a while (10 update intervals), this usermod will be disabled.
|
||||
|
||||
If enabled measured temperature and humidity will be published to the following MQTT topics
|
||||
* `{devceTopic}/dht/temperature`
|
||||
* `{devceTopic}/dht/humidity`
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
@@ -17,6 +21,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
|
||||
* `USERMOD_DHT_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
* `USERMOD_DHT_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
* `USERMOD_DHT_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
* `USERMOD_DHT_MQTT` - publish measurements to the MQTT broker
|
||||
* `USERMOD_DHT_STATS` - For debug, report delay stats
|
||||
|
||||
## Project link
|
||||
@@ -29,13 +34,15 @@ If you are using `platformio_override.ini`, you should be able to refresh the ta
|
||||
|
||||
|
||||
## Change Log
|
||||
|
||||
2022-10-15
|
||||
* Add possibility to publish sensor readings to an MQTT broker
|
||||
* fix compilation error for sample [env:d1_mini_usermod_dht_C] task
|
||||
2020-02-04
|
||||
* Change default QuinLed pin to Q2
|
||||
* Instead of trying to keep updates at constant cadence, space readings out by measurement interval; hope this helps to avoid occasional bursts of readings with errors
|
||||
* Add some more (optional) stats
|
||||
2020-02-03
|
||||
* Due to poor readouts on ESP32 with previous DHT library, rewrote to use https://github.com/alwynallan/DHT_nonblocking
|
||||
* The new library serializes/delays up to 5ms for the sensor readout
|
||||
2020-02-02
|
||||
* The new library serializes/delays up to 5ms for the sensor readout
|
||||
2020-02-02
|
||||
* Created
|
||||
|
||||
@@ -62,6 +62,10 @@ class UsermodDHT : public Usermod {
|
||||
float humidity, temperature = 0;
|
||||
bool initializing = true;
|
||||
bool disabled = false;
|
||||
#ifdef USERMOD_DHT_MQTT
|
||||
char dhtMqttTopic[64];
|
||||
size_t dhtMqttTopicLen;
|
||||
#endif
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long nextResetStatsTime = 0;
|
||||
uint16_t updates = 0;
|
||||
@@ -76,6 +80,10 @@ class UsermodDHT : public Usermod {
|
||||
void setup() {
|
||||
nextReadTime = millis() + USERMOD_DHT_FIRST_MEASUREMENT_AT;
|
||||
lastReadTime = millis();
|
||||
#ifdef USERMOD_DHT_MQTT
|
||||
sprintf(dhtMqttTopic, "%s/dht", mqttDeviceTopic);
|
||||
dhtMqttTopicLen = strlen(dhtMqttTopic);
|
||||
#endif
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
nextResetStatsTime = millis() + 60*60*1000;
|
||||
#endif
|
||||
@@ -110,10 +118,29 @@ class UsermodDHT : public Usermod {
|
||||
temperature = tempC * 9 / 5 + 32;
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_DHT_MQTT
|
||||
// 10^n where n is number of decimal places to display in mqtt message. Please adjust buff size together with this constant
|
||||
#define FLOAT_PREC 100
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char buff[10];
|
||||
|
||||
strcpy(dhtMqttTopic + dhtMqttTopicLen, "/temperature");
|
||||
sprintf(buff, "%d.%d", (int)temperature, ((int)(temperature * FLOAT_PREC)) % FLOAT_PREC);
|
||||
mqtt->publish(dhtMqttTopic, 0, false, buff);
|
||||
|
||||
sprintf(buff, "%d.%d", (int)humidity, ((int)(humidity * FLOAT_PREC)) % FLOAT_PREC);
|
||||
strcpy(dhtMqttTopic + dhtMqttTopicLen, "/humidity");
|
||||
mqtt->publish(dhtMqttTopic, 0, false, buff);
|
||||
|
||||
dhtMqttTopic[dhtMqttTopicLen] = '\0';
|
||||
}
|
||||
#undef FLOAT_PREC
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + USERMOD_DHT_MEASUREMENT_INTERVAL;
|
||||
lastReadTime = millis();
|
||||
initializing = false;
|
||||
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long icalc = millis() - currentIteration;
|
||||
if (icalc > maxIteration) {
|
||||
@@ -134,7 +161,7 @@ class UsermodDHT : public Usermod {
|
||||
dcalc = millis() - dcalc;
|
||||
if (dcalc > maxDelay) {
|
||||
maxDelay = dcalc;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (((millis() - lastReadTime) > 10*USERMOD_DHT_MEASUREMENT_INTERVAL)) {
|
||||
@@ -207,7 +234,7 @@ class UsermodDHT : public Usermod {
|
||||
temp.add("°F");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_DHT;
|
||||
|
||||
@@ -5,13 +5,12 @@ It can be useful for kitchen strips to avoid any touches.
|
||||
- on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
|
||||
- brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
|
||||
Configure brightness by changing distance to the sensor (see parameters below for customization).
|
||||
"macroLongPress" is also called here.
|
||||
|
||||
## Installation
|
||||
|
||||
## Installation
|
||||
|
||||
1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
|
||||
2. Add `-D USERMOD_VL53L0X_GESTURES` to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
|
||||
In my case, for example: `build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES`
|
||||
In my case, for example: `build_flags = ${env.build_flags} -D USERMOD_VL53L0X_GESTURES`
|
||||
3. Add "pololu/VL53L0X" dependency below to `lib_deps` like this:
|
||||
```ini
|
||||
lib_deps = ${env.lib_deps}
|
||||
@@ -21,15 +20,10 @@ lib_deps = ${env.lib_deps}
|
||||
My entire `platformio_override.ini` for example (for nodemcu board):
|
||||
```ini
|
||||
[platformio]
|
||||
default_envs = nodemcu
|
||||
default_envs = nodemcuv2
|
||||
|
||||
[env:nodemcu]
|
||||
board = nodemcu
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
|
||||
[env:nodemcuv2]
|
||||
build_flags = ${env.build_flags} -D USERMOD_VL53L0X_GESTURES
|
||||
lib_deps = ${env.lib_deps}
|
||||
pololu/VL53L0X @ ^1.3.0
|
||||
pololu/VL53L0X @ ^1.3.0
|
||||
```
|
||||
@@ -3,14 +3,13 @@
|
||||
* It can be useful for kitchen strips to avoid any touches.
|
||||
* - on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
|
||||
* - brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
|
||||
* Configure brightness by changing distance to the sensor (see parameters below for customization).
|
||||
* "macroLongPress" is also called here.
|
||||
Configure brightness by changing distance to the sensor (see parameters below for customization).
|
||||
*
|
||||
* Enabling this mod usermod:
|
||||
* Enabling this usermod:
|
||||
* 1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
|
||||
* 2. Add "-D USERMOD_VL53L0X_GESTURES" to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
|
||||
* In my case, for example: build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
|
||||
* 3. Add "pololu/VL53L0X" dependency to lib_deps like this:
|
||||
* 2. Add `-D USERMOD_VL53L0X_GESTURES` to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
|
||||
* In my case, for example: `build_flags = ${env.build_flags} -D USERMOD_VL53L0X_GESTURES`
|
||||
* 3. Add "pololu/VL53L0X" dependency below to `lib_deps` like this:
|
||||
* lib_deps = ${env.lib_deps}
|
||||
* pololu/VL53L0X @ ^1.3.0
|
||||
*/
|
||||
@@ -22,19 +21,19 @@
|
||||
#include <VL53L0X.h>
|
||||
|
||||
#ifndef VL53L0X_MAX_RANGE_MM
|
||||
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
|
||||
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimeters to react for motions
|
||||
#endif
|
||||
|
||||
#ifndef VL53L0X_MIN_RANGE_OFFSET
|
||||
#define VL53L0X_MIN_RANGE_OFFSET 60 // minimal range in millimiters that sensor can detect. Used in long motions to correct brightnes calculation.
|
||||
#define VL53L0X_MIN_RANGE_OFFSET 60 // minimal range in millimeters that sensor can detect. Used in long motions to correct brightness calculation.
|
||||
#endif
|
||||
|
||||
#ifndef VL53L0X_DELAY_MS
|
||||
#define VL53L0X_DELAY_MS 100 // how often to get data from sensor
|
||||
#define VL53L0X_DELAY_MS 100 // how often to get data from sensor
|
||||
#endif
|
||||
|
||||
#ifndef VL53L0X_LONG_MOTION_DELAY_MS
|
||||
#define VL53L0X_LONG_MOTION_DELAY_MS 1000 // how often to get data from sensor
|
||||
#define VL53L0X_LONG_MOTION_DELAY_MS 1000 // switch onto "long motion" action after this delay
|
||||
#endif
|
||||
|
||||
class UsermodVL53L0XGestures : public Usermod {
|
||||
@@ -47,7 +46,7 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
bool wasMotionBefore = false;
|
||||
bool isLongMotion = false;
|
||||
unsigned long motionStartTime = 0;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
@@ -72,40 +71,34 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
lastTime = millis();
|
||||
|
||||
int range = sensor.readRangeSingleMillimeters();
|
||||
DEBUG_PRINTF(F("range: %d, brightness: %d"), range, bri);
|
||||
DEBUG_PRINTF("range: %d, brightness: %d\r\n", range, bri);
|
||||
|
||||
if (range < VL53L0X_MAX_RANGE_MM)
|
||||
{
|
||||
if (!wasMotionBefore)
|
||||
{
|
||||
motionStartTime = millis();
|
||||
DEBUG_PRINTF(F("motionStartTime: %d"), motionStartTime);
|
||||
DEBUG_PRINTF("motionStartTime: %d\r\n", motionStartTime);
|
||||
}
|
||||
wasMotionBefore = true;
|
||||
|
||||
if (millis() - motionStartTime > VL53L0X_LONG_MOTION_DELAY_MS) //long motion
|
||||
{
|
||||
DEBUG_PRINTF(F("long motion: %d"), motionStartTime);
|
||||
DEBUG_PRINTF("long motion: %d\r\n", motionStartTime);
|
||||
if (!isLongMotion)
|
||||
{
|
||||
if (macroLongPress)
|
||||
{
|
||||
applyMacro(macroLongPress);
|
||||
}
|
||||
isLongMotion = true;
|
||||
}
|
||||
|
||||
// set brightness according to range
|
||||
bri = (VL53L0X_MAX_RANGE_MM - max(range, VL53L0X_MIN_RANGE_OFFSET)) * 255 / (VL53L0X_MAX_RANGE_MM - VL53L0X_MIN_RANGE_OFFSET);
|
||||
DEBUG_PRINTF(F("new brightness: %d"), bri);
|
||||
DEBUG_PRINTF("new brightness: %d", bri);
|
||||
stateUpdated(1);
|
||||
}
|
||||
} else if (wasMotionBefore) { //released
|
||||
long dur = millis() - motionStartTime;
|
||||
|
||||
if (!isLongMotion)
|
||||
{ //short press
|
||||
DEBUG_PRINTF(F("shortPressAction..."));
|
||||
DEBUG_PRINTLN(F("shortPressAction..."));
|
||||
shortPressAction();
|
||||
}
|
||||
wasMotionBefore = false;
|
||||
|
||||
@@ -36,7 +36,7 @@ lib_deps =
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
IRremoteESP8266@2.7.3
|
||||
I2Cdevlib-MPU6050@fbde122cc5
|
||||
jrowberg/I2Cdevlib-MPU6050@^1.0.0
|
||||
```
|
||||
|
||||
## Wiring
|
||||
@@ -78,7 +78,7 @@ to the info object
|
||||
## Usermod installation
|
||||
|
||||
1. Copy the file `usermod_mpu6050_imu.h` to the `wled00` directory.
|
||||
2. Register the usermod by adding `#include "usermod_mpu6050_imu.h.h"` in the top and `registerUsermod(new MPU6050Driver());` in the bottom of `usermods_list.cpp`.
|
||||
2. Register the usermod by adding `#include "usermod_mpu6050_imu.h"` in the top and `registerUsermod(new MPU6050Driver());` in the bottom of `usermods_list.cpp`.
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ public:
|
||||
if (!umSSDRDisplayTime || strip.isUpdating()) {
|
||||
return;
|
||||
}
|
||||
#ifdef USERMOD_ID_SN_PHOTORESISTOR
|
||||
#ifdef USERMOD_SN_PHOTORESISTOR
|
||||
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
|
||||
if (ptr != nullptr) {
|
||||
uint16_t lux = ptr->getLastLDRValue();
|
||||
|
||||
61
usermods/smartnest/readme.md
Normal file
61
usermods/smartnest/readme.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Smartnest
|
||||
|
||||
This usermod-v2 modification allows integration with `smartnest.cz` service which provides MQTT integration with voice assistants.
|
||||
In order to setup Smartnest follow the [documentation](https://www.docu.smartnest.cz/).
|
||||
|
||||
## MQTT API
|
||||
|
||||
The API is described in the Smartnest [Github repo](https://github.com/aososam/Smartnest/blob/master/Devices/lightRgb/lightRgb.ino).
|
||||
|
||||
|
||||
## Usermod installation
|
||||
|
||||
1. Register the usermod by adding `#include "../usermods/smartnest/usermod_smartnest.h"` at the top and `usermods.add(new Smartnest());` at the bottom of `usermods_list.cpp`.
|
||||
or
|
||||
2. Use `#define USERMOD_SMARTNEST` in wled.h or `-D USERMOD_SMARTNEST` in your platformio.ini
|
||||
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
* (for v1 usermods using just usermod.cpp, you can ignore this file)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add/uncomment your usermod filename here (and once more below)
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//#include "usermod_v2_example.h"
|
||||
//#include "usermod_temperature.h"
|
||||
#include "../usermods/usermod_smartnest.h"
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
* Add your usermod class name here
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//usermods.add(new MyExampleUsermod());
|
||||
//usermods.add(new UsermodTemperature());
|
||||
usermods.add(new Smartnest());
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Usermod has no configuration but relies on the MQTT configuration.\
|
||||
Under Config > Sync Interfaces > MQTT:
|
||||
* Enable MQTT check box
|
||||
* Set the `Broker` field to: `smartnest.cz`
|
||||
* The `Username` and `Password` fields are the login information from the `smartnest.cz` website.
|
||||
* `Client ID` field is obtained from the device configuration panel in `smartnest.cz`.
|
||||
|
||||
## Change log
|
||||
2022-09
|
||||
* First implementation.
|
||||
167
usermods/smartnest/usermod_smartnest.h
Normal file
167
usermods/smartnest/usermod_smartnest.h
Normal file
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class Smartnest : public Usermod
|
||||
{
|
||||
private:
|
||||
void sendToBroker(const char *const topic, const char *const message)
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String topic_ = String(mqttClientID) + "/" + String(topic);
|
||||
mqtt->publish(topic_.c_str(), 0, true, message);
|
||||
}
|
||||
|
||||
void turnOff()
|
||||
{
|
||||
setBrightness(0);
|
||||
turnOnAtBoot = false;
|
||||
offMode = true;
|
||||
sendToBroker("report/powerState", "OFF");
|
||||
}
|
||||
|
||||
void turnOn()
|
||||
{
|
||||
setBrightness(briLast);
|
||||
turnOnAtBoot = true;
|
||||
offMode = false;
|
||||
sendToBroker("report/powerState", "ON");
|
||||
}
|
||||
|
||||
void setBrightness(int value)
|
||||
{
|
||||
if (value == 0 && bri > 0) briLast = bri;
|
||||
bri = value;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
void setColor(int r, int g, int b)
|
||||
{
|
||||
strip.setColor(0, r, g, b);
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
char msg[18] {};
|
||||
sprintf(msg, "rgb(%d,%d,%d)", r, g, b);
|
||||
sendToBroker("report/color", msg);
|
||||
}
|
||||
|
||||
int splitColor(const char *const color, int * const rgb)
|
||||
{
|
||||
char *color_ = NULL;
|
||||
const char delim[] = ",";
|
||||
char *cxt = NULL;
|
||||
char *token = NULL;
|
||||
int position = 0;
|
||||
|
||||
// We need to copy the string in order to keep it read only as strtok_r function requires mutable string
|
||||
color_ = (char *)malloc(strlen(color));
|
||||
if (NULL == color_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(color_, color);
|
||||
token = strtok_r(color_, delim, &cxt);
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
rgb[position++] = (int)strtoul(token, NULL, 10);
|
||||
token = strtok_r(NULL, delim, &cxt);
|
||||
}
|
||||
free(color_);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public:
|
||||
// Functions called by WLED
|
||||
|
||||
/**
|
||||
* handling of MQTT message
|
||||
* topic should look like: /<mqttClientID>/<Command>/<Message>
|
||||
*/
|
||||
bool onMqttMessage(char *topic, char *message)
|
||||
{
|
||||
String topic_{topic};
|
||||
String topic_prefix{mqttClientID + String("/directive/")};
|
||||
|
||||
if (!topic_.startsWith(topic_prefix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String subtopic = topic_.substring(topic_prefix.length());
|
||||
String message_(message);
|
||||
|
||||
if (subtopic == "powerState")
|
||||
{
|
||||
if (strcmp(message, "ON") == 0)
|
||||
{
|
||||
turnOn();
|
||||
}
|
||||
else if (strcmp(message, "OFF") == 0)
|
||||
{
|
||||
turnOff();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subtopic == "percentage")
|
||||
{
|
||||
int val = (int)strtoul(message, NULL, 10);
|
||||
if (val >= 0 && val <= 100)
|
||||
{
|
||||
setBrightness(map(val, 0, 100, 0, 255));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subtopic == "color")
|
||||
{
|
||||
// Parse the message which is in the format "rgb(<0-255>,<0-255>,<0-255>)"
|
||||
int rgb[3] = {};
|
||||
String colors = message_.substring(String("rgb(").length(), message_.lastIndexOf(')'));
|
||||
if (3 != splitColor(colors.c_str(), rgb))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
setColor(rgb[0], rgb[1], rgb[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* subscribe to MQTT topic and send publish current status.
|
||||
*/
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
String topic = String(mqttClientID) + "/#";
|
||||
|
||||
mqtt->subscribe(topic.c_str(), 0);
|
||||
sendToBroker("report/online", (bri ? "true" : "false")); // Reports that the device is online
|
||||
delay(100);
|
||||
sendToBroker("report/firmware", versionString); // Reports the firmware version
|
||||
delay(100);
|
||||
sendToBroker("report/ip", (char *)WiFi.localIP().toString().c_str()); // Reports the ip
|
||||
delay(100);
|
||||
sendToBroker("report/network", (char *)WiFi.SSID().c_str()); // Reports the network name
|
||||
delay(100);
|
||||
|
||||
String signal(WiFi.RSSI(), 10);
|
||||
sendToBroker("report/signal", signal.c_str()); // Reports the signal strength
|
||||
delay(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
return USERMOD_ID_SMARTNEST;
|
||||
}
|
||||
};
|
||||
8
usermods/usermod_v2_ping_pong_clock/readme.md
Normal file
8
usermods/usermod_v2_ping_pong_clock/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Ping Pong LED Clock
|
||||
|
||||
This Usermod File contains a modification to use WLED in combination with the Ping Pong Ball LED Clock as built in [Instructables](https://www.instructables.com/Ping-Pong-Ball-LED-Clock/).
|
||||
|
||||
## Installation
|
||||
|
||||
To install this Usermod you instruct PlatformIO to compile the Projekt with the USERMOD_PING_PONG_CLOCK flag. WLED then automatically provides you with various settings in the Usermod Page to configure this Usermod.
|
||||
Note: If your clock is bigger or smaller then mine, you may have to update the led indices for the indivdual numbers and the base indices.
|
||||
119
usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h
Normal file
119
usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class PingPongClockUsermod : public Usermod
|
||||
{
|
||||
private:
|
||||
// Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
bool colonOn = true;
|
||||
|
||||
// ---- Variables modified by settings below -----
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool pingPongClockEnabled = true;
|
||||
int colorR = 0xFF;
|
||||
int colorG = 0xFF;
|
||||
int colorB = 0xFF;
|
||||
|
||||
// ---- Variables for correct LED numbering below, edit only if your clock is built different ----
|
||||
|
||||
int baseH = 43; // Adress for the one place of the hours
|
||||
int baseHH = 7; // Adress for the tens place of the hours
|
||||
int baseM = 133; // Adress for the one place of the minutes
|
||||
int baseMM = 97; // Adress for the tens place of the minutes
|
||||
int colon1 = 79; // Adress for the first colon led
|
||||
int colon2 = 80; // Adress for the second colon led
|
||||
|
||||
// Matrix for the illumination of the numbers
|
||||
// Note: These only define the increments of the base adress. e.g. to define the second Minute you have to add the baseMM to every led position
|
||||
const int numbers[10][10] =
|
||||
{
|
||||
{ 0, 1, 4, 6, 13, 15, 18, 19, -1, -1 }, // 0: null
|
||||
{ 13, 14, 15, 18, 19, -1, -1, -1, -1, -1 }, // 1: eins
|
||||
{ 0, 4, 5, 6, 13, 14, 15, 19, -1, -1 }, // 2: zwei
|
||||
{ 4, 5, 6, 13, 14, 15, 18, 19, -1, -1 }, // 3: drei
|
||||
{ 1, 4, 5, 14, 15, 18, 19, -1, -1, -1 }, // 4: vier
|
||||
{ 1, 4, 5, 6, 13, 14, 15, 18, -1, -1 }, // 5: fünf
|
||||
{ 0, 5, 6, 10, 13, 14, 15, 18, -1, -1 }, // 6: sechs
|
||||
{ 4, 6, 9, 13, 14, 19, -1, -1, -1, -1 }, // 7: sieben
|
||||
{ 0, 1, 4, 5, 6, 13, 14, 15, 18, 19 }, // 8: acht
|
||||
{ 1, 4, 5, 6, 9, 13, 14, 19, -1, -1 } // 9: neun
|
||||
};
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{ }
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (millis() - lastTime > 1000)
|
||||
{
|
||||
lastTime = millis();
|
||||
colonOn = !colonOn;
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray lightArr = user.createNestedArray("Uhrzeit-Anzeige"); //name
|
||||
lightArr.add(pingPongClockEnabled ? "aktiv" : "inaktiv"); //value
|
||||
lightArr.add(""); //unit
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("Ping Pong Clock");
|
||||
top["enabled"] = pingPongClockEnabled;
|
||||
top["colorR"] = colorR;
|
||||
top["colorG"] = colorG;
|
||||
top["colorB"] = colorB;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root["Ping Pong Clock"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["enabled"], pingPongClockEnabled);
|
||||
configComplete &= getJsonValue(top["colorR"], colorR);
|
||||
configComplete &= getJsonValue(top["colorG"], colorG);
|
||||
configComplete &= getJsonValue(top["colorB"], colorB);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
void drawNumber(int base, int number)
|
||||
{
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
if(numbers[number][i] > -1)
|
||||
strip.setPixelColor(numbers[number][i] + base, RGBW32(colorR, colorG, colorB, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
if(pingPongClockEnabled){
|
||||
if(colonOn)
|
||||
{
|
||||
strip.setPixelColor(colon1, RGBW32(colorR, colorG, colorB, 0));
|
||||
strip.setPixelColor(colon2, RGBW32(colorR, colorG, colorB, 0));
|
||||
}
|
||||
drawNumber(baseHH, (hour(localTime) / 10) % 10);
|
||||
drawNumber(baseH, hour(localTime) % 10);
|
||||
drawNumber(baseM, (minute(localTime) / 10) % 10);
|
||||
drawNumber(baseMM, minute(localTime) % 10);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_PING_PONG_CLOCK;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -8,6 +8,16 @@ active: enable/disable usermod
|
||||
diplayItIs: enable/disable display of "Es ist" on the clock
|
||||
ledOffset: number of LEDs before the wordclock LEDs
|
||||
|
||||
### Update for alternatative wiring pattern
|
||||
Based on this fantastic work I added an alternative wiring pattern.
|
||||
For original you have to use a long wire to connect DO - DI from first line to the next line.
|
||||
|
||||
I wired my clock in meander style. So the first LED in second line is in the right.
|
||||
With this problem every second line was inverted and showed the wrong letter.
|
||||
|
||||
I added a switch in usermod called "meander wiring?" to enable/disable alternativ wiring pattern.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample`
|
||||
@@ -24,4 +34,6 @@ No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2022/03/30 initial commit
|
||||
2022/08/18 added meander wiring pattern.
|
||||
|
||||
2022/03/30 initial commit
|
||||
|
||||
@@ -24,15 +24,19 @@ class WordClockUsermod : public Usermod
|
||||
bool usermodActive = false;
|
||||
bool displayItIs = false;
|
||||
int ledOffset = 100;
|
||||
bool meander = false;
|
||||
|
||||
// defines for mask sizes
|
||||
#define maskSizeLeds 114
|
||||
#define maskSizeMinutes 12
|
||||
#define maskSizeMinutesMea 12
|
||||
#define maskSizeHours 6
|
||||
#define maskSizeHoursMea 6
|
||||
#define maskSizeItIs 5
|
||||
#define maskSizeMinuteDots 4
|
||||
|
||||
// "minute" masks
|
||||
// Normal wiring
|
||||
const int maskMinutes[12][maskSizeMinutes] =
|
||||
{
|
||||
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
|
||||
@@ -49,7 +53,25 @@ class WordClockUsermod : public Usermod
|
||||
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1} // :55 fünf vor
|
||||
};
|
||||
|
||||
// Meander wiring
|
||||
const int maskMinutesMea[12][maskSizeMinutesMea] =
|
||||
{
|
||||
{ 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // :05 fünf nach
|
||||
{ 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // :10 zehn nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel
|
||||
{ 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // :20 zwanzig nach
|
||||
{ 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // :25 fünf vor halb
|
||||
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // :35 fünf nach halb
|
||||
{ 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // :40 zwanzig vor
|
||||
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel
|
||||
{ 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // :50 zehn vor
|
||||
{ 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1} // :55 fünf vor
|
||||
};
|
||||
|
||||
// hour masks
|
||||
// Normal wiring
|
||||
const int maskHours[13][maskSizeHours] =
|
||||
{
|
||||
{ 55, 56, 57, -1, -1, -1}, // 01: ein
|
||||
@@ -66,6 +88,23 @@ class WordClockUsermod : public Usermod
|
||||
{ 49, 50, 51, -1, -1, -1}, // 11: elf
|
||||
{ 94, 95, 96, 97, 98, -1} // 12: zwölf and 00: null
|
||||
};
|
||||
// Meander wiring
|
||||
const int maskHoursMea[13][maskSizeHoursMea] =
|
||||
{
|
||||
{ 63, 64, 65, -1, -1, -1}, // 01: ein
|
||||
{ 62, 63, 64, 65, -1, -1}, // 01: eins
|
||||
{ 55, 56, 57, 58, -1, -1}, // 02: zwei
|
||||
{ 66, 67, 68, 69, -1, -1}, // 03: drei
|
||||
{ 73, 74, 75, 76, -1, -1}, // 04: vier
|
||||
{ 51, 52, 53, 54, -1, -1}, // 05: fünf
|
||||
{ 83, 84, 85, 86, 87, -1}, // 06: sechs
|
||||
{ 88, 89, 90, 91, 92, 93}, // 07: sieben
|
||||
{ 77, 78, 79, 80, -1, -1}, // 08: acht
|
||||
{103, 104, 105, 106, -1, -1}, // 09: neun
|
||||
{106, 107, 108, 109, -1, -1}, // 10: zehn
|
||||
{ 49, 50, 51, -1, -1, -1}, // 11: elf
|
||||
{ 94, 95, 96, 97, 98, -1} // 12: zwölf and 00: null
|
||||
};
|
||||
|
||||
// mask "it is"
|
||||
const int maskItIs[maskSizeItIs] = {0, 1, 3, 4, 5};
|
||||
@@ -128,14 +167,24 @@ class WordClockUsermod : public Usermod
|
||||
}
|
||||
|
||||
// update led mask
|
||||
if (meander)
|
||||
{
|
||||
updateLedMask(maskHoursMea[index], maskSizeHoursMea);
|
||||
} else {
|
||||
updateLedMask(maskHours[index], maskSizeHours);
|
||||
}
|
||||
}
|
||||
|
||||
// set minutes
|
||||
void setMinutes(int index)
|
||||
{
|
||||
// update led mask
|
||||
if (meander)
|
||||
{
|
||||
updateLedMask(maskMinutesMea[index], maskSizeMinutesMea);
|
||||
} else {
|
||||
updateLedMask(maskMinutes[index], maskSizeMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
// set minutes dot
|
||||
@@ -360,6 +409,7 @@ class WordClockUsermod : public Usermod
|
||||
top["active"] = usermodActive;
|
||||
top["displayItIs"] = displayItIs;
|
||||
top["ledOffset"] = ledOffset;
|
||||
top["Meander wiring?"] = meander;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -389,6 +439,7 @@ class WordClockUsermod : public Usermod
|
||||
configComplete &= getJsonValue(top["active"], usermodActive);
|
||||
configComplete &= getJsonValue(top["displayItIs"], displayItIs);
|
||||
configComplete &= getJsonValue(top["ledOffset"], ledOffset);
|
||||
configComplete &= getJsonValue(top["Meander wiring?"], meander);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user