2022-12-28 21:40:13 +00:00
# pragma once
# include "wled.h"
# include "battery_defaults.h"
2024-05-06 15:39:40 +00:00
# include "UMBattery.h"
# include "types/UnkownUMBattery.h"
# include "types/LionUMBattery.h"
2024-07-11 11:56:33 +00:00
# include "types/LipoUMBattery.h"
2022-12-28 21:40:13 +00:00
/*
* Usermod by Maximilian Mewes
2024-04-30 12:54:53 +00:00
* E - mail : mewes . maximilian @ gmx . de
* Created at : 25.12 .2022
2022-12-28 21:40:13 +00:00
* If you have any questions , please feel free to contact me .
*/
class UsermodBattery : public Usermod
{
private :
// battery pin can be defined in my_config.h
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN ;
2023-01-06 16:00:29 +00:00
2024-05-06 15:39:40 +00:00
UMBattery * bat = new UnkownUMBattery ( ) ;
2023-09-09 19:50:30 +00:00
batteryConfig cfg ;
2023-01-05 23:19:16 +00:00
2024-05-09 22:09:45 +00:00
// Initial delay before first reading to allow voltage stabilization
unsigned long initialDelay = USERMOD_BATTERY_INITIAL_DELAY ;
bool initialDelayComplete = false ;
bool isFirstVoltageReading = true ;
2022-12-28 21:40:13 +00:00
// how often to read the battery voltage
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL ;
unsigned long nextReadTime = 0 ;
unsigned long lastReadTime = 0 ;
2023-03-10 00:28:04 +00:00
// between 0 and 1, to control strength of voltage smoothing filter
2023-09-09 19:50:30 +00:00
float alpha = USERMOD_BATTERY_AVERAGING_ALPHA ;
2022-12-28 21:40:13 +00:00
// auto shutdown/shutoff/master off feature
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED ;
2023-01-20 23:39:51 +00:00
uint8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD ;
2022-12-28 21:40:13 +00:00
// low power indicator feature
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED ;
2023-01-20 23:39:51 +00:00
uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET ;
uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD ;
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10 ;
uint8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION ;
2022-12-28 21:40:13 +00:00
bool lowPowerIndicationDone = false ;
unsigned long lowPowerActivationTime = 0 ; // used temporary during active time
2023-01-20 23:39:51 +00:00
uint8_t lastPreset = 0 ;
2022-12-28 21:40:13 +00:00
2024-04-30 15:02:57 +00:00
//
2022-12-28 21:40:13 +00:00
bool initDone = false ;
bool initializing = true ;
2024-08-20 10:37:01 +00:00
bool HomeAssistantDiscovery = false ;
2022-12-28 21:40:13 +00:00
// strings to reduce flash memory usage (used more than twice)
static const char _name [ ] ;
static const char _readInterval [ ] ;
static const char _enabled [ ] ;
static const char _threshold [ ] ;
static const char _preset [ ] ;
static const char _duration [ ] ;
static const char _init [ ] ;
2024-08-20 10:37:01 +00:00
static const char _haDiscovery [ ] ;
2022-12-28 21:40:13 +00:00
2023-09-09 19:50:30 +00:00
/**
* Helper for rounding floating point values
*/
2022-12-28 21:40:13 +00:00
float dot2round ( float x )
{
float nx = ( int ) ( x * 100 + .5 ) ;
return ( float ) ( nx / 100 ) ;
}
2024-08-20 10:37:01 +00:00
/**
* Helper for converting a string to lowercase
*/
String stringToLower ( String str )
{
for ( int i = 0 ; i < str . length ( ) ; i + + )
if ( str [ i ] > = ' A ' & & str [ i ] < = ' Z ' )
str [ i ] + = 32 ;
return str ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Turn off all leds
*/
void turnOff ( )
{
bri = 0 ;
stateUpdated ( CALL_MODE_DIRECT_CHANGE ) ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously
*/
void lowPowerIndicator ( )
{
if ( ! lowPowerIndicatorEnabled ) return ;
2023-01-04 11:32:31 +00:00
if ( batteryPin < 0 ) return ; // no measurement
2023-01-05 18:48:53 +00:00
if ( lowPowerIndicationDone & & lowPowerIndicatorReactivationThreshold < = bat - > getLevel ( ) ) lowPowerIndicationDone = false ;
if ( lowPowerIndicatorThreshold < = bat - > getLevel ( ) ) return ;
2022-12-28 21:40:13 +00:00
if ( lowPowerIndicationDone ) return ;
if ( lowPowerActivationTime < = 1 ) {
lowPowerActivationTime = millis ( ) ;
lastPreset = currentPreset ;
applyPreset ( lowPowerIndicatorPreset ) ;
}
if ( lowPowerActivationTime + ( lowPowerIndicatorDuration * 1000 ) < = millis ( ) ) {
lowPowerIndicationDone = true ;
lowPowerActivationTime = 0 ;
applyPreset ( lastPreset ) ;
}
}
2023-09-09 19:50:30 +00:00
/**
* read the battery voltage in different ways depending on the architecture
*/
float readVoltage ( )
2023-09-09 19:01:55 +00:00
{
2023-09-09 19:50:30 +00:00
# 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 ) * bat - > getVoltageMultiplier ( ) + bat - > getCalibration ( ) ;
# 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 ) * bat - > getVoltageMultiplier ( ) + bat - > getCalibration ( ) ;
# endif
2023-09-09 19:01:55 +00:00
}
2023-09-09 19:50:30 +00:00
2024-08-19 20:07:08 +00:00
# ifndef WLED_DISABLE_MQTT
void addMqttSensor ( const String & name , const String & type , const String & topic , const String & deviceClass , const String & unitOfMeasurement = " " , const bool & isDiagnostic = false )
2024-08-19 20:22:46 +00:00
{
2024-08-19 20:07:08 +00:00
StaticJsonDocument < 600 > doc ;
char uid [ 128 ] , json_str [ 1024 ] , buf [ 128 ] ;
doc [ F ( " name " ) ] = name ;
doc [ F ( " stat_t " ) ] = topic ;
2024-08-20 10:37:01 +00:00
sprintf_P ( uid , PSTR ( " %s_%s_%s " ) , escapedMac . c_str ( ) , stringToLower ( name ) . c_str ( ) , type ) ;
2024-08-19 20:07:08 +00:00
doc [ F ( " uniq_id " ) ] = uid ;
doc [ F ( " dev_cla " ) ] = deviceClass ;
2024-08-20 10:37:01 +00:00
doc [ F ( " exp_aft " ) ] = 1800 ;
2024-08-19 20:07:08 +00:00
if ( type = = " binary_sensor " ) {
doc [ F ( " pl_on " ) ] = " on " ;
doc [ F ( " pl_off " ) ] = " off " ;
}
if ( unitOfMeasurement ! = " " )
doc [ F ( " unit_of_measurement " ) ] = unitOfMeasurement ;
if ( isDiagnostic )
doc [ F ( " entity_category " ) ] = " diagnostic " ;
JsonObject device = doc . createNestedObject ( F ( " device " ) ) ; // attach the sensor to the same device
device [ F ( " name " ) ] = serverDescription ;
device [ F ( " ids " ) ] = String ( F ( " wled-sensor- " ) ) + mqttClientID ;
device [ F ( " mf " ) ] = F ( WLED_BRAND ) ;
device [ F ( " mdl " ) ] = F ( WLED_PRODUCT_NAME ) ;
device [ F ( " sw " ) ] = versionString ;
sprintf_P ( buf , PSTR ( " homeassistant/%s/%s/%s/config " ) , type , mqttClientID , uid ) ;
DEBUG_PRINTLN ( buf ) ;
size_t payload_size = serializeJson ( doc , json_str ) ;
DEBUG_PRINTLN ( json_str ) ;
mqtt - > publish ( buf , 0 , true , json_str , payload_size ) ;
}
void publishMqtt ( const char * topic , const char * state )
{
if ( WLED_MQTT_CONNECTED ) {
char buf [ 128 ] ;
snprintf_P ( buf , 127 , PSTR ( " %s/%s " ) , mqttDeviceTopic , topic ) ;
mqtt - > publish ( buf , 0 , false , state ) ;
}
}
# endif
2023-09-09 19:50:30 +00:00
public :
2022-12-28 21:40:13 +00:00
//Functions called by WLED
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* setup ( ) is called once at boot . WiFi is not yet connected at this point .
* You can use it to initialize variables , sensors or similar .
*/
void setup ( )
{
2024-05-06 15:45:02 +00:00
// plug in the right battery type
if ( cfg . type = = ( batteryType ) lipo ) {
bat = new LipoUMBattery ( ) ;
} else if ( cfg . type = = ( batteryType ) lion ) {
2024-05-06 15:46:26 +00:00
bat = new LionUMBattery ( ) ;
2024-05-06 15:45:02 +00:00
}
// update the choosen battery type with configured values
bat - > update ( cfg ) ;
2022-12-28 21:40:13 +00:00
# ifdef ARDUINO_ARCH_ESP32
bool success = false ;
DEBUG_PRINTLN ( F ( " Allocating battery pin... " ) ) ;
if ( batteryPin > = 0 & & digitalPinToAnalogChannel ( batteryPin ) > = 0 )
2024-09-19 19:44:11 +00:00
if ( PinManager : : allocatePin ( batteryPin , false , PinOwner : : UM_Battery ) ) {
2022-12-28 21:40:13 +00:00
DEBUG_PRINTLN ( F ( " Battery pin allocation succeeded. " ) ) ;
success = true ;
}
if ( ! success ) {
DEBUG_PRINTLN ( F ( " Battery pin allocation failed. " ) ) ;
batteryPin = - 1 ; // allocation failed
2023-01-04 11:32:31 +00:00
} else {
pinMode ( batteryPin , INPUT ) ;
2022-12-28 21:40:13 +00:00
}
# else //ESP8266 boards have only one analog input pin A0
pinMode ( batteryPin , INPUT ) ;
# endif
2024-05-09 22:09:45 +00:00
// First voltage reading is delayed to allow voltage stabilization after powering up
nextReadTime = millis ( ) + initialDelay ;
2022-12-28 21:40:13 +00:00
lastReadTime = millis ( ) ;
initDone = true ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* connected ( ) is called every time the WiFi is ( re ) connected
* Use it to initialize network interfaces
*/
void connected ( )
{
//Serial.println("Connected to WiFi!");
}
/*
* loop ( ) is called continuously . Here you can check for events , read sensors , etc .
*
*/
void loop ( )
{
if ( strip . isUpdating ( ) ) return ;
lowPowerIndicator ( ) ;
2024-05-09 22:09:45 +00:00
// Handling the initial delay
if ( ! initialDelayComplete & & millis ( ) < nextReadTime )
return ; // Continue to return until the initial delay is over
// Once the initial delay is over, set it as complete
if ( ! initialDelayComplete )
{
initialDelayComplete = true ;
// Set the regular interval after initial delay
nextReadTime = millis ( ) + readingInterval ;
}
// Make the first voltage reading after the initial delay has elapsed
if ( isFirstVoltageReading )
{
bat - > setVoltage ( readVoltage ( ) ) ;
isFirstVoltageReading = false ;
}
2022-12-28 21:40:13 +00:00
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if ( millis ( ) < nextReadTime ) return ;
nextReadTime = millis ( ) + readingInterval ;
lastReadTime = millis ( ) ;
2023-01-04 11:32:31 +00:00
if ( batteryPin < 0 ) return ; // nothing to read
2023-09-09 19:50:30 +00:00
initializing = false ;
float rawValue = readVoltage ( ) ;
2023-09-09 19:01:55 +00:00
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
2023-09-09 19:50:30 +00:00
float filteredVoltage = bat - > getVoltage ( ) + alpha * ( rawValue - bat - > getVoltage ( ) ) ;
2022-12-28 21:40:13 +00:00
2023-09-09 19:50:30 +00:00
bat - > setVoltage ( filteredVoltage ) ;
2022-12-28 21:40:13 +00:00
// translate battery voltage into percentage
2023-09-09 19:50:30 +00:00
bat - > calculateAndSetLevel ( filteredVoltage ) ;
2022-12-28 21:40:13 +00:00
// Auto off -- Master power off
2023-01-05 18:48:53 +00:00
if ( autoOffEnabled & & ( autoOffThreshold > = bat - > getLevel ( ) ) )
2022-12-28 21:40:13 +00:00
turnOff ( ) ;
2023-01-12 19:35:34 +00:00
# ifndef WLED_DISABLE_MQTT
2024-08-19 20:07:08 +00:00
publishMqtt ( " battery " , String ( bat - > getLevel ( ) , 0 ) . c_str ( ) ) ;
publishMqtt ( " voltage " , String ( bat - > getVoltage ( ) ) . c_str ( ) ) ;
2023-01-12 19:35:34 +00:00
# endif
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* 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 )
{
JsonObject user = root [ " u " ] ;
if ( user . isNull ( ) ) user = root . createNestedObject ( " u " ) ;
2023-01-04 11:32:31 +00:00
if ( batteryPin < 0 ) {
JsonArray infoVoltage = user . createNestedArray ( F ( " Battery voltage " ) ) ;
infoVoltage . add ( F ( " n/a " ) ) ;
infoVoltage . add ( F ( " invalid GPIO " ) ) ;
return ; // no GPIO - nothing to report
}
2022-12-28 21:40:13 +00:00
// info modal display names
JsonArray infoPercentage = user . createNestedArray ( F ( " Battery level " ) ) ;
JsonArray infoVoltage = user . createNestedArray ( F ( " Battery voltage " ) ) ;
JsonArray infoNextUpdate = user . createNestedArray ( F ( " Next update " ) ) ;
infoNextUpdate . add ( ( nextReadTime - millis ( ) ) / 1000 ) ;
infoNextUpdate . add ( F ( " sec " ) ) ;
if ( initializing ) {
infoPercentage . add ( FPSTR ( _init ) ) ;
infoVoltage . add ( FPSTR ( _init ) ) ;
return ;
}
2023-01-05 18:48:53 +00:00
if ( bat - > getLevel ( ) < 0 ) {
2022-12-28 21:40:13 +00:00
infoPercentage . add ( F ( " invalid " ) ) ;
} else {
2023-01-05 18:48:53 +00:00
infoPercentage . add ( bat - > getLevel ( ) ) ;
2022-12-28 21:40:13 +00:00
}
infoPercentage . add ( F ( " % " ) ) ;
2023-01-05 18:48:53 +00:00
if ( bat - > getVoltage ( ) < 0 ) {
2022-12-28 21:40:13 +00:00
infoVoltage . add ( F ( " invalid " ) ) ;
} else {
2023-01-05 18:48:53 +00:00
infoVoltage . add ( dot2round ( bat - > getVoltage ( ) ) ) ;
2022-12-28 21:40:13 +00:00
}
infoVoltage . add ( F ( " V " ) ) ;
}
2023-01-20 23:39:51 +00:00
void addBatteryToJsonObject ( JsonObject & battery , bool forJsonState )
{
2023-09-09 19:50:30 +00:00
if ( forJsonState ) { battery [ F ( " type " ) ] = cfg . type ; } else { battery [ F ( " type " ) ] = ( String ) cfg . type ; } // has to be a String otherwise it won't get converted to a Dropdown
2023-01-20 23:39:51 +00:00
battery [ F ( " min-voltage " ) ] = bat - > getMinVoltage ( ) ;
battery [ F ( " max-voltage " ) ] = bat - > getMaxVoltage ( ) ;
battery [ F ( " calibration " ) ] = bat - > getCalibration ( ) ;
2024-04-30 13:24:02 +00:00
battery [ F ( " voltage-multiplier " ) ] = bat - > getVoltageMultiplier ( ) ;
2023-01-20 23:39:51 +00:00
battery [ FPSTR ( _readInterval ) ] = readingInterval ;
2024-08-20 10:37:01 +00:00
battery [ FPSTR ( _haDiscovery ) ] = HomeAssistantDiscovery ;
2023-01-20 23:39:51 +00:00
JsonObject ao = battery . createNestedObject ( F ( " auto-off " ) ) ; // auto off section
ao [ FPSTR ( _enabled ) ] = autoOffEnabled ;
ao [ FPSTR ( _threshold ) ] = autoOffThreshold ;
JsonObject lp = battery . createNestedObject ( F ( " indicator " ) ) ; // low power section
lp [ FPSTR ( _enabled ) ] = lowPowerIndicatorEnabled ;
lp [ FPSTR ( _preset ) ] = lowPowerIndicatorPreset ; // dropdown trickery (String)lowPowerIndicatorPreset;
lp [ FPSTR ( _threshold ) ] = lowPowerIndicatorThreshold ;
lp [ FPSTR ( _duration ) ] = lowPowerIndicatorDuration ;
}
2022-12-28 21:40:13 +00:00
2023-01-21 00:44:50 +00:00
void getUsermodConfigFromJsonObject ( JsonObject & battery )
{
2023-09-09 19:50:30 +00:00
getJsonValue ( battery [ F ( " type " ) ] , cfg . type ) ;
getJsonValue ( battery [ F ( " min-voltage " ) ] , cfg . minVoltage ) ;
getJsonValue ( battery [ F ( " max-voltage " ) ] , cfg . maxVoltage ) ;
getJsonValue ( battery [ F ( " calibration " ) ] , cfg . calibration ) ;
2024-04-30 13:24:02 +00:00
getJsonValue ( battery [ F ( " voltage-multiplier " ) ] , cfg . voltageMultiplier ) ;
2023-01-21 00:44:50 +00:00
setReadingInterval ( battery [ FPSTR ( _readInterval ) ] | readingInterval ) ;
2024-08-20 10:37:01 +00:00
setHomeAssistantDiscovery ( battery [ FPSTR ( _haDiscovery ) ] | HomeAssistantDiscovery ) ;
2023-01-21 00:44:50 +00:00
JsonObject ao = battery [ F ( " auto-off " ) ] ;
setAutoOffEnabled ( ao [ FPSTR ( _enabled ) ] | autoOffEnabled ) ;
setAutoOffThreshold ( ao [ FPSTR ( _threshold ) ] | autoOffThreshold ) ;
JsonObject lp = battery [ F ( " indicator " ) ] ;
setLowPowerIndicatorEnabled ( lp [ FPSTR ( _enabled ) ] | lowPowerIndicatorEnabled ) ;
setLowPowerIndicatorPreset ( lp [ FPSTR ( _preset ) ] | lowPowerIndicatorPreset ) ;
setLowPowerIndicatorThreshold ( lp [ FPSTR ( _threshold ) ] | lowPowerIndicatorThreshold ) ;
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10 ;
setLowPowerIndicatorDuration ( lp [ FPSTR ( _duration ) ] | lowPowerIndicatorDuration ) ;
if ( initDone )
2023-09-09 19:50:30 +00:00
bat - > update ( cfg ) ;
2023-01-21 00:44:50 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* 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 )
{
2023-01-20 23:39:51 +00:00
JsonObject battery = root . createNestedObject ( FPSTR ( _name ) ) ;
2024-04-30 15:02:57 +00:00
if ( battery . isNull ( ) )
2023-01-20 23:39:51 +00:00
battery = root . createNestedObject ( FPSTR ( _name ) ) ;
addBatteryToJsonObject ( battery , true ) ;
DEBUG_PRINTLN ( F ( " Battery state exposed in JSON API. " ) ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* readFromJsonState ( ) can be used to receive data clients send to the / json / state part of the JSON API ( state object ) .
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState ( JsonObject & root )
{
2023-01-21 00:44:50 +00:00
if ( ! initDone ) return ; // prevent crash on boot applyPreset()
JsonObject battery = root [ FPSTR ( _name ) ] ;
if ( ! battery . isNull ( ) ) {
getUsermodConfigFromJsonObject ( battery ) ;
DEBUG_PRINTLN ( F ( " Battery state read from JSON API. " ) ) ;
}
2022-12-28 21:40:13 +00:00
}
*/
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* addToConfig ( ) can be used to add custom persistent settings to the cfg . json file in the " um " ( usermod ) object .
* It will be called by WLED when settings are actually saved ( for example , LED settings are saved )
* If you want to force saving the current state , use serializeConfig ( ) in your loop ( ) .
*
* CAUTION : serializeConfig ( ) will initiate a filesystem write operation .
* It might cause the LEDs to stutter and will cause flash wear if called too often .
* Use it sparingly and always in the loop , never in network callbacks !
*
* addToConfig ( ) will make your settings editable through the Usermod Settings page automatically .
*
* Usermod Settings Overview :
* - Numeric values are treated as floats in the browser .
* - If the numeric value entered into the browser contains a decimal point , it will be parsed as a C float
* before being returned to the Usermod . The float data type has only 6 - 7 decimal digits of precision , and
* doubles are not supported , numbers will be rounded to the nearest float value when being parsed .
* The range accepted by the input field is + / - 1.175494351e-38 to + / - 3.402823466e+38 .
* - If the numeric value entered into the browser doesn ' t contain a decimal point , it will be parsed as a
* C int32_t ( range : - 2147483648 to 2147483647 ) before being returned to the usermod .
* Overflows or underflows are truncated to the max / min value for an int32_t , and again truncated to the type
* used in the Usermod when reading the value from ArduinoJson .
* - Pin values can be treated differently from an integer value by using the key name " pin "
* - " pin " can contain a single or array of integer values
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
* - Red color indicates a conflict . Yellow color indicates a pin with a warning ( e . g . an input - only pin )
* - Tip : use int8_t to store the pin value in the Usermod , so a - 1 value ( pin not set ) can be used
*
* See usermod_v2_auto_save . h for an example that saves Flash space by reusing ArduinoJson key name strings
*
* If you need a dedicated settings page with custom layout for your Usermod , that takes a lot more work .
* You will have to add the setting to the HTML , xml . cpp and set . cpp manually .
* See the WLED Soundreactive fork ( code and wiki ) for reference . https : //github.com/atuline/WLED
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings !
*/
void addToConfig ( JsonObject & root )
{
2023-01-20 23:39:51 +00:00
JsonObject battery = root . createNestedObject ( FPSTR ( _name ) ) ;
if ( battery . isNull ( ) ) {
battery = root . createNestedObject ( FPSTR ( _name ) ) ;
}
2022-12-28 21:40:13 +00:00
# ifdef ARDUINO_ARCH_ESP32
2023-01-04 16:30:08 +00:00
battery [ F ( " pin " ) ] = batteryPin ;
2022-12-28 21:40:13 +00:00
# endif
2023-01-20 23:39:51 +00:00
addBatteryToJsonObject ( battery , false ) ;
2022-12-28 21:40:13 +00:00
2023-03-16 00:33:57 +00:00
// read voltage in case calibration or voltage multiplier changed to see immediate effect
2024-04-30 16:11:18 +00:00
bat - > setVoltage ( readVoltage ( ) ) ;
2023-03-16 00:33:57 +00:00
2022-12-28 21:40:13 +00:00
DEBUG_PRINTLN ( F ( " Battery config saved. " ) ) ;
}
void appendConfigData ( )
{
2023-09-09 19:01:55 +00:00
// Total: 462 Bytes
2024-08-20 10:37:01 +00:00
oappend ( SET_F ( " td=addDropdown('Battery','type'); " ) ) ; // 34 Bytes
oappend ( SET_F ( " addOption(td,'Unkown','0'); " ) ) ; // 28 Bytes
oappend ( SET_F ( " addOption(td,'LiPo','1'); " ) ) ; // 26 Bytes
oappend ( SET_F ( " addOption(td,'LiOn','2'); " ) ) ; // 26 Bytes
2023-01-20 23:39:51 +00:00
oappend ( SET_F ( " addInfo('Battery:type',1,'<small style= \" color:orange \" >requires reboot</small>'); " ) ) ; // 81 Bytes
2024-08-20 10:37:01 +00:00
oappend ( SET_F ( " addInfo('Battery:min-voltage',1,'v'); " ) ) ; // 38 Bytes
oappend ( SET_F ( " addInfo('Battery:max-voltage',1,'v'); " ) ) ; // 38 Bytes
oappend ( SET_F ( " addInfo('Battery:interval',1,'ms'); " ) ) ; // 36 Bytes
oappend ( SET_F ( " addInfo('Battery:HA-discovery',1,''); " ) ) ; // 38 Bytes
oappend ( SET_F ( " addInfo('Battery:auto-off:threshold',1,'%'); " ) ) ; // 45 Bytes
oappend ( SET_F ( " addInfo('Battery:indicator:threshold',1,'%'); " ) ) ; // 46 Bytes
oappend ( SET_F ( " addInfo('Battery:indicator:duration',1,'s'); " ) ) ; // 45 Bytes
2022-12-28 21:40:13 +00:00
2023-01-20 23:39:51 +00:00
// this option list would exeed the oappend() buffer
// a list of all presets to select one from
2022-12-28 21:40:13 +00:00
// oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
// the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
// for(int8_t i=1; i < 42; i++) {
// oappend(SET_F("addOption(bd, 'Preset#"));
// oappendi(i);
// oappend(SET_F("',"));
// oappendi(i);
// oappend(SET_F(");"));
// }
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* readFromConfig ( ) can be used to read back the custom settings you added with addToConfig ( ) .
* This is called by WLED when settings are loaded ( currently this only happens immediately after boot , or after saving on the Usermod Settings page )
*
* readFromConfig ( ) is called BEFORE setup ( ) . This means you can use your persistent values in setup ( ) ( e . g . pin assignments , buffer sizes ) ,
* but also that if you want to write persistent values to a dynamic buffer , you ' d need to allocate it here instead of in setup .
* If you don ' t know what that is , don ' t fret . It most likely doesn ' t affect your use case : )
*
* Return true in case the config values returned from Usermod Settings were complete , or false if you ' d like WLED to save your defaults to disk ( so any missing values are editable in Usermod Settings )
*
* getJsonValue ( ) returns false if the value is missing , or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the " exampleUsermod " object and all values are present . If any values are missing , WLED will know to call addToConfig ( ) to save them
*
* This function is guaranteed to be called on boot , but could also be called every time settings are updated
*/
bool readFromConfig ( JsonObject & root )
{
# ifdef ARDUINO_ARCH_ESP32
int8_t newBatteryPin = batteryPin ;
# endif
JsonObject battery = root [ FPSTR ( _name ) ] ;
if ( battery . isNull ( ) )
{
DEBUG_PRINT ( FPSTR ( _name ) ) ;
DEBUG_PRINTLN ( F ( " : No config found. (Using defaults.) " ) ) ;
return false ;
}
# ifdef ARDUINO_ARCH_ESP32
2023-01-02 21:52:37 +00:00
newBatteryPin = battery [ F ( " pin " ) ] | newBatteryPin ;
2022-12-28 21:40:13 +00:00
# endif
2023-09-09 19:50:30 +00:00
setMinBatteryVoltage ( battery [ F ( " min-voltage " ) ] | bat - > getMinVoltage ( ) ) ;
setMaxBatteryVoltage ( battery [ F ( " max-voltage " ) ] | bat - > getMaxVoltage ( ) ) ;
2024-04-30 15:02:57 +00:00
setCalibration ( battery [ F ( " calibration " ) ] | bat - > getCalibration ( ) ) ;
setVoltageMultiplier ( battery [ F ( " voltage-multiplier " ) ] | bat - > getVoltageMultiplier ( ) ) ;
2022-12-28 21:40:13 +00:00
setReadingInterval ( battery [ FPSTR ( _readInterval ) ] | readingInterval ) ;
2024-08-20 10:37:01 +00:00
setHomeAssistantDiscovery ( battery [ FPSTR ( _haDiscovery ) ] | HomeAssistantDiscovery ) ;
2023-01-05 18:48:53 +00:00
2023-01-21 00:44:50 +00:00
getUsermodConfigFromJsonObject ( battery ) ;
2022-12-28 21:40:13 +00:00
# ifdef ARDUINO_ARCH_ESP32
if ( ! initDone )
{
// first run: reading from cfg.json
2023-01-04 16:30:08 +00:00
batteryPin = newBatteryPin ;
2022-12-28 21:40:13 +00:00
DEBUG_PRINTLN ( F ( " config loaded. " ) ) ;
}
else
{
DEBUG_PRINTLN ( F ( " config (re)loaded. " ) ) ;
2023-01-04 16:30:08 +00:00
// changing parameters from settings page
2022-12-28 21:40:13 +00:00
if ( newBatteryPin ! = batteryPin )
{
// deallocate pin
2024-09-19 19:44:11 +00:00
PinManager : : deallocatePin ( batteryPin , PinOwner : : UM_Battery ) ;
2022-12-28 21:40:13 +00:00
batteryPin = newBatteryPin ;
// initialise
setup ( ) ;
}
}
# endif
2023-01-06 18:07:39 +00:00
return ! battery [ FPSTR ( _readInterval ) ] . isNull ( ) ;
2022-12-28 21:40:13 +00:00
}
2024-08-19 20:07:08 +00:00
# ifndef WLED_DISABLE_MQTT
void onMqttConnect ( bool sessionPresent )
{
// Home Assistant Autodiscovery
2024-08-20 10:37:01 +00:00
if ( ! HomeAssistantDiscovery )
return ;
2024-08-19 20:07:08 +00:00
// battery percentage
char mqttBatteryTopic [ 128 ] ;
snprintf_P ( mqttBatteryTopic , 127 , PSTR ( " %s/battery " ) , mqttDeviceTopic ) ;
this - > addMqttSensor ( F ( " Battery " ) , " sensor " , mqttBatteryTopic , " battery " , " % " , true ) ;
// voltage
char mqttVoltageTopic [ 128 ] ;
snprintf_P ( mqttVoltageTopic , 127 , PSTR ( " %s/voltage " ) , mqttDeviceTopic ) ;
this - > addMqttSensor ( F ( " Voltage " ) , " sensor " , mqttVoltageTopic , " voltage " , " V " , true ) ;
}
2024-08-19 20:22:46 +00:00
# endif
2022-12-28 21:40:13 +00:00
/*
*
* Getter and Setter . Just in case some other usermod wants to interact with this in the future
*
*/
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* 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_BATTERY ;
}
2024-04-30 13:24:02 +00:00
/**
* get currently active battery type
*/
batteryType getBatteryType ( )
{
return cfg . type ;
}
2022-12-28 21:40:13 +00:00
2024-04-30 13:24:02 +00:00
/**
*
*/
2022-12-28 21:40:13 +00:00
unsigned long getReadingInterval ( )
{
return readingInterval ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* minimum repetition is 3000 ms ( 3 s )
*/
void setReadingInterval ( unsigned long newReadingInterval )
{
readingInterval = max ( ( unsigned long ) 3000 , newReadingInterval ) ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get lowest configured battery voltage
*/
float getMinBatteryVoltage ( )
{
2023-09-09 19:50:30 +00:00
return bat - > getMinVoltage ( ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set lowest battery voltage
2024-04-30 13:24:02 +00:00
* can ' t be below 0 volt
2022-12-28 21:40:13 +00:00
*/
void setMinBatteryVoltage ( float voltage )
{
2023-09-09 19:50:30 +00:00
bat - > setMinVoltage ( voltage ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get highest configured battery voltage
*/
float getMaxBatteryVoltage ( )
{
2023-09-09 19:50:30 +00:00
return bat - > getMaxVoltage ( ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set highest battery voltage
* can ' t be below minBatteryVoltage
*/
void setMaxBatteryVoltage ( float voltage )
{
2023-09-09 19:50:30 +00:00
bat - > setMaxVoltage ( voltage ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get the calculated voltage
* formula : ( adc pin value / adc precision * max voltage ) + calibration
*/
float getVoltage ( )
{
2023-09-09 19:50:30 +00:00
return bat - > getVoltage ( ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get the mapped battery level ( 0 - 100 ) based on voltage
* important : voltage can drop when a load is applied , so its only an estimate
*/
int8_t getBatteryLevel ( )
{
2023-09-09 19:50:30 +00:00
return bat - > getLevel ( ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get the configured calibration value
* a offset value to fine - tune the calculated voltage .
*/
float getCalibration ( )
{
2023-09-09 19:50:30 +00:00
return bat - > getCalibration ( ) ;
2022-12-28 21:40:13 +00:00
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set the voltage calibration offset value
* a offset value to fine - tune the calculated voltage .
*/
void setCalibration ( float offset )
{
2023-09-09 19:50:30 +00:00
bat - > setCalibration ( offset ) ;
2022-12-28 21:40:13 +00:00
}
2024-04-30 13:24:02 +00:00
/**
* Set the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups
*/
void setVoltageMultiplier ( float multiplier )
{
bat - > setVoltageMultiplier ( multiplier ) ;
}
/*
* Get the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups
*/
float getVoltageMultiplier ( )
{
return bat - > getVoltageMultiplier ( ) ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get auto - off feature enabled status
* is auto - off enabled , true / false
*/
bool getAutoOffEnabled ( )
{
return autoOffEnabled ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set auto - off feature status
*/
void setAutoOffEnabled ( bool enabled )
{
autoOffEnabled = enabled ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get auto - off threshold in percent ( 0 - 100 )
*/
int8_t getAutoOffThreshold ( )
{
return autoOffThreshold ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set auto - off threshold in percent ( 0 - 100 )
*/
void setAutoOffThreshold ( int8_t threshold )
{
autoOffThreshold = min ( ( int8_t ) 100 , max ( ( int8_t ) 0 , threshold ) ) ;
// when low power indicator is enabled the auto-off threshold cannot be above indicator threshold
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min ( lowPowerIndicatorThreshold - 1 , ( int ) autoOffThreshold ) : autoOffThreshold ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get low - power - indicator feature enabled status
* is the low - power - indicator enabled , true / false
*/
bool getLowPowerIndicatorEnabled ( )
{
return lowPowerIndicatorEnabled ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set low - power - indicator feature status
*/
void setLowPowerIndicatorEnabled ( bool enabled )
{
lowPowerIndicatorEnabled = enabled ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get low - power - indicator preset to activate when low power is detected
*/
int8_t getLowPowerIndicatorPreset ( )
{
return lowPowerIndicatorPreset ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set low - power - indicator preset to activate when low power is detected
*/
void setLowPowerIndicatorPreset ( int8_t presetId )
{
// String tmp = ""; For what ever reason this doesn't work :(
// lowPowerIndicatorPreset = getPresetName(presetId, tmp) ? presetId : lowPowerIndicatorPreset;
lowPowerIndicatorPreset = presetId ;
}
/*
* Get low - power - indicator threshold in percent ( 0 - 100 )
*/
int8_t getLowPowerIndicatorThreshold ( )
{
return lowPowerIndicatorThreshold ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set low - power - indicator threshold in percent ( 0 - 100 )
*/
void setLowPowerIndicatorThreshold ( int8_t threshold )
{
lowPowerIndicatorThreshold = threshold ;
// when auto-off is enabled the indicator threshold cannot be below auto-off threshold
lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max ( autoOffThreshold + 1 , ( int ) lowPowerIndicatorThreshold ) : max ( 5 , ( int ) lowPowerIndicatorThreshold ) ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Get low - power - indicator duration in seconds
*/
int8_t getLowPowerIndicatorDuration ( )
{
return lowPowerIndicatorDuration ;
}
2023-09-09 19:50:30 +00:00
/**
2022-12-28 21:40:13 +00:00
* Set low - power - indicator duration in seconds
*/
void setLowPowerIndicatorDuration ( int8_t duration )
{
lowPowerIndicatorDuration = duration ;
}
2023-09-09 19:50:30 +00:00
/**
2024-04-30 13:24:02 +00:00
* Get low - power - indicator status when the indication is done this returns true
2022-12-28 21:40:13 +00:00
*/
bool getLowPowerIndicatorDone ( )
{
return lowPowerIndicationDone ;
}
2024-08-20 10:37:01 +00:00
/**
* Set Home Assistant auto discovery
*/
void setHomeAssistantDiscovery ( bool enable )
{
HomeAssistantDiscovery = enable ;
}
/**
* Get Home Assistant auto discovery
*/
bool getHomeAssistantDiscovery ( )
{
return HomeAssistantDiscovery ;
}
2022-12-28 21:40:13 +00:00
} ;
// strings to reduce flash memory usage (used more than twice)
const char UsermodBattery : : _name [ ] PROGMEM = " Battery " ;
const char UsermodBattery : : _readInterval [ ] PROGMEM = " interval " ;
const char UsermodBattery : : _enabled [ ] PROGMEM = " enabled " ;
const char UsermodBattery : : _threshold [ ] PROGMEM = " threshold " ;
const char UsermodBattery : : _preset [ ] PROGMEM = " preset " ;
const char UsermodBattery : : _duration [ ] PROGMEM = " duration " ;
2023-01-02 21:52:37 +00:00
const char UsermodBattery : : _init [ ] PROGMEM = " init " ;
2024-08-20 10:37:01 +00:00
const char UsermodBattery : : _haDiscovery [ ] PROGMEM = " HA-discovery " ;