From b001ba10b3c1d1e1a416dc206b86dbfb43b78200 Mon Sep 17 00:00:00 2001 From: Fisch Date: Sat, 19 Sep 2020 18:33:39 +0200 Subject: [PATCH] copied brightness relevant code from tischlicht --- src/main.cpp | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 src/main.cpp diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0ca23de --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,354 @@ +/* + * Wemos d1 mini + * Flash Size: 4M (1M SPIFFS) + */ + +//Upload config: platformio run --target uploadfs + +#include +#include + + +#define FW_NAME "kuechenlicht" +#define FW_VERSION "1.0.0" + +bool enableHandler(const HomieRange& range, const String& value); +bool fadetimeHandler(const HomieRange& range, const String& value); +bool brightnessHandler(const HomieRange& range, const String& value); +void loopHandler(); + +//#define DEBUG //turns on continuous serial debug printing + + +HomieNode lightNode("light", "Light", "light"); //paramters: topic, $name, $type + + + +#define LED_PWM D5 + +#define BTN_A D7 + +#define PWM_MAX 1023 //10 bit dac +#define PWM_FREQUENCY 1000 //default: 1000 Hz + +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; + +float set_brightness=2; //0 to 1 +#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 +#define BRIGHTNESSCURVE 1.4 +float brightness=set_brightness; +float brightness_change_per_loop=0; //will be calculated by Handler + + +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; +#define BTNHOLDTIME 1000 + +boolean holdDirection_brightness=false; + +#define HOLDBRIGHTNESSCHANGE_PER_LOOP 0.01 //depends on BUTTONUPDATETIME. BUTTONUPDATETIME/1000/HOLDBRIGHTNESSCHANGE_PER_LOOP=seconds to change a full cycle(0 to 1) + +boolean flag_updatePWM=false; //if manually set brightness or temperature, set this flag + +//Debug +long last_debugupdatetime=0; +#define DEBUGUPDATETIME 500 + +//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; + +void setup() { + Serial.begin(115200); + Serial.println("Hello"); + pinMode(LED_PWM, OUTPUT); + analogWriteFreq(PWM_FREQUENCY); + analogWrite(LED_PWM, PWM_MAX); //high = off + + pinMode(BTN_A, INPUT); + + Homie_setFirmware(FW_NAME, FW_VERSION); + Homie_setBrand(FW_NAME); + Homie.setLoopFunction(loopHandler); + + lightNode.advertise("brightness").settable(brightnessHandler); + lightNode.advertise("fadetime").settable(fadetimeHandler); + lightNode.advertise("enable").settable(enableHandler); + + + 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 handling + if (flag_btnApress){ //short press button + enable = !enable; //switch on/off + flag_updatePWM=true; //update pwm values + } + if (!enable && flag_btnAholdstart ) { //in not enabled and brightness button held down + enable=true; //enable light + set_brightness=0; //reset brightness + brightness=set_brightness; //immediately + holdDirection_brightness=true; //increase brightness + } else if (enable) { //only change values if enabled + // Button A Longpress Handling + if (flag_btnAholdstart) { + /* //Change only hold direction at extremes + 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 + } + */ + holdDirection_brightness=!holdDirection_brightness; //change direction everytime + + } + 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) { + + } + + } + + } + + if (loopmillis >= last_updatetime+UPDATETIME ) { + last_updatetime = loopmillis; + + + float old_brightness = brightness; //store last brightness + if ( (brightness_change_per_loop<0 && brightness>set_brightness) || (brightness_change_per_loop>0 && brightness= set_brightness && set_brightness >= brightness ) ) { //overshot set value + brightness = set_brightness; + } + flag_updatePWM=true; //force update + } + + + //Sleep + 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; + }else{ //turn on + enable_fadevalue+=enable_fadevalue_change_per_loop; + } + if (enable_fadevalue>1) { enable_fadevalue=1; } //limit + if (enable_fadevalue<0) { enable_fadevalue=0; } //limit + flag_updatePWM=true; //force update + } + + //calculate and update pwm + if (brightness != set_brightness || flag_updatePWM) { //if target not reached + flag_updatePWM=false; // reset flag + //calculate pwm values + uint16_t pwm; + pwm=pow((brightness*enable_fadevalue)/2.0, BRIGHTNESSCURVE) *2.0 *PWM_MAX; //calculate brightness for led stripe, scale to 0-1, ^2, rescale to 0-2, scale for pwm + + + if (pwm>PWM_MAX) { pwm=PWM_MAX; } //limit + + analogWrite(LED_PWM, PWM_MAX-pwm); //full pwm is led off + + } + } + + //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; + } + + + + #ifdef DEBUG + if (loopmillis >= last_debugupdatetime+DEBUGUPDATETIME ) { + last_debugupdatetime = loopmillis; + + /* + if (!enable) { Serial.print("not enable. "); } + Serial.print("bright="); + Serial.print(brightness); + Serial.print(" set="); + Serial.print(set_brightness); + Serial.print("| temp="); + Serial.print(temperature); + Serial.print(" set="); + Serial.print(set_temperature); + Serial.print(" change="); + Serial.print(brightness_change_per_loop); + Serial.print(" enable_fadevalue="); + Serial.println(enable_fadevalue); + */ + + + + uint16_t pwmCW; + uint16_t pwmWW; + float temp=mapFloat(temperature, TEMPERATURE_MIN, TEMPERATURE_MAX, 0.0,1.0); //0=warmwhite, 1=coldwhite + 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; + + if (pwmCW>PWM_MAX) { pwmCW=PWM_MAX; } //limit + if (pwmWW>PWM_MAX) { pwmWW=PWM_MAX; } //limit + + if (enable) { + Serial.print("bright="); + Serial.print(brightness); + Serial.print(PWM_MAX-pwmWW); + Serial.print(", "); + Serial.println(PWM_MAX-pwmCW); + }else{ + Serial.print("off"); + Serial.print(", "); + Serial.println("off"); + } + + + } + #endif + +} + + +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; + //lightNode.setProperty("brightness").send(value); //done in main loop + 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; + if (fadetime>0) { + brightness_change_per_loop = _difference/fadetime*UPDATETIME; + } else { //special case for instant change + brightness_change_per_loop = _difference; + } + + 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; +} + +bool enableHandler(const HomieRange& range, const String& value) { //change on off + if (range.isRange) { + return false; //if range is given but index is not in allowed range + } + Homie.getLogger() << "enable " << ": " << value << endl; + //lightNode.setProperty("enable").send(value); //done in main loop + + if (value=="false") { + enable=false; + } else if(value=="true") { + enable=true; + } else { + Homie.getLogger() << "Value outside range" << endl; + return false; + } + + flag_updatePWM=true; //force update + + return true; +} + +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; +} +