Merge branch 'MoonModules:mdev' into ES8388-Lyra-T-Fixes-Test
This commit is contained in:
@@ -48,11 +48,11 @@ private:
|
||||
bool getLuminanceComplete = false;
|
||||
|
||||
// flag set at startup
|
||||
bool enabled = true;
|
||||
//bool enabled = true; //WLEDMM not needed as we use global attributes
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
//static const char _name[]; //WLEDMM not needed as we use global attributes
|
||||
//static const char _enabled[]; //WLEDMM not needed as we use global attributes
|
||||
static const char _maxReadInterval[];
|
||||
static const char _minReadInterval[];
|
||||
static const char _offset[];
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
bool sensorFound = false;
|
||||
|
||||
// Home Assistant and MQTT
|
||||
String mqttLuminanceTopic = F("");
|
||||
String mqttLuminanceTopic = FPSTR("");
|
||||
bool mqttInitialized = false;
|
||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
||||
|
||||
@@ -132,6 +132,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
Usermod_BH1750(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM
|
||||
|
||||
void setup()
|
||||
{
|
||||
#if 0
|
||||
@@ -157,22 +159,22 @@ public:
|
||||
if (!pinManager.joinWire()) { // WLEDMM - this allocates global I2C pins, then starts Wire - if not started previously
|
||||
sensorFound = false;
|
||||
//enabled = false;
|
||||
USER_PRINTLN(F("BH1750: failed to join I2C bus."));
|
||||
USER_PRINTLN(F("[BH1750]: failed to join I2C bus."));
|
||||
return;
|
||||
}
|
||||
|
||||
sensorFound = lightMeter.begin();
|
||||
if (sensorFound) { USER_PRINTLN(F("BH1750 sensor found.")); }
|
||||
else{ USER_PRINTLN(F("BH1750 sensor not found.")); }
|
||||
sensorFound = lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); // WLEDMM set mode explicitly
|
||||
if (sensorFound) { USER_PRINTLN(F("[BH1750] sensor found.")); }
|
||||
else{ USER_PRINTLN(F("[BH1750] sensor not found.")); }
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if ((!enabled) || strip.isUpdating())
|
||||
if (!sensorFound || !initDone) return; // WLEDMM bugfix
|
||||
if ((!enabled) || (strip.isUpdating() && (millis() - lastMeasurement < 450))) // WLEDMM be nice, but not too nice
|
||||
return;
|
||||
|
||||
if (!sensorFound) return; // WLEDMM bugfix
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
@@ -184,10 +186,12 @@ public:
|
||||
}
|
||||
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
float lux = lastLux;
|
||||
if (lightMeter.measurementReady()) { //WLEDMM do not block in case the sensor is still busy
|
||||
lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
}
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
@@ -255,7 +259,7 @@ public:
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[F("enabled")] = enabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
|
||||
@@ -269,7 +273,7 @@ public:
|
||||
|
||||
// top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
|
||||
|
||||
DEBUG_PRINTLN(F("BH1750 config saved."));
|
||||
DEBUG_PRINTLN(F("[BH1750] config saved."));
|
||||
}
|
||||
|
||||
// called before setup() to populate properties from values stored in cfg.json
|
||||
@@ -283,13 +287,13 @@ public:
|
||||
if (top.isNull())
|
||||
{
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINT(F("BH1750"));
|
||||
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[F("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);
|
||||
@@ -335,8 +339,8 @@ public:
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
|
||||
//const char Usermod_BH1750::_name[] PROGMEM = "BH1750"; //WLEDMM not needed as we use global attributes
|
||||
//const char Usermod_BH1750::_enabled[] PROGMEM = "enabled"; //WLEDMM not needed as we use global attributes
|
||||
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";
|
||||
|
||||
@@ -26,6 +26,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//the default ratio for the voltage divider
|
||||
#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
|
||||
#else //ESP8266 boards
|
||||
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 4.2f
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,10 @@ class UsermodBattery : public Usermod
|
||||
float rawValue = 0.0f;
|
||||
// calculated voltage
|
||||
float voltage = maxBatteryVoltage;
|
||||
// between 0 and 1, to control strength of voltage smoothing filter
|
||||
float alpha = 0.05f;
|
||||
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
|
||||
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
|
||||
// mapped battery level based on voltage
|
||||
int8_t batteryLevel = 100;
|
||||
// offset or calibration value to fine tune the calculated voltage
|
||||
@@ -110,6 +114,17 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
}
|
||||
|
||||
float readVoltage()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
|
||||
return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration;
|
||||
#else
|
||||
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
|
||||
return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
@@ -126,6 +141,7 @@ class UsermodBattery : public Usermod
|
||||
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
|
||||
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
|
||||
success = true;
|
||||
voltage = readVoltage();
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
@@ -135,8 +151,8 @@ class UsermodBattery : public Usermod
|
||||
pinMode(batteryPin, INPUT);
|
||||
}
|
||||
#else //ESP8266 boards have only one analog input pin A0
|
||||
|
||||
pinMode(batteryPin, INPUT);
|
||||
voltage = readVoltage();
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + readingInterval;
|
||||
@@ -176,22 +192,12 @@ class UsermodBattery : public Usermod
|
||||
|
||||
initializing = false;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV)
|
||||
rawValue = analogReadMilliVolts(batteryPin);
|
||||
// calculate the voltage
|
||||
voltage = (rawValue / 1000.0f) + calibration;
|
||||
// usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2
|
||||
voltage *= 2.0f;
|
||||
#else
|
||||
// read battery raw input
|
||||
rawValue = analogRead(batteryPin);
|
||||
rawValue = readVoltage();
|
||||
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
|
||||
voltage = voltage + alpha * (rawValue - voltage);
|
||||
|
||||
// calculate the voltage
|
||||
voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration;
|
||||
#endif
|
||||
// check if voltage is within specified voltage range, allow 10% over/under voltage
|
||||
voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
|
||||
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
|
||||
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
|
||||
|
||||
// translate battery voltage into percentage
|
||||
/*
|
||||
@@ -363,6 +369,7 @@ class UsermodBattery : public Usermod
|
||||
battery[F("max-voltage")] = maxBatteryVoltage;
|
||||
battery[F("capacity")] = totalBatteryCapacity;
|
||||
battery[F("calibration")] = calibration;
|
||||
battery[F("voltage-multiplier")] = voltageMultiplier;
|
||||
battery[FPSTR(_readInterval)] = readingInterval;
|
||||
|
||||
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
|
||||
@@ -375,6 +382,9 @@ class UsermodBattery : public Usermod
|
||||
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
|
||||
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
|
||||
|
||||
// read voltage in case calibration or voltage multiplier changed to see immediate effect
|
||||
voltage = readVoltage();
|
||||
|
||||
DEBUG_PRINTLN(F("Battery config saved."));
|
||||
}
|
||||
|
||||
@@ -441,6 +451,7 @@ class UsermodBattery : public Usermod
|
||||
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
|
||||
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
|
||||
setCalibration(battery[F("calibration")] | calibration);
|
||||
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
|
||||
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
|
||||
|
||||
JsonObject ao = battery[F("auto-off")];
|
||||
@@ -597,21 +608,7 @@ class UsermodBattery : public Usermod
|
||||
totalBatteryCapacity = capacity;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the choosen adc precision
|
||||
* esp8266 = 10bit resolution = 1024.0f
|
||||
* esp32 = 12bit resolution = 4095.0f
|
||||
*/
|
||||
float getAdcPrecision()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// esp32
|
||||
return 4096.0f;
|
||||
#else
|
||||
// esp8266
|
||||
return 1024.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the calculated voltage
|
||||
@@ -649,6 +646,23 @@ class UsermodBattery : public Usermod
|
||||
calibration = offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the voltage multiplier value
|
||||
* A multiplier that may need adjusting for different voltage divider setups
|
||||
*/
|
||||
void setVoltageMultiplier(float multiplier)
|
||||
{
|
||||
voltageMultiplier = multiplier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the voltage multiplier value
|
||||
* A multiplier that may need adjusting for different voltage divider setups
|
||||
*/
|
||||
float getVoltageMultiplier()
|
||||
{
|
||||
return voltageMultiplier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get auto-off feature enabled status
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_DALLASTEMPERATURE_CELSIUS - define this to report temperatures in degrees celsius, otherwise fahrenheit will be reported
|
||||
; USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
; USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
|
||||
;
|
||||
[env:d1_mini_usermod_dallas_temperature_C]
|
||||
extends = env:d1_mini
|
||||
build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE -D USERMOD_DALLASTEMPERATURE_CELSIUS
|
||||
build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE
|
||||
lib_deps = ${env.lib_deps}
|
||||
milesburton/DallasTemperature@^3.9.0
|
||||
OneWire@~2.3.5
|
||||
paulstoffregen/OneWire@~2.3.7
|
||||
# you may want to use following with ESP32
|
||||
; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32
|
||||
@@ -7,6 +7,8 @@ May be expanded with support for different sensor types in the future.
|
||||
|
||||
If temperature sensor is not detected during boot, this usermod will be disabled.
|
||||
|
||||
Maintained by @blazoncek
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
@@ -14,7 +16,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
|
||||
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - number of milliseconds after boot to take first measurement, defaults to 20000 ms
|
||||
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s)
|
||||
|
||||
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval.
|
||||
|
||||
@@ -27,7 +29,6 @@ All parameters can be configured at runtime via the Usermods settings page, incl
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
|
||||
|
||||
|
||||
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
|
||||
|
||||
```ini
|
||||
@@ -43,8 +44,9 @@ default_envs = d1_mini
|
||||
lib_deps =
|
||||
...
|
||||
#For Dallas sensor uncomment following line
|
||||
OneWire@~2.3.5
|
||||
...
|
||||
OneWire@~2.3.7
|
||||
# ... or you may want to use following with ESP32
|
||||
; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32...
|
||||
```
|
||||
|
||||
## Change Log
|
||||
@@ -56,3 +58,6 @@ lib_deps =
|
||||
* Report the number of seconds until the first read in the info screen instead of sensor error
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
2023-05
|
||||
* Rewrite to conform to newer recommendations.
|
||||
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error
|
||||
@@ -52,348 +52,378 @@ class UsermodTemperature : public Usermod {
|
||||
static const char _parasitePin[];
|
||||
|
||||
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
||||
float readDallas() {
|
||||
byte data[9];
|
||||
int16_t result; // raw data from sensor
|
||||
float retVal = -127.0f;
|
||||
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
|
||||
#ifdef WLED_DEBUG
|
||||
if (OneWire::crc8(data,8) != data[8]) {
|
||||
DEBUG_PRINTLN(F("CRC error reading temperature."));
|
||||
for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
|
||||
DEBUG_PRINT(F(" => "));
|
||||
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
|
||||
}
|
||||
#endif
|
||||
switch(sensorFound) {
|
||||
case 0x10: // DS18S20 has 9-bit precision
|
||||
result = (data[1] << 8) | data[0];
|
||||
retVal = float(result) * 0.5f;
|
||||
break;
|
||||
case 0x22: // DS18B20
|
||||
case 0x28: // DS1822
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
|
||||
if (data[1] & 0x80) result |= 0xF000; // fix negative value
|
||||
retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (byte i=1; i<9; i++) data[0] &= data[i];
|
||||
return data[0]==0xFF ? -127.0f : retVal;
|
||||
}
|
||||
|
||||
void requestTemperatures() {
|
||||
DEBUG_PRINTLN(F("Requesting temperature."));
|
||||
oneWire->reset();
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0x44,parasite); // request new temperature reading
|
||||
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET)
|
||||
lastTemperaturesRequest = millis();
|
||||
waitingForConversion = true;
|
||||
}
|
||||
|
||||
void readTemperature() {
|
||||
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
|
||||
temperature = readDallas();
|
||||
lastMeasurement = millis();
|
||||
waitingForConversion = false;
|
||||
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
|
||||
DEBUG_PRINT(F("Read temperature "));
|
||||
DEBUG_PRINTLN(temperature);
|
||||
}
|
||||
|
||||
bool findSensor() {
|
||||
DEBUG_PRINTLN(F("Searching for sensor..."));
|
||||
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
|
||||
// find out if we have DS18xxx sensor attached
|
||||
oneWire->reset_search();
|
||||
delay(10);
|
||||
while (oneWire->search(deviceAddress)) {
|
||||
DEBUG_PRINTLN(F("Found something..."));
|
||||
if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) {
|
||||
switch (deviceAddress[0]) {
|
||||
case 0x10: // DS18S20
|
||||
case 0x22: // DS18B20
|
||||
case 0x28: // DS1822
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
DEBUG_PRINTLN(F("Sensor found."));
|
||||
sensorFound = deviceAddress[0];
|
||||
DEBUG_PRINTF("0x%02X\n", sensorFound);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("Sensor NOT found."));
|
||||
return false;
|
||||
}
|
||||
|
||||
float readDallas();
|
||||
void requestTemperatures();
|
||||
void readTemperature();
|
||||
bool findSensor();
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void publishHomeAssistantAutodiscovery() {
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
|
||||
char json_str[1024], buf[128];
|
||||
size_t payload_size;
|
||||
StaticJsonDocument<1024> json;
|
||||
|
||||
sprintf_P(buf, PSTR("%s Temperature"), serverDescription);
|
||||
json[F("name")] = buf;
|
||||
strcpy(buf, mqttDeviceTopic);
|
||||
strcat_P(buf, PSTR("/temperature"));
|
||||
json[F("state_topic")] = buf;
|
||||
json[F("device_class")] = F("temperature");
|
||||
json[F("unique_id")] = escapedMac.c_str();
|
||||
json[F("unit_of_measurement")] = F("°C");
|
||||
payload_size = serializeJson(json, json_str);
|
||||
|
||||
sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str());
|
||||
mqtt->publish(buf, 0, true, json_str, payload_size);
|
||||
HApublished = true;
|
||||
}
|
||||
void publishHomeAssistantAutodiscovery();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
UsermodTemperature(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM: this shouldn't be necessary (passthrough of constructor), maybe because Usermod is an abstract class
|
||||
|
||||
void setup() {
|
||||
int retries = 10;
|
||||
sensorFound = 0;
|
||||
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
USER_PRINTLN(F("Finding temperature pin..."));
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
|
||||
oneWire = new OneWire(temperaturePin);
|
||||
if (oneWire->reset()) {
|
||||
while (!findSensor() && retries--) {
|
||||
delay(25); // try to find sensor
|
||||
}
|
||||
}
|
||||
if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) {
|
||||
pinMode(parasitePin, OUTPUT);
|
||||
digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
|
||||
} else {
|
||||
parasitePin = -1;
|
||||
}
|
||||
} else {
|
||||
if (temperaturePin >= 0) {
|
||||
USER_PRINTLN(F("Temperature pin allocation failed."));
|
||||
}
|
||||
temperaturePin = -1; // allocation failed
|
||||
}
|
||||
}
|
||||
lastMeasurement = millis() - readingInterval + 10000;
|
||||
initDone = true;
|
||||
USER_PRINTLN(F("temperature usermod initialized."));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!enabled || !sensorFound || strip.isUpdating()) return;
|
||||
|
||||
static uint8_t errorCount = 0;
|
||||
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;
|
||||
|
||||
// we are due for a measurement, if we are not already waiting
|
||||
// for a conversion to complete, then make a new request for temps
|
||||
if (!waitingForConversion) {
|
||||
requestTemperatures();
|
||||
return;
|
||||
}
|
||||
|
||||
// we were waiting for a conversion to complete, have we waited log enough?
|
||||
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
|
||||
readTemperature();
|
||||
if (getTemperatureC() < -100.0f) {
|
||||
if (++errorCount > 10) sensorFound = 0;
|
||||
lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
|
||||
return;
|
||||
}
|
||||
errorCount = 0;
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
if (temperature > -100.0f) {
|
||||
// dont publish super low temperature as the graph will get messed up
|
||||
// the DallasTemperature library returns -127C or -196.6F when problem
|
||||
// reading the sensor
|
||||
strcat_P(subuf, PSTR("/temperature"));
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
|
||||
strcat_P(subuf, PSTR("_f"));
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
|
||||
} else {
|
||||
// publish something else to indicate status?
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
//void connected() {}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
/**
|
||||
* subscribe to MQTT topic if needed
|
||||
*/
|
||||
void onMqttConnect(bool sessionPresent) {
|
||||
//(re)subscribe to required topics
|
||||
//char subuf[64];
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
publishHomeAssistantAutodiscovery();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* API calls te enable data exchange between WLED modules
|
||||
*/
|
||||
inline float getTemperatureC() {
|
||||
return (float)temperature;
|
||||
inline float getTemperatureC() { return temperature; }
|
||||
inline float getTemperatureF() { return temperature * 1.8f + 32.0f; }
|
||||
float getTemperature();
|
||||
const char *getTemperatureUnit();
|
||||
uint16_t getId() { return USERMOD_ID_TEMPERATURE; }
|
||||
|
||||
void setup();
|
||||
void loop();
|
||||
//void connected();
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
#endif
|
||||
//void onUpdateBegin(bool init);
|
||||
|
||||
//bool handleButton(uint8_t b);
|
||||
//void handleOverlayDraw();
|
||||
|
||||
void addToJsonInfo(JsonObject& root);
|
||||
//void addToJsonState(JsonObject &root);
|
||||
//void readFromJsonState(JsonObject &root);
|
||||
void addToConfig(JsonObject &root);
|
||||
bool readFromConfig(JsonObject &root);
|
||||
|
||||
void appendConfigData();
|
||||
};
|
||||
|
||||
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
||||
float UsermodTemperature::readDallas() {
|
||||
byte data[9];
|
||||
int16_t result; // raw data from sensor
|
||||
float retVal = -127.0f;
|
||||
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
|
||||
#ifdef WLED_DEBUG
|
||||
if (OneWire::crc8(data,8) != data[8]) {
|
||||
DEBUG_PRINTLN(F("CRC error reading temperature."));
|
||||
for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
|
||||
DEBUG_PRINT(F(" => "));
|
||||
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
|
||||
}
|
||||
inline float getTemperatureF() {
|
||||
return (float)temperature * 1.8f + 32;
|
||||
#endif
|
||||
switch(sensorFound) {
|
||||
case 0x10: // DS18S20 has 9-bit precision
|
||||
result = (data[1] << 8) | data[0];
|
||||
retVal = float(result) * 0.5f;
|
||||
break;
|
||||
case 0x22: // DS18B20
|
||||
case 0x28: // DS1822
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
|
||||
if (data[1] & 0x80) result |= 0xF000; // fix negative value
|
||||
retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (byte i=1; i<9; i++) data[0] &= data[i];
|
||||
return data[0]==0xFF ? -127.0f : retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
// dont add temperature to info if we are disabled
|
||||
if (!enabled) return;
|
||||
void UsermodTemperature::requestTemperatures() {
|
||||
DEBUG_PRINTLN(F("Requesting temperature."));
|
||||
oneWire->reset();
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0x44,parasite); // request new temperature reading
|
||||
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET)
|
||||
lastTemperaturesRequest = millis();
|
||||
waitingForConversion = true;
|
||||
}
|
||||
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
void UsermodTemperature::readTemperature() {
|
||||
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
|
||||
temperature = readDallas();
|
||||
lastMeasurement = millis();
|
||||
waitingForConversion = false;
|
||||
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
|
||||
DEBUG_PRINT(F("Read temperature "));
|
||||
DEBUG_PRINTLN(temperature);
|
||||
}
|
||||
|
||||
JsonArray temp = user.createNestedArray(FPSTR(_name));
|
||||
|
||||
if (temperature <= -100.0f) {
|
||||
temp.add(0);
|
||||
temp.add(F(" Sensor Error!"));
|
||||
return;
|
||||
bool UsermodTemperature::findSensor() {
|
||||
DEBUG_PRINTLN(F("Searching for sensor..."));
|
||||
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
|
||||
// find out if we have DS18xxx sensor attached
|
||||
oneWire->reset_search();
|
||||
delay(10);
|
||||
while (oneWire->search(deviceAddress)) {
|
||||
DEBUG_PRINTLN(F("Found something..."));
|
||||
if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) {
|
||||
switch (deviceAddress[0]) {
|
||||
case 0x10: // DS18S20
|
||||
case 0x22: // DS18B20
|
||||
case 0x28: // DS1822
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
DEBUG_PRINTLN(F("Sensor found."));
|
||||
sensorFound = deviceAddress[0];
|
||||
DEBUG_PRINTF("0x%02X\n", sensorFound);
|
||||
return true;
|
||||
}
|
||||
|
||||
temp.add(degC ? getTemperatureC() : getTemperatureF());
|
||||
temp.add(degC ? F("°C") : F("°F"));
|
||||
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
temp = sensor.createNestedArray(F("temp"));
|
||||
temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
|
||||
temp.add(degC ? F("°C") : F("°F"));
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("Sensor NOT found."));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
//{
|
||||
//}
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void UsermodTemperature::publishHomeAssistantAutodiscovery() {
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
|
||||
*/
|
||||
//void readFromJsonState(JsonObject &root) {
|
||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
//}
|
||||
char json_str[1024], buf[128];
|
||||
size_t payload_size;
|
||||
StaticJsonDocument<1024> json;
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
Usermod::addToConfig(root);
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
sprintf_P(buf, PSTR("%s Temperature"), serverDescription);
|
||||
json[F("name")] = buf;
|
||||
strcpy(buf, mqttDeviceTopic);
|
||||
strcat_P(buf, PSTR("/temperature"));
|
||||
json[F("state_topic")] = buf;
|
||||
json[F("device_class")] = F("temperature");
|
||||
json[F("unique_id")] = escapedMac.c_str();
|
||||
json[F("unit_of_measurement")] = F("°C");
|
||||
payload_size = serializeJson(json, json_str);
|
||||
|
||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
top["pin"] = temperaturePin; // usermodparam
|
||||
top["degC"] = degC; // usermodparam
|
||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
||||
top[FPSTR(_parasite)] = parasite;
|
||||
top[FPSTR(_parasitePin)] = parasitePin;
|
||||
DEBUG_PRINTLN(F("Temperature config saved."));
|
||||
}
|
||||
sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str());
|
||||
mqtt->publish(buf, 0, true, json_str, payload_size);
|
||||
HApublished = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
/*bool configComplete = */Usermod::readFromConfig(root); //WLEDMM: configComplete not implemented here (todo?)
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
int8_t newTemperaturePin = temperaturePin;
|
||||
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
||||
degC = top["degC"] | degC;
|
||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
||||
parasite = top[FPSTR(_parasite)] | parasite;
|
||||
parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
|
||||
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
temperaturePin = newTemperaturePin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing paramters from settings page
|
||||
if (newTemperaturePin != temperaturePin) {
|
||||
DEBUG_PRINTLN(F("Re-init temperature."));
|
||||
// deallocate pin and release memory
|
||||
delete oneWire;
|
||||
pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
|
||||
temperaturePin = newTemperaturePin;
|
||||
pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature);
|
||||
// initialise
|
||||
setup();
|
||||
void UsermodTemperature::setup() {
|
||||
int retries = 10;
|
||||
sensorFound = 0;
|
||||
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
DEBUG_PRINTLN(F("Allocating temperature pin..."));
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
|
||||
oneWire = new OneWire(temperaturePin);
|
||||
if (oneWire->reset()) {
|
||||
while (!findSensor() && retries--) {
|
||||
delay(25); // try to find sensor
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_parasitePin)].isNull();
|
||||
if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) {
|
||||
pinMode(parasitePin, OUTPUT);
|
||||
digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
|
||||
} else {
|
||||
parasitePin = -1;
|
||||
}
|
||||
} else {
|
||||
if (temperaturePin >= 0) {
|
||||
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
|
||||
}
|
||||
temperaturePin = -1; // allocation failed
|
||||
}
|
||||
}
|
||||
lastMeasurement = millis() - readingInterval + 10000;
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addHB('Temperature');")); // WLEDMM
|
||||
oappend(SET_F("addInfo('Temperature:parasite-pwr")); //WLEDMM use literals
|
||||
oappend(SET_F("',1,'<i>(if no Vcc connected)</i>');")); // 0 is field type, 1 is actual field
|
||||
oappend(SET_F("addInfo('Temperature:parasite-pwr-pin")); //WLEDMM use literals
|
||||
oappend(SET_F("',1,'<i>(for external MOSFET)</i>');")); // 0 is field type, 1 is actual field
|
||||
}
|
||||
void UsermodTemperature::loop() {
|
||||
if (!enabled || !sensorFound || strip.isUpdating()) return;
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_TEMPERATURE;
|
||||
static uint8_t errorCount = 0;
|
||||
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;
|
||||
|
||||
// we are due for a measurement, if we are not already waiting
|
||||
// for a conversion to complete, then make a new request for temps
|
||||
if (!waitingForConversion) {
|
||||
requestTemperatures();
|
||||
return;
|
||||
}
|
||||
|
||||
// we were waiting for a conversion to complete, have we waited log enough?
|
||||
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
|
||||
readTemperature();
|
||||
if (getTemperatureC() < -100.0f) {
|
||||
if (++errorCount > 10) sensorFound = 0;
|
||||
lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
|
||||
return;
|
||||
}
|
||||
};
|
||||
errorCount = 0;
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
if (temperature > -100.0f) {
|
||||
// dont publish super low temperature as the graph will get messed up
|
||||
// the DallasTemperature library returns -127C or -196.6F when problem
|
||||
// reading the sensor
|
||||
strcat_P(subuf, PSTR("/temperature"));
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
|
||||
strcat_P(subuf, PSTR("_f"));
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
|
||||
} else {
|
||||
// publish something else to indicate status?
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
//void UsermodTemperature::connected() {}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
/**
|
||||
* subscribe to MQTT topic if needed
|
||||
*/
|
||||
void UsermodTemperature::onMqttConnect(bool sessionPresent) {
|
||||
//(re)subscribe to required topics
|
||||
//char subuf[64];
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
publishHomeAssistantAutodiscovery();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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 UsermodTemperature::addToJsonInfo(JsonObject& root) {
|
||||
// dont add temperature to info if we are disabled
|
||||
if (!enabled) return;
|
||||
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray temp = user.createNestedArray(FPSTR(_name));
|
||||
|
||||
if (temperature <= -100.0f) {
|
||||
temp.add(0);
|
||||
temp.add(F(" Sensor Error!"));
|
||||
return;
|
||||
}
|
||||
|
||||
temp.add(getTemperature());
|
||||
temp.add(getTemperatureUnit());
|
||||
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
temp = sensor.createNestedArray(F("temperature"));
|
||||
temp.add(getTemperature());
|
||||
temp.add(getTemperatureUnit());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 UsermodTemperature::addToJsonState(JsonObject &root)
|
||||
//{
|
||||
//}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
|
||||
*/
|
||||
//void UsermodTemperature::readFromJsonState(JsonObject &root) {
|
||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
//}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void UsermodTemperature::addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
//WLEDMM: call superclass
|
||||
Usermod::addToConfig(root);
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
top["pin"] = temperaturePin; // usermodparam
|
||||
top["degC"] = degC; // usermodparam
|
||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
||||
top[FPSTR(_parasite)] = parasite;
|
||||
top[FPSTR(_parasitePin)] = parasitePin;
|
||||
DEBUG_PRINTLN(F("Temperature config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool UsermodTemperature::readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
//WLEDMM call superclass
|
||||
Usermod::readFromConfig(root); //WLEDMM: configComplete not implemented here (todo?)
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
int8_t newTemperaturePin = temperaturePin;
|
||||
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
||||
degC = top["degC"] | degC;
|
||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
||||
parasite = top[FPSTR(_parasite)] | parasite;
|
||||
parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
|
||||
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
temperaturePin = newTemperaturePin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing paramters from settings page
|
||||
if (newTemperaturePin != temperaturePin) {
|
||||
DEBUG_PRINTLN(F("Re-init temperature."));
|
||||
// deallocate pin and release memory
|
||||
delete oneWire;
|
||||
pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
|
||||
temperaturePin = newTemperaturePin;
|
||||
pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature);
|
||||
// initialise
|
||||
setup();
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_parasitePin)].isNull();
|
||||
}
|
||||
|
||||
void UsermodTemperature::appendConfigData() {
|
||||
oappend(SET_F("addHB('Temperature');")); // WLEDMM
|
||||
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str());
|
||||
oappend(SET_F("',1,'<i>(if no Vcc connected)</i>');")); // 0 is field type, 1 is actual field
|
||||
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str());
|
||||
oappend(SET_F("',1,'<i>(for external MOSFET)</i>');")); // 0 is field type, 1 is actual field
|
||||
}
|
||||
|
||||
float UsermodTemperature::getTemperature() {
|
||||
return degC ? getTemperatureC() : getTemperatureF();
|
||||
}
|
||||
|
||||
const char *UsermodTemperature::getTemperatureUnit() {
|
||||
return degC ? "°C" : "°F";
|
||||
}
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add/uncomment your usermod filename here (and once more below)
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//#include "usermod_v2_example.h"
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
#include "../usermods/Temperature/usermod_temperature.h"
|
||||
#endif
|
||||
|
||||
//#include "usermod_v2_empty.h"
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
* Add your usermod class name here
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//usermods.add(new MyExampleUsermod());
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
usermods.add(new UsermodTemperature());
|
||||
#endif
|
||||
|
||||
//usermods.add(new UsermodRenameMe());
|
||||
}
|
||||
@@ -9,33 +9,9 @@ function toggleCEEditor(name, segID) {
|
||||
d.getElementById('ceEditor').style.transform = (isCEEditor) ? "translateY(0px)":"translateY(100%)";
|
||||
}
|
||||
|
||||
function fetchAndExecute(url, name, callback, callError)
|
||||
{
|
||||
fetch
|
||||
(url+name, {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
callError("File " + name + " not found");
|
||||
return "";
|
||||
}
|
||||
return res.text();
|
||||
})
|
||||
.then(text => {
|
||||
callback(text);
|
||||
})
|
||||
.catch(function (error) {
|
||||
callError("Error getting " + name);
|
||||
})
|
||||
.finally(() => {
|
||||
// if (callback) setTimeout(callback,99);
|
||||
});
|
||||
}
|
||||
|
||||
function loadLogFile(name, attempt) {
|
||||
var ceLogArea = d.getElementById("ceLogArea");
|
||||
fetchAndExecute((loc?`http://${locip}`:'.') + "/", name , function(logtext)
|
||||
fetchAndExecute((loc?`http://${locip}`:'.') + "/", name, null, function(parms,logtext)
|
||||
{
|
||||
if (logtext == "") {
|
||||
if (attempt < 10) {
|
||||
@@ -50,7 +26,7 @@ function loadLogFile(name, attempt) {
|
||||
}
|
||||
else
|
||||
ceLogArea.value = logtext;
|
||||
}, function(error){
|
||||
}, function(parms,error){
|
||||
showToast(error);
|
||||
console.log(error);
|
||||
});
|
||||
@@ -91,7 +67,7 @@ function saveCE(name, segID) {
|
||||
|
||||
function populateCEEditor(name, segID)
|
||||
{
|
||||
fetchAndExecute((loc?`http://${locip}`:'.') + "/", name + ".wled", function(text)
|
||||
fetchAndExecute((loc?`http://${locip}`:'.') + "/", name + ".wled", null, function(parms,text)
|
||||
{
|
||||
var cn=`ARTI-FX Editor<br>
|
||||
<i>${name}.wled</i><br>
|
||||
@@ -117,7 +93,7 @@ function populateCEEditor(name, segID)
|
||||
ceLogArea.value = ".";
|
||||
loadLogFile(name + ".log", 1);
|
||||
|
||||
}, function(error){
|
||||
}, function(parms,error){
|
||||
showToast(error);
|
||||
console.log(error);
|
||||
});
|
||||
@@ -129,7 +105,7 @@ function downloadGHFile(url, name, save=false, warn=false) { //Githubfile
|
||||
if (url == "HBE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Effects%20pack/";
|
||||
if (url == "LM") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Ledmaps/";
|
||||
|
||||
fetchAndExecute(url, name, function(text) {
|
||||
fetchAndExecute(url, name, null, function(parms,text) {
|
||||
if (save) {
|
||||
if (warn && !confirm('Are you sure to download/overwrite ' + name + '?'))
|
||||
return;
|
||||
@@ -140,7 +116,7 @@ function downloadGHFile(url, name, save=false, warn=false) { //Githubfile
|
||||
var ceProgramArea = d.getElementById("ceProgramArea");
|
||||
ceProgramArea.value = text;
|
||||
}
|
||||
}, function(error){
|
||||
}, function(parms,error){
|
||||
showToast(error);
|
||||
console.log(url + name,error);
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
|
||||
*/
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 3))
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 4))
|
||||
// espressif bug: only_left has no sound, left and right are swapped
|
||||
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
|
||||
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef WLED_ENABLE_MQTT
|
||||
#error "This user mod requires MQTT to be enabled."
|
||||
#warning "This user mod expects MQTT to be enabled."
|
||||
#endif
|
||||
|
||||
#pragma once
|
||||
@@ -14,7 +14,7 @@
|
||||
class ShtUsermod : public Usermod
|
||||
{
|
||||
private:
|
||||
bool enabled = false; // Is usermod enabled or not
|
||||
//bool enabled = false; // Is usermod enabled or not //WLEDMM use public attribute of class UserMod
|
||||
bool firstRunDone = false; // Remembers if the first config load run had been done
|
||||
bool pinAllocDone = true; // Remembers if we have allocated pins
|
||||
bool initDone = false; // Remembers if the mod has been completely initialised
|
||||
@@ -22,7 +22,7 @@ class ShtUsermod : public Usermod
|
||||
bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics
|
||||
|
||||
// SHT vars
|
||||
SHT *shtTempHumidSensor; // Instance of SHT lib
|
||||
SHT *shtTempHumidSensor = nullptr; // Instance of SHT lib
|
||||
byte shtType = 0; // SHT sensor type to be used. Default: SHT30
|
||||
byte unitOfTemp = 0; // Temperature unit to be used. Default: Celsius (0 = Celsius, 1 = Fahrenheit)
|
||||
bool shtInitDone = false; // Remembers if SHT sensor has been initialised
|
||||
@@ -37,16 +37,17 @@ class ShtUsermod : public Usermod
|
||||
void initShtTempHumiditySensor();
|
||||
void cleanupShtTempHumiditySensor();
|
||||
void cleanup();
|
||||
bool isShtReady();
|
||||
inline bool isShtReady() { return shtInitDone; } // Checks if the SHT sensor has been initialised.
|
||||
|
||||
void publishTemperatureAndHumidityViaMqtt();
|
||||
void publishHomeAssistantAutodiscovery();
|
||||
void appendDeviceToMqttDiscoveryMessage(JsonDocument& root);
|
||||
|
||||
public:
|
||||
ShtUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM
|
||||
// Strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
//static const char _name[]; //WLEDMM use public attribute of class UserMod
|
||||
//static const char _enabled[]; //WLEDMM not needed
|
||||
static const char _shtType[];
|
||||
static const char _unitOfTemp[];
|
||||
static const char _haMqttDiscovery[];
|
||||
@@ -71,8 +72,8 @@ class ShtUsermod : public Usermod
|
||||
};
|
||||
|
||||
// Strings to reduce flash memory usage (used more than twice)
|
||||
const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor";
|
||||
const char ShtUsermod::_enabled[] PROGMEM = "Enabled";
|
||||
//const char ShtUsermod::_name[] PROGMEM = "SHT-Sensor"; //WLEDMM use public attribute of class UserMod
|
||||
//const char ShtUsermod::_enabled[] PROGMEM = "Enabled"; //WLEDMM not needed
|
||||
const char ShtUsermod::_shtType[] PROGMEM = "SHT-Type";
|
||||
const char ShtUsermod::_unitOfTemp[] PROGMEM = "Unit";
|
||||
const char ShtUsermod::_haMqttDiscovery[] PROGMEM = "Add-To-HA-MQTT-Discovery";
|
||||
@@ -93,10 +94,13 @@ void ShtUsermod::initShtTempHumiditySensor()
|
||||
case USERMOD_SHT_TYPE_SHT35: shtTempHumidSensor = (SHT *) new SHT35(); break;
|
||||
case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl);
|
||||
#else
|
||||
shtTempHumidSensor->begin((uint8_t)shtI2cAddress); // WLEDMM this connects to an existing Wire (I2C) object, instead starting a new driver
|
||||
#endif
|
||||
if (shtTempHumidSensor->readStatus() == 0xFFFF) {
|
||||
DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
|
||||
USER_PRINTF("[%s] SHT init failed, Sensor not found!\n", _name);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
@@ -113,8 +117,11 @@ void ShtUsermod::initShtTempHumiditySensor()
|
||||
*/
|
||||
void ShtUsermod::cleanupShtTempHumiditySensor()
|
||||
{
|
||||
if (isShtReady()) shtTempHumidSensor->reset();
|
||||
delete shtTempHumidSensor;
|
||||
if (isShtReady()) {
|
||||
shtTempHumidSensor->reset();
|
||||
delete shtTempHumidSensor;
|
||||
shtTempHumidSensor = nullptr;
|
||||
}
|
||||
shtInitDone = false;
|
||||
}
|
||||
|
||||
@@ -131,22 +138,15 @@ void ShtUsermod::cleanup()
|
||||
cleanupShtTempHumiditySensor();
|
||||
|
||||
if (pinAllocDone) {
|
||||
#if 0 // WLEDMM not needed
|
||||
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
|
||||
pinManager.deallocateMultiplePins(pins, 2, PinOwner::HW_I2C);
|
||||
#endif
|
||||
pinAllocDone = false;
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the SHT sensor has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool ShtUsermod::isShtReady()
|
||||
{
|
||||
return shtInitDone;
|
||||
shtInitDone = false; // WLEDMM bugfix
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,6 +158,7 @@ bool ShtUsermod::isShtReady()
|
||||
* @return void
|
||||
*/
|
||||
void ShtUsermod::publishTemperatureAndHumidityViaMqtt() {
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
char buf[128];
|
||||
|
||||
@@ -165,6 +166,7 @@ void ShtUsermod::publishTemperatureAndHumidityViaMqtt() {
|
||||
mqtt->publish(buf, 0, false, String(getTemperature()).c_str());
|
||||
snprintf_P(buf, 127, PSTR("%s/humidity"), mqttDeviceTopic);
|
||||
mqtt->publish(buf, 0, false, String(getHumidity()).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,6 +179,7 @@ void ShtUsermod::publishTemperatureAndHumidityViaMqtt() {
|
||||
* @return void
|
||||
*/
|
||||
void ShtUsermod::publishHomeAssistantAutodiscovery() {
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
|
||||
char json_str[1024], buf[128];
|
||||
@@ -214,6 +217,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
|
||||
mqtt->publish(buf, 0, true, json_str, payload_size);
|
||||
|
||||
haMqttDiscoveryDone = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +225,7 @@ void ShtUsermod::publishHomeAssistantAutodiscovery() {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
|
||||
JsonObject device = root.createNestedObject(F("dev"));
|
||||
device[F("ids")] = escapedMac.c_str();
|
||||
@@ -229,6 +234,7 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
|
||||
device[F("mdl")] = ESP.getChipModel();
|
||||
device[F("mf")] = F("espressif");
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Setup the mod.
|
||||
@@ -246,19 +252,35 @@ void ShtUsermod::setup()
|
||||
if (enabled) {
|
||||
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
|
||||
// GPIOs can be set to -1 and allocateMultiplePins() will return true, so check they're gt zero
|
||||
#if 0 // WLEDMM done by pinManager.joinWire()
|
||||
if (i2c_sda < 0 || i2c_scl < 0 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) {
|
||||
#else
|
||||
if (i2c_sda < 0 || i2c_scl < 0) {
|
||||
#endif
|
||||
DEBUG_PRINTF("[%s] SHT pin allocation failed!\n", _name);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
pinAllocDone = true;
|
||||
// WLEDMM join hardware I2C
|
||||
if (pinManager.joinWire()) { // WLEDMM - this allocates global I2C pins, then starts Wire - if not started previously
|
||||
pinAllocDone = true;
|
||||
|
||||
initShtTempHumiditySensor();
|
||||
initShtTempHumiditySensor();
|
||||
|
||||
initDone = true;
|
||||
initDone = true;
|
||||
} else {
|
||||
DEBUG_PRINTF("[%s] SHT I2C pin allocation failed!\n", _name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
firstRunDone = true;
|
||||
|
||||
if (enabled && initDone && pinAllocDone && isShtReady()) {
|
||||
USER_PRINTF(PSTR("[%s] SHT sensor ready.\n"), _name);
|
||||
} else {
|
||||
USER_PRINTF(PSTR("[%s] SHT sensor not ready.\n"), _name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +298,9 @@ void ShtUsermod::setup()
|
||||
*/
|
||||
void ShtUsermod::loop()
|
||||
{
|
||||
if (!enabled || !initDone || strip.isUpdating()) return;
|
||||
unsigned long last_runtime = 0; // WLEDMM ensure that strip.isUpdating() will not block longer that 1000ms
|
||||
if (!enabled || !initDone || !pinAllocDone || (strip.isUpdating() && (millis()-last_runtime < 1000))) return; // WLEDMM be nice, but not too nice
|
||||
last_runtime = millis();
|
||||
|
||||
if (isShtReady()) {
|
||||
if (millis() - shtLastTimeUpdated > 30000 && !shtDataRequested) {
|
||||
@@ -315,7 +339,9 @@ void ShtUsermod::loop()
|
||||
* @return void
|
||||
*/
|
||||
void ShtUsermod::onMqttConnect(bool sessionPresent) {
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
if (haMqttDiscovery && !haMqttDiscoveryDone) publishHomeAssistantAutodiscovery();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,7 +383,7 @@ void ShtUsermod::addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[F("enabled")] = enabled;
|
||||
top[FPSTR(_shtType)] = shtType;
|
||||
top[FPSTR(_unitOfTemp)] = unitOfTemp;
|
||||
top[FPSTR(_haMqttDiscovery)] = haMqttDiscovery;
|
||||
@@ -388,7 +414,7 @@ bool ShtUsermod::readFromConfig(JsonObject &root)
|
||||
byte oldUnitOfTemp = unitOfTemp;
|
||||
bool oldHaMqttDiscovery = haMqttDiscovery;
|
||||
|
||||
getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
getJsonValue(top[F("enabled")], enabled);
|
||||
getJsonValue(top[FPSTR(_shtType)], shtType);
|
||||
getJsonValue(top[FPSTR(_unitOfTemp)], unitOfTemp);
|
||||
getJsonValue(top[FPSTR(_haMqttDiscovery)], haMqttDiscovery);
|
||||
@@ -463,7 +489,19 @@ void ShtUsermod::addToJsonInfo(JsonObject& root)
|
||||
jsonHumidity.add(F(" RH"));
|
||||
|
||||
jsonTemp.add(getTemperature());
|
||||
jsonTemp.add(unitOfTemp ? "°F" : "°C");
|
||||
jsonTemp.add(getUnitString());
|
||||
|
||||
// sensor object
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
|
||||
jsonTemp = sensor.createNestedArray(F("temp"));
|
||||
jsonTemp.add(getTemperature());
|
||||
jsonTemp.add(getUnitString());
|
||||
|
||||
jsonHumidity = sensor.createNestedArray(F("humidity"));
|
||||
jsonHumidity.add(getHumidity());
|
||||
jsonHumidity.add(F(" RH"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ In this usermod file you can find the documentation on how to take advantage of
|
||||
|
||||
## Installation
|
||||
|
||||
Copy `usermod_v2_fastled.h` to the wled00 directory.
|
||||
Copy `usermod_v2_animartrix.h` to the wled00 directory.
|
||||
Uncomment the corresponding lines in `usermods_list.cpp` and compile!
|
||||
_(You shouldn't need to actually install this, it does nothing useful)_
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
//based on: https://gist.github.com/StefanPetrick/9c091d9a28a902af5a7b540e40442c64
|
||||
|
||||
class StefanPetrickCore {
|
||||
class AnimartrixCore {
|
||||
private:
|
||||
|
||||
public:
|
||||
@@ -60,11 +60,11 @@ class StefanPetrickCore {
|
||||
float noise_angle_c, noise_angle_d, noise_angle_e, noise_angle_f; // angles based on linear noise travel
|
||||
float dir_c, dir_d, dir_e, dir_f; // direction multiplicators
|
||||
|
||||
StefanPetrickCore() {
|
||||
USER_PRINTLN("StefanPetrickCore constructor");
|
||||
AnimartrixCore() {
|
||||
USER_PRINTLN("AnimartrixCore constructor");
|
||||
}
|
||||
~StefanPetrickCore() {
|
||||
USER_PRINTLN("StefanPetrickCore destructor");
|
||||
~AnimartrixCore() {
|
||||
USER_PRINTLN("AnimartrixCore destructor");
|
||||
}
|
||||
|
||||
void init() {
|
||||
@@ -177,7 +177,7 @@ class StefanPetrickCore {
|
||||
}
|
||||
};
|
||||
|
||||
class PolarBasics:public StefanPetrickCore {
|
||||
class PolarBasics:public AnimartrixCore {
|
||||
private:
|
||||
|
||||
public:
|
||||
@@ -262,7 +262,7 @@ class PolarBasics:public StefanPetrickCore {
|
||||
|
||||
void calculate_oscillators() {
|
||||
|
||||
StefanPetrickCore::calculate_oscillators();
|
||||
AnimartrixCore::calculate_oscillators();
|
||||
|
||||
uint16_t noi;
|
||||
noi = inoise16(10000 + linear_c * 100000); // some noise controlled angular offsets
|
||||
@@ -369,7 +369,7 @@ class PolarBasics:public StefanPetrickCore {
|
||||
//based on https://gist.github.com/StefanPetrick/35ffd8467df22a77067545cfb889aa4f
|
||||
//and Fastled podcast nr 3: https://www.youtube.com/watch?v=3tfjP7GJnZo
|
||||
|
||||
class CircularBlobs:public StefanPetrickCore {
|
||||
class CircularBlobs:public AnimartrixCore {
|
||||
private:
|
||||
|
||||
float fade(float t){ return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
@@ -498,7 +498,7 @@ class CircularBlobs:public StefanPetrickCore {
|
||||
|
||||
void calculate_oscillators() {
|
||||
|
||||
StefanPetrickCore::calculate_oscillators();
|
||||
AnimartrixCore::calculate_oscillators();
|
||||
|
||||
float n;
|
||||
|
||||
@@ -615,11 +615,11 @@ uint16_t mode_CircularBlobs(void) {
|
||||
static const char _data_FX_mode_CircularBlobs[] PROGMEM = "💡CircularBlobs ☾@AngleDist,AngleMult;;!;2;sx=51,ix=51,c1=0,c2=0,c3=0";
|
||||
|
||||
|
||||
class FastledUsermod : public Usermod {
|
||||
class AnimartrixUsermod : public Usermod {
|
||||
|
||||
public:
|
||||
|
||||
FastledUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM
|
||||
AnimartrixUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM
|
||||
|
||||
void setup() {
|
||||
strip.addEffect(255, &mode_PolarBasics, _data_FX_mode_PolarBasics);
|
||||
@@ -640,7 +640,7 @@ class FastledUsermod : public Usermod {
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_FASTLED;
|
||||
return USERMOD_ID_ANIMARTRIX;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -93,10 +93,11 @@ typedef enum {
|
||||
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_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||
SSD1306_SPI64=7, // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||
SSD1309_SPI64=8, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
|
||||
SSD1327_SPI128=9 // U8X8_SSD1327_WS_128X128_4W_SW_SPI
|
||||
SSD1327_SPI128=9,// U8X8_SSD1327_WS_128X128_4W_SW_SPI
|
||||
SSD1309_64=10 // U8X8_SSD1309_128X64_NONAME2_HW_I2C
|
||||
} DisplayType;
|
||||
|
||||
|
||||
@@ -213,6 +214,20 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
|
||||
// is this display using SPI?
|
||||
bool displayIsSPI(DisplayType disp) {
|
||||
switch(disp) {
|
||||
case SSD1306_SPI: // falls thru
|
||||
case SSD1306_SPI64: // falls thru
|
||||
case SSD1309_SPI64: // falls thru
|
||||
case SSD1327_SPI128:
|
||||
return true; // yes its SPI
|
||||
break; // makes compiler happy
|
||||
default:
|
||||
return false; // no anything else is I2C
|
||||
}
|
||||
}
|
||||
|
||||
// some displays need this to properly apply contrast
|
||||
void setVcomh(bool highContrast) {
|
||||
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
|
||||
@@ -418,7 +433,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
void setup() {
|
||||
if (!enabled) return; // typeOK = true will be set after successful setup
|
||||
|
||||
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
|
||||
bool isHW = false;
|
||||
bool isSPI = displayIsSPI(type);
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
if (isSPI) {
|
||||
if (ioPin[0] < 0 || ioPin[1] < 0) {
|
||||
@@ -529,6 +545,10 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
break;
|
||||
case SSD1309_64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME2_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME2_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
break;
|
||||
case SSD1305:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
@@ -1259,13 +1279,14 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
oappend(SET_F("addOption(dd,'SSD1306',1);"));
|
||||
oappend(SET_F("addOption(dd,'SH1106',2);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 128x64',3);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1309 128x64',10);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1305',4);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1327 SPI 128x128',9);"));
|
||||
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
|
||||
bool isSPI = displayIsSPI(type);
|
||||
// WLEDMM add defaults
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','I2C/SPI CLK');"));
|
||||
oappend(SET_F("dRO('4LineDisplay:pin[]',0);")); // disable read only pins
|
||||
@@ -1328,7 +1349,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
void addToConfig(JsonObject& root) {
|
||||
// determine if we are using global HW pins (data & clock)
|
||||
int8_t hw_dta, hw_clk;
|
||||
if ((type == SSD1306_SPI || type == SSD1306_SPI64) || (type > 7)) {
|
||||
if (displayIsSPI(type)) {
|
||||
hw_clk = spi_sclk;
|
||||
hw_dta = spi_mosi;
|
||||
} else {
|
||||
@@ -1394,7 +1415,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
showSeconds = top[FPSTR(_showSeconds)] | showSeconds;
|
||||
contrastFix = top[FPSTR(_contrastFix)] | contrastFix;
|
||||
if (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType > 7)
|
||||
if (displayIsSPI(newType))
|
||||
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
else
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
@@ -1424,7 +1445,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
USER_PRINTLN(F("Display terminated."));
|
||||
}
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
|
||||
bool isSPI = displayIsSPI(type);
|
||||
if (isSPI) {
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po);
|
||||
bool isHW = (oldPin[0]==spi_sclk && oldPin[1]==spi_mosi);
|
||||
|
||||
@@ -40,39 +40,39 @@ class WordClockUsermod : public Usermod
|
||||
// Normal wiring
|
||||
const int maskMinutes[14][maskSizeMinutes] =
|
||||
{
|
||||
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
|
||||
{ 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // :05 fünf nach
|
||||
{ 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // :10 zehn nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // :20 zwanzig nach
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 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, 40, 41, 42, 43, 44, 45, 46, 47}, // :35 fünf nach halb
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // :40 zwanzig vor
|
||||
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel
|
||||
{ 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // :50 zehn vor
|
||||
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // :55 fünf vor
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // :15 alternative viertel nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // :45 alternative viertel vor
|
||||
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00
|
||||
{ 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // 1 - 05 fünf nach
|
||||
{ 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // 2 - 10 zehn nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // 4 - 20 zwanzig nach
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb
|
||||
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb
|
||||
{ 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // 7 - 35 fünf nach halb
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // 8 - 40 zwanzig vor
|
||||
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel
|
||||
{ 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor
|
||||
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // 12 - 15 alternative viertel nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // 13 - 45 alternative viertel vor
|
||||
};
|
||||
|
||||
// Meander wiring
|
||||
const int maskMinutesMea[14][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
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // :15 alternative viertel nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // :45 alternative viertel vor
|
||||
{ 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // 1 - 05 fünf nach
|
||||
{ 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // 2 - 10 zehn nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel
|
||||
{ 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // 4 - 20 zwanzig nach
|
||||
{ 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb
|
||||
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // 7 - 35 fünf nach halb
|
||||
{ 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // 8 - 40 zwanzig vor
|
||||
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel
|
||||
{ 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor
|
||||
{ 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // 12 - 15 alternative viertel nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // 13 - 45 alternative viertel vor
|
||||
};
|
||||
|
||||
|
||||
@@ -284,12 +284,13 @@ class WordClockUsermod : public Usermod
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 9:
|
||||
// viertel vor bzw dreiviertel
|
||||
// viertel vor
|
||||
if (nord) {
|
||||
setMinutes(9);
|
||||
setMinutes(13);
|
||||
}
|
||||
// dreiviertel
|
||||
else {
|
||||
setMinutes(12);
|
||||
setMinutes(9);
|
||||
}
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
@@ -422,12 +423,18 @@ class WordClockUsermod : public Usermod
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("WordClockUsermod");
|
||||
top["active"] = usermodActive;
|
||||
top["displayItIs"] = displayItIs;
|
||||
top["ledOffset"] = ledOffset;
|
||||
top["Meander wiring?"] = meander;
|
||||
top["Norddeutsch"] = nord;
|
||||
JsonObject top = root.createNestedObject(F("WordClockUsermod"));
|
||||
top[F("active")] = usermodActive;
|
||||
top[F("displayItIs")] = displayItIs;
|
||||
top[F("ledOffset")] = ledOffset;
|
||||
top[F("Meander wiring?")] = meander;
|
||||
top[F("Norddeutsch")] = nord;
|
||||
}
|
||||
|
||||
void appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('WordClockUsermod:ledOffset', 1, 'Number of LEDs before the letters');"));
|
||||
oappend(SET_F("addInfo('WordClockUsermod:Norddeutsch', 1, 'Viertel vor instead of Dreiviertel');"));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -450,15 +457,15 @@ class WordClockUsermod : public Usermod
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root["WordClockUsermod"];
|
||||
JsonObject top = root[F("WordClockUsermod")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["active"], usermodActive);
|
||||
configComplete &= getJsonValue(top["displayItIs"], displayItIs);
|
||||
configComplete &= getJsonValue(top["ledOffset"], ledOffset);
|
||||
configComplete &= getJsonValue(top["Meander wiring?"], meander);
|
||||
configComplete &= getJsonValue(top["Norddeutsch"], nord);
|
||||
configComplete &= getJsonValue(top[F("active")], usermodActive);
|
||||
configComplete &= getJsonValue(top[F("displayItIs")], displayItIs);
|
||||
configComplete &= getJsonValue(top[F("ledOffset")], ledOffset);
|
||||
configComplete &= getJsonValue(top[F("Meander wiring?")], meander);
|
||||
configComplete &= getJsonValue(top[F("Norddeutsch")], nord);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user