2019-10-10 18:10:45 +00:00
/*
2019-10-10 18:20:51 +00:00
* Wemos d1 mini
2019-10-10 18:10:45 +00:00
* Flash Size : 4 M ( 1 M SPIFFS )
*/
# include <Homie.h>
# define FW_NAME "tischlicht"
2019-10-12 21:40:56 +00:00
# define FW_VERSION "1.0.3"
2019-10-10 18:10:45 +00:00
2019-10-19 11:43:17 +00:00
//#define DEBUG //turns on continuous serial debug printing
2019-10-12 15:32:20 +00:00
2019-10-10 18:10:45 +00:00
/*
* To Update configuration ( wifi credentials ) from data / homie / config . json :
* Connect to serial . On ESP - 12 E connect flash jumper
* Apply Power to ESP
* Optional : upload sketch
* Tools - Sketch Data Upload
* Remove jumper
* /
//http://homieiot.github.io/homie-esp8266/docs/develop/configuration/json-configuration-file/
//curl -X PUT http://homie.config/config -d @config.json --header "Content-Type: application/json"
/*Example data/homie/config.json
{
" name " : " pringleslight " ,
" device_id " : " pringleslight " ,
" wifi " : {
" ssid " : " CTDO-IoT " ,
" password " : " 12345678 "
} ,
" mqtt " : {
" host " : " raum.ctdo.de " ,
" port " : 1883 ,
" auth " : false
} ,
" ota " : {
" enabled " : false
}
}
*/
HomieNode lightNode ( " light " , " light " ) ;
//(pin x) nunbering CCW starting with rst
2019-10-10 18:20:51 +00:00
# define LED_WW 14 //D5 = GPIO14 (pin5)
# define LED_CW 12 //D6 = GPIO12 (pin6)
2019-10-10 18:10:45 +00:00
# define BTN_A 13 //D7 = GPIO13 (pin 7)
# define BTN_B 15 //D8 = GPIO15 (pin 10)
2019-10-12 15:49:26 +00:00
2019-10-10 18:10:45 +00:00
/*
* VCC ( pin 8 )
* GND ( pin 9 )
*/
# define PWM_MAX 1023 //10 bit dac
2019-10-12 21:43:53 +00:00
# define PWM_FREQUENCY 500 //default: 1000 Hz
2019-10-10 18:10:45 +00:00
2019-10-12 21:40:56 +00:00
boolean enable = false ;
float enable_fadevalue = 0 ; //0=off, 1=on
float enable_fadevalue_change_per_loop = 0.01 ; //fixed value. For manual calculatoin: enable_fadevalue_change_per_loop=_difference/fadetime*UPDATETIME;
2019-10-10 18:10:45 +00:00
2019-10-12 15:32:20 +00:00
float set_brightness = 2 ; //0 to 1
2019-10-10 18:10:45 +00:00
# define BRIGHTNESS_MIN 0.0
# define BRIGHTNESS_MAX 2.0 //if temperature is in between both strips brightness of 2 means both are at full power. otherwise brightness will be clipped
2019-10-21 17:06:17 +00:00
# define BRIGHTNESSCURVE 1.4
2019-10-10 18:10:45 +00:00
float brightness = set_brightness ;
2019-10-12 15:11:53 +00:00
float brightness_change_per_loop = 0 ; //will be calculated by Handler
2019-10-10 18:10:45 +00:00
2019-10-12 14:57:33 +00:00
# define TEMPERATURE_MIN 2760 //temperature of warm white leds
# define TEMPERATURE_MAX 5640 //temperature of cold white leds
2019-10-10 18:10:45 +00:00
float set_temperature = ( TEMPERATURE_MAX + TEMPERATURE_MIN ) / 2 ;
float temperature = set_temperature ;
2019-10-12 15:11:53 +00:00
float temperature_change_per_loop = 0 ; //will be calculated by Handler
2019-10-10 18:10:45 +00:00
uint16_t fadetime = 0 ; //0=instant. value is time in milliseconds
# define FADETIME_MIN 0
# define FADETIME_MAX 60000
long last_updatetime = 0 ;
# define UPDATETIME 10 //after how many ms pwm values will be updated
//Button stuff
# define BUTTONUPDATETIME 20
long last_buttonupdatetime = 0 ;
uint8_t btnAstate = 0 ; //for button state machine
long btnAtime = 0 ;
uint8_t btnBstate = 0 ; //for button state machine
long btnBtime = 0 ;
# define BTNHOLDTIME 1000
boolean holdDirection_brightness = false ;
boolean holdDirection_temperature = false ;
# define HOLDBRIGHTNESSCHANGE_PER_LOOP 0.01 //depends on BUTTONUPDATETIME. BUTTONUPDATETIME/1000/HOLDBRIGHTNESSCHANGE_PER_LOOP=seconds to change a full cycle(0 to 1)
# define HOLDTEMPERATURECHANGE_PER_LOOP 10.0 // (TEMPERATURE_MAX-TEMPERATURE_MIN)*BUTTONUPDATETIME/1000/HOLDBRIGHTNESSCHANGE_PER_LOOP=seconds to change a full cycle (min to max)
boolean flag_updatePWM = false ; //if manually set brightness or temperature, set this flag
//Debug
long last_debugupdatetime = 0 ;
# define DEBUGUPDATETIME 500
2019-10-12 21:40:56 +00:00
//To check if values changed (for mqtt response)
boolean known_enable = ! enable ; //start with differend known values, actual values will be send first
float known_set_brightness = set_brightness + 1 ;
float known_set_temperature = set_temperature + 1 ;
2019-10-10 18:10:45 +00:00
void setup ( ) {
Serial . begin ( 115200 ) ;
Serial . println ( " Hello " ) ;
pinMode ( LED_WW , OUTPUT ) ;
pinMode ( LED_CW , OUTPUT ) ;
2019-10-12 15:49:26 +00:00
analogWriteFreq ( PWM_FREQUENCY ) ;
2019-10-12 15:11:53 +00:00
analogWrite ( LED_CW , PWM_MAX ) ; //high = off
analogWrite ( LED_WW , PWM_MAX ) ; //high = off
2019-10-10 18:10:45 +00:00
pinMode ( BTN_A , INPUT ) ;
pinMode ( BTN_B , INPUT ) ;
Homie_setFirmware ( FW_NAME , FW_VERSION ) ;
Homie_setBrand ( FW_NAME ) ;
Homie . setLoopFunction ( loopHandler ) ;
lightNode . advertise ( " brightness " ) . settable ( brightnessHandler ) ;
lightNode . advertise ( " temperature " ) . settable ( temperatureHandler ) ;
lightNode . advertise ( " fadetime " ) . settable ( fadetimeHandler ) ;
2019-10-12 21:40:56 +00:00
lightNode . advertise ( " enable " ) . settable ( enableHandler ) ;
2019-10-10 18:10:45 +00:00
Homie . setup ( ) ;
}
void loop ( ) {
Homie . loop ( ) ;
}
void loopHandler ( ) {
long loopmillis = millis ( ) ;
if ( loopmillis > = last_buttonupdatetime + BUTTONUPDATETIME ) {
last_buttonupdatetime = loopmillis ;
// #### Button A ####
boolean flag_btnApress = false ; //short press on release
boolean flag_btnAholdstart = false ; //long press on start
boolean flag_btnAhold = false ; //long press after long press time
boolean flag_btnAholdrelease = false ; //long press on release
if ( digitalRead ( BTN_A ) ) { //Button State Machine
switch ( btnAstate ) {
case 0 : //was not pressed
btnAstate = 1 ;
btnAtime = loopmillis ; //start timer
break ;
case 1 : //was pressed last time checked
if ( loopmillis > btnAtime + BTNHOLDTIME ) {
btnAstate = 2 ;
flag_btnAholdstart = true ;
}
break ;
case 2 : //button hold time reached
flag_btnAhold = true ;
break ;
}
} else {
if ( btnAstate = = 1 ) { //short press
btnAstate = 0 ; //reset state
flag_btnApress = true ;
} else if ( btnAstate = = 2 ) { //long press released
flag_btnAholdrelease = true ;
btnAstate = 0 ; //reset state
}
}
// #### END Button A Check ####
// #### Button B ####
boolean flag_btnBpress = false ; //short press on release
boolean flag_btnBholdstart = false ; //long press on start
boolean flag_btnBhold = false ; //long press after long press time
boolean flag_btnBholdrelease = false ; //long press on release
if ( digitalRead ( BTN_B ) ) { //Button State Machine
switch ( btnBstate ) {
case 0 : //was not pressed
btnBstate = 1 ;
btnBtime = loopmillis ; //start timer
break ;
case 1 : //was pressed last time checked
if ( loopmillis > btnBtime + BTNHOLDTIME ) {
btnBstate = 2 ;
flag_btnBholdstart = true ;
}
break ;
case 2 : //button hold time reached
flag_btnBhold = true ;
break ;
}
} else {
if ( btnBstate = = 1 ) { //short press
btnBstate = 0 ; //reset state
flag_btnBpress = true ;
} else if ( btnBstate = = 2 ) { //long press released
flag_btnBholdrelease = true ;
btnBstate = 0 ; //reset state
}
}
// #### END Button B Check ####
//Button handling
if ( flag_btnApress | | flag_btnBpress ) { //short press either button
2019-10-12 21:40:56 +00:00
enable = ! enable ; //switch on/off
2019-10-10 18:10:45 +00:00
flag_updatePWM = true ; //update pwm values
}
2019-10-12 21:40:56 +00:00
if ( ! enable & & flag_btnAholdstart ) { //in not enabled and brightness button held down
enable = true ; //enable light
2019-10-10 18:30:34 +00:00
set_brightness = 0 ; //reset brightness
brightness = set_brightness ; //immediately
2019-10-19 11:43:17 +00:00
holdDirection_brightness = true ; //increase brightness
} else if ( enable ) { //only change values if enabled
2019-10-10 18:10:45 +00:00
// Button A Longpress Handling
if ( flag_btnAholdstart ) {
2019-10-12 15:02:04 +00:00
/* //Change only hold direction at extremes
2019-10-10 18:30:34 +00:00
if ( set_brightness > = BRIGHTNESS_MAX ) { //if hold started with brightness at one extreme
holdDirection_brightness = false ; //direction decrease
}
if ( set_brightness < = BRIGHTNESS_MIN ) { //if hold started with brightness at one extreme
holdDirection_brightness = true ; //direction increase
2019-10-10 18:10:45 +00:00
}
2019-10-12 15:02:04 +00:00
*/
holdDirection_brightness = ! holdDirection_brightness ; //change direction everytime
2019-10-10 18:30:34 +00:00
2019-10-10 18:10:45 +00:00
}
if ( flag_btnAhold ) { //brightness
if ( holdDirection_brightness ) {
set_brightness + = HOLDBRIGHTNESSCHANGE_PER_LOOP ;
} else {
set_brightness - = HOLDBRIGHTNESSCHANGE_PER_LOOP ;
}
set_brightness = constrain ( set_brightness , BRIGHTNESS_MIN , BRIGHTNESS_MAX ) ;
brightness = set_brightness ; //change immediately
flag_updatePWM = true ; //update pwm values
}
if ( flag_btnAholdrelease ) {
}
// Button B Longpress Handling
if ( flag_btnBholdstart ) {
2019-10-12 15:02:04 +00:00
/* //Change only hold direction at extremes
2019-10-10 18:30:34 +00:00
if ( set_temperature > = TEMPERATURE_MAX ) { //if hold started with brightness at one extreme
holdDirection_temperature = false ; //direction decrease
}
if ( set_temperature < = TEMPERATURE_MIN ) { //if hold started with brightness at one extreme
holdDirection_temperature = true ; //direction increase
}
2019-10-12 15:02:04 +00:00
*/
holdDirection_temperature = ! holdDirection_temperature ; //change direction everytime
2019-10-10 18:10:45 +00:00
}
if ( flag_btnBhold ) { //brightness
if ( holdDirection_temperature ) {
set_temperature + = HOLDTEMPERATURECHANGE_PER_LOOP ;
} else {
set_temperature - = HOLDTEMPERATURECHANGE_PER_LOOP ;
}
set_temperature = constrain ( set_temperature , TEMPERATURE_MIN , TEMPERATURE_MAX ) ;
temperature = set_temperature ; //change immediately
flag_updatePWM = true ; //update pwm values
}
if ( flag_btnBholdrelease ) {
}
}
}
if ( loopmillis > = last_updatetime + UPDATETIME ) {
last_updatetime = loopmillis ;
2019-10-12 14:56:30 +00:00
float old_brightness = brightness ; //store last brightness
2019-10-10 18:10:45 +00:00
if ( ( brightness_change_per_loop < 0 & & brightness > set_brightness ) | | ( brightness_change_per_loop > 0 & & brightness < set_brightness ) ) { //if brightness not reached
brightness + = brightness_change_per_loop ;
2019-10-12 14:56:30 +00:00
if ( ( old_brightness < = set_brightness & & set_brightness < = brightness ) | | ( old_brightness > = set_brightness & & set_brightness > = brightness ) ) { //overshot set value
brightness = set_brightness ;
}
flag_updatePWM = true ; //force update
2019-10-10 18:10:45 +00:00
}
2019-10-12 14:56:30 +00:00
float old_temperature = temperature ; //store last temperature
2019-10-10 18:10:45 +00:00
if ( ( temperature_change_per_loop < 0 & & temperature > set_temperature ) | | ( temperature_change_per_loop > 0 & & temperature < set_temperature ) ) { //if temperature not reached
temperature + = temperature_change_per_loop ;
2019-10-12 14:56:30 +00:00
if ( ( old_temperature < = set_temperature & & set_temperature < = temperature ) | | ( old_temperature > = set_temperature & & set_temperature > = temperature ) ) { //overshot set value
temperature = set_temperature ;
}
flag_updatePWM = true ; //force update
2019-10-10 18:10:45 +00:00
}
2019-10-12 15:32:20 +00:00
//Sleep
2019-10-12 21:40:56 +00:00
if ( ( ! enable & & enable_fadevalue > 0 ) | | ( enable & & enable_fadevalue < 1 ) ) { //not fully turned off or on
if ( ! enable ) { //turn off
enable_fadevalue - = enable_fadevalue_change_per_loop ;
2019-10-12 15:32:20 +00:00
} else { //turn on
2019-10-12 21:40:56 +00:00
enable_fadevalue + = enable_fadevalue_change_per_loop ;
2019-10-12 15:32:20 +00:00
}
2019-10-12 21:40:56 +00:00
if ( enable_fadevalue > 1 ) { enable_fadevalue = 1 ; } //limit
if ( enable_fadevalue < 0 ) { enable_fadevalue = 0 ; } //limit
2019-10-12 15:32:20 +00:00
flag_updatePWM = true ; //force update
}
2019-10-10 18:10:45 +00:00
//calculate and update pwm
if ( brightness ! = set_brightness | | temperature ! = set_temperature | | flag_updatePWM ) { //if target not reached
flag_updatePWM = false ; // reset flag
//calculate pwm values
uint16_t pwmCW ;
uint16_t pwmWW ;
float temp = mapFloat ( temperature , TEMPERATURE_MIN , TEMPERATURE_MAX , 0.0 , 1.0 ) ; //0=warmwhite, 1=coldwhite
2019-10-21 17:06:17 +00:00
pwmCW = pow ( ( brightness * enable_fadevalue ) / 2.0 , BRIGHTNESSCURVE ) * 2.0 * temp * PWM_MAX ; //calculate brightness for led stripe, scale to 0-1, ^2, rescale to 0-2, scale for pwm
pwmWW = pow ( ( brightness * enable_fadevalue ) / 2.0 , BRIGHTNESSCURVE ) * 2.0 * ( 1 - temp ) * PWM_MAX ;
2019-10-10 18:10:45 +00:00
if ( pwmCW > PWM_MAX ) { pwmCW = PWM_MAX ; } //limit
if ( pwmWW > PWM_MAX ) { pwmWW = PWM_MAX ; } //limit
2019-10-12 15:32:20 +00:00
analogWrite ( LED_WW , PWM_MAX - pwmWW ) ; //full pwm is led off
analogWrite ( LED_CW , PWM_MAX - pwmCW ) ; //full pwm is led off
/*
2019-10-12 21:40:56 +00:00
if ( enable ) {
2019-10-10 18:20:51 +00:00
analogWrite ( LED_WW , PWM_MAX - pwmWW ) ; //full pwm is led off
analogWrite ( LED_CW , PWM_MAX - pwmCW ) ; //full pwm is led off
2019-10-10 18:10:45 +00:00
} else {
2019-10-10 18:20:51 +00:00
analogWrite ( LED_WW , PWM_MAX ) ; //light off
analogWrite ( LED_CW , PWM_MAX ) ; //light off
2019-10-12 15:32:20 +00:00
} */
2019-10-10 18:10:45 +00:00
}
}
2019-10-12 15:32:20 +00:00
2019-10-12 21:40:56 +00:00
//send new values back to broker
if ( known_enable ! = enable ) {
lightNode . setProperty ( " enable " ) . send ( enable ? " true " : " false " ) ;
known_enable = enable ;
}
if ( known_set_brightness ! = set_brightness ) {
lightNode . setProperty ( " brightness " ) . send ( String ( set_brightness ) ) ;
known_set_brightness = set_brightness ;
}
if ( known_set_temperature ! = set_temperature ) {
lightNode . setProperty ( " temperature " ) . send ( String ( set_temperature ) ) ;
known_set_temperature = set_temperature ;
}
2019-10-12 15:32:20 +00:00
# ifdef DEBUG
2019-10-10 18:10:45 +00:00
if ( loopmillis > = last_debugupdatetime + DEBUGUPDATETIME ) {
last_debugupdatetime = loopmillis ;
2019-10-19 11:39:53 +00:00
/*
2019-10-12 21:40:56 +00:00
if ( ! enable ) { Serial . print ( " not enable. " ) ; }
2019-10-12 14:56:30 +00:00
Serial . print ( " bright= " ) ;
2019-10-10 18:10:45 +00:00
Serial . print ( brightness ) ;
Serial . print ( " set= " ) ;
Serial . print ( set_brightness ) ;
Serial . print ( " | temp= " ) ;
Serial . print ( temperature ) ;
Serial . print ( " set= " ) ;
2019-10-12 14:56:30 +00:00
Serial . print ( set_temperature ) ;
Serial . print ( " change= " ) ;
2019-10-12 15:32:20 +00:00
Serial . print ( brightness_change_per_loop ) ;
2019-10-12 21:40:56 +00:00
Serial . print ( " enable_fadevalue= " ) ;
Serial . println ( enable_fadevalue ) ;
2019-10-19 11:39:53 +00:00
*/
2019-10-12 14:56:30 +00:00
2019-10-19 11:39:53 +00:00
2019-10-10 18:10:45 +00:00
uint16_t pwmCW ;
uint16_t pwmWW ;
float temp = mapFloat ( temperature , TEMPERATURE_MIN , TEMPERATURE_MAX , 0.0 , 1.0 ) ; //0=warmwhite, 1=coldwhite
2019-10-19 11:39:53 +00:00
pwmCW = pow ( ( brightness * enable_fadevalue ) * temp / 2.0 , 2 ) * 2.0 * PWM_MAX ; //calculate brightness for led stripe, scale to 0-1, ^2, rescale to 0-2, scale for pwm
pwmWW = pow ( ( brightness * enable_fadevalue ) * ( 1 - temp ) / 2.0 , 2 ) * 2.0 * PWM_MAX ;
2019-10-10 18:10:45 +00:00
if ( pwmCW > PWM_MAX ) { pwmCW = PWM_MAX ; } //limit
if ( pwmWW > PWM_MAX ) { pwmWW = PWM_MAX ; } //limit
2019-10-12 21:40:56 +00:00
if ( enable ) {
2019-10-19 11:39:53 +00:00
Serial . print ( " bright= " ) ;
Serial . print ( brightness ) ;
2019-10-10 18:20:51 +00:00
Serial . print ( PWM_MAX - pwmWW ) ;
2019-10-10 18:10:45 +00:00
Serial . print ( " , " ) ;
2019-10-10 18:20:51 +00:00
Serial . println ( PWM_MAX - pwmCW ) ;
2019-10-10 18:10:45 +00:00
} else {
2019-10-19 11:39:53 +00:00
Serial . print ( " off " ) ;
2019-10-10 18:10:45 +00:00
Serial . print ( " , " ) ;
2019-10-19 11:39:53 +00:00
Serial . println ( " off " ) ;
2019-10-10 18:10:45 +00:00
}
2019-10-19 11:39:53 +00:00
2019-10-10 18:10:45 +00:00
}
2019-10-12 15:32:20 +00:00
# endif
2019-10-10 18:10:45 +00:00
}
bool brightnessHandler ( const HomieRange & range , const String & value ) {
if ( range . isRange ) {
return false ; //if range is given but index is not in allowed range
}
Homie . getLogger ( ) < < " brightness " < < " : " < < value < < endl ;
2019-10-12 21:40:56 +00:00
//lightNode.setProperty("brightness").send(value); //done in main loop
2019-10-10 18:10:45 +00:00
if ( value . toFloat ( ) > = BRIGHTNESS_MIN & & value . toFloat ( ) < = BRIGHTNESS_MAX ) {
set_brightness = value . toFloat ( ) ;
} else {
Homie . getLogger ( ) < < " Value outside range " < < endl ;
return false ;
}
float _difference = set_brightness - brightness ;
2019-10-12 14:56:30 +00:00
if ( fadetime > 0 ) {
brightness_change_per_loop = _difference / fadetime * UPDATETIME ;
} else { //special case for instant change
brightness_change_per_loop = _difference ;
}
2019-10-10 18:10:45 +00:00
return true ;
}
bool temperatureHandler ( const HomieRange & range , const String & value ) {
if ( range . isRange ) {
return false ; //if range is given but index is not in allowed range
}
Homie . getLogger ( ) < < " temperature " < < " : " < < value < < endl ;
2019-10-12 21:40:56 +00:00
//lightNode.setProperty("temperature").send(value); //done in main loop
2019-10-10 18:10:45 +00:00
if ( value . toFloat ( ) > = TEMPERATURE_MIN & & value . toFloat ( ) < = TEMPERATURE_MAX ) {
set_temperature = value . toFloat ( ) ;
} else {
Homie . getLogger ( ) < < " Value outside range " < < endl ;
return false ;
}
float _difference = set_temperature - temperature ;
2019-10-12 14:56:30 +00:00
if ( fadetime > 0 ) {
temperature_change_per_loop = _difference / fadetime * UPDATETIME ;
} else { //special case for instant change
temperature_change_per_loop = _difference ;
}
2019-10-10 18:10:45 +00:00
return true ;
}
bool fadetimeHandler ( const HomieRange & range , const String & value ) { //fadetime for temperature and brightness in milliseconds
if ( range . isRange ) {
return false ; //if range is given but index is not in allowed range
}
Homie . getLogger ( ) < < " fadetime " < < " : " < < value < < endl ;
lightNode . setProperty ( " fadetime " ) . send ( value ) ;
if ( value . toInt ( ) > = FADETIME_MIN & & value . toInt ( ) < = FADETIME_MAX ) {
fadetime = value . toInt ( ) ;
} else {
Homie . getLogger ( ) < < " Value outside range " < < endl ;
return false ;
}
return true ;
}
2019-10-12 21:40:56 +00:00
bool enableHandler ( const HomieRange & range , const String & value ) { //change on off
2019-10-12 14:56:30 +00:00
if ( range . isRange ) {
return false ; //if range is given but index is not in allowed range
}
2019-10-12 21:40:56 +00:00
Homie . getLogger ( ) < < " enable " < < " : " < < value < < endl ;
//lightNode.setProperty("enable").send(value); //done in main loop
2019-10-12 14:56:30 +00:00
2019-10-12 21:40:56 +00:00
if ( value = = " false " ) {
enable = false ;
} else if ( value = = " true " ) {
enable = true ;
2019-10-12 14:56:30 +00:00
} else {
Homie . getLogger ( ) < < " Value outside range " < < endl ;
return false ;
}
flag_updatePWM = true ; //force update
return true ;
}
2019-10-10 18:10:45 +00:00
float mapFloat ( float x , float in_min , float in_max , float out_min , float out_max ) {
return ( x - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min ;
}