diff --git a/firmware/config.h b/firmware/config.h index 725d32e..376be55 100644 --- a/firmware/config.h +++ b/firmware/config.h @@ -5,7 +5,7 @@ #define LOCAL_TIMEZONE_STRING "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" // Berlin, Germany - https://sites.google.com/a/usapiens.com/opnode/time-zones -//#define DEVELOPMENT +#define DEVELOPMENT #define LIGHT_NAME_STR "Dimmable Hue Light" // default light name diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 5d4cd7f..92e1c54 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -55,26 +55,12 @@ IPAddress dns (192, 168, 0, 1); #define SCENE_BRIGHT 1 #define SCENE_NIGHTLY 2 -#define TEST_PWM_STATE_INIT 0 -#define TEST_PWM_STATE_CH1_INC 1 -#define TEST_PWM_STATE_CH1_DEC 2 -#define TEST_PWM_STATE_CH2_INC 3 -#define TEST_PWM_STATE_CH2_DEC 4 -#define TEST_PWM_STATE_CH3_INC 5 -#define TEST_PWM_STATE_CH3_DEC 6 -#define TEST_PWM_STATE_CH4_INC 7 -#define TEST_PWM_STATE_CH4_DEC 8 - //********************************// uint8_t scene; uint8_t tc_enabled; uint8_t tc_enabled_old; -bool test_pwm = false; -uint32_t test_pwm_lastcheck_ms = 0; -uint8_t test_pwm_state = TEST_PWM_STATE_INIT; - bool light_state[LIGHTS_COUNT]; bool in_transition; @@ -95,191 +81,6 @@ uint32_t last_lightengine_activity = 0; //********************************// -void apply_scene(uint8_t new_scene, uint8_t light) -{ - if (new_scene == SCENE_RELEAX) - { - bri[light] = 144; - } else if (new_scene == SCENE_BRIGHT) - { - bri[light] = 254; - } else if (new_scene == SCENE_NIGHTLY) - { - bri[0] = 25; - bri[1] = 0; - bri[2] = 0; - bri[3] = 0; - } -} - -//********************************// - -void process_lightdata(uint8_t light, float tt) -{ - if (tt <= 0) - { - tt = default_transitiontime; - } - if (light_state[light]) - { - step_level[light] = (bri[light] - current_bri[light]) / tt; - - } else { - step_level[light] = current_bri[light] / tt; - } -} - -//********************************// - -void lightEngine() -{ - - if (millis() < (last_lightengine_activity + TIME_LIGHTENGINE_INTERVAL_MS)) { - // abort processing, the transition setting is a delay of seconds - return; - } - - last_lightengine_activity = millis(); - - 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] / 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]); - current_pwm[i] = tmp_pwm; - //Serial.println("lon: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); - analogWrite(pins[i], tmp_pwm); - } - } else { // light state is off - - if (current_bri[i] != 0) - { - in_transition = true; - 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]); - current_pwm[i] = tmp_pwm; - //Serial.println("loff: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); - analogWrite(pins[i], tmp_pwm); - } - } - - } // for loop end - - if (in_transition) - { - delay(6); - in_transition = false; - } -} - -//********************************// - -uint16_t calcPWM(float tbri) -{ - uint16_t tmp_pwm = PWM_OFF; - if (tbri > 0.0) - { - tmp_pwm = PWM_MIN + (int)(tbri * BRI_TO_PWM_FACTOR); - } - if (tmp_pwm > PWM_MAX) { - tmp_pwm = PWM_MAX; - } - - return tmp_pwm; -} - -//********************************// - -void read_eeprom_config() -{ - uint8_t tmp = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS); - if (tmp == TIMING_CONTROL_DISABLED) - { - tc_enabled = TIMING_CONTROL_DISABLED; - - } else if (tmp == TIMING_CONTROL_ENABLED) - { - tc_enabled = TIMING_CONTROL_ENABLED; - - } else { - // Write default value - EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED); - EEPROM.commit(); - tc_enabled = TIMING_CONTROL_DISABLED; - Serial.println("Written default timing control config to EEPROM (disabled)"); - } - Serial.println("Timing Control status: " + (String)tc_enabled); - - if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) > 2) - { - // set the default value on uninitialized EEPROM - EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, 0); - EEPROM.commit(); - Serial.println("Written default 'last state' config to EEPROM"); - } - Serial.println("Last state startup setting: " + (String)EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS)); - - if (EEPROM.read(EEPROM_SCENE_ADDRESS) > 2) - { - // set the default value on uninitialized EEPROM - EEPROM.write(EEPROM_SCENE_ADDRESS, 0); - EEPROM.commit(); - Serial.println("Written default scene config to EEPROM"); - } - Serial.println("Scene setting: " + (String)EEPROM.read(EEPROM_SCENE_ADDRESS)); - -#ifdef USE_STATIC_IP - if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1) - { - EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 0); - EEPROM.commit(); - Serial.println("Written default dynamic IP setting (disabled) to EEPROM"); - } -#else - if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1) - { - EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 1); - EEPROM.commit(); - Serial.println("Written default dynamic IP setting (enabled) to EEPROM"); - } -#endif - Serial.println("Dynamic IP setting: " + (String)EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS)); - - for (uint8_t light = 0; light < LIGHTS_COUNT; light++) - { - apply_scene(EEPROM.read(EEPROM_SCENE_ADDRESS), light); - step_level[light] = bri[light] / 150.0; - - if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE || - (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_ON_STATE && - EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON)) - { - light_state[light] = true; // set the light state to on - } - Serial.println("light[" + (String)light + "] = " + (String)light_state[light]); - } - -} - -//********************************// - void setup() { EEPROM.begin(256); @@ -373,607 +174,76 @@ void loop() //********************************// -void handleNotFound() -{ - String message = "File Not Found\n\n"; - message += "URI: "; - message += server.uri(); - message += "\nMethod: "; - message += (server.method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += server.args(); - message += "\n"; - - for (uint8_t i = 0; i < server.args(); i++) - { - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - } - server.send(404, "text/plain", message); -} - -//********************************// - -void init_webserver() +void read_eeprom_config() { - server.on("/state", HTTP_PUT, []() - { // HTTP PUT request used to set a new light state - DynamicJsonDocument root(512); - DeserializationError error = deserializeJson(root, server.arg("plain")); - - if (error) { - server.send(404, "text/plain", "FAIL. " + server.arg("plain")); - } else { - - for (JsonPair state : root.as()) - { - const char* key = state.key().c_str(); - int light = atoi(key) - 1; - if (light < 0) - { - light = 0; - } else if (light > (LIGHTS_COUNT-1)) - { - light = (LIGHTS_COUNT-1); - } - JsonObject values = state.value(); - - uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); - if (values.containsKey("on")) - { - if (values["on"]) - { - light_state[light] = true; - if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_OFF) - { - EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); - } - } else { - light_state[light] = false; - if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON) - { - EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); - } - } - } - - if (values.containsKey("bri")) - { - bri[light] = values["bri"]; - } - - if (values.containsKey("bri_inc")) - { - bri[light] += (int)values["bri_inc"]; - if (bri[light] > 255) bri[light] = 255; - else if (bri[light] < 1) bri[light] = 1; - } - - if (values.containsKey("transitiontime")) - { - default_transitiontime = values["transitiontime"]; - if (default_transitiontime < 0) - { - default_transitiontime = 0; - } - if (tc_enabled == TIMING_CONTROL_DISABLED) - { - for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) - { - // set the default transition time for all lights - process_lightdata(i, default_transitiontime); - } - } - } - - } - String output; - serializeJson(root, output); - server.send(200, "text/plain", output); - } - }); - - server.on("/state", HTTP_GET, []() - { // HTTP GET request used to fetch current light state - uint8_t light = server.arg("light").toInt() - 1; - if (light < 0) - { - light = 0; - } else if (light > (LIGHTS_COUNT-1)) - { - light = (LIGHTS_COUNT-1); - } - DynamicJsonDocument root(512); - root["on"] = light_state[light]; - root["bri"] = bri[light]; - root["curbri"] = (int)current_bri[light]; - root["curpwm"] = current_pwm[light]; - String output; - serializeJson(root, output); - server.send(200, "text/plain", output); - }); - - server.on("/detect", []() - { // HTTP GET request used to discover the light type - char macString[32] = { 0 }; - sprintf(macString, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - DynamicJsonDocument root(1024); - root["name"] = LIGHT_NAME; - root["lights"] = LIGHTS_COUNT; - root["protocol"] = "native_multi"; - root["modelid"] = "LWB010"; - root["type"] = "dimmable_light"; - root["mac"] = String(macString); - root["version"] = LIGHT_VERSION; - String output; - serializeJson(root, output); - server.send(200, "text/plain", output); - }); - - server.on("/test_pwm", []() + uint8_t tmp = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS); + if (tmp == TIMING_CONTROL_DISABLED) { - test_pwm = true; - tc_enabled_old = tc_enabled; tc_enabled = TIMING_CONTROL_DISABLED; - server.send(200, "text/html", "OK"); - }); - - server.on("/tc_data_blocks_read", []() + } else if (tmp == TIMING_CONTROL_ENABLED) { - String output = tc_getJsonData(); + tc_enabled = TIMING_CONTROL_ENABLED; - server.send(200, "application/json", output); - }); + } else { + // Write default value + EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED); + EEPROM.commit(); + tc_enabled = TIMING_CONTROL_DISABLED; + Serial.println("Written default timing control config to EEPROM (disabled)"); + } + Serial.println("Timing Control status: " + (String)tc_enabled); - server.on("/tc_data_blocks_store", []() + if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) > 2) { - if (server.hasArg("data")) + // set the default value on uninitialized EEPROM + EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, 0); + EEPROM.commit(); + Serial.println("Written default 'last state' config to EEPROM"); + } + Serial.println("Last state startup setting: " + (String)EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS)); + + if (EEPROM.read(EEPROM_SCENE_ADDRESS) > 2) + { + // set the default value on uninitialized EEPROM + EEPROM.write(EEPROM_SCENE_ADDRESS, 0); + EEPROM.commit(); + Serial.println("Written default scene config to EEPROM"); + } + Serial.println("Scene setting: " + (String)EEPROM.read(EEPROM_SCENE_ADDRESS)); + +#ifdef USE_STATIC_IP + if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1) + { + EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 0); + EEPROM.commit(); + Serial.println("Written default dynamic IP setting (disabled) to EEPROM"); + } +#else + if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1) + { + EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 1); + EEPROM.commit(); + Serial.println("Written default dynamic IP setting (enabled) to EEPROM"); + } +#endif + Serial.println("Dynamic IP setting: " + (String)EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS)); + + for (uint8_t light = 0; light < LIGHTS_COUNT; light++) + { + apply_scene(EEPROM.read(EEPROM_SCENE_ADDRESS), light); + step_level[light] = bri[light] / 150.0; + + if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE || + (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_ON_STATE && + EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON)) { - String jsonData = server.arg("data"); - Serial.println("Received: " + jsonData); - tc_jsonDataBlocksToEEPROM(jsonData); - server.send(200, "text/html", "tcdata saved"); + light_state[light] = true; // set the light state to on } - }); - - server.on("/js_top", []() - { - server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/top.js"))); - }); - - server.on("/js_bottom", []() - { - server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/bottom.js"))); - }); - - server.on("/css", []() - { - server.send(200, "text/css", loadSPIFFSFile("/style.css")); - }); - - server.on("/", []() - { - - if (server.hasArg("transition")) - { - default_transitiontime = server.arg("transition").toFloat(); - if (default_transitiontime < 0) - { - default_transitiontime = 0; - } - if (tc_enabled == TIMING_CONTROL_DISABLED) - { - for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) - { - // set the default transition time for all lights - process_lightdata(i, default_transitiontime); - Serial.println("transition for light " + (String)i + " set to " + (String)default_transitiontime); - } - } - } - - // startup behavior switch handling - if (server.hasArg("startup")) - { - int startup = server.arg("startup").toInt(); - if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) != startup) - { - EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, startup); - - for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { - uint8_t tmp = (light_state[i] == true ? LIGHT_STATE_ON : LIGHT_STATE_OFF); - if (EEPROM.read(EEPROM_LAST_STATE_ADDRESS + i) != tmp) - { - EEPROM.write(EEPROM_LAST_STATE_ADDRESS + i, tmp); - } - } - - EEPROM.commit(); - Serial.print("Startup behavior set to "); - Serial.println(EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS)); - } - } - - // timing controller switch handling - if (server.hasArg("tc")) - { - if (server.arg("tc") == "true") - { - if (tc_enabled == TIMING_CONTROL_DISABLED) - { - if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_ENABLED) - { - tc_enabled = TIMING_CONTROL_ENABLED; - EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_ENABLED); - EEPROM.commit(); - Serial.print("Timing control = "); - Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); - tc_update_main(); // call the main update function to read data and set the light states - } - } - - } else { // tc is set to false or something else - - if (tc_enabled == TIMING_CONTROL_ENABLED) - { - tc_enabled = TIMING_CONTROL_DISABLED; - if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_DISABLED) - { - EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED); - EEPROM.commit(); - Serial.print("Timing control = "); - Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); - - for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) - { - // set the default transition time for all lights - process_lightdata(i, default_transitiontime); - } - } - } - } - } - - // scene switch handling - if (server.hasArg("scene")) - { - scene = server.arg("scene").toInt(); - if (EEPROM.read(EEPROM_SCENE_ADDRESS) != scene) - { - EEPROM.write(EEPROM_SCENE_ADDRESS, scene); - EEPROM.commit(); - Serial.print("Scene set to "); - Serial.println(EEPROM.read(EEPROM_SCENE_ADDRESS)); - } - } - - if (server.hasArg("dip")) { - uint8_t tmp = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); - uint8_t tmp2 = (server.arg("dip") == "true" ? 1 : 0); - if (tmp != tmp2) - { - EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, tmp2); - EEPROM.commit(); - Serial.print("Set dynamic IP to "); - Serial.println(EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS)); - } - } - - // process the received data for every light - for (int light = 0; light < LIGHTS_COUNT; light++) - { - - if (server.hasArg("bri" + (String)light)) - { - int tmp = (int)server.arg("bri" + (String)light).toInt(); - - if (tmp > 255) - { - tmp = 255; - } else if (tmp < 0) { - tmp = 0; - } - bri[light] = tmp; - Serial.print("Brightness "); - Serial.print(light); - Serial.print(" set to "); - Serial.println(bri[light]); - } - - if (server.hasArg("on" + (String)light)) - { - uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); - if (server.arg("on" + (String)light) == "true" && light_state[light] == false) - { - light_state[light] = true; - if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 0) - { - EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); - EEPROM.commit(); - } - Serial.print("Light "); - Serial.print(light); - Serial.print(" state set to "); - Serial.println(light_state[light]); - - } else if (server.arg("on" + (String)light) == "false" && light_state[light] == true) - { - light_state[light] = false; - bri[light] = 0; - if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 1) - { - EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); - EEPROM.commit(); - } - Serial.print("Light "); - Serial.print(light); - Serial.print(" state set to "); - Serial.println(light_state[light]); - } - - if (tc_enabled == TIMING_CONTROL_DISABLED) - { - process_lightdata(light, default_transitiontime); - } - } else { - // light is off - if (tc_enabled == TIMING_CONTROL_DISABLED) - { - process_lightdata(light, default_transitiontime); - } - } - - // start alerting for every light - if (server.hasArg("alert")) - { - if (light_state[light]) - { - current_bri[light] = 0; - } else { - current_bri[light] = 255; - } - } - - } // process all lights - - if (server.hasArg("resettc")) - { // reqrite the tc config and reboot - tc_write_default(); - ESP.reset(); - } - - if (server.hasArg("reset")) - { - ESP.reset(); - } - - // ***** Generate static HTML page ***** // - - String tmp1 = genHMTLTop(); - - String tmp2 = genLightControlHTML(); - - String tmp3 = getIndexHTMLMiddle(); - - String tmp4 = genConfigHTML(); - - String tmp5 = genHMTLBottom(); - - server.send(200, "text/html", tmp1 + tmp2 + tmp3 + tmp4 + tmp5); - - }); - - server.on("/reset", []() - { // trigger manual reset - server.send(200, "text/html", "reset"); - delay(1000); - ESP.restart(); - }); - - server.onNotFound(handleNotFound); -} - -//********************************// - -String genHMTLTop() -{ - return replacePlaceholder(getIndexHTMLTop()); -} - -String genHMTLBottom() -{ - return replacePlaceholder(getIndexHTMLBottom()); -} - -String genConfigHTML() -{ - // +++++ Generate config part of the page +++++ - return replacePlaceholder(getConfigHTML()); -} - -String genTCEditHTML() -{ - return replacePlaceholder(getTCDataEditHTML()); -} - -String genLightControlHTML() -{ - String http_content = ""; - // +++++ Generate lights part of the HTML page +++++ - // Light control - for (uint8 light_num = 0; light_num < LIGHTS_COUNT; light_num++) - { - // Generate lights part of the HTML page - String tmp_light_content = getLightControlHTML(); - - // on/off buttons and slider - tmp_light_content.replace("{{LIGHT_NUMBER}}", (String)(light_num + 1)); - tmp_light_content.replace("{{LIGHT_NUMBER_DEC}}", (String)light_num); - - // add the lights code to the html output string - http_content += tmp_light_content; - } - return http_content; -} - -//********************************// - -String replacePlaceholder(String http_content) -{ - - http_content.replace("{{LIGHT_NAME}}", (String)LIGHT_NAME); - - http_content.replace("{{LIGHT_COUNT}}", (String)LIGHTS_COUNT); - - int tc_val = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS); - if (tc_val == TIMING_CONTROL_ENABLED) - { - http_content.replace("{{TC_LINK_PRIMARY_ON}}", "pure-button-primary"); - - } else { - http_content.replace("{{TC_LINK_PRIMARY_ON}}", ""); - } - if (tc_val == TIMING_CONTROL_DISABLED) - { - http_content.replace("{{TC_LINK_PRIMARY_OFF}}", "pure-button-primary"); - - } else { - http_content.replace("{{TC_LINK_PRIMARY_OFF}}", ""); + Serial.println("light[" + (String)light + "] = " + (String)light_state[light]); } - http_content.replace("{{TRANSITION_TIME}}", (String)default_transitiontime); - - int ls_val = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); - if (ls_val == LAST_STATE_STARTUP_LIGHT_LAST_STATE) - { - http_content.replace("{{STARTUP_SELECTED_LS_0}}", "selected=\"selected\""); - } else { - http_content.replace("{{STARTUP_SELECTED_LS_0}}", ""); - } - - if (ls_val == LAST_STATE_STARTUP_LIGHT_ON_STATE) - { - http_content.replace("{{STARTUP_SELECTED_ON_1}}", "selected=\"selected\""); - } else { - http_content.replace("{{STARTUP_SELECTED_ON_1}}", ""); - } - - if (ls_val == LAST_STATE_STARTUP_LIGHT_OFF_STATE) - { - http_content.replace("{{STARTUP_SELECTED_OFF_2}}", "selected=\"selected\""); - } else { - http_content.replace("{{STARTUP_SELECTED_OFF_2}}", ""); - } - - // scene - int sc_val = EEPROM.read(EEPROM_SCENE_ADDRESS); - if (sc_val == SCENE_RELEAX) - { - http_content.replace("{{SCENE_SELECTED_RELAX_0}}", "selected=\"selected\""); - } else { - http_content.replace("{{SCENE_SELECTED_RELAX_0}}", ""); - } - if (sc_val == SCENE_BRIGHT) - { - http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", "selected=\"selected\""); - } else { - http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", ""); - } - if (sc_val == SCENE_NIGHTLY) - { - http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", "selected=\"selected\""); - } else { - http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", ""); - } - - // Wifi settings - http_content.replace("{{WIFI_SSID}}", WiFi.SSID()); - - // Network settings - uint8_t dip = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); - if (dip) - { - http_content.replace("{{DIP_LINK_ON_PRIMARY}}", "pure-button-primary"); - http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", ""); - } else { - http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", "pure-button-primary"); - http_content.replace("{{DIP_LINK_ON_PRIMARY}}", ""); - } - - // network config - http_content.replace("{{WIFI_CFG_IP}}", WiFi.localIP().toString()); - http_content.replace("{{WIFI_CFG_GW}}", WiFi.gatewayIP().toString()); - http_content.replace("{{WIFI_CFG_NM}}", WiFi.subnetMask().toString()); - http_content.replace("{{WIFI_CFG_DNS}}", WiFi.dnsIP().toString()); - - // add the current ip address to the page - http_content.replace("{{IP_ADDRESS}}", WiFi.localIP().toString()); - - // set the pwm values - http_content.replace("{{PWM_MIN}}", (String)PWM_MIN); - http_content.replace("{{PWM_MAX}}", (String)PWM_MAX); - - return http_content; -} - -//********************************// - -String loadSPIFFSFile(String fname) -{ - File file = SPIFFS.open(fname, "r"); - if (!file) - { - Serial.println("Failed to open file " + fname); - return ""; - } - String contents = file.readString(); - file.close(); - return contents; -} - -//********************************// - -String getIndexHTMLTop() -{ - // load file - return loadSPIFFSFile("/index_template_top.html"); -} - -String getIndexHTMLMiddle() -{ - // load file - return loadSPIFFSFile("/index_template_middle.html"); -} - -String getIndexHTMLBottom() -{ - // load file - return loadSPIFFSFile("/index_template_bottom.html"); -} - -//********************************// - -String getConfigHTML() -{ - // load file - return loadSPIFFSFile("/config_template.html"); -} - -//********************************// - -String getTCDataEditHTML() -{ - // load file - return loadSPIFFSFile("/tc_data_edit.html"); -} - -//********************************// - -String getLightControlHTML() -{ - // load file - return loadSPIFFSFile("/light_control_template.html"); } //********************************// @@ -995,208 +265,3 @@ String formatBytes(size_t bytes) } //********************************// - -void test_pwm_main() -{ - if (test_pwm == false) - { - return; - } else if ((test_pwm_lastcheck_ms + PWM_TEST_INTERVA_MS) <= millis()) - { - test_pwm_lastcheck_ms = millis(); - - switch (test_pwm_state) - { - // ----------------------- // - case TEST_PWM_STATE_INIT: - // disable the lights - for (uint8_t i = 0; i < LIGHTS_COUNT; i++) - { - light_state[i] = false; - bri[i] = 0; - current_bri[i] = 0; - current_pwm[i] = 0; - transitiontime[i] = 0; - process_lightdata(i, transitiontime[i]); - } - - light_state[0] = true; - test_pwm_state = TEST_PWM_STATE_CH1_INC; - break; - - // ----------------------- // - case TEST_PWM_STATE_CH1_INC: - - if (bri[0] < 255) - { - bri[0] += TEST_PWM_CHG_CNT; - transitiontime[0] = 1; - process_lightdata(0, transitiontime[0]); - - } else { - - test_pwm_state = TEST_PWM_STATE_CH1_DEC; - } - break; - - case TEST_PWM_STATE_CH1_DEC: - - if (bri[0] > 0) - { - bri[0] -= TEST_PWM_CHG_CNT; - transitiontime[0] = 1; - process_lightdata(0, transitiontime[0]); - - } else { - - light_state[0] = false; - bri[0] = 0; - current_bri[0] = 0; - current_pwm[0] = 0; - transitiontime[0] = 0; - process_lightdata(0, transitiontime[0]); - - light_state[1] = true; - test_pwm_state = TEST_PWM_STATE_CH2_INC; - } - break; - - // ----------------------- // - case TEST_PWM_STATE_CH2_INC: - - if (bri[1] < 255) - { - bri[1] += TEST_PWM_CHG_CNT; - transitiontime[1] = 1; - process_lightdata(1, transitiontime[1]); - - } else { - - test_pwm_state = TEST_PWM_STATE_CH2_DEC; - } - break; - - case TEST_PWM_STATE_CH2_DEC: - - if (bri[1] > 0) - { - bri[1] -= TEST_PWM_CHG_CNT; - transitiontime[1] = 1; - process_lightdata(1, transitiontime[1]); - - } else { - - light_state[1] = false; - bri[1] = 0; - current_bri[1] = 0; - current_pwm[1] = 0; - transitiontime[1] = 0; - process_lightdata(1, transitiontime[1]); - - light_state[2] = true; - test_pwm_state = TEST_PWM_STATE_CH3_INC; - } - break; - - // ----------------------- // - case TEST_PWM_STATE_CH3_INC: - - if (bri[2] < 255) - { - bri[2] += TEST_PWM_CHG_CNT; - transitiontime[2] = 1; - process_lightdata(2, transitiontime[2]); - - } else { - - test_pwm_state = TEST_PWM_STATE_CH3_DEC; - } - break; - - case TEST_PWM_STATE_CH3_DEC: - - if (bri[2] > 0) - { - bri[2] -= TEST_PWM_CHG_CNT; - transitiontime[2] = 1; - process_lightdata(2, transitiontime[2]); - - } else { - - light_state[2] = false; - bri[2] = 0; - current_bri[2] = 0; - current_pwm[2] = 0; - transitiontime[2] = 0; - process_lightdata(2, transitiontime[2]); - - light_state[3] = true; - test_pwm_state = TEST_PWM_STATE_CH4_INC; - } - break; - - // ----------------------- // - case TEST_PWM_STATE_CH4_INC: - - if (bri[3] < 255) - { - bri[3] += TEST_PWM_CHG_CNT; - transitiontime[3] = 1; - process_lightdata(3, transitiontime[3]); - - } else { - - test_pwm_state = TEST_PWM_STATE_CH4_DEC; - } - break; - - case TEST_PWM_STATE_CH4_DEC: - - if (bri[3] > 0) - { - bri[3] -= TEST_PWM_CHG_CNT; - transitiontime[3] = 1; - process_lightdata(3, transitiontime[3]); - - } else { - - test_pwm = false; - tc_enabled = tc_enabled_old; - - for (uint8_t i = 0; i < LIGHTS_COUNT; i++) - { - light_state[i] = false; - bri[i] = 0; - current_bri[i] = 0; - current_pwm[i] = 0; - transitiontime[i] = 0; - process_lightdata(i, transitiontime[i]); - } - - tc_reset(); - tc_update_main(); // load the tc if required - - test_pwm_state = TEST_PWM_STATE_INIT; - } - break; - - // ----------------------- // - default: - test_pwm_state = TEST_PWM_STATE_INIT; - } - - Serial.println("---"); - for (uint8_t i = 0; i < LIGHTS_COUNT; i++) - { - Serial.println("light_state[" + (String)i + "] = " + (String)light_state[i]); - Serial.println("bri[" + (String)i + "] = " + (String)bri[i]); - Serial.println("current_bri[" + (String)i + "] = " + (String)current_bri[i]); - Serial.println("current_pwm[" + (String)i + "] = " + (String)current_pwm[i]); - Serial.println("transitiontime[" + (String)i + "] = " + (String)transitiontime[i]); - } - - } - -} - -//********************************// diff --git a/firmware/light_engine.ino b/firmware/light_engine.ino new file mode 100644 index 0000000..a1a6245 --- /dev/null +++ b/firmware/light_engine.ino @@ -0,0 +1,115 @@ + +//********************************// + +void apply_scene(uint8_t new_scene, uint8_t light) +{ + if (new_scene == SCENE_RELEAX) + { + bri[light] = 144; + } else if (new_scene == SCENE_BRIGHT) + { + bri[light] = 254; + } else if (new_scene == SCENE_NIGHTLY) + { + bri[0] = 25; + bri[1] = 0; + bri[2] = 0; + bri[3] = 0; + } +} + +//********************************// + +void process_lightdata(uint8_t light, float tt) +{ + if (tt <= 0) + { + tt = default_transitiontime; + } + if (light_state[light]) + { + step_level[light] = (bri[light] - current_bri[light]) / tt; + + } else { + step_level[light] = current_bri[light] / tt; + } +} + +//********************************// + +void lightEngine() +{ + + if (millis() < (last_lightengine_activity + TIME_LIGHTENGINE_INTERVAL_MS)) { + // abort processing, the transition setting is a delay of seconds + return; + } + + last_lightengine_activity = millis(); + + for (int i = 0; i < LIGHTS_COUNT; i++) + { + + if (light_state[i] == true) + { + if (bri[i] != current_bri[i]) + { + in_transition = true; + 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]); + current_pwm[i] = tmp_pwm; + //Serial.println("lon: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); + analogWrite(pins[i], tmp_pwm); + } + } else { // light state is off + + if (current_bri[i] != 0) + { + in_transition = true; + 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]); + current_pwm[i] = tmp_pwm; + //Serial.println("loff: pin" + (String)i + " = PWM(" + (String)tmp_pwm + ")"); + analogWrite(pins[i], tmp_pwm); + } + } + + } // for loop end + + if (in_transition) + { + delay(6); + in_transition = false; + } +} + +//********************************// + +uint16_t calcPWM(float tbri) +{ + uint16_t tmp_pwm = PWM_OFF; + if (tbri > 0.0) + { + tmp_pwm = PWM_MIN + (int)(tbri * BRI_TO_PWM_FACTOR); + } + if (tmp_pwm > PWM_MAX) + { + tmp_pwm = PWM_MAX; + } + + return tmp_pwm; +} + +//********************************// diff --git a/firmware/test_pwm.ino b/firmware/test_pwm.ino new file mode 100644 index 0000000..a13538e --- /dev/null +++ b/firmware/test_pwm.ino @@ -0,0 +1,225 @@ + +//********************************// + +#define TEST_PWM_STATE_INIT 0 +#define TEST_PWM_STATE_CH1_INC 1 +#define TEST_PWM_STATE_CH1_DEC 2 +#define TEST_PWM_STATE_CH2_INC 3 +#define TEST_PWM_STATE_CH2_DEC 4 +#define TEST_PWM_STATE_CH3_INC 5 +#define TEST_PWM_STATE_CH3_DEC 6 +#define TEST_PWM_STATE_CH4_INC 7 +#define TEST_PWM_STATE_CH4_DEC 8 + +//********************************// + +bool test_pwm = false; +uint32_t test_pwm_lastcheck_ms = 0; +uint8_t test_pwm_state = TEST_PWM_STATE_INIT; + +//********************************// + +void test_pwm_main() +{ + if (test_pwm == false) + { + return; + } else if ((test_pwm_lastcheck_ms + PWM_TEST_INTERVA_MS) <= millis()) + { + test_pwm_lastcheck_ms = millis(); + + switch (test_pwm_state) + { + // ----------------------- // + case TEST_PWM_STATE_INIT: + // disable the lights + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + light_state[i] = false; + bri[i] = 0; + current_bri[i] = 0; + current_pwm[i] = 0; + transitiontime[i] = 0; + process_lightdata(i, transitiontime[i]); + } + + light_state[0] = true; + test_pwm_state = TEST_PWM_STATE_CH1_INC; + break; + + // ----------------------- // + case TEST_PWM_STATE_CH1_INC: + + if (bri[0] < 255) + { + bri[0] += TEST_PWM_CHG_CNT; + transitiontime[0] = 1; + process_lightdata(0, transitiontime[0]); + + } else { + + test_pwm_state = TEST_PWM_STATE_CH1_DEC; + } + break; + + case TEST_PWM_STATE_CH1_DEC: + + if (bri[0] > 0) + { + bri[0] -= TEST_PWM_CHG_CNT; + transitiontime[0] = 1; + process_lightdata(0, transitiontime[0]); + + } else { + + light_state[0] = false; + bri[0] = 0; + current_bri[0] = 0; + current_pwm[0] = 0; + transitiontime[0] = 0; + process_lightdata(0, transitiontime[0]); + + light_state[1] = true; + test_pwm_state = TEST_PWM_STATE_CH2_INC; + } + break; + + // ----------------------- // + case TEST_PWM_STATE_CH2_INC: + + if (bri[1] < 255) + { + bri[1] += TEST_PWM_CHG_CNT; + transitiontime[1] = 1; + process_lightdata(1, transitiontime[1]); + + } else { + + test_pwm_state = TEST_PWM_STATE_CH2_DEC; + } + break; + + case TEST_PWM_STATE_CH2_DEC: + + if (bri[1] > 0) + { + bri[1] -= TEST_PWM_CHG_CNT; + transitiontime[1] = 1; + process_lightdata(1, transitiontime[1]); + + } else { + + light_state[1] = false; + bri[1] = 0; + current_bri[1] = 0; + current_pwm[1] = 0; + transitiontime[1] = 0; + process_lightdata(1, transitiontime[1]); + + light_state[2] = true; + test_pwm_state = TEST_PWM_STATE_CH3_INC; + } + break; + + // ----------------------- // + case TEST_PWM_STATE_CH3_INC: + + if (bri[2] < 255) + { + bri[2] += TEST_PWM_CHG_CNT; + transitiontime[2] = 1; + process_lightdata(2, transitiontime[2]); + + } else { + + test_pwm_state = TEST_PWM_STATE_CH3_DEC; + } + break; + + case TEST_PWM_STATE_CH3_DEC: + + if (bri[2] > 0) + { + bri[2] -= TEST_PWM_CHG_CNT; + transitiontime[2] = 1; + process_lightdata(2, transitiontime[2]); + + } else { + + light_state[2] = false; + bri[2] = 0; + current_bri[2] = 0; + current_pwm[2] = 0; + transitiontime[2] = 0; + process_lightdata(2, transitiontime[2]); + + light_state[3] = true; + test_pwm_state = TEST_PWM_STATE_CH4_INC; + } + break; + + // ----------------------- // + case TEST_PWM_STATE_CH4_INC: + + if (bri[3] < 255) + { + bri[3] += TEST_PWM_CHG_CNT; + transitiontime[3] = 1; + process_lightdata(3, transitiontime[3]); + + } else { + + test_pwm_state = TEST_PWM_STATE_CH4_DEC; + } + break; + + case TEST_PWM_STATE_CH4_DEC: + + if (bri[3] > 0) + { + bri[3] -= TEST_PWM_CHG_CNT; + transitiontime[3] = 1; + process_lightdata(3, transitiontime[3]); + + } else { + + test_pwm = false; + tc_enabled = tc_enabled_old; + + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + light_state[i] = false; + bri[i] = 0; + current_bri[i] = 0; + current_pwm[i] = 0; + transitiontime[i] = 0; + process_lightdata(i, transitiontime[i]); + } + + tc_reset(); + tc_update_main(); // load the tc if required + + test_pwm_state = TEST_PWM_STATE_INIT; + } + break; + + // ----------------------- // + default: + test_pwm_state = TEST_PWM_STATE_INIT; + } + + Serial.println("---"); + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) + { + Serial.println("light_state[" + (String)i + "] = " + (String)light_state[i]); + Serial.println("bri[" + (String)i + "] = " + (String)bri[i]); + Serial.println("current_bri[" + (String)i + "] = " + (String)current_bri[i]); + Serial.println("current_pwm[" + (String)i + "] = " + (String)current_pwm[i]); + Serial.println("transitiontime[" + (String)i + "] = " + (String)transitiontime[i]); + } + + } + +} + +//********************************// diff --git a/firmware/webserver.ino b/firmware/webserver.ino new file mode 100644 index 0000000..a1d05ce --- /dev/null +++ b/firmware/webserver.ino @@ -0,0 +1,605 @@ + +//********************************// + +void handleNotFound() +{ + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + + for (uint8_t i = 0; i < server.args(); i++) + { + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + } + server.send(404, "text/plain", message); +} + +//********************************// + +void init_webserver() +{ + + server.on("/state", HTTP_PUT, []() + { // HTTP PUT request used to set a new light state + DynamicJsonDocument root(512); + DeserializationError error = deserializeJson(root, server.arg("plain")); + + if (error) { + server.send(404, "text/plain", "FAIL. " + server.arg("plain")); + } else { + + for (JsonPair state : root.as()) + { + const char* key = state.key().c_str(); + int light = atoi(key) - 1; + if (light < 0) + { + light = 0; + } else if (light > (LIGHTS_COUNT-1)) + { + light = (LIGHTS_COUNT-1); + } + JsonObject values = state.value(); + + uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); + if (values.containsKey("on")) + { + if (values["on"]) + { + light_state[light] = true; + if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_OFF) + { + EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); + } + } else { + light_state[light] = false; + if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON) + { + EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); + } + } + } + + if (values.containsKey("bri")) + { + bri[light] = values["bri"]; + } + + if (values.containsKey("bri_inc")) + { + bri[light] += (int)values["bri_inc"]; + if (bri[light] > 255) bri[light] = 255; + else if (bri[light] < 1) bri[light] = 1; + } + + if (values.containsKey("transitiontime")) + { + default_transitiontime = values["transitiontime"]; + if (default_transitiontime <= 0) + { + default_transitiontime = 1; + } + if (tc_enabled == TIMING_CONTROL_DISABLED) + { + for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) + { + // set the default transition time for all lights + process_lightdata(i, default_transitiontime); + } + } + } + + } + String output; + serializeJson(root, output); + server.send(200, "text/plain", output); + } + }); + + server.on("/state", HTTP_GET, []() + { // HTTP GET request used to fetch current light state + uint8_t light = server.arg("light").toInt() - 1; + if (light < 0) + { + light = 0; + } else if (light > (LIGHTS_COUNT-1)) + { + light = (LIGHTS_COUNT-1); + } + DynamicJsonDocument root(512); + root["on"] = light_state[light]; + root["bri"] = bri[light]; + root["curbri"] = (int)current_bri[light]; + root["curpwm"] = current_pwm[light]; + String output; + serializeJson(root, output); + server.send(200, "text/plain", output); + }); + + server.on("/detect", []() + { // HTTP GET request used to discover the light type + char macString[32] = { 0 }; + sprintf(macString, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + DynamicJsonDocument root(1024); + root["name"] = LIGHT_NAME; + root["lights"] = LIGHTS_COUNT; + root["protocol"] = "native_multi"; + root["modelid"] = "LWB010"; + root["type"] = "dimmable_light"; + root["mac"] = String(macString); + root["version"] = LIGHT_VERSION; + String output; + serializeJson(root, output); + server.send(200, "text/plain", output); + }); + + server.on("/test_pwm", []() + { + test_pwm = true; + tc_enabled_old = tc_enabled; + tc_enabled = TIMING_CONTROL_DISABLED; + + server.send(200, "text/html", "OK"); + }); + + server.on("/tc_data_blocks_read", []() + { + String output = tc_getJsonData(); + + server.send(200, "application/json", output); + }); + + server.on("/tc_data_blocks_store", []() + { + if (server.hasArg("data")) + { + String jsonData = server.arg("data"); + Serial.println("Received: " + jsonData); + tc_jsonDataBlocksToEEPROM(jsonData); + server.send(200, "text/html", "tcdata saved"); + } + }); + + server.on("/js_top", []() + { + server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/top.js"))); + }); + + server.on("/js_bottom", []() + { + server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/bottom.js"))); + }); + + server.on("/css", []() + { + server.send(200, "text/css", loadSPIFFSFile("/style.css")); + }); + + server.on("/", []() + { + + if (server.hasArg("transition")) + { + default_transitiontime = server.arg("transition").toFloat(); + if (default_transitiontime <= 0) + { + default_transitiontime = 1; + } + if (tc_enabled == TIMING_CONTROL_DISABLED) + { + for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) + { + // set the default transition time for all lights + process_lightdata(i, default_transitiontime); + Serial.println("transition for light " + (String)i + " set to " + (String)default_transitiontime); + } + } + } + + // startup behavior switch handling + if (server.hasArg("startup")) + { + int startup = server.arg("startup").toInt(); + if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) != startup) + { + EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, startup); + + for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { + uint8_t tmp = (light_state[i] == true ? LIGHT_STATE_ON : LIGHT_STATE_OFF); + if (EEPROM.read(EEPROM_LAST_STATE_ADDRESS + i) != tmp) + { + EEPROM.write(EEPROM_LAST_STATE_ADDRESS + i, tmp); + } + } + + EEPROM.commit(); + Serial.print("Startup behavior set to "); + Serial.println(EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS)); + } + } + + // timing controller switch handling + if (server.hasArg("tc")) + { + if (server.arg("tc") == "true") + { + if (tc_enabled == TIMING_CONTROL_DISABLED) + { + if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_ENABLED) + { + tc_enabled = TIMING_CONTROL_ENABLED; + EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_ENABLED); + EEPROM.commit(); + Serial.print("Timing control = "); + Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); + tc_update_main(); // call the main update function to read data and set the light states + } + } + + } else { // tc is set to false or something else + + if (tc_enabled == TIMING_CONTROL_ENABLED) + { + tc_enabled = TIMING_CONTROL_DISABLED; + if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_DISABLED) + { + EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED); + EEPROM.commit(); + Serial.print("Timing control = "); + Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); + + for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) + { + // set the default transition time for all lights + process_lightdata(i, default_transitiontime); + } + } + } + } + } + + // scene switch handling + if (server.hasArg("scene")) + { + scene = server.arg("scene").toInt(); + if (EEPROM.read(EEPROM_SCENE_ADDRESS) != scene) + { + EEPROM.write(EEPROM_SCENE_ADDRESS, scene); + EEPROM.commit(); + Serial.print("Scene set to "); + Serial.println(EEPROM.read(EEPROM_SCENE_ADDRESS)); + } + } + + if (server.hasArg("dip")) { + uint8_t tmp = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); + uint8_t tmp2 = (server.arg("dip") == "true" ? 1 : 0); + if (tmp != tmp2) + { + EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, tmp2); + EEPROM.commit(); + Serial.print("Set dynamic IP to "); + Serial.println(EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS)); + } + } + + // process the received data for every light + for (int light = 0; light < LIGHTS_COUNT; light++) + { + + if (server.hasArg("bri" + (String)light)) + { + int tmp = (int)server.arg("bri" + (String)light).toInt(); + + if (tmp > 255) + { + tmp = 255; + } else if (tmp < 0) { + tmp = 0; + } + bri[light] = tmp; + Serial.print("Brightness "); + Serial.print(light); + Serial.print(" set to "); + Serial.println(bri[light]); + } + + if (server.hasArg("on" + (String)light)) + { + uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); + if (server.arg("on" + (String)light) == "true" && light_state[light] == false) + { + light_state[light] = true; + if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 0) + { + EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); + EEPROM.commit(); + } + Serial.print("Light "); + Serial.print(light); + Serial.print(" state set to "); + Serial.println(light_state[light]); + + } else if (server.arg("on" + (String)light) == "false" && light_state[light] == true) + { + light_state[light] = false; + bri[light] = 0; + if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 1) + { + EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); + EEPROM.commit(); + } + Serial.print("Light "); + Serial.print(light); + Serial.print(" state set to "); + Serial.println(light_state[light]); + } + + if (tc_enabled == TIMING_CONTROL_DISABLED) + { + process_lightdata(light, default_transitiontime); + } + } else { + // light is off + if (tc_enabled == TIMING_CONTROL_DISABLED) + { + process_lightdata(light, default_transitiontime); + } + } + + // start alerting for every light + if (server.hasArg("alert")) + { + if (light_state[light]) + { + current_bri[light] = 0; + } else { + current_bri[light] = 255; + } + } + + } // process all lights + + if (server.hasArg("resettc")) + { // reqrite the tc config and reboot + tc_write_default(); + ESP.reset(); + } + + if (server.hasArg("reset")) + { + ESP.reset(); + } + + // ***** Generate static HTML page ***** // + + String tmp1 = genHMTLTop(); + + String tmp2 = genLightControlHTML(); + + String tmp3 = getIndexHTMLMiddle(); + + String tmp4 = genConfigHTML(); + + String tmp5 = genHMTLBottom(); + + server.send(200, "text/html", tmp1 + tmp2 + tmp3 + tmp4 + tmp5); + + }); + + server.on("/reset", []() + { // trigger manual reset + server.send(200, "text/html", "reset"); + delay(1000); + ESP.restart(); + }); + + server.onNotFound(handleNotFound); +} + +//********************************// + +String genHMTLTop() +{ + return replacePlaceholder(getIndexHTMLTop()); +} + +String genHMTLBottom() +{ + return replacePlaceholder(getIndexHTMLBottom()); +} + +String genConfigHTML() +{ + // +++++ Generate config part of the page +++++ + return replacePlaceholder(getConfigHTML()); +} + +String genTCEditHTML() +{ + return replacePlaceholder(getTCDataEditHTML()); +} + +String genLightControlHTML() +{ + String http_content = ""; + // +++++ Generate lights part of the HTML page +++++ + // Light control + for (uint8 light_num = 0; light_num < LIGHTS_COUNT; light_num++) + { + // Generate lights part of the HTML page + String tmp_light_content = getLightControlHTML(); + + // on/off buttons and slider + tmp_light_content.replace("{{LIGHT_NUMBER}}", (String)(light_num + 1)); + tmp_light_content.replace("{{LIGHT_NUMBER_DEC}}", (String)light_num); + + // add the lights code to the html output string + http_content += tmp_light_content; + } + return http_content; +} + +//********************************// + +String replacePlaceholder(String http_content) +{ + + http_content.replace("{{LIGHT_NAME}}", (String)LIGHT_NAME); + + http_content.replace("{{LIGHT_COUNT}}", (String)LIGHTS_COUNT); + + int tc_val = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS); + if (tc_val == TIMING_CONTROL_ENABLED) + { + http_content.replace("{{TC_LINK_PRIMARY_ON}}", "pure-button-primary"); + + } else { + http_content.replace("{{TC_LINK_PRIMARY_ON}}", ""); + } + if (tc_val == TIMING_CONTROL_DISABLED) + { + http_content.replace("{{TC_LINK_PRIMARY_OFF}}", "pure-button-primary"); + + } else { + http_content.replace("{{TC_LINK_PRIMARY_OFF}}", ""); + } + + http_content.replace("{{TRANSITION_TIME}}", (String)default_transitiontime); + + int ls_val = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); + if (ls_val == LAST_STATE_STARTUP_LIGHT_LAST_STATE) + { + http_content.replace("{{STARTUP_SELECTED_LS_0}}", "selected=\"selected\""); + } else { + http_content.replace("{{STARTUP_SELECTED_LS_0}}", ""); + } + + if (ls_val == LAST_STATE_STARTUP_LIGHT_ON_STATE) + { + http_content.replace("{{STARTUP_SELECTED_ON_1}}", "selected=\"selected\""); + } else { + http_content.replace("{{STARTUP_SELECTED_ON_1}}", ""); + } + + if (ls_val == LAST_STATE_STARTUP_LIGHT_OFF_STATE) + { + http_content.replace("{{STARTUP_SELECTED_OFF_2}}", "selected=\"selected\""); + } else { + http_content.replace("{{STARTUP_SELECTED_OFF_2}}", ""); + } + + // scene + int sc_val = EEPROM.read(EEPROM_SCENE_ADDRESS); + if (sc_val == SCENE_RELEAX) + { + http_content.replace("{{SCENE_SELECTED_RELAX_0}}", "selected=\"selected\""); + } else { + http_content.replace("{{SCENE_SELECTED_RELAX_0}}", ""); + } + if (sc_val == SCENE_BRIGHT) + { + http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", "selected=\"selected\""); + } else { + http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", ""); + } + if (sc_val == SCENE_NIGHTLY) + { + http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", "selected=\"selected\""); + } else { + http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", ""); + } + + // Wifi settings + http_content.replace("{{WIFI_SSID}}", WiFi.SSID()); + + // Network settings + uint8_t dip = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); + if (dip) + { + http_content.replace("{{DIP_LINK_ON_PRIMARY}}", "pure-button-primary"); + http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", ""); + } else { + http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", "pure-button-primary"); + http_content.replace("{{DIP_LINK_ON_PRIMARY}}", ""); + } + + // network config + http_content.replace("{{WIFI_CFG_IP}}", WiFi.localIP().toString()); + http_content.replace("{{WIFI_CFG_GW}}", WiFi.gatewayIP().toString()); + http_content.replace("{{WIFI_CFG_NM}}", WiFi.subnetMask().toString()); + http_content.replace("{{WIFI_CFG_DNS}}", WiFi.dnsIP().toString()); + + // add the current ip address to the page + http_content.replace("{{IP_ADDRESS}}", WiFi.localIP().toString()); + + // set the pwm values + http_content.replace("{{PWM_MIN}}", (String)PWM_MIN); + http_content.replace("{{PWM_MAX}}", (String)PWM_MAX); + + return http_content; +} + +//********************************// + +String loadSPIFFSFile(String fname) +{ + File file = SPIFFS.open(fname, "r"); + if (!file) + { + Serial.println("Failed to open file " + fname); + return ""; + } + String contents = file.readString(); + file.close(); + return contents; +} + +//********************************// + +String getIndexHTMLTop() +{ + // load file + return loadSPIFFSFile("/index_template_top.html"); +} + +String getIndexHTMLMiddle() +{ + // load file + return loadSPIFFSFile("/index_template_middle.html"); +} + +String getIndexHTMLBottom() +{ + // load file + return loadSPIFFSFile("/index_template_bottom.html"); +} + +//********************************// + +String getConfigHTML() +{ + // load file + return loadSPIFFSFile("/config_template.html"); +} + +//********************************// + +String getTCDataEditHTML() +{ + // load file + return loadSPIFFSFile("/tc_data_edit.html"); +} + +//********************************// + +String getLightControlHTML() +{ + // load file + return loadSPIFFSFile("/light_control_template.html"); +}