From 211b06ed623cf5eea749bdd224f7130c7546927e Mon Sep 17 00:00:00 2001 From: Kai Lauterbach Date: Thu, 27 Apr 2023 11:26:21 +0200 Subject: [PATCH] First almost completely working timing controller. --- firmware/config.h | 6 +- firmware/firmware.ino | 33 +++--- firmware/timing_control.ino | 225 +++++++++++++++++++++++++++--------- 3 files changed, 193 insertions(+), 71 deletions(-) diff --git a/firmware/config.h b/firmware/config.h index ba39213..3fcbb02 100644 --- a/firmware/config.h +++ b/firmware/config.h @@ -14,8 +14,10 @@ #define EEPROM_LAST_STATE_ADDRESS 4 // the first "last state" information for the first light #define EEPROM_TIMING_DATA_ADDRESS (EEPROM_LAST_STATE_ADDRESS + LIGHTS_COUNT) // Stored data date per light ELE_USED; HH; MM; CH1; CH2; CH3; CH4; -#define TIME_CHECK_INTERVAL_MS (60000UL) // 60 second interval -#define TIME_LIGHTENGINE_INTERVAL_MS (100UL) +#define BRI_MOD_STEPS_PER_SEC 5.0 + +#define TIME_CHECK_INTERVAL_MS (60000UL) // 60 second interval +#define TIME_LIGHTENGINE_INTERVAL_MS (1000UL / BRI_MOD_STEPS_PER_SEC) // BRI_MOD_STEPS_PER_SEC steps per second to in-/decrease the brightness #define MY_NTP_SERVER "de.pool.ntp.org" diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 8ef1394..c2e8f43 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -82,14 +82,14 @@ void apply_scene(uint8_t new_scene, uint8_t light) //********************************// -void process_lightdata(uint8_t light, float transitiontime) +void process_lightdata(uint8_t light, float tt) { - transitiontime *= 16; if (light_state[light]) { - step_level[light] = (bri[light] - current_bri[light]) / transitiontime; + step_level[light] = (bri[light] - current_bri[light]) / tt; + } else { - step_level[light] = current_bri[light] / transitiontime; + step_level[light] = current_bri[light] / tt; } } @@ -108,20 +108,22 @@ void lightEngine() for (int i = 0; i < LIGHTS_COUNT; i++) { + if (light_state[i]) { if (bri[i] != current_bri[i]) { in_transition = true; - current_bri[i] += step_level[i] / 10.0; + current_bri[i] += step_level[i] / BRI_MOD_STEPS_PER_SEC; if ((step_level[i] > 0.0 && current_bri[i] > bri[i]) || (step_level[i] < 0.0 && current_bri[i] < bri[i])) { current_bri[i] = bri[i]; + //Serial.println("Reached target bri[" + (String)i + "] = " + (String)bri[i]); } uint16_t tmp_pwm = calcPWM(current_bri[i]); - //Serial.println("pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); + //Serial.println("lon: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); analogWrite(pins[i], tmp_pwm); } } else { @@ -129,18 +131,20 @@ void lightEngine() if (current_bri[i] != 0) { in_transition = true; - current_bri[i] -= step_level[i] / 10.0; + current_bri[i] -= step_level[i] / BRI_MOD_STEPS_PER_SEC; if (current_bri[i] < 0) { current_bri[i] = 0; + //Serial.println("Reached target bri[" + (String)i + "] = " + (String)bri[i]); } uint16_t tmp_pwm = calcPWM(current_bri[i]); - //Serial.println("pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); + //Serial.println("loff: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); analogWrite(pins[i], tmp_pwm); } } - } + + } // for loop end if (in_transition) { @@ -281,7 +285,7 @@ void loop() if (tc_enabled == TIMING_CONTROL_ENABLED) { - tc_update(); + tc_update_loop(); } } @@ -545,14 +549,13 @@ void init_webserver() } // set the light step level - if (light_state[light]) + if (server.hasArg("transition") && light_state[light]) { + Serial.println("Webinterface transitiontime = " + (String)transitiontime); + transitiontime = server.arg("transition").toFloat(); step_level[light] = ((float)bri[light] - current_bri[light]) / transitiontime; - - } else { - step_level[light] = current_bri[light] / transitiontime; } - } + } // process all lights #endif // DISABLE_WEB_CONTROL diff --git a/firmware/timing_control.ino b/firmware/timing_control.ino index 8def94c..6645c9d 100644 --- a/firmware/timing_control.ino +++ b/firmware/timing_control.ino @@ -6,33 +6,30 @@ //***********************************// -// 7 byte -#define TIMER_DATA_ENSTATE 0 -#define TIMER_DATA_HH 1 -#define TIMER_DATA_MM 2 -#define TIMER_DATA_CH1 3 // the brightness of the channel -#define TIMER_DATA_CH2 4 // the brightness of the channel -#define TIMER_DATA_CH3 5 // the brightness of the channel -#define TIMER_DATA_CH4 6 // the brightness of the channel +// 6 byte +#define TIMER_DATA_HH 0 +#define TIMER_DATA_MM 1 +#define TIMER_DATA_CH1 2 // the brightness of the channel +#define TIMER_DATA_CH2 3 // the brightness of the channel +#define TIMER_DATA_CH3 4 // the brightness of the channel +#define TIMER_DATA_CH4 5 // the brightness of the channel #define LENGTH_OF_TIMER_DATA_BLOCK (TIMER_DATA_CH4 + 1) #define NUMBER_OF_TIMER_DATA_BLOCKS 10 -#define ENSTATE_ENABLED 1 -#define ENSTATE_DISABLED 0 - //***********************************// /* Globals */ +bool tc_testOngoing = true; + uint32_t tc_last_check = 60000; -const long utcOffsetInSeconds = 3600; +long utcOffsetInSeconds = 3600; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, MY_NTP_SERVER, utcOffsetInSeconds); -struct tc_data_st { - uint8_t enstate; +struct tc_data_st { uint8_t hh; uint8_t mm; uint8_t ch1; @@ -44,17 +41,31 @@ struct tc_data_st { struct tc_data_st tc_data[10]; uint8_t example_timer_data_block[] = { +// hour min ch1 ch2 ch3 ch3 + 8, 0, 0, 0, 0, 0, // 0: off + 8, 30, 25, 0, 0, 0, // 1: 10% ch1 blues + 9, 0, 25, 0, 25, 0, // 2: 10% all blues + 13, 0, 205, 205, 205, 205, // 3: 80% all + 18, 0, 205, 205, 205, 205, // 4: 80% all for 5 hours + 19, 0, 50, 50, 50, 50, // 5: 20% all + 19, 30, 50, 0, 50, 0, // 6: 20% all blues + 20, 30, 25, 0, 0, 0, // 7: 10% ch1 blues + 21, 0, 0, 0, 0, 0, // 8: 0% all + 20, 0, 0, 0, 0, 0, // 9: disabled +}; + +uint8_t test_timer_data_block[] = { // state hour min ch1 ch2 ch3 ch3 - ENSTATE_ENABLED, 8, 0, 0, 0, 0, 0, // off - ENSTATE_ENABLED, 8, 30, 25, 0, 0, 0, // 10% ch1 blues - ENSTATE_ENABLED, 9, 0, 25, 0, 25, 0, // 10% all blues - ENSTATE_ENABLED, 13, 0, 205, 205, 205, 205, // 80% all - ENSTATE_ENABLED, 18, 0, 50, 50, 50, 50, // 20% blue - ENSTATE_ENABLED, 20, 0, 50, 0, 50, 0, // 20% all blues - ENSTATE_ENABLED, 20, 0, 25, 0, 0, 0, // 10% ch1 blues - ENSTATE_ENABLED, 21, 0, 0, 0, 0, 0, // 0% all - ENSTATE_DISABLED, 20, 0, 0, 0, 0, 0, // disabled - ENSTATE_DISABLED, 20, 0, 0, 0, 0, 0, // disabled + 9, 20, 0, 0, 0, 0, // 0: off + 9, 30, 25, 0, 0, 0, // 1: 10% ch1 blues + 9, 40, 25, 0, 25, 0, // 2: 10% all blues + 9, 50, 205, 205, 205, 205, // 3: 80% all + 10, 0, 100, 100, 100, 100, // 4: 80% all for 5 hours + 10, 10, 50, 50, 50, 50, // 5: 20% all + 10, 20, 50, 0, 50, 0, // 6: 20% all blues + 10, 30, 25, 0, 0, 0, // 7: 10% ch1 blues + 10, 40, 0, 0, 0, 0, // 8: all off + 11, 0, 0, 0, 0, 0, // 9: all off }; //***********************************// @@ -81,83 +92,176 @@ void tc_init() Serial.println("TC: Read data block from eeprom"); tc_readConfig(); + if (tc_testOngoing == true) + { + Serial.println("TC: Test ongoing, reading test data"); + for (uint8_t i = 0; i < NUMBER_OF_TIMER_DATA_BLOCKS * LENGTH_OF_TIMER_DATA_BLOCK; i += LENGTH_OF_TIMER_DATA_BLOCK) + { + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].hh = test_timer_data_block[i]; + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].mm = test_timer_data_block[i+1]; + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch1 = test_timer_data_block[i+2]; + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch2 = test_timer_data_block[i+3]; + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch3 = test_timer_data_block[i+4]; + tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch4 = test_timer_data_block[i+5]; + + Serial.print("data block: "); Serial.println(i / LENGTH_OF_TIMER_DATA_BLOCK); + Serial.print(" hh: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].hh); + Serial.print(" mm: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].mm); + Serial.print(" ch1: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch1); + Serial.print(" ch2: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch2); + Serial.print(" ch3: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch3); + Serial.print(" ch4: "); Serial.println(tc_data[i / LENGTH_OF_TIMER_DATA_BLOCK].ch4); + } + } + + tc_update_main(); + } //********************************// -void tc_update() +void tc_update_loop() { + static uint8_t last_min_check = 255; + + if ((timeClient.getMinutes() % 10) != 0 || last_min_check == timeClient.getMinutes()) // && tc_testOngoing == false + { + last_min_check = timeClient.getMinutes(); + return; // only run every 10 minutes + } + + tc_update_main(); +} + +void tc_update_main() +{ + static uint8_t current_target_data_block = 255; uint8_t target_data_block = 255; tc_updateTime(); - if ((timeClient.getMinutes() % 10) != 0) + // search for the current active time slot + for (int i = NUMBER_OF_TIMER_DATA_BLOCKS-1; i >= 0 && target_data_block == 255; i--) { - return; // only run every 10 minutes - } + //Serial.println((String)tc_data[i].hh + ":" + (String)tc_data[i].mm); - // 2. find current active time slot - for (uint8_t i = 0; i < NUMBER_OF_TIMER_DATA_BLOCKS-1; i++) - { - if (tc_data[i].hh == timeClient.getHours() && - tc_data[i].mm == timeClient.getMinutes() && - tc_data[i].enstate == ENSTATE_ENABLED) + if (tc_data[i].hh <= timeClient.getHours() && + tc_data[i].mm <= timeClient.getMinutes()) { - // we have a new time data slot reached - for (uint8_t j = i+1; j < NUMBER_OF_TIMER_DATA_BLOCKS; j++) - { // search for the next enabled successor - if (tc_data[j].enstate == ENSTATE_ENABLED) - { - target_data_block = j; // get the next block to activate - } - } + target_data_block = i+1; // found the next block to load } + //Serial.println((String)i + " => " + target_data_block); } if (target_data_block == 255) { - // no new successor found + // no new predecessor or successor found, start over + current_target_data_block = 255; return; } - // 3. set the channels brightness + if (current_target_data_block != target_data_block) + { + // new target block should be reached + current_target_data_block = target_data_block; + + } else { + // drop the found target block, we are already going on to reach it + return; + } + + if (target_data_block >= NUMBER_OF_TIMER_DATA_BLOCKS) + { + target_data_block = 255; + current_target_data_block = 255; + return; + } + + // print out the current light state + Serial.println("-----\nCurrent values"); + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("current_bri[" + (String)i + "] = " + (String)current_bri[i]); + } + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("bri[" + (String)i + "] = " + (String)bri[i]); + } + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("step_level[" + (String)i + "] = " + (String)step_level[i]); + } + + Serial.println("-----\ntdb = " + (String)target_data_block); + Serial.print("target time: "); Serial.print(tc_data[target_data_block].hh); Serial.print(":"); Serial.print(tc_data[target_data_block].mm); Serial.println(); + + // set the channels current and target brightness bri[0] = tc_data[target_data_block].ch1; bri[1] = tc_data[target_data_block].ch2; bri[2] = tc_data[target_data_block].ch3; bri[3] = tc_data[target_data_block].ch4; + if (tc_data[target_data_block-1].ch1 != current_bri[0]) + { + current_bri[0] = tc_data[target_data_block-1].ch1; + } + if (tc_data[target_data_block-1].ch2 != current_bri[1]) + { + current_bri[1] = tc_data[target_data_block-1].ch2; + } + if (tc_data[target_data_block-1].ch3 != current_bri[2]) + { + current_bri[2] = tc_data[target_data_block-1].ch3; + } + if (tc_data[target_data_block-1].ch4 != current_bri[3]) + { + current_bri[3] = tc_data[target_data_block-1].ch4; + } + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("current_bri[" + (String)i + "] = " + (String)current_bri[i]); + } for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { Serial.println("bri[" + (String)i + "] = " + (String)bri[i]); } - // 4. enable/disable the lights - light_state[0] = tc_data[target_data_block].ch1 > 0 ? true : false; - light_state[1] = tc_data[target_data_block].ch2 > 0 ? true : false; - light_state[2] = tc_data[target_data_block].ch3 > 0 ? true : false; - light_state[3] = tc_data[target_data_block].ch4 > 0 ? true : false; + // enable the lights + light_state[0] = true; + light_state[1] = true; + light_state[2] = true; + light_state[3] = true; for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { Serial.println("light_state[" + (String)i + "] = " + (String)light_state[i]); } - // 5. set the transition time + // set the transition time int t_time = 0; if (target_data_block > 0) { - t_time = ((uint16_t)tc_data[target_data_block].hh * 60 * 30) - ((uint16_t)tc_data[target_data_block-1].hh * 60 * 30); // hours as seconds from now on to the next enabled block - t_time += ((uint16_t)tc_data[target_data_block].mm * 60) - ((uint16_t)tc_data[target_data_block-1].mm * 60); // add the left over seconds to the next enabled block + // hours as seconds from now on to the next enabled block + t_time = ((uint16_t)tc_data[target_data_block].hh * 60 * 60) - ((uint16_t)timeClient.getHours() * 60 * 60); + // add the left over seconds to the next enabled block + t_time += ((uint16_t)tc_data[target_data_block].mm * 60) - ((uint16_t)timeClient.getMinutes() * 60); + } transitiontime[0] = t_time; transitiontime[1] = t_time; transitiontime[2] = t_time; transitiontime[3] = t_time; + // calculate the step level for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { + process_lightdata(i, transitiontime[i]); Serial.println("transitiontime[" + (String)i + "] = " + (String)transitiontime[i]); } + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("step_level[" + (String)i + "] = " + (String)step_level[i]); + } } @@ -172,7 +276,21 @@ void tc_updateTime() //Serial.println("TC: Read time from server..."); //Serial.println(timeClient.getFormattedTime()); - Serial.print("Local time: "); + /* + if (dow == 7 && mo == 10 && d >= 25 && h == 3 && DST==1) + { + // DST=0; + timeClient.setTimeOffset(utcOffsetInSeconds); + timeClient.forceUpdate(); + } else if (dow == 7 && mo == 3 && d >= 25 && h ==2 && DST==0) + { + // DST = 1 + timeClient.setTimeOffset(utcOffsetInSeconds * 2); + timeClient.forceUpdate(); + } + */ + + Serial.print("=====\nLocal time: "); Serial.print(timeClient.getHours()); Serial.print(":"); Serial.println(timeClient.getMinutes()); @@ -194,7 +312,6 @@ void tc_readConfig() Serial.print(") + "); Serial.println(TIMER_DATA_ENSTATE);*/ - tc_data[i].enstate = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_ENSTATE); tc_data[i].hh = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_HH); tc_data[i].mm = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_MM); tc_data[i].ch1 = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_CH1);