diff --git a/FC.ino b/FC.ino new file mode 100644 index 0000000..88ed966 --- /dev/null +++ b/FC.ino @@ -0,0 +1,369 @@ +bool loadConfig(){ // Завантаження даних збереженних в файлі config.json + File configFile = SPIFFS.open("/config.json", "r"); // Відкриваемо файл на читання + if(!configFile) { // якщо файл не знайдено ствоюємого його та записуємо в ньго данні з наших змінних + if(printCom) Serial.println("Failed to open config file"); + saveConfig(); // Створюємо файл + configFile.close(); + return false; // Повернення з помилкою + } + size_t size = configFile.size(); // Перевіряємо ромір файлу, будемо використовувати файл довжиною в 1024 байта + if(printCom) { + Serial.println("size config.file = " + (String) size); + } + if(size > 4096) { + if(printCom) { + Serial.println("Config file size is too large"); + } + configFile.close(); + return false; // Повернення з помилкою + } + jsonConfig = configFile.readString(); // завантажуємо файл конфігурації в глобальну змінну + DynamicJsonDocument doc(8192); // Резервуємо память для json обекту буфер може розти по мірі необхідності переважно для ESP8266 + deserializeJson(doc, jsonConfig); + configFile.close(); + + ssidAP = doc["ssidAP"].as(); + passwordAP = doc["passwordAP"].as(); + ssid[0] = doc["ssid0"].as(); + password[0] = doc["password0"].as(); + ssid[1] = doc["ssid1"].as(); + password[1] = doc["password1"].as(); + auth = doc["auth"].as(); + authOn = doc["authOn"]; + kuOn = doc["kuOn"]; + kuOff = doc["kuOff"]; + weatherHost = doc["weatherHost"]; + weatherKey0 = doc["weatherKey0"].as(); + weatherKey1 = doc["weatherKey1"].as(); + cityID0 = doc["cityID0"].as(); + cityID1 = doc["cityID1"].as(); + snprintf(personalCityName, 51, "%s", (doc["PSN"].as()).c_str()); + weatherLang = doc["weatherLang"].as(); + displayForecast = doc["displayForecast"]; + displayCityName = doc["displayCityName"]; + displayForecastNow = doc["displayForecastNow"]; + displayForecastToday = doc["displayForecastToday"]; + displayForecastTomorrow = doc["displayForecastTomorrow"]; + animNotWeather = doc["animNotWeather"]; + timeStartViewWeather = doc["timeStartViewWeather"]; + timeEndViewWeather = doc["timeEndViewWeather"]; + timeScrollSpeed = doc["timeScrollSpeed"]; + MAX7219_ROTATE = doc["MAX7219_ROTATE"]; + updateOTAEnable = doc["updateOTAEnable"]; + volBrightnessD = doc["volBrightnessD"]; + volBrightnessN = doc["volBrightnessN"]; + timeDay = doc["timeDay"]; + timeNight = doc["timeNight"]; + volBrightnessAuto = doc["volBrightnessAuto"]; + lowLivelBrightness = doc["lowLivelBrightness"]; + upLivelBrightness = doc["upLivelBrightness"]; + clockNight = doc["clockNight"]; + isActiveBuzzer = doc["isActiveBuzzer"]; + function[0] = doc["function00"]; + function[1] = doc["function01"]; + function[2] = doc["function02"]; + function[3] = doc["function03"]; + function[4] = doc["function04"]; + function[5] = doc["function05"]; + function[6] = doc["function06"]; + function[7] = doc["function07"]; + function[8] = doc["function08"]; + function[9] = doc["function09"]; + function[10] = doc["function10"]; + function[11] = doc["function11"]; + period[0] = doc["period00"]; + period[1] = doc["period01"]; + period[2] = doc["period02"]; + period[3] = doc["period03"]; + period[4] = doc["period04"]; + period[5] = doc["period05"]; + period[6] = doc["period06"]; + period[7] = doc["period07"]; + period[8] = doc["period08"]; + period[9] = doc["period09"]; + period[10] = doc["period10"]; + period[11] = doc["period11"]; + printCom = doc["printCom"]; + sensore[0] = doc["sensor00"]; + sensore[1] = doc["sensor01"]; + sensore[2] = doc["sensor02"]; + sensore[3] = doc["sensor03"]; + sensore[4] = doc["sensor04"]; + param0 = doc["param0"]; + param1 = doc["param1"]; + param2 = doc["param2"]; + param3 = doc["param3"]; + param4 = doc["param4"]; + pressSys = doc["pressSys"]; + fontCLOCK = doc["fontCLOCK"]; + aliData = doc["aliData"]; + animPoint = doc["animPoint"]; + corr00 = doc["corr00"]; + corr01 = doc["corr01"]; + corr02 = doc["corr02"]; + corr03 = doc["corr03"]; + corr04 = doc["corr04"]; + MAX7219_NUM = doc["MAX7219_NUM"]; + rtcStat = doc["rtcStat"]; + uuid = doc["uuid"].as(); + api_key = doc["api_key"].as(); + sensors_ID0 = doc["sensors_ID0"]; + sensors_ID1 = doc["sensors_ID1"]; + sensors_ID2 = doc["sensors_ID2"]; + displayData = doc["displayData"]; + butStat = doc["butStat"]; + sgpCo2LivelAlarm = doc["sgpCo2LivelAlarm"]; + eCo2AlarmEsp = doc["eCo2AlarmEsp"]; + eCo2Led = doc["eCo2Led"]; + sgpTvocLivelAlarm = doc["sgpTvocLivelAlarm"]; + tvocAlarmEsp = doc["tvocAlarmEsp"]; + tvocLed = doc["tvocLed"]; + setSgpCorr = doc["setSgpCorr"]; + +printCom = true; +updateOTAEnable = true; + + if(MAX7219_NUM == 0) { + MAX7219_NUM = 4; + } + + if(printCom) { + printTime(); + Serial.print("Load Config : "); + Serial.println(jsonConfig); + } + return true; +} + +//================================================================================================================================================ +bool loadAlarm(){ // Завантаження даних збереженних в файлі config.json + File configFile = SPIFFS.open("/alarm.json", "r"); // Відкриваемо файл на читання + if(!configFile) { // якщо файл не знайдено ствоюємого його та записуємо в ньго данні з наших змінних + if(printCom) Serial.println("Failed to open alarm file"); + saveAlarm(); // Створюємо файл + configFile.close(); + return false; // Повернення з помилкою + } + size_t size = configFile.size(); // Перевіряємо ромір файлу, будемо використовувати файл довжиною в 1024 байта + if(printCom) Serial.println("size alarme.file = " + (String) size); + if(size > 2048) { + if(printCom) Serial.println("Config file size is too large"); + configFile.close(); + return false; // Повернення з помилкою + } + jsonAlarm = configFile.readString(); // завантажуємо файл конфігурації в глобальну змінну + DynamicJsonDocument doc(4096); // Резервуємо память для json обекту буфер може розти по мірі необхідності переважно для ESP8266 + deserializeJson(doc, jsonAlarm); + configFile.close(); + ntpServerName = doc["ntpServerName"].as(); + timeZone = doc["timeZone"]; // Так отримуємо число + isDayLightSaving = doc["isDayLightSaving"]; + alarme[0][0] = doc["al_0_0"]; + alarme[0][1] = doc["al_0_1"]; + alarme[0][2] = doc["al_0_2"]; + alarme[1][0] = doc["al_1_0"]; + alarme[1][1] = doc["al_1_1"]; + alarme[1][2] = doc["al_1_2"]; + alarme[2][0] = doc["al_2_0"]; + alarme[2][1] = doc["al_2_1"]; + alarme[2][2] = doc["al_2_2"]; + alarme[3][0] = doc["al_3_0"]; + alarme[3][1] = doc["al_3_1"]; + alarme[3][2] = doc["al_3_2"]; + alarme[4][0] = doc["al_4_0"]; + alarme[4][1] = doc["al_4_1"]; + alarme[4][2] = doc["al_4_2"]; + rtcStat = doc["rtcStat"]; + + if(printCom) { + printTime(); + Serial.print("Load Alarm(config): "); + Serial.println(jsonAlarm); + } + return true; +} + +//================================================================= +bool saveConfig(){ + DynamicJsonDocument doc(8192); + deserializeJson(doc, jsonConfig); + doc["ssidAP"] = ssidAP; + doc["passwordAP"] = passwordAP; + doc["ssid0"] = ssid[0]; + doc["password0"] = password[0]; + doc["ssid1"] = ssid[1]; + doc["password1"] = password[1]; + doc["auth"] = auth; + doc["authOn"] = authOn; + doc["kuOn"] = kuOn; + doc["kuOff"] = kuOff; + doc["weatherHost"] = weatherHost; + doc["weatherKey0"] = weatherKey0; + doc["weatherKey1"] = weatherKey1; + doc["cityID0"] = cityID0; + doc["cityID1"] = cityID1; + doc["PSN"] = chr_to_str(personalCityName); + doc["weatherLang"] = weatherLang; + doc["displayForecast"] = displayForecast; + doc["displayCityName"] = displayCityName; + doc["displayForecastNow"] = displayForecastNow; + doc["displayForecastToday"] = displayForecastToday; + doc["displayForecastTomorrow"] = displayForecastTomorrow; + doc["timeStartViewWeather"] = timeStartViewWeather; + doc["timeEndViewWeather"] = timeEndViewWeather; + doc["timeScrollSpeed"] = timeScrollSpeed; + doc["MAX7219_ROTATE"] = MAX7219_ROTATE; + doc["updateOTAEnable"] = updateOTAEnable; + doc["volBrightnessD"] = volBrightnessD; + doc["volBrightnessN"] = volBrightnessN; + doc["timeDay"] = timeDay; + doc["timeNight"] = timeNight; + doc["volBrightnessAuto"] = volBrightnessAuto; + doc["lowLivelBrightness"] = lowLivelBrightness; + doc["upLivelBrightness"] = upLivelBrightness; + doc["clockNight"] = clockNight; + doc["isActiveBuzzer"] = isActiveBuzzer; + doc["function00"] = function[0]; + doc["function01"] = function[1]; + doc["function02"] = function[2]; + doc["function03"] = function[3]; + doc["function04"] = function[4]; + doc["function05"] = function[5]; + doc["function06"] = function[6]; + doc["function07"] = function[7]; + doc["function08"] = function[8]; + doc["function09"] = function[9]; + doc["function10"] = function[10]; + doc["function11"] = function[11]; + doc["period00"] = period[0]; + doc["period01"] = period[1]; + doc["period02"] = period[2]; + doc["period03"] = period[3]; + doc["period04"] = period[4]; + doc["period05"] = period[5]; + doc["period06"] = period[6]; + doc["period07"] = period[7]; + doc["period08"] = period[8]; + doc["period09"] = period[9]; + doc["period10"] = period[10]; + doc["period11"] = period[11]; + doc["printCom"] = printCom; + doc["sensor00"] = sensore[0]; + doc["sensor01"] = sensore[1]; + doc["sensor02"] = sensore[2]; + doc["sensor03"] = sensore[3]; + doc["sensor04"] = sensore[4]; + doc["param0"] = param0; + doc["param1"] = param1; + doc["param2"] = param2; + doc["param3"] = param3; + doc["param4"] = param4; + doc["pressSys"] = pressSys; + doc["fontCLOCK"] = fontCLOCK; + doc["aliData"] = aliData; + doc["animPoint"] = animPoint; + doc["corr00"] = corr00; + doc["corr01"] = corr01; + doc["corr02"] = corr02; + doc["corr03"] = corr03; + doc["corr04"] = corr04; + doc["MAX7219_NUM"] = MAX7219_NUM; + doc["rtcStat"] = rtcStat; + doc["uuid"] = uuid; + doc["api_key"] = api_key; + doc["sensors_ID0"] = sensors_ID0; + doc["sensors_ID1"] = sensors_ID1; + doc["sensors_ID2"] = sensors_ID2; + doc["displayData"] = displayData; + doc["butStat"] = butStat; + doc["sgpCo2LivelAlarm"] = sgpCo2LivelAlarm; + doc["eCo2AlarmEsp"] = eCo2AlarmEsp; + doc["eCo2Led"] = eCo2Led; + doc["sgpTvocLivelAlarm"] = sgpTvocLivelAlarm; + doc["tvocAlarmEsp"] = tvocAlarmEsp; + doc["tvocLed"] = tvocLed; + doc["setSgpCorr"] = setSgpCorr; + + jsonConfig = ""; + if(serializeJson(doc, jsonConfig) == 0) { + if(printCom) { + Serial.println(F("Failed to write to jsonConfig")); + } + } + + // Відкриваємо файл для запису + File configFile = SPIFFS.open("/config.json", "w"); + if(!configFile) { + configFile.close(); + return false; + } + + if(serializeJson(doc, configFile) == 0) { + if(printCom) { + Serial.println(F("Failed to write to file")); + } + } + + if(printCom) { + printTime(); + Serial.print("Save Config : "); + Serial.println(jsonConfig); + } + configFile.close(); + bip(); + return true; +} + +//================================================================= +bool saveAlarm() { + DynamicJsonDocument doc(4096); + deserializeJson(doc, jsonAlarm); + doc["ntpServerName"] = ntpServerName; + doc["timeZone"] = timeZone; + doc["isDayLightSaving"] = isDayLightSaving; + doc["al_0_0"] = alarme[0][0]; + doc["al_0_1"] = alarme[0][1]; + doc["al_0_2"] = alarme[0][2]; + doc["al_1_0"] = alarme[1][0]; + doc["al_1_1"] = alarme[1][1]; + doc["al_1_2"] = alarme[1][2]; + doc["al_2_0"] = alarme[2][0]; + doc["al_2_1"] = alarme[2][1]; + doc["al_2_2"] = alarme[2][2]; + doc["al_3_0"] = alarme[3][0]; + doc["al_3_1"] = alarme[3][1]; + doc["al_3_2"] = alarme[3][2]; + doc["al_4_0"] = alarme[4][0]; + doc["al_4_1"] = alarme[4][1]; + doc["al_4_2"] = alarme[4][2]; + doc["rtcStat"] = rtcStat; + + jsonAlarm = ""; + if(serializeJson(doc, jsonAlarm) == 0) { + if(printCom) { + Serial.println(F("Failed to write to jsonConfig")); + } + } + + // Відкриваємо файл для запису + File configFile = SPIFFS.open("/alarm.json", "w"); + if(!configFile) { + configFile.close(); + return false; + } + + if(serializeJson(doc, configFile) == 0) { + if(printCom) { + Serial.println(F("Failed to write to file")); + } + } + + if(printCom) { + printTime(); + Serial.print("Save Alarm : "); + Serial.println(jsonAlarm); + } + configFile.close(); + bip(); + return true; +} diff --git a/P_auth.h b/P_auth.h new file mode 100644 index 0000000..cf8e044 --- /dev/null +++ b/P_auth.h @@ -0,0 +1,64 @@ +const char P_auth[] PROGMEM = R"=====( + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: +
+
+
+
+
+ +
+
+
Для доступа к данному устройству необходимо пройти авторизацию.
+
+ + + + + +
Введите токен авторизации
+
+
+ Войти +
+
+
+
+ + + +)====="; diff --git a/P_css.h b/P_css.h new file mode 100644 index 0000000..5e3c943 --- /dev/null +++ b/P_css.h @@ -0,0 +1,305 @@ +const char P_css[] PROGMEM = R"=====( +@charset "utf-8"; +html { + height: 100%; + width: 100%; +} +body { + color: #4e4e4e; + background: #e8e8e8; + height: 100%; + width: 100%; + font-size: 10pt; + font-family: 'Roboto', sans-serif; + margin: 0 auto; +} +table { + font-size: 10pt; + font-weight: 450; +} +header { + height: 54px; + margin-bottom: 22px; +} +.header-block { + padding-top: 6px; + position: fixed; + font-size: 11pt; + font-weight: bold; + text-align: center; + line-height: 150%; + height: 54px; + border-bottom: 1px solid #b3b3b3; + width: 100%; + background-color: #f4f4f4; + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.16); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.16); +} +.central-unit { + margin-left: auto; + margin-right: auto; + width: 500px; + max-width: 100%; + display: block; + box-shadow: 3px 8px 15px rgba(0, 0, 0, 0.4); + border: 1px solid #d9d9d9; +} +.main-menu { + background: #eeeeee; + font-weight: 500; + font-size: 0; + display: block; + width: 100%; +} +.unit-body { + background-color: white; + padding: 10px; + margin-left: auto; + margin-right: auto; + max-width: 100%; + display: block; +} +.unit-footer { + display: block; + background: #f8f8f8; +} +footer { + height: 41px; +} +.informer { + background-color: #f5f5f5; + margin: 5px; + padding: 1%; + border: 1px solid #d9d9d9; + border-radius: 2px; + font-style: italic; + text-align: justify; + font-size: 0.8em; + display: block; +} +fieldset { + border: 1px solid #d9d9d9; + border-radius: 4px; + margin-top: 5px; + margin-bottom: 5px; + padding: 5px 5px 10px 5px; + background-color: #f5f5f5; +} +fieldset legend { + font-weight: 600; + text-align: left; + padding-left: 4px; + padding-right: 5px; + margin-left: 6px; +} +#main_unit { + text-align: center; +} +.ico-text { + font-size: 10pt; + color: #434343; + cursor: pointer; + background-color: #eeeeee; + transition: 0.5s linear; + padding: 6px 5px 6px 8px; + border-bottom: 1px solid #d9d9d9; + display: table-cell; +} +.ico-text:hover { + background-color: white; +} +.ico-text-active { + background-color: white; + border-bottom: 1px solid white; +} +.ico-text-right { + display: table-cell; + width: inherit; + font-size: 10pt; + color: #434343; + background-color: #eeeeee; + border-bottom: 1px solid #d9d9d9; +} + +.switch { + font-weight: 450; + display: block; + margin: 6px 0 6px 0; + } +.opt_cn { + font-weight: 450; +} +.table { + border-collapse: collapse; + margin-left: auto; + margin-right: auto; +} +.table-centered { + font-weight: 450; + margin-left: auto; + margin-right: auto; +} +.table-centered > * > tr > td { + text-align: left; +} +.left { + text-align: left !important; +} +.right { + text-align: right !important; +} +.center { + text-align: center !important; +} +.table > * > tr > td { + border: 1px solid #a2a9b1; + padding: 0.5em 0.65em; + text-align: left; +} +.table > * > tr > th { + border: 1px solid #a2a9b1; + padding: 0.5em 0.65em; + text-align: center; +} +input[type=text].field, input[type=password].field { + padding: 3px; + margin: 1px; + border: 1px solid #575757; + border-radius: 2px; +} +input:focus[type=text].field{ + border: 1px solid #03d703 +} +input:invalid[type=text] { + border:1px solid red !important; +} +.save_button { + background-color: rgb(221 239 255); + padding: 6px; + border-radius: 3px; + border: 1px solid #c6c6c6; + font-weight: bold; + line-height: 100%; + cursor: pointer; + display: inline-block; + z-index: 1005; +} +.save_button:hover { + box-shadow: 0 0 10px 2px rgba(0,0,0,0.3); +} +.form-buttons { + text-align: right; + padding: 12px; +} +.center { + text-align:center; +} +.link__img{ + text-decoration: none; +} +.footer-band { + margin: auto; + position: fixed; + left: 0; + bottom: 0; + background: #b6b6b6; + width: 100%; + text-align: center; + padding: 5px 0 5px 0; +} +.footer-band-labels { + width: 500px; + margin-left: auto; + margin-right: auto; +} +.footer-left { + float: left; + padding-right: 15px; +} +.footer-right { + float: right; + padding-left: 15px; +} +.login-main { + height: 75%; + display: flex; + align-items: center; + justify-content: center; +} +.login-form-title { + text-align: center; + font-size: 16pt; + font-weight: 450; + padding: 10px 0 10px 0; +} +::selection { + background: green; color: #fff +} +::-moz-selection { + background: green; color: #fff +} +select { + height: 22px; + padding: 0 5px 0; + border-radius: 2px; + border: 1px solid #909090; +} +a { + color: blue; +} +a:visited { + color: blue; +} +a:hover { + color: #1b63ff; +} +.board-container { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} +.board-block-full { + width: 100%; +} +.board-block-half { + width: 50%; +} +.board-block { + box-sizing: border-box; + display: flex; + justify-content: flex-start; + padding: 5px; +} +.board-block-form { + border: 1px solid #eaeaea; + border-radius: 5px; + background-color: #fbfbfb; + font-size: 26pt; + color: #2c7087; + font-weight: 500; + width: 100%; +} +.main-time-font { + font-size: 65pt; + text-align: center; + font-weight: 600; +} +.board-block-title { + padding: 6px 0 3px 10px; + font-weight: 450; + font-size: 11pt; + color: #4f4f4f; +} +.board-block-text { + font-weight: 400; + font-size: 10pt; + color: #3f3f3f; +} +.board-block-data { + color: #2c7087; + font-weight: 600; + font-size: 37pt; + text-align: right; + padding: 6px 17px 6px 0; +} +)====="; diff --git a/P_help.h b/P_help.h new file mode 100644 index 0000000..1fc8e47 --- /dev/null +++ b/P_help.h @@ -0,0 +1,112 @@ +const char P_help[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+ Если что-то пошло не так... +
ВНИМАНИЕ!!! Некоторые изменения могут быть необратимыми! +
+
+ +
+
+
+
+ Update: select and download the firmware (bin) +
+
+ + +
+
+
+
+
+ Если вы ввели что-то не так и не можете изменить его, верните устройство к настройкам по умолчанию. Файл конфигурации будет удален. Устройство перезагрузится! +
+
+
+ +
+
+ + + +)====="; diff --git a/P_index.h b/P_index.h new file mode 100644 index 0000000..8fa6cd1 --- /dev/null +++ b/P_index.h @@ -0,0 +1,86 @@ +const char P_index[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+
+
+
+
+ +
+
+
+
+
Время ESP
+
{{time}}
+
+
+
+
+
Погода
+
Сервер:
+
Прогноз недоступен
+
+
+
+
+
Датчик 1
+
-28.9 C
+
+
+
+
+
Датчик 2
+
148 %
+
+
+
+
+
Датчик sgp30
+
148 %
+
+
+
+
+
+
+ + + +)====="; diff --git a/P_js.h b/P_js.h new file mode 100644 index 0000000..04689a3 --- /dev/null +++ b/P_js.h @@ -0,0 +1,135 @@ +const char P_js[] PROGMEM = R"=====( +let xhr=createXmlHttpObject(); + +function createXmlHttpObject() { + let xhr; + if(window.XMLHttpRequest) { + xhr=new XMLHttpRequest(); + } else { + xhr=new ActiveXObject('Microsoft.XMLHTTP'); + } + return xhr; +} +function load(submit) { + if(xhr.readyState===0 || xhr.readyState===4){ + submit+=".json"; + let auth = document.location.search; + if(auth.match(/auth=/)) { + submit+=auth; + } + xhr.open('GET',submit,true); + xhr.send(null); + xhr.onload=function() { + jsonResponse=JSON.parse(xhr.responseText); + loadBlock(xhr.onload); + } + } +} +function loadBlock(){ + let data2 = JSON.parse(xhr.responseText); + let data = document.getElementsByTagName('body')[0].innerHTML; + let new_string; + for (let key in data2){ + new_string=data.replace(new RegExp('{{'+key+'}}', 'g'), data2[key]); + data=new_string; + } + document.getElementsByTagName('body')[0].innerHTML=new_string; + let inputs=document.getElementsByTagName("input"); + let selects=document.getElementsByTagName("select"); + for (let key in data2) { + if(data2[key]=='checked'){ + for (let i=0; i=60) { + min=Number(min)+1;sec=0; + } + if (min>=60) { + hours=Number(hours)+1; + min=0; + } + if (hours>=24) { + hours=0 + }; + document.getElementById("time").innerHTML = hours+":"+(min<10?"0":"")+min+":"+(sec<10?"0":"")+sec; + set_real_time = setTimeout("real_time("+hours+","+min+","+sec+");", 1000); +} +function load_time(submit) { + server = "/Time"; + send_request(submit,server); + load('/configs.json'); +} +function get_html(submit) { + let auth = document.location.search; + if(auth.match(/auth=/)) submit += auth; + window.location.href = submit; +} +function convert(submit) { + let auth = document.location.search; + if(auth.match(/auth=/)) { + submit+=auth.replace("\?", "&"); + } + return (submit); +} +)====="; diff --git a/P_setup.h b/P_setup.h new file mode 100644 index 0000000..e731ccc --- /dev/null +++ b/P_setup.h @@ -0,0 +1,1028 @@ +const char P_setup[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+
+ Настройки Часов +
+ Здесь можно настроить параметры экрана, отображение данных на экране, параметры и отображение датчиков. +
+
+

Настройки экрана

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Кол-во модулей (8x8)
Вращение внутри модуля + +
Скорость бегущей строки
Выравнивание данных
Отображать дату + +
Анимация точек + +
Шрифт часов + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Функция на экране Длительность
+
+
+
+ Управление яркостью +
+ + + + + + + + + + + + + + +
Дневной режим экрана с:до:
Уровень яркости днем:ночью:
+ +
+ + + + + + + + +
Уровень c фоторезистора низ:верх:
+
+
+
+ Настройки датчиков +
+
+ Уровень яркости фоторезистора (0-15): {{lba}} +
+ +
+ + + + + + + + + + + +
Сигнал в начале каждого часа с:до:
Кнопка подключена к GPIO16 и к:
+
+ Подтягивающий резистор подключен соответственно к GPIO16 и к противоположному полюсу питания +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Датчик Корр. Данные
+ + + + {{Td}}
{{Tu}}
{{Th}}
{{Hd}}
{{Pu}}
+
+ {{sgp}} +
+
+ Сохранить настройки +
+
+
+
+ + + +)====="; diff --git a/P_sgp.h b/P_sgp.h new file mode 100644 index 0000000..0bc0c0c --- /dev/null +++ b/P_sgp.h @@ -0,0 +1,198 @@ +const char P_sgp[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+ Настройки SGP-30 +

Настройка параметров eCO₂:

+
+ SGP30 измеряет два параметра: TVOC и eCO₂. + eCO₂ - эквивалент диоксида углерода СО₂-экв: единица, используемая для сравнения излучающей способности парниковых газов с диоксидом углерода. + Картинка из интернета +
+
+ Текущий уровень eCO₂ (400-5000++): {{sgpCo2}}ppm +
+ ({{textCo2}}) +
+
+
+ + + + + +
Отображать уровень eCO₂ на экране
+
+ Уровни оповещения eCO₂: 0=(400-699), 1=(700-999), 2=(1000-2499), 3=(2500-4999), 4=(5000++) +
+ + + + + + + + + + + + + +
Установить уровень оповещения:
Выводить оповещение на экран
Отправлять Alarm в информ топик MQTT
+
+
+ +

Настройка параметров TVOC:

+
+ TVOC - совокупные летучие органические соединения. В некоторых случаях измерение одного только CO₂ не показывает превышения VOC. + Картинка из интернета +
+
+ Текущий уровень TVOC (0-2200++): {{sgpTvoc}}ppb +
+ ({{textTvoc}}) +
+
+
+ + + + + +
Отображать уровень TVOC на экране
+
+ Уровни оповещения TVCO: 0=(0-64), 1=(65-219), 2=(220-659), 3=(660-2199), 4=(2200++) +
+ + + + + + + + + + + + + +
Установить уровень оповещения:
Выводить оповещение на экран
Отправлять Alarm в информ топик MQTT
+
+
+
+

Настройка коррекци данных SGP-30:

+
+
+ Во избежание ошибок измерения необходимо указать датчику текущую абсолютную влажность воздуха, которая будет учтена при расчёте концентрации. Для этого, в паре с SGP30, рекомендуется использовать датчик влажности/температуры. +
+ +
+ Данные ручного ввода: +
+ + + + + + + + + +
Коррекция температуры (°С):
Коррекция влажности (%):
+
+
+
+ + +
+
+ + + +)====="; diff --git a/P_time.h b/P_time.h new file mode 100644 index 0000000..24c11dd --- /dev/null +++ b/P_time.h @@ -0,0 +1,968 @@ +const char P_time[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+ Настройки Времени + + + + + + + + + + + + + + + + + + + + + + + + +
Сервер точного времени (пример) "time.nist.gov"
Адрес сервера
Ваша часовая зона + +
Переход на летнее время + + +
Использовать модуль RTC + + +
+ +
+
+
+ Настройка будильника + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Номер Часы Минуты Режим
1 + + + + + +
2 + + + + + +
3 + + + + + +
4 + + + + + +
5 + + + + + +
+
+
+ Настройки Времени + + + + + + + + + + + + + +
+ Время + + + + : + + + + Дата + + + + - + + + + - + + +
+
+ Установить +
+
+
+ +
+
+ + + +)====="; diff --git a/P_weath.h b/P_weath.h new file mode 100644 index 0000000..8f5c118 --- /dev/null +++ b/P_weath.h @@ -0,0 +1,304 @@ +const char P_weath[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+ Настройки погоды +
+ Чтобы получить прогноз погоды, + weatherbit.io + openweathermap.org + здесь мы берем ключи API. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Погодный сервер
API weatherbit ключ
Название города
API openweathermap ключ
Название или код города
Отображаемое название города
Язык прогноза
+ + + + + + + + + + + + + + + + + + + + + +
Получать и выводить погоду на экран + + +
Отображать название города + + +
Отображать прогноз на сейчас + + +
Отображать прогноз на сегодня + + +
Получать и выводить прогноз на завтра + + +
+ + + + + + + +
Показывать погоду с:до:
+
+
+
+ Данные сервера + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Название города :{{location_name}}
Регион :{{location_region}}
Страна :{{location_country}}
Время обновления :{{location_localtime}}
Рассвет :{{location_sunrise}}
Закат :{{location_sunset}}
Температура :{{location_temp}}
Ощущается как :{{location_app_temp}}
Влажность (%) :{{location_rh}}
Давление (mb) :{{location_pres}}
Скорость ветра (m/s) :{{location_wind_spd}}
Направление ветра :{{location_wind_cdir_full}}
Облачность (%) :{{location_clouds}}
Видимость (km) :{{location_vis}}
Индекс UV (0-11+) :{{location_uv}}
Описание погоды :{{location_weather_description}}
+
+
+ +
+
+ + + +)====="; diff --git a/P_wifi.h b/P_wifi.h new file mode 100644 index 0000000..2da295b --- /dev/null +++ b/P_wifi.h @@ -0,0 +1,144 @@ +const char P_wifi[] PROGMEM = R"=====( + + + + + Часы информер WiFi_Clock + + + + + + +
+
+ Часы информер WiFi_Clock_{{ver}}
+ Время на ESP: + {{time}} +
+
+
+
+ +
+
+
+ Настройки WIFI +
+ Введите настройки интернет соединения для обновления времени, получения данных о погоде. +
+

+ Подключение к локальной WiFi сети +

+
+ + + + + + + + + + + + + + + + +
SSID WiFi сетиПароль
1
2
+
+
+ +
+ Точка доступа часов (IP_192.168.4.1) +
+ + + + + + + + + +
Введите имя точки доступа часов
Пароль к точке доступа
+
+
+
+ Авторизация WEB сервера +
+ + + + + + + + + +
Использовать авторизацию
Токен авторизации
+
+
+
+ Тестовое сообщение + + Отправить +
+
+
+ +
+
+ + + +)====="; diff --git a/README.md b/README.md index 4b0a9c8..77807f3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ # WiFi_Clock_esp8266 WiFi Clock on esp8266 based on VZ_Clock (by IvanZah) + () : +- , ; +- DHT22 SPG30; +- WiFi ; +- WiFi ; +- ; +- , ; +- . diff --git a/SRV.ino b/SRV.ino new file mode 100644 index 0000000..a46c0ea --- /dev/null +++ b/SRV.ino @@ -0,0 +1,1081 @@ +void server_init(void) { + server.on("/", auth_auth); + server.on("/index.vz", authIndex); + server.on("/wifi.vz", auth_wifi); + server.on("/time.vz", auth_time); + server.on("/weather.vz", auth_weather); + server.on("/setup.vz", auth_setup); + server.on("/sgp.vz", auth_sgp); + server.on("/help.vz", auth_help); + server.on("/style.css", [](){server.send_P ( 200, "text/css", P_css);}); + server.on("/function.js", [](){server.send_P ( 200, "text/plain", P_js);}); + server.on("/favicon.ico", [](){server.send(200, "text/html", "");}); + server.on("/configs.json", handle_ConfigJSON); // формування configs.json сторінки для передачі данних в web інтерфейс + server.on("/index-data.json", get_IndexDataJson); + server.on("/configs_wifi.json", get_ConfigWifiJson); + server.on("/configs_time.json", handle_ConfigTimeJson); + server.on("/configs_weath.json", handle_ConfigWeathJson); + server.on("/configs_setup.json", handle_ConfigSetupJson); + server.on("/configs_sgp.json", handle_ConfigSgpJson); + server.on("/ssid", handle_Set_Ssid); + server.on("/ntp", handle_ntp); // Установка часової зони по запиту типа http://192.168.2.100/timeZone?timeZone=3 + server.on("/set_time", handle_set_time); + server.on("/timepc", handle_timepc); + server.on("/weatherUpdate", handle_weather_update); + server.on("/weather", handle_weather); // Установка сервера погоди по запиту типа http://192.168.2.100/weatherHost?weatherHost=api.openweathermap.org + server.on("/weathOn", handle_weath_on); + server.on("/setup", handle_setup); + server.on("/sgp", handle_sgp); + server.on("/mess", handleMessage); + server.on("/restart", handle_Restart); // перезавантаження модуля по запиту типу http://192.168.1.11/restart?device=ok + server.on("/stopalarm", handle_stopAlarm); + server.on("/resetConfig", handle_resetConfig); + server.on("/printCom", handle_set_printCom); + server.onNotFound([]() {(404, "text/plain", "FileNotFound");}); + httpUpdater.setup(&server); + server.begin(); +} + +void sendAuthPage() { + server.send_P(200, "text/html", P_auth); +} + +//====================================================================================================== +void auth_auth() { + if(!authOn) { + server.send_P(200, "text/html", P_index); + } else { + sendAuthPage(); + } +} + +//====================================================================================================== +void authIndex() { + server.send_P(200, "text/html", P_index); +} + +//====================================================================================================== +void auth_wifi() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_wifi); + } else { + sendAuthPage(); + } +} + +//====================================================================================================== +void auth_time() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_time); + } else { + sendAuthPage(); + } +} +//====================================================================================================== +void auth_weather() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_weath); + } else { + sendAuthPage(); + } +} +//====================================================================================================== +void auth_setup() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_setup); + } else { + sendAuthPage(); + } +} +//====================================================================================================== +void auth_sgp() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_sgp); + } else { + sendAuthPage(); + } +} + +//====================================================================================================== +void auth_help() { + if(server.arg("auth")==auth || !authOn) { + server.send_P(200, "text/html", P_help); + } else { + sendAuthPage(); + } +} + +//========================================== +bool isNotVerified() { + if(authOn && server.arg("auth") != auth) { + server.send_P(404, "text/html", "404, you are not authorized!"); + return true; + } + return false; +} + +String getJsonHeaderPart() { + String json = "\"ver\":\"" + + String(ver) + + "\",\"time\":\"" + + (String(hour) + ":" + (minute < 10 ? "0" : "") + String(minute) + ":" + (second < 10 ? "0" : "") + String(second)); + return json; +} + +void get_IndexDataJson() { + String json = "{" + + getJsonHeaderPart() + + "\",\"ip\":\"" + + WiFi.localIP().toString() + + "\",\"printCom\":\"" + + (printCom == 1 ? "checked" : "") + + "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void handle_ConfigJSON() { + if(isNotVerified()) { + return; + } + String json = "{" + + getJsonHeaderPart() + + "\",\"ip\":\"" + + WiFi.localIP().toString() + + "\",\"printCom\":\"" + + (printCom==1?"checked":"") + + "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void get_ConfigWifiJson() { + if(isNotVerified()) { + return; + } + String json = "{" + + getJsonHeaderPart() + + "\",\"auth\":\"" + + auth + + "\",\"ssid0\":\"" + + ssid[0] + + "\",\"password0\":\"" + + password[0] + + "\",\"ssid1\":\"" + + ssid[1] + + "\",\"password1\":\"" + + password[1] + + "\",\"ssidAP\":\"" + + ssidAP + + "\",\"passwordAP\":\"" + + passwordAP + + "\",\"authOn\":\"" + + (authOn==1 ? "checked" : "") + + "\"}"; + server.send(200, "text/json", json); +} +//====================================================================================================== +void handle_ConfigTimeJson(){ + if(isNotVerified()) { + return; + } + String json = "{"; + json += getJsonHeaderPart(); + json += "\",\"ntpServerName\":\""; + json += ntpServerName; + json += "\",\"timeZone\":\""; + json += timeZone; + json += "\",\"isDayLightSaving\":\""; + json += (isDayLightSaving==1?"checked":""); + json += "\",\"rtcStat\":\""; + json += (rtcStat==1?"checked":""); + json += "\",\"setTMes\":\""; + json += "\",\"al_0_0\":\""; + json += alarme[0][0]; + json += "\",\"al_0_1\":\""; + json += alarme[0][1]; + json += "\",\"al_0_2\":\""; + json += alarme[0][2]; + json += "\",\"al_1_0\":\""; + json += alarme[1][0]; + json += "\",\"al_1_1\":\""; + json += alarme[1][1]; + json += "\",\"al_1_2\":\""; + json += alarme[1][2]; + json += "\",\"al_2_0\":\""; + json += alarme[2][0]; + json += "\",\"al_2_1\":\""; + json += alarme[2][1]; + json += "\",\"al_2_2\":\""; + json += alarme[2][2]; + json += "\",\"al_3_0\":\""; + json += alarme[3][0]; + json += "\",\"al_3_1\":\""; + json += alarme[3][1]; + json += "\",\"al_3_2\":\""; + json += alarme[3][2]; + json += "\",\"al_4_0\":\""; + json += alarme[4][0]; + json += "\",\"al_4_1\":\""; + json += alarme[4][1]; + json += "\",\"al_4_2\":\""; + json += alarme[4][2]; + json += "\",\"t0\":\""; + json += hour; + json += "\",\"t1\":\""; + json += minute; + json += "\",\"d0\":\""; + json += day; + json += "\",\"d1\":\""; + json += month; + json += "\",\"d2\":\""; + json += year; + json += "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void handle_ConfigWeathJson(){ + if(isNotVerified()) { + return; + } + int sr = location_sunrise.substring(0, 2).toInt() + (int)hourCorr; + if(sr>23) sr -= 24; + if(sr<0) sr += 24; + String sunrise = String(sr) + location_sunrise.substring(2, 5); + int ss = location_sunset.substring(0, 2).toInt() + (int)hourCorr; + if(ss>23) ss -= 24; + if(ss<0) ss += 24; + String sunset = String(ss) + location_sunset.substring(2, 5); + int st = location_localtime.substring(11, 13).toInt() + (int)hourCorr; + int ly = location_localtime.substring(0, 4).toInt(); + byte lm = location_localtime.substring(5, 7).toInt(); + byte ld = location_localtime.substring(8, 10).toInt(); + if(st>23) { + st -= 24; + ld++; + if(ld==32 || (ld==31 && (lm==4 || lm==6 || lm==9 || lm==11)) || (lm==2 && ((ld==29 && ly%4!=0) || (ld==30 && ly%4==0)))) { + ld=1; + lm++; + if(lm>12){ + lm=1; + ly++; + } + } + } + if(st<0) { + st += 24; + ld--; + if(ld<1) { + ld = 0 + ((lm==5 || lm==7 || lm==10 || lm==12 || (lm==3 && ly%4==0))?30:(lm==3 && ly%4!=0)?29:31); + lm--; + if(lm<1){ + lm=12; + ly--; + } + } + } + String lt = String(ly) + "-" + (lm<10?"0":"") + String(lm) + "-" + (ld<10?"0":"") + String(ld) + " " + (st<10?"0":"") + String(st) + location_localtime.substring(13, 16); + String json = "{"; + json += getJsonHeaderPart(); + json += "\",\"weatherKey0\":\""; + json += weatherKey0; + json += "\",\"weatherKey1\":\""; + json += weatherKey1; + json += "\",\"weatherHost\":\""; + json += weatherHost; + json += "\",\"cityID0\":\""; + json += cityID0; + json += "\",\"cityID1\":\""; + json += cityID1; + json += "\",\"personalCityName\":\""; + json += personalCityName; + json += "\",\"weatherLang\":\""; + json += weatherLang; + json += "\",\"displayForecast\":\""; + json += (displayForecast==1?"checked":""); + json += "\",\"displayCityName\":\""; + json += (displayCityName==1?"checked":""); + json += "\",\"displayForecastNow\":\""; + json += (displayForecastNow==1?"checked":""); + json += "\",\"displayForecastToday\":\""; + json += (displayForecastToday==1?"checked":""); + json += "\",\"displayForecastTomorrow\":\""; + json += (displayForecastTomorrow==1?"checked":""); + json += "\",\"animNotWeather\":\""; + json += (animNotWeather==1?"checked":""); + json += "\",\"timeStartViewWeather\":\""; + json += timeStartViewWeather; + json += "\",\"timeEndViewWeather\":\""; + json += timeEndViewWeather; + json += "\",\"location_name\":\""; + json += location_name; + json += "\",\"location_region\":\""; + json += location_region; + json += "\",\"location_country\":\""; + json += location_country; + json += "\",\"location_localtime\":\""; + json += lt; + json += "\",\"location_temp\":\""; + json += location_temp; + json += "\",\"location_app_temp\":\""; + json += location_app_temp; + json += "\",\"location_rh\":\""; + json += location_rh; + json += "\",\"location_pres\":\""; + json += location_pres; + json += "\",\"location_wind_spd\":\""; + json += location_wind_spd; + json += "\",\"location_wind_cdir_full\":\""; + json += location_wind_cdir_full; + json += "\",\"location_sunrise\":\""; + json += sunrise; + json += "\",\"location_sunset\":\""; + json += sunset; + json += "\",\"location_clouds\":\""; + json += location_clouds; + json += "\",\"location_vis\":\""; + json += location_vis; + json += "\",\"location_uv\":\""; + json += location_uv; + json += "\",\"location_weather_description\":\""; + json += location_weather_description; + json += "\",\"uuid\":\""; + json += uuid; + json += "\",\"api_key\":\""; + json += api_key; + json += "\",\"sensors_ID0\":\""; + json += sensors_ID0; + json += "\",\"sensors_ID1\":\""; + json += sensors_ID1; + json += "\",\"sensors_ID2\":\""; + json += sensors_ID2; + json += "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void handle_ConfigSetupJson(){ + if(isNotVerified()) { + return; + } + String json = "{"; + json += getJsonHeaderPart(); + json += "\",\"kuOn\":\""; + json += kuOn; + json += "\",\"kuOff\":\""; + json += kuOff; + json += "\",\"timeDay\":\""; + json += timeDay; + json += "\",\"volBrightnessD\":\""; + json += volBrightnessD; + json += "\",\"volBrightnessN\":\""; + json += volBrightnessN; + json += "\",\"timeNight\":\""; + json += timeNight; + json += "\",\"timeScrollSpeed\":\""; + json += 100 - timeScrollSpeed; + json += "\",\"clockNight\":\""; + json += (clockNight==1?"checked":""); + json += "\",\"volBrightnessAuto\":\""; + json += (volBrightnessAuto==1?"checked":""); + json += "\",\"lowLivelBrightness\":\""; + json += lowLivelBrightness; + json += "\",\"upLivelBrightness\":\""; + json += upLivelBrightness; + json += "\",\"lba\":\""; + json += levelBridhtness; + json += "\",\"isActiveBuzzer\":\""; + json += (isActiveBuzzer == 1 ? "checked" : ""); + json += "\",\"sensor00\":\""; + json += sensore[0]; + json += "\",\"corr00\":\""; + json += corr00; + float Td=data00; + if(param0==20){ + if(pressSys==1 && Td>815) Td /= 1.3332239; + if(pressSys!=1 && Td<815) Td /= 0.7500615613026439; + } + json += "\",\"Td\":\""; + json += Td; + json += "\",\"sensor01\":\""; + json += sensore[1]; + json += "\",\"corr01\":\""; + json += corr01; + float Tu=data01; + if(param1==20){ + if(pressSys==1 && Tu>815) Tu /= 1.3332239; + if(pressSys!=1 && Tu<815) Tu /= 0.7500615613026439; + } + json += "\",\"Tu\":\""; + json += Tu; + json += "\",\"sensor02\":\""; + json += sensore[2]; + json += "\",\"corr02\":\""; + json += corr02; + float Th=data02; + if(param2==20){ + if(pressSys==1 && Th>815) Th /= 1.3332239; + if(pressSys!=1 && Th<815) Th /= 0.7500615613026439; + } + json += "\",\"Th\":\""; + json += Th; + json += "\",\"sensor03\":\""; + json += sensore[3]; + json += "\",\"corr03\":\""; + json += corr03; + float Hd=data03; + if(param3==20){ + if(pressSys==1 && Hd>815) Hd /= 1.3332239; + if(pressSys!=1 && Hd<815) Hd /= 0.7500615613026439; + } + json += "\",\"Hd\":\""; + json += Hd; + json += "\",\"sensor04\":\""; + json += sensore[4]; + json += "\",\"corr04\":\""; + json += corr04; + float Pu=data04; + if(param4==20){ + if(pressSys==1 && Pu>815) Pu /= 1.3332239; + if(pressSys!=1 && Pu<815) Pu /= 0.7500615613026439; + } + json += "\",\"Pu\":\""; + json += Pu; + json += "\",\"pressSys\":\""; + json += pressSys; + json += "\",\"param0\":\""; + json += param0; + json += "\",\"param1\":\""; + json += param1; + json += "\",\"param2\":\""; + json += param2; + json += "\",\"param3\":\""; + json += param3; + json += "\",\"param4\":\""; + json += param4; + json += "\",\"MAX7219_ROTATE\":\""; + json += MAX7219_ROTATE; + json += "\",\"MAX7219_NUM\":\""; + json += MAX7219_NUM; + json += "\",\"fontCLOCK\":\""; + json += fontCLOCK; + json += "\",\"animPoint\":\""; + json += animPoint; + json += "\",\"aliData\":\""; + json += aliData; + json += "\",\"butStat\":\""; + json += butStat; + json += "\",\"displayData\":\""; + json += displayData; + json += "\",\"function00\":\""; + json += function[0]; + json += "\",\"function01\":\""; + json += function[1]; + json += "\",\"function02\":\""; + json += function[2]; + json += "\",\"function03\":\""; + json += function[3]; + json += "\",\"function04\":\""; + json += function[4]; + json += "\",\"function05\":\""; + json += function[5]; + json += "\",\"function06\":\""; + json += function[6]; + json += "\",\"function07\":\""; + json += function[7]; + json += "\",\"function08\":\""; + json += function[8]; + json += "\",\"function09\":\""; + json += function[9]; + json += "\",\"function10\":\""; + json += function[10]; + json += "\",\"function11\":\""; + json += function[11]; + json += "\",\"period00\":\""; + json += period[0]; + json += "\",\"period01\":\""; + json += period[1]; + json += "\",\"period02\":\""; + json += period[2]; + json += "\",\"period03\":\""; + json += period[3]; + json += "\",\"period04\":\""; + json += period[4]; + json += "\",\"period05\":\""; + json += period[5]; + json += "\",\"period06\":\""; + json += period[6]; + json += "\",\"period07\":\""; + json += period[7]; + json += "\",\"period08\":\""; + json += period[8]; + json += "\",\"period09\":\""; + json += period[9]; + json += "\",\"period10\":\""; + json += period[10]; + json += "\",\"period11\":\""; + json += period[11]; + json += "\",\"sgp\":\""; + if(sgpFound){ + json += "SGP30"; + } else json += ""; + json += "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void handle_ConfigSgpJson() { + if(isNotVerified()) { + return; + } + String json = "{" + + getJsonHeaderPart() + + "\",\"sgpCo2\":\"" + + sgpCo2 + + "\",\"textCo2\":\"" + + sgpCo2Message[sgpValues.co2Livel] + + "\",\"sgpCo2LivelAlarm\":\"" + + sgpCo2LivelAlarm + + "\",\"eCo2AlarmEsp\":\"" + + (eCo2AlarmEsp==1?"checked":"") + + "\",\"eCo2Led\":\"" + + (eCo2Led==1?"checked":"") + + "\",\"sgpTvoc\":\"" + + sgpTvoc + + "\",\"textTvoc\":\"" + + sgpTvocMessage[sgpValues.tvocLivel] + + "\",\"sgpTvocLivelAlarm\":\"" + + sgpTvocLivelAlarm + + "\",\"tvocAlarmEsp\":\"" + + (tvocAlarmEsp==1?"checked":"") + + "\",\"tvocLed\":\"" + + (tvocLed==1?"checked":"") + + "\",\"setSgpCorr\":\"" + + setSgpCorr + + "\",\"sgpCorrTemp\":\"" + + sgpCorrTemp + + "\",\"sgpCorrHumi\":\"" + + sgpCorrHumi + + "\"}"; + server.send(200, "text/json", json); +} + +//====================================================================================================== +void handle_Set_Ssid(){ + if(isNotVerified()) { + return; + } + if(server.arg("ssid0")!="") ssid[0] = server.arg("ssid0").c_str(); + password[0] = server.arg("password0").c_str(); + ssid[1] = server.arg("ssid1").c_str(); + password[1] = server.arg("password1").c_str(); + if(server.arg("ssidAP")!="") ssidAP = server.arg("ssidAP").c_str(); + passwordAP = server.arg("passwordAP").c_str(); + if(server.arg("auth")!="") auth = server.arg("auth").c_str(); + if(server.arg("authOn")!="") authOn = server.arg("authOn").toInt(); + saveConfig(); + if(printCom) { + printTime(); + Serial.println("Set ssid0: " + ssid[0] + ", Set password0: " + password[0] + ", ssid1: " + ssid[1] + ", password1: " + password[1] + ", ssidAP: " + ssidAP + ", AP password: " + passwordAP + ", Set auth: " + auth + ", Set authOn: " + authOn); + } + server.send(200, "text/plain", "OK"); + ESP.reset(); +} + +void handle_ntp() { + if(isNotVerified()) { + return; + } + if(server.arg("ntpServerName")!="") { + ntpServerName = server.arg("ntpServerName").c_str(); + } + if(server.arg("timeZone")!="") { + timeZone = server.arg("timeZone").toFloat(); + } + if(server.arg("isDayLightSaving")!="") { + isDayLightSaving = server.arg("isDayLightSaving").toInt(); + } + if(server.arg("rtcStat")!="") { + rtcStat = server.arg("rtcStat").toInt(); + } +// if(server.arg("setTMes")!="") setTMes = server.arg("setTMes").toInt(); + if(server.arg("al_0_0")!="") { + alarme[0][0]=server.arg("al_0_0").toInt(); + } + if(server.arg("al_0_1")!="") { + alarme[0][1]=server.arg("al_0_1").toInt(); + } + if(server.arg("al_0_2")!="") { + alarme[0][2]=server.arg("al_0_2").toInt(); + } + if(server.arg("al_1_0")!="") { + alarme[1][0]=server.arg("al_1_0").toInt(); + } + if(server.arg("al_1_1")!="") { + alarme[1][1]=server.arg("al_1_1").toInt(); + } + if(server.arg("al_1_2")!="") { + alarme[1][2]=server.arg("al_1_2").toInt(); + } + if(server.arg("al_2_0")!="") { + alarme[2][0]=server.arg("al_2_0").toInt(); + } + if(server.arg("al_2_1")!="") { + alarme[2][1]=server.arg("al_2_1").toInt(); + } + if(server.arg("al_2_2")!="") { + alarme[2][2]=server.arg("al_2_2").toInt(); + } + if(server.arg("al_3_0")!="") { + alarme[3][0]=server.arg("al_3_0").toInt(); + } + if(server.arg("al_3_1")!="") { + alarme[3][1]=server.arg("al_3_1").toInt(); + } + if(server.arg("al_3_2")!="") { + alarme[3][2]=server.arg("al_3_2").toInt(); + } + if(server.arg("al_4_0")!="") { + alarme[4][0]=server.arg("al_4_0").toInt(); + } + if(server.arg("al_4_1")!="") { + alarme[4][1]=server.arg("al_4_1").toInt(); + } + if(server.arg("al_4_2")!="") { + alarme[4][2]=server.arg("al_4_2").toInt(); + } + if(printCom) { + printTime(); + Serial.println("Set NTP Server Name: " + ntpServerName + ", NTP Time Zone: " + String(timeZone) + ", isDayLightSaving: " + String(isDayLightSaving)); + } + alarm_hold = 0; + saveAlarm(); + timeUpdateNTP(); + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_set_time(){ + if(isNotVerified()) { + return; + } + if(server.arg("t0")!="") hour = server.arg("t0").toInt(); + if(server.arg("t1")!="") minute = server.arg("t1").toInt(); + if(server.arg("d0")!="") day = server.arg("d0").toInt(); + if(server.arg("d1")!="") month = server.arg("d1").toInt(); + if(server.arg("d2")!="") year = server.arg("d2").toInt(); + if(printCom) { + printTime(); + Serial.println("Set manual time: " + String(hour) + ":" + String(minute) + " " + String(day) + "-" + String(month) + "-" + String(year)); + } + server.send(200, "text/plain", "OK"); + + localEpoc = (hour * 60 * 60 + minute * 60); + showSimpleDate(); + + rtcStruct.hour = hour; + rtcStruct.minute = minute; + rtcStruct.second = 0; + rtcStruct.month = month; + rtcStruct.day = day; + rtcStruct.year = year; + printTime(); + setRTCDateTime(); +} + +//====================================================================================================== +void handle_timepc(){ + if(isNotVerified()) { + return; + } + if(server.arg("hours")!="") { + hour = server.arg("hours").toInt(); + } + if(server.arg("minute")!="") { + minute = server.arg("minute").toInt(); + } + if(server.arg("sec")!="") { + second = server.arg("sec").toInt(); + } + if(server.arg("tz")!="") { + timeZone = server.arg("tz").toFloat(); + } + if(server.arg("day")!="") { + day = server.arg("day").toInt(); + } + if(server.arg("month")!="") { + month = server.arg("month").toInt(); + } + if(server.arg("year")!="") { + year = server.arg("year").toInt(); + } + if(printCom) { + printTime(); + Serial.println("Set Date/Time from PC - "+String(day)+"."+String(month)+"."+String(year)+" "+String(hour)+":"+String(minute)+":"+String(second)+" timeZone="+String(timeZone)); + } + localMillisAtUpdate = millis(); + localEpoc = (hour * 60 * 60 + minute * 60 + second); + saveConfig(); + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void weather_update() { + if(displayForecast) { + if(!weatherHost) { + getWeatherData0(); + getWeatherDataz0(); + } else { + getWeatherData1(); + getWeatherDataz1(); + } + } +} + +//====================================================================================================== +void handle_weath_on() { + if(isNotVerified()) { + return; + } + if(server.arg("displayForecast")!="") { + displayForecast = server.arg("displayForecast").toInt(); + } + if(printCom) { + printTime(); + Serial.println("displayForecast = " + String(displayForecast)); + } + if(displayForecast) { + weather_update(); + } + saveConfig(); + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_weather_update() { + if(isNotVerified()) { + return; + } + if(server.arg("update") == "ok") { + weather_update(); + bip(); + server.send(200, "text/plain", "OK"); + } +} + +//====================================================================================================== +void handle_weather() { + if(server.arg("weatherHost")!="") { + weatherHost = server.arg("weatherHost").toInt(); + } + if(server.arg("weatherKey0")!="") { + weatherKey0 = server.arg("weatherKey0").c_str(); + } + if(server.arg("weatherKey1")!="") { + weatherKey1 = server.arg("weatherKey1").c_str(); + } + if(server.arg("cityID0")!="") { + cityID0 = server.arg("cityID0").c_str(); + } + if(server.arg("cityID1")!="") { + cityID1 = server.arg("cityID1").c_str(); + } + if(server.arg("weatherLang")!="") { + weatherLang = server.arg("weatherLang").c_str(); + } + if(server.arg("displayForecast")!="") { + displayForecast = server.arg("displayForecast").toInt(); + } + if(server.arg("displayCityName")!="") { + displayCityName = server.arg("displayCityName").toInt(); + } + if(server.arg("displayForecastNow")!="") { + displayForecastNow = server.arg("displayForecastNow").toInt(); + } + if(server.arg("displayForecastToday")!="") { + displayForecastToday = server.arg("displayForecastToday").toInt(); + } + if(server.arg("displayForecastTomorrow")!="") { + displayForecastTomorrow = server.arg("displayForecastTomorrow").toInt(); + } + if(server.arg("timeStartViewWeather")!="") { + timeStartViewWeather = server.arg("timeStartViewWeather").toInt(); + } + if(server.arg("timeEndViewWeather")!="") { + timeEndViewWeather = server.arg("timeEndViewWeather").toInt(); + } + if(server.arg("timeScrollSpeed")!="") { + timeScrollSpeed = 100 - server.arg("timeScrollSpeed").toInt(); + } + uuid = server.arg("uuid").c_str(); + api_key = server.arg("api_key").c_str(); + sensors_ID0 = server.arg("sensors_ID0").toInt(); + sensors_ID1 = server.arg("sensors_ID1").toInt(); + sensors_ID2 = server.arg("sensors_ID2").toInt(); + if(server.arg("personalCityName")!="") { + snprintf(personalCityName, 51, "%s", server.arg("personalCityName").c_str()); + } + if(printCom) { + printTime(); + Serial.print("Set Weather Server: "); + if(!weatherHost) { + Serial.print(weatherHost0); + } else { + Serial.print(weatherHost1); + } + Serial.println(", Key0: " + weatherKey0 + ", Key1: " + weatherKey1 + ", City ID0: " + cityID0 + ", City ID1: " + cityID1 + ", weatherLang: " + weatherLang); + Serial.println(" displayCityName: " + String(displayCityName) + ", displayForecastNow: " + String(displayForecastNow) + ", displayForecastTomorrow: " + String(displayForecastTomorrow) + ", personalCityName: " + String(personalCityName)); + Serial.println(" timeStartViewWeather: " + String(timeStartViewWeather) + ", timeEndViewWeather: " + String(timeEndViewWeather) + ", timeScrollSpeed: " + String(timeScrollSpeed) + ", uuid: " + String(uuid) + ", api_key: " + String(api_key) + ", sensors_ID0: " + String(sensors_ID0) + ", sensors_ID1: " + String(sensors_ID1) + ", sensors_ID2: " + String(sensors_ID2)); + } + + saveConfig(); + initLang(); + weather_update(); + + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_setup(){ + if(isNotVerified()) { + return; + } + if(server.arg("timeDay")!="") timeDay = server.arg("timeDay").toInt(); + if(server.arg("volBrightnessD")!="") volBrightnessD = server.arg("volBrightnessD").toInt(); + if(server.arg("timeNight")!="") timeNight = server.arg("timeNight").toInt(); + if(server.arg("volBrightnessN")!="") volBrightnessN = server.arg("volBrightnessN").toInt(); + if(server.arg("volBrightnessAuto")!="") volBrightnessAuto = server.arg("volBrightnessAuto").toInt(); + if(server.arg("lowLivelBrightness")!="") lowLivelBrightness = server.arg("lowLivelBrightness").toInt(); + if(server.arg("upLivelBrightness")!="") upLivelBrightness = server.arg("upLivelBrightness").toInt(); + if(server.arg("isActiveBuzzer")!="") isActiveBuzzer = server.arg("isActiveBuzzer").toInt(); + if(server.arg("clockNight")!="") clockNight = server.arg("clockNight").toInt(); + if(server.arg("MAX7219_ROTATE")!="") MAX7219_ROTATE = server.arg("MAX7219_ROTATE").toInt(); + if(server.arg("MAX7219_NUM")!="") MAX7219_NUM = server.arg("MAX7219_NUM").toInt(); + if(server.arg("kuOn")!="") kuOn = server.arg("kuOn").toInt(); + if(server.arg("kuOff")!="") kuOff = server.arg("kuOff").toInt(); + if(server.arg("sensor00")!="" && server.arg("param0")!=""){ + int sens = server.arg("sensor00").toInt(); + int param = server.arg("param0").toInt(); + if(sens==0||(sens==1&¶m>=0&¶m<5)||((sens==2||sens==5||sens==10)&¶m>=0&¶m<15)||(sens==3&&(param>=0&¶m<5)||param==20)||(sens==4&&((param>=0&¶m<15)||param==20))||(((sens>5&&sens<10)||(sens>10&&sens<19))&¶m<24)){ + sensore[0] = server.arg("sensor00").toInt(); + param0 = server.arg("param0").toInt(); + } + } + if(server.arg("sensor01")!="" && server.arg("param1")!=""){ + int sens = server.arg("sensor01").toInt(); + int param = server.arg("param1").toInt(); + if(sens==0||(sens==1&¶m>=0&¶m<5)||((sens==2||sens==5||sens==10)&¶m>=0&¶m<15)||(sens==3&&(param>=0&¶m<5)||param==20)||(sens==4&&((param>=0&¶m<15)||param==20))||(((sens>5&&sens<10)||(sens>10&&sens<19))&¶m<24)){ + sensore[1] = sens; + param1 = param; + } + } + if(server.arg("sensor02")!="" && server.arg("param2")!=""){ + int sens = server.arg("sensor02").toInt(); + int param = server.arg("param2").toInt(); + if(sens==0||(sens==1&¶m>=0&¶m<5)||((sens==2||sens==5||sens==10)&¶m>=0&¶m<15)||(sens==3&&(param>=0&¶m<5)||param==20)||(sens==4&&((param>=0&¶m<15)||param==20))||(((sens>5&&sens<10)||(sens>10&&sens<19))&¶m<24)){ + sensore[2] = server.arg("sensor02").toInt(); + param2 = server.arg("param2").toInt(); + } + } + if(server.arg("sensor03")!="" && server.arg("param3")!=""){ + int sens = server.arg("sensor03").toInt(); + int param = server.arg("param3").toInt(); + if(sens==0||(sens==1&¶m>=0&¶m<5)||((sens==2||sens==5||sens==10)&¶m>=0&¶m<15)||(sens==3&&(param>=0&¶m<5)||param==20)||(sens==4&&((param>=0&¶m<15)||param==20))||(((sens>5&&sens<10)||(sens>10&&sens<19))&¶m<24)||(sens==99&¶m==3)){ + sensore[3] = server.arg("sensor03").toInt(); + param3 = server.arg("param3").toInt(); + } + } + if(server.arg("sensor04")!="" && server.arg("param4")!=""){ + int sens = server.arg("sensor04").toInt(); + int param = server.arg("param4").toInt(); + if(sens==0||(sens==1&¶m>=0&¶m<5)||((sens==2||sens==5||sens==10)&¶m>=0&¶m<15)||(sens==3&&(param>=0&¶m<5)||param==20)||(sens==4&&((param>=0&¶m<15)||param==20))||(((sens>5&&sens<10)||(sens>10&&sens<19))&¶m<24)||(sens==99&¶m==21)){ + sensore[4] = server.arg("sensor04").toInt(); + param4 = server.arg("param4").toInt(); + } + } + if(server.arg("pressSys")!="") pressSys = server.arg("pressSys").toInt(); + if(server.arg("fontCLOCK")!="") fontCLOCK = server.arg("fontCLOCK").toInt(); + if(server.arg("animPoint")!="") animPoint = server.arg("animPoint").toInt(); + if(server.arg("aliData")!="") aliData = server.arg("aliData").toInt(); + if(server.arg("timeScrollSpeed")!="") timeScrollSpeed = 100 - server.arg("timeScrollSpeed").toInt(); + if(server.arg("corr00")!="") corr00 = server.arg("corr00").toFloat(); + if(server.arg("corr01")!="") corr01 = server.arg("corr01").toFloat(); + if(server.arg("corr02")!="") corr02 = server.arg("corr02").toFloat(); + if(server.arg("corr03")!="") corr03 = server.arg("corr03").toFloat(); + if(server.arg("corr04")!="") corr04 = server.arg("corr04").toInt(); + if(server.arg("displayData")!="") displayData = server.arg("displayData").toInt(); + if(server.arg("butStat")!="") butStat = server.arg("butStat").toInt(); + + if(server.arg("function00")!="") function[0] = server.arg("function00").toInt(); + if(server.arg("function01")!="") function[1] = server.arg("function01").toInt(); + if(server.arg("function02")!="") function[2] = server.arg("function02").toInt(); + if(server.arg("function03")!="") function[3] = server.arg("function03").toInt(); + if(server.arg("function04")!="") function[4] = server.arg("function04").toInt(); + if(server.arg("function05")!="") function[5] = server.arg("function05").toInt(); + if(server.arg("function06")!="") function[6] = server.arg("function06").toInt(); + if(server.arg("function07")!="") function[7] = server.arg("function07").toInt(); + if(server.arg("function08")!="") function[8] = server.arg("function08").toInt(); + if(server.arg("function09")!="") function[9] = server.arg("function09").toInt(); + if(server.arg("function10")!="") function[10] = server.arg("function10").toInt(); + if(server.arg("function11")!="") function[11] = server.arg("function11").toInt(); + + if(server.arg("period00")!="") period[0] = server.arg("period00").toInt(); + if(server.arg("period01")!="") period[1] = server.arg("period01").toInt(); + if(server.arg("period02")!="") period[2] = server.arg("period02").toInt(); + if(server.arg("period03")!="") period[3] = server.arg("period03").toInt(); + if(server.arg("period04")!="") period[4] = server.arg("period04").toInt(); + if(server.arg("period05")!="") period[5] = server.arg("period05").toInt(); + if(server.arg("period06")!="") period[6] = server.arg("period06").toInt(); + if(server.arg("period07")!="") period[7] = server.arg("period07").toInt(); + if(server.arg("period08")!="") period[8] = server.arg("period08").toInt(); + if(server.arg("period09")!="") period[9] = server.arg("period09").toInt(); + if(server.arg("period10")!="") period[10] = server.arg("period10").toInt(); + if(server.arg("period11")!="") period[11] = server.arg("period11").toInt(); + + if(printCom){ + printTime(); + Serial.println("timeDay: " + String(timeDay) + ", volBrightnessD: " + String(volBrightnessD) + ", timeNight: " + String(timeNight) + ", volBrightnessN: " + String(volBrightnessN) + ", kuOn: " + String(kuOn) + ", kuOff: " + String(kuOff) + ", MAX7219_ROTATE: " + String(MAX7219_ROTATE) + ", clockNight: " + String(clockNight) + ", isActiveBuzzer: "+String(isActiveBuzzer)); + Serial.println(" sensore[0]: "+String(sensore[0])+", sensore[1]: "+String(sensore[1])+", sensore[2]: "+String(sensore[2])+", sensore[3]: "+String(sensore[3])+", sensore[4]: "+String(sensore[4])); + Serial.println(" param0: "+String(param0)+", param1: "+String(param1)+", param2: "+String(param2)+", param3: "+String(param3)+", param4: "+String(param4)); + Serial.println(" func00: "+String(function[0])+", func01: "+String(function[1])+", func02: "+String(function[2])+", func03: "+String(function[3])+", func04: "+String(function[4])+", func05: "+String(function[5])+", func06: "+String(function[6])+", func07: "+String(function[7])+", func08: "+String(function[8])+", func09: "+String(function[9])+", func10: "+String(function[10])+", func11: "+String(function[11])); + Serial.println(" peri00: "+String(period[0])+", peri01: "+String(period[1])+", peri02: "+String(period[2])+", peri03: "+String(period[3])+", peri04: "+String(period[4])+", peri05: "+String(period[5])+", peri06: "+String(period[6])+", peri07: "+String(period[7])+", peri08: "+String(period[8])+", peri09: "+String(period[9])+", peri10: "+String(period[10])+", peri11: "+String(period[11])); + } + saveConfig(); + sensorsAll(); + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_sgp() { + if(isNotVerified()) { + return; + } + if(server.arg("eCo2Led")!="") { + sgpCo2 = server.arg("eCo2Led").toInt(); + } + if(server.arg("sgpCo2LivelAlarm")!="") { + sgpCo2LivelAlarm = server.arg("sgpCo2LivelAlarm").toInt(); + } + if(server.arg("eCo2AlarmEsp")!="") { + eCo2AlarmEsp = server.arg("eCo2AlarmEsp").toInt(); + } + if(server.arg("tvocLed")!="") { + tvocLed = server.arg("tvocLed").toInt(); + } + if(server.arg("sgpTvocLivelAlarm")!="") { + sgpTvocLivelAlarm = server.arg("sgpTvocLivelAlarm").toInt(); + } + if(server.arg("tvocAlarmEsp")!="") { + tvocAlarmEsp = server.arg("tvocAlarmEsp").toInt(); + } + if(server.arg("setSgpCorr")!="") { + setSgpCorr = server.arg("setSgpCorr").toInt(); + } + if(setSgpCorr == 99) { + if(server.arg("sgpCorrTemp")!="") { + sgpCorrTemp = server.arg("sgpCorrTemp").toFloat(); + } + if(server.arg("sgpCorrHumi")!="") { + sgpCorrHumi = server.arg("sgpCorrHumi").toFloat(); + } + } + if(printCom) { + printTime(); + Serial.println("Set eCo2Led: " + String(eCo2Led) + ", sgpCo2LivelAlarm: " + String(sgpCo2LivelAlarm) + ", eCo2AlarmEsp: " + String(eCo2AlarmEsp)); + Serial.print(" tvocLed: " + String(tvocLed) + ", sgpTvocLivelAlarm: " + String(sgpTvocLivelAlarm) + ", tvocAlarmEsp: " + String(tvocAlarmEsp)+ ", setSgpCorr: " + String(setSgpCorr)); + if(setSgpCorr == 99) { + if(printCom) { + Serial.println(", sgpCorrTemp: " + String(sgpCorrTemp) + ", sgpCorrMan: " + String(sgpCorrHumi)); + } + } else { + if(printCom) { + Serial.println(""); + } + } + } + + saveConfig(); + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handleMessage() { + if(isNotVerified()) { + return; + } + if(server.arg("text") != "") { + server.send(200, "text/plain", "OK"); + String text = server.arg("text").c_str(); + bip(3); + printStringWithShift((space + String(text) + space).c_str(), timeScrollSpeed); + if(printCom) { + printTime(); + Serial.println(text); + } + } +} + +//====================================================================================================== +void handle_stopAlarm(){ + if(isNotVerified()) { + return; + } + if(server.arg("stopAlarm")=="ok") { + if(alarm_stat) { + stopAlarm = true; + if(printCom) { + printTime(); + Serial.println("STOP ALARM"); + } + bip(); + } + } + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_resetConfig() { + if(isNotVerified()) { + return; + } + if(server.arg("device") == "ok") { + SPIFFS.remove("/config.json"); + if(printCom) { + printTime(); + Serial.println("ESP erase Config file"); + } + delay(3000); + server.send(200, "text/plain", "OK"); + delay(3000); + bip(); + ESP.reset(); + } +} + +//====================================================================================================== +void handle_set_printCom() { + if(isNotVerified()) { + return; + } + if(server.arg("printCom")!=""){ + printCom = server.arg("printCom").toInt(); + if(printCom){ + printTime(); + Serial.println("Set printCom: " + String(printCom)); + } + saveConfig(); + } + server.send(200, "text/plain", "OK"); +} + +//====================================================================================================== +void handle_Restart(){ + if(isNotVerified()) { + return; + } + if(server.arg("device") == "ok"){ + server.send(200, "text/plain", "OK"); + ESP.reset(); + } +} diff --git a/WiFi_Clock.ino b/WiFi_Clock.ino new file mode 100644 index 0000000..30f4be7 --- /dev/null +++ b/WiFi_Clock.ino @@ -0,0 +1,1570 @@ + +#define ver "0.1" + +#define DIN_PIN 13 //GPIO 13 / D7 +#define CS_PIN 15 //GPIO 15 / D8 +#define CLK_PIN 14 //GPIO 14 / D5 +#define buzzerPin 12 //GPIO 12 / D6 + +#define MAX_DIGITS 16 + +#define BUT_PIN 16 + +#define brightPin A0 + +// заглушки +void printTime(); +void printStringWithShift(const char* s, int shiftDelay); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Adafruit_SGP30.h" + +#include "globals.h" +#include "soundModule.h" +#include "max7219.h" +#include "fonts.h" +#include "rtc.h" + + +// WEB страницы +#include "P_js.h" +#include "P_css.h" +#include "P_index.h" +#include "P_time.h" +#include "P_weath.h" +#include "P_setup.h" +#include "P_sgp.h" +#include "P_help.h" +#include "P_auth.h" +#include "P_wifi.h" + +Ticker blinker; +ESP8266HTTPUpdateServer httpUpdater; +WiFiClient ESPclient; +ESP8266WebServer server(80); // Веб сервер +File fsUploadFile; +IPAddress apIP(192, 168, 4, 1); + +// ===================================================================================== +String ssid[2] = {"LocalWire"}; // Назва локального WiFi +String password[2] = {"12345678"}; // Пароль локального WiFi +String ssidAP = "WiFi-Clock"; // Назва точки доступу +String passwordAP = "11223344"; +String auth = "1234asdf5678"; + +// переменные сервера погоды +String weatherHost0 = "api.weatherbit.io"; +String weatherHost1 = "api.openweathermap.org"; +String weatherKey0 = ""; +String weatherKey1 = ""; +String cityID0 = "Kyiv"; +String cityID1 = "703448"; +boolean authOn = true; +boolean weatherHost = 0; +char personalCityName[51] = ""; +String weatherLang = "uk"; // Мова отримання прогнозу погоди +String location_name = ""; +String location_region = ""; +String location_country = ""; +String location_localtime = ""; +float location_temp; +float location_app_temp; +int location_rh; +float location_pres; +float location_wind_spd; +String location_wind_cdir_full = ""; +String location_sunrise = ""; +String location_sunset = ""; +int location_clouds; +int location_vis; +int location_uv; +String location_weather_description = ""; +String cityName; + +String weatherString; +String weatherStringZ; +bool animNotWeather = true; + +// -------------------------------------------- +String uuid = ""; +String api_key = ""; +int sensors_ID0 = 3300; //88733 Frankfurt +int sensors_ID1 = 0; //88459 Frankfurt +int sensors_ID2 = 0; +float nMon00 = 0.0; +float nMon01 = 0.0; +float nMon02 = 0.0; + +// ===================================================================================== +byte function[12] = {1, 2, 1, 3, 1, 4, 5, 6, 7, 8, 0, 0}; +byte period[12] = {10, 5, 10, 1, 10, 5, 5, 5, 5, 5, 0, 0}; +byte fnCount = 0; +byte oldCount = 0; +unsigned long fnTimer; +bool endString = true; + + +// индикаторы + +byte fontCLOCK = 8; // 0-крупный, 1-крупный цифровой, 2-полу жирный, 3-полу жирный цифровой, 4-обычный, 5-обычный цифровой, 6-узкий, 7-узкий цифровой. +byte animPoint = 4; +byte aliData = 8; +byte volBrightnessD = 8; +byte volBrightnessN = 2; +bool volBrightnessAuto = 0; +byte levelBridhtness = 0; +int lowLivelBrightness = 0; +int upLivelBrightness = 1023; +byte timeDay = 7; +byte timeNight = 21; + +byte timeStartViewWeather = 6; +byte timeEndViewWeather = 23; +byte timeScrollSpeed = 20; + +// ---------- Настройка оновлення часу +IPAddress timeServerIP; +String ntpServerName = "ntp3.time.in.ua"; +const int NTP_PACKET_SIZE = 48; +byte packetBuffer[NTP_PACKET_SIZE]; + + +bool stopAlarm = false; + +int pinDHT = 2; + +SimpleDHT22 dht22; + +boolean WIFI_connected = false; + + +WiFiUDP udp; +unsigned int localPort = 2390; +unsigned long epochNM; + +#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100)||!((1970+Y)%400))) // Високосні літа + +String jsonConfig = "{}"; +String jsonAlarm = "{}"; +String jsonTime = "{}"; + +// ---------- Змінні для роботи локального годинника +float timeZone = 2.0; // часовий пояс +float hourCorr; +bool isDayLightSaving = true; +int displayData = 1; // 0 - Не отображать, 1 - отображать статику, 2 - отображать в бегущей строке +long localEpoc = 0; +long localMillisAtUpdate = 0; + +int g_hour, g_minute, g_second, g_month = 1, g_day, g_dayOfWeek, g_year; + +bool statusUpdateNtpTime = 0; // якщо не "0" - то останнє оновленя часу було вдалим +String y, mon, wd, d, h, m, s, mes; +uint8_t hourTest[3], minuteTest[3]; + +String date; + +bool clockNight = 0; + +// ---------- +byte dig[MAX_DIGITS] = {0}; +byte digold[MAX_DIGITS] = {0}; +byte digtrans[MAX_DIGITS] = {0}; +int dx = 0; +int dy = 0; +byte del = 0; + +bool updateOTAEnable = 1; +// пароль обновления +String passwordOTA = "11223344"; + + +#include "sgpModule.h" + + +unsigned long minCount; +unsigned long minCount2; +unsigned long weatherCount; +unsigned long weatherZCount; +bool displayForecast = true; +bool displayCityName = true; // отображать название города +bool displayForecastNow = true; // отображать прогноз на сейчас +bool displayForecastToday = true; // отображать прогноз на сегодня +bool displayForecastTomorrow = true; // отображать прогноз на завтра +bool updateForecastNot = true; +int updateForecast = 0; +int updateForecasttomorrow = 0; + +float data00; // данные первого датчика +float data01; // данные второго датчика +float data02; // данные третьего датчика +float data03; // данные 4 датчика +float data04; // данные пятого датчика + +bool pressSys = 1; +byte humidity; // влажность для прогноза +float pressure; // давление для прогноза +float temp; // температура для прогноза + +bool ds18b20Found = false; +bool dhtFound = false; + +float tempDht = 0; +float humiDht = 0; + +float corr00 = 0; +float corr01 = 0; +float corr02 = 0; +float corr03 = 0; +float corr04 = 0; + +byte sensore[5] = {10,6,7,10,0}; //NONE=0, DS18B20=1, Si7021=2, BMP280=3, BME280=4, DHT=5, MQTT1=6, MQTT2=7, MQTT3=8; =9, AHTx0=10, THING1=11, THING2=12, THING3=13, THING4=14, THING5=15, nMon00=16, nMon00=17, nMon00=18,; +byte param0 = 0; // 0-темп.дом(tD), 1-темп.улица(tU), 2-темп.Н(tН), 3-(tT), 4-(tL) +byte param1 = 1; // 10-влажность1(hD), 11-(h1), 12-(h2), 13-(h3), 14-(h4) +byte param2 = 2; // 20-давление(P), 21-целое число(С) 22-батарейка(V), 23-батарейка(A) +byte param3 = 10; +byte param4 = 20; + + +//bool setTMes = true; +bool alarm_stat = 0; +bool alarm_hold = 0; +byte alarm_numer = 255; +byte alarme[5][3] {{12, 30, 0}, {7, 15, 0}, {22, 55, 0}, {0, 30, 0}, {0, 0, 0}}; //1-часы, 2-минуты, 3-откл(0)/1раз(11)/пон-пят(8)/пон-сб(9)/сб-вс(10)/вс(1)/пон(2)/вто(3)/сре(4)/чет(5)/пят(6)/сб(7)/всегда(12) + +bool firstStart = 0; +bool apStart=0; +byte amountNotStarts = 0; +String jsonLine = ""; + +bool rtcStat = false; + + + +byte errorRTC; +bool butStat = 0; +byte butMode = 0; // 0 - не нажата, 1 - нажата один раз, 2 - нажата два раза, 3 - 5 секунд нажата, 4 - 30 секунд нажата. +byte butFlag = 0; // 1 - кнопка нажата, 0 - не нажата +int butCount = 0; // счетчик времени нажатия кнопки +int butMillis = 0; +bool runningLine = 0; + +String tJanuary, tFebruary, tMarch, tApril, tMay, tJune, tJuly, tAugust, tSeptember, tOctober, tNovember, tDecember; +String tMonday, tTuesday, tWednesday, tThursday, tFriday, tSaturday, tSunday; + +String space = ""; +bool startLine = false; +String textLine; + +//====================================================================================== +void setup() { + Wire.begin(); + Serial.begin(115200); + if (printCom) { + Serial.println(""); + } + pinMode(BUT_PIN, INPUT); + digitalWrite(BUT_PIN, !butStat); + delay(500); + SPIFFS.begin(); + loadConfig(); + loadAlarm(); + + initLang(); + initMAX7219(); + sendCmdAll(CMD_SHUTDOWN, 1); + sendCmdAll(CMD_INTENSITY, 1); + + // задаем количество пробелов в зависимости от кол-ва знаков + for(int i = 0; i < MAX7219_NUM; i++) { + space += " "; + } + + Wire.beginTransmission(0x67); + errorRTC = Wire.endTransmission(); + if (errorRTC == 0) { + rtcAddr = 0x67; + if(printCom) { + Serial.println("YES!!! find RTC module addr: 0x67!"); + } + } else { + Wire.beginTransmission(0x68); + errorRTC = Wire.endTransmission(); + if(errorRTC == 0) { + rtcAddr = 0x68; + if(printCom) { + Serial.println("YES!!! find RTC module addr: 0x68!"); + } + } else { + rtcStat = false; + } + } + if(rtcStat) { + if(printCom) { + Serial.println("RTC START"); + } + getRTCDateTime(); + hour = rtcStruct.hour; + minute = rtcStruct.minute; + second = rtcStruct.second; + day = rtcStruct.day; + month = rtcStruct.month; + year = rtcStruct.year; + dayOfWeek = rtcStruct.dayOfWeek; + if (printCom) { + Serial.println("RTC update: " + String(hour) + ":" + String(minute) + ":" + String(second) + " " + String(day) + "." + String(month) + "." + String(year) + " D=" + String(dayOfWeek)); + } + } else if (printCom) { + Serial.println("RTC module off!"); + } + + // ------------------ + sensorsDht(); + + sensors(); + server_init(); + // ---------- + localMillisAtUpdate = millis(); + localEpoc = (hour * 60 * 60 + minute * 60 + second); + udp.begin(localPort); + pinMode(buzzerPin, OUTPUT); + // ---------- Підключення до WiFi + wifiConnect(); + blinker.attach(0.05, ledPrint); + // *********** OTA SETUP + + if(updateOTAEnable) { + ArduinoOTA.setPort(8266); + ArduinoOTA.setHostname("ESP-ZAL"); + //ArduinoOTA.setPassword((const char *)"123"); + ArduinoOTA.onEnd([](){ESP.restart();}); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR && printCom) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR && printCom) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR && printCom) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR && printCom) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR && printCom) { + Serial.println("End Failed"); + } + ESP.restart(); + }); + ArduinoOTA.begin(); + } + if (WiFi.status() == WL_CONNECTED) { + if (displayForecast) { + if (!weatherHost) { + getWeatherData0(); + getWeatherDataz0(); + } else { + getWeatherData1(); + getWeatherDataz1(); + } + } + } + + if(!sgp.begin()){ + if(printCom){ + Serial.println("Sensor SGP-30 not found :("); + } + sgpFound = false; + } else { + if(printCom){ + printTime(); + Serial.print("Found SGP30 serial #"); + Serial.print(sgp.serialnumber[0], HEX); + Serial.print(sgp.serialnumber[1], HEX); + Serial.println(sgp.serialnumber[2], HEX); + } + sgpFound = true; + } +} + +//====================================================================================== +void reconnect(){ + if(printCom) { + printTime(); + Serial.println("Start Reconnect void..."); + } + if(WiFi.status() == WL_CONNECTED) { // && !ESPclient.connected() + if(printCom) { + printTime(); + Serial.print("MQTT reconnection..."); + } + } +} +void ledPrint(){ + updateTime(); + if(endString){ + if(function[fnCount]==1) showAnimClock(); + if(!startLine){ + if(function[fnCount]==2){ + if(displayData==1){ + showSimpleDate(); + } else if(displayData==2 && hour>=timeStartViewWeather && hour=timeStartViewWeather && hour=timeDay && hour<=timeNight):(hour>=timeDay || hour 30 && updateForecast < 360) weatherString = space + tWeatrTN + " - " + String(updateForecast) + "мин." + space; + else if(updateForecast >= 360) weatherString = space + tWeatrNot + space; + textLine=weatherString; + if(updateForecasttomorrow<30) textLine+=weatherStringZ; + startLine=true; + } + //} else fnTimer=millis(); + } else fnTimer=millis(); + } else if(function[fnCount]>3 && function[fnCount]<9){ + if(sensore[function[fnCount]-4]){ + showSimple(function[fnCount]-4); + } else fnTimer=millis(); + } + } + } +} +//====================================================================================== +//====================================================================================== +void loop() { + // дозволяємо HTTP серверу відповідать на запити + server.handleClient(); + + if(updateOTAEnable) { + ArduinoOTA.handle(); + } + + if(fnTimer < millis() && endString) { + fnCount++; + if(fnCount >= 12) { + fnCount=0; + } + fnTimer = millis() + (period[fnCount] * 1000); + } + if(startLine) { + printStringWithShift(textLine.c_str(), timeScrollSpeed); + startLine = false; + } + + buttonInter(); + + // сигнал кожду годину + checkNeedHourSound(); + if(secFr==0 && second==10 && !alarm_stat) { + sensorsAll(); + } + //----------- РОБОТА З БУДИЛЬНИКОМ------------------------------------------------------ + if(secFr==0) { + if(second>0 && alarms()){ + if(!alarm_stat && alarm_numer!=255 && !alarm_hold) alarm_stat=1; + } else if(alarm_stat){ + alarm_stat=0; + if(alarme[alarm_numer][2]==11) alarme[alarm_numer][2]=0; + } else if(alarm_hold!=0); + } + if(alarm_stat){ + if(secFr==0 && second>1 && second<=59){ + invert(); + refreshAll(); + bip(); + bip(); + } + } + //------------- РОБОТА З ЯСКРАВІСТЮ ЕКРАНУ -------------------------------- + if(secFr==0){ + if(volBrightnessAuto){ + //levelBridhtness = map(analogRead(brightPin), 1023, 0, 0, 15); + int br=analogRead(brightPin); + if(lowLivelBrightness<=upLivelBrightness){ + if(brupLivelBrightness) upLivelBrightness=br; + } else{ + if(brlowLivelBrightness) lowLivelBrightness=br; + } + levelBridhtness=map(br,lowLivelBrightness, upLivelBrightness, volBrightnessN, volBrightnessD); + sendCmdAll(CMD_INTENSITY, levelBridhtness); + } else{ + if(hour>=timeDay && hour 0 ? 1 : -1)) % 10; + int znak = znakT % 10; + byte digPos[10] {3, 4, 5, 6, 17, 18, 19, 20, 21, 22}; + byte indent = aliData * (MAX7219_NUM - 4); + dx = dy = 0; + clr(); + showDigit((temp0 < 0.0 ? digPos[znak*2+1]:digPos[znak*2]), indent, znaki5x8); // друкуємо D+ альбо D- + if(temp1 <= -10.0 || temp1 >= 10) showDigit((temp1 < 0 ? (temp1 * -1) / 10 : temp1 / 10), 4 + indent, dig5x8); + showDigit((temp1 < 0 ? (temp1 * -1) % 10 : temp1 % 10), 10 + indent, dig5x8); + showDigit(2, 16 + indent, znaki5x8); + showDigit(temp2, 18 + indent, dig5x8); + showDigit(0, 24 + indent, znaki5x8); + showDigit(1, 27 + indent, znaki5x8); + refreshAll(); +} + +//==========ВИВІД НА ЕКРАН ВОЛОГОСТІ======================================== +void showSimpleHumidity(byte znakT, float humi0){ + if(humi0>0){ + int humi1 = int(humi0); + int humi2 = int(humi0*10*(humi0>0?1:-1))%10; + int znak = znakT%10; + byte digPos[5] {7, 23, 24, 25, 26}; + byte indent = aliData * (MAX7219_NUM - 4); + dx = dy = 0; + clr(); + showDigit(digPos[znak], indent, znaki5x8); // друкуємо знак вологості + if (humi1 >= 10) showDigit(humi1/10, 6 + indent, dig5x8); + showDigit((humi1-(humi1/10)*10), 12 + indent, dig5x8); + showDigit(2, 18 + indent, znaki5x8); + showDigit(humi2, 20 + indent, dig5x8); + showDigit(8, 26 + indent, znaki5x8); + refreshAll(); + } +} + +//==========ВИВІД НА ЕКРАН ТИСКУ======================================== +void showSimplePressure(byte znakT, float press0){ + if(pressSys==1 && press0>815) press0 /= 1.3332239; + if(pressSys!=1 && press0<815) press0 /= 0.7500615613026439; + if(press0 > 0){ + int press1 = (int) press0 / 1000; + int press2 = ((int) press0 - press1 * 1000) / 100; + int press3 = ((int) press0 - press1 * 1000 - press2 * 100) / 10; + int press4 = (int)press0 % 10; + byte indent = aliData * (MAX7219_NUM - 4); + dx = dy = 0; + clr(); + showDigit(9, 0 + indent, znaki5x8); // друкуємо знак тиску + if(press1 > 0) showDigit(press1, 5 + indent, dig5x8); + showDigit(press2, (press1 > 0 ? 10 : 6) + indent, dig5x8); + showDigit(press3, (press1 > 0 ? 16 : 12) + indent, dig5x8); + showDigit(press4, (press1 > 0 ? 22 : 18) + indent, dig5x8); + showDigit((pressSys == 1 ? 10 : 15), (press1 > 0 ? 28 : 24) + indent, znaki5x8); + showDigit((pressSys == 1 ? 11 : 16), (press1 > 0 ? (pressSys == 1 ? 33 : 32) : (pressSys == 1 ? 29 : 28)) + indent, znaki5x8); + refreshAll(); + } +} + +//==========ВИВІД НА ЕКРАН ДОДАТКОВИХ ДАННИХ======================================== +void showSimpleNumeric(byte znakT, float numer0) { + byte indent = aliData * (MAX7219_NUM - 4); + dx = dy = 0; + clr(); + showDigit((numer0 < 0.0 ? 32 : 31), 0 + indent, znaki5x8); + float numer1 = numer0 * (numer0 >= 0 ? 1 : -1); + if (numer1 >= 10000) { + showDigit((int)numer1 % 10, 28 + indent, dig4x8); + showDigit((int)(numer1 / 10) % 10, 23 + indent, dig4x8); + showDigit((int)(numer1 / 100) % 10, 18 + indent, dig4x8); + showDigit((int)(numer1 / 1000) % 10, 13 + indent, dig4x8); + showDigit((int)(numer1 / 10000) % 10, 8 + indent, dig4x8); + } else if (numer1 >= 1000) { + showDigit((int)(numer1 * 10) % 10, 28 + indent, dig4x8); + showDigit(2, 26 + indent, znaki5x8); + showDigit((int)numer1 % 10, 21 + indent, dig4x8); + showDigit((int)(numer1 / 10) % 10, 16 + indent, dig4x8); + showDigit((int)(numer1 / 100) % 10, 11 + indent, dig4x8); + showDigit((int)(numer1 / 1000) % 10, 6 + indent, dig4x8); + } else { + showDigit((int)(numer1 * 100) % 10, 28 + indent, dig4x8); + showDigit((int)(numer1 * 10) % 10, 23 + indent, dig4x8); + showDigit(2, 21 + indent, znaki5x8); + showDigit((int)numer1 % 10, 16 + indent, dig4x8); + if (numer1 >= 10) showDigit((int)(numer1 / 10) % 10, 11 + indent, dig4x8); + if (numer1 >= 100) showDigit((int)(numer1 / 100) % 10, 6 + indent, dig4x8); + } + refreshAll(); +} + +//==========ВИВІД НА ЕКРАН ДАТИ========================================================= +void showSimpleDate() { + bool nonsens = false; + byte digPos[8] {0, 5, 10, 12, 17, 22, 23, 28}; + if(year % 10 == 1) { + digPos[6]++; + if(month%10 == 1) { + digPos[3]++; + digPos[2]++; + digPos[1]++; + digPos[0]++; + } + if(month / 10 == 1) { + digPos[2]++; + digPos[1]++; + digPos[0]++; + } + if(day % 10 == 1) { + digPos[0]++; + } + } else { + if(month % 10 == 1) { + digPos[5]--; + digPos[4]--; + } else if(month / 10 == 1) { + digPos[5]--; + digPos[4]--; + digPos[3]--; + } + if(month % 10 == 1 && month / 10 == 1) { + digPos[2]++; + digPos[1]++; + digPos[0]++; + } + if(month % 10 != 1 && month / 10 != 1 && (day % 10 == 1 || day / 10 == 1)) { + digPos[5]--; + digPos[4]--; + digPos[3]--; + digPos[2]--; + digPos[1]--; + } else if(day % 10 == 1) { + digPos[0]++; + } + if(month % 10 != 1 && month / 10 != 1 && day % 10 != 1 && day / 10 == 1) { + nonsens = true; + } + } + byte indent = aliData * (MAX7219_NUM - 4); + dx = dy = 0; + clr(); + showDigit(nonsens?10:day / 10, digPos[0] + indent, dig4x8); + showDigit(day % 10, digPos[1] + indent, dig4x8); + showDigit(2, digPos[2] + indent, znaki5x8); + showDigit(month / 10, digPos[3] + indent, dig4x8); + showDigit(month % 10, digPos[4] + indent, dig4x8); + showDigit(2, digPos[5] + indent, znaki5x8); + showDigit((year - 2000) / 10, digPos[6] + indent, dig4x8); + showDigit((year - 2000) % 10, digPos[7] + indent, dig4x8); + refreshAll(); +} + +//==========ВИВІД НА ЕКРАН АНІМАЦІЙНОГО ГОДИННИКА======================================= +void showAnimClock() { + if(fontCLOCK > 7) { + showAnimClock2(); + return; + } + byte indent = (hour < 10 ? 12 : 15) + 4 * (MAX7219_NUM - 4); + byte digPos[5] = {(indent-(fontCLOCK<2?14:fontCLOCK<6?12:10)), (indent-(fontCLOCK<2?7:fontCLOCK<6?6:5)), (indent+3), (indent+(fontCLOCK<2?10:fontCLOCK<6?9:8)), indent}; + int digHt = 16; + int num=hour<10?1:0; + int i; + if(del==0){ + del=digHt; + for(i=num; i<4; i++) digold[i]=dig[i]; + dig[0]=hour/10; + dig[1]=hour%10; + dig[2]=minute/10; + dig[3]=minute%10; + for(i=num; i<4; i++) digtrans[i]=(dig[i]==digold[i])?0:digHt; + } else del--; + clr(); + for(i=num; i<4; i++){ + if(digtrans[i]==0){ + dy=0; + if(fontCLOCK==0) showDigit(dig[i], digPos[i], dig6x8); + else if(fontCLOCK==1) showDigit(dig[i], digPos[i], dig6x8dig); + else if(fontCLOCK==2) showDigit(dig[i], digPos[i], dig5x8rn); + else if(fontCLOCK==3) showDigit(dig[i], digPos[i], dig5x8rndig); + else if(fontCLOCK==4) showDigit(dig[i], digPos[i], dig5x8); + else if(fontCLOCK==5) showDigit(dig[i], digPos[i], dig5x8dig); + else if(fontCLOCK==6) showDigit(dig[i], digPos[i], dig4x8); + else if(fontCLOCK==7) showDigit(dig[i], digPos[i], dig4x8dig); + } else{ + dy=digHt-digtrans[i]; + if(fontCLOCK==0) showDigit(digold[i], digPos[i], dig6x8); + else if(fontCLOCK==1) showDigit(digold[i], digPos[i], dig6x8dig); + else if(fontCLOCK==2) showDigit(digold[i], digPos[i], dig5x8rn); + else if(fontCLOCK==3) showDigit(digold[i], digPos[i], dig5x8rndig); + else if(fontCLOCK==4) showDigit(digold[i], digPos[i], dig5x8); + else if(fontCLOCK==5) showDigit(digold[i], digPos[i], dig5x8dig); + else if(fontCLOCK==6) showDigit(digold[i], digPos[i], dig4x8); + else if(fontCLOCK==7) showDigit(digold[i], digPos[i], dig4x8dig); + dy=-digtrans[i]; + if(fontCLOCK==0) showDigit(dig[i], digPos[i], dig6x8); + else if(fontCLOCK==1) showDigit(dig[i], digPos[i], dig6x8dig); + else if(fontCLOCK==2) showDigit(dig[i], digPos[i], dig5x8rn); + else if(fontCLOCK==3) showDigit(dig[i], digPos[i], dig5x8rndig); + else if(fontCLOCK==4) showDigit(dig[i], digPos[i], dig5x8); + else if(fontCLOCK==5) showDigit(dig[i], digPos[i], dig5x8dig); + else if(fontCLOCK==6) showDigit(dig[i], digPos[i], dig4x8); + else if(fontCLOCK==7) showDigit(dig[i], digPos[i], dig4x8dig); + digtrans[i]--; + } + } + dy=0; + int flash=millis()%2000; + if(animPoint%2) flash=flash%1000; + else flash=flash/2; + if (!alarm_stat){ + if(!WIFI_connected){ + if(flash<500){ + setCol(digPos[4], 0xC0); setCol(digPos[4]+1, 0xC0); // полная точка + } + } else if(!statusUpdateNtpTime){ + if(flash<149){ + setCol(digPos[4], 0x66); setCol(digPos[4]+1, 0x66); + } + } else if(animPoint<3){ // Простая + if(flash<499 || animPoint==0){ + setCol(digPos[4], 0x66); setCol(digPos[4]+1, 0x66); + } + } else if(animPoint==3 || animPoint==4){ // MAX1 + if(flash<500){ + setCol(digPos[4], 0x62); setCol(digPos[4]+1, 0x62); + } else{ + setCol(digPos[4], 0x46); setCol(digPos[4]+1, 0x46); + } + } else if(animPoint==5 || animPoint==6){ // MAX2 + if((flash>=200 && flash<400) || flash>=600){ + setCol(digPos[4], 0x66); setCol(digPos[4]+1, 0x66); + } + if(flash>=0 && flash<200){ + setCol(digPos[4], 0x24); setCol(digPos[4]+1, 0x24); + } + if(flash>=400 && flash<600){ + setCol(digPos[4], 0x42); setCol(digPos[4]+1, 0x42); + } + } else if(animPoint==7 || animPoint==8){ // Мерцание + if((flash>=(animPoint==5?180:200) && flash<(animPoint==5?360:400)) || flash>=(animPoint==5?540:600)){ + setCol(digPos[4], 0x66); setCol(digPos[4]+1, 0x66); + } + if(flash>=0 && flash<(animPoint==5?180:200)){ + setCol(digPos[4], 0x24); setCol(digPos[4]+1, 0x42); + } + if(flash>=(animPoint==5?360:400) && flash<(animPoint==5?540:600)){ + setCol(digPos[4], 0x42); setCol(digPos[4]+1, 0x24); + } + } else if(animPoint==9 || animPoint==10){ // Вращение + if(flash<250){ + setCol(digPos[4], 0x06); setCol(digPos[4]+1, 0x60); + } else if(flash>=250 && flash<500){ + setCol(digPos[4], 0x42); setCol(digPos[4]+1, 0x42); + } else if(flash>=500 && flash<750){ + setCol(digPos[4], 0x60); setCol(digPos[4]+1, 0x06); + } else if(flash>=750){ + setCol(digPos[4], 0x24); setCol(digPos[4]+1, 0x24); + } + } + if(displayForecast && updateForecast && WIFI_connected) { + setCol(00, flash<500?0x80:0x00); + } + if(displayForecastTomorrow && displayForecast && updateForecasttomorrow && WIFI_connected) { + setCol((MAX7219_NUM*8-1), flash < 500 ? 0x80 : 0x00); + } + } else { + setCol(digPos[4], 0x66); + setCol(digPos[4]+1, 0x66); + } + refreshAll(); +} + +//========================================================================================= +void showAnimClock2() { + int num = hour < 10 ? 1 : 0; + int indent = 4 * (MAX7219_NUM - 4) / 2 - (1 * num); + byte digPos[6] = {0, 6, 13, 19, 25, 29}; + int digHt=16; + int i; + if(del==0){ + del=digHt; + for(i=num; i<4; i++) digold[i]=dig[i]; + dig[0]=hour/10; + dig[1]=hour%10; + dig[2]=minute/10; + dig[3]=minute%10; + for(i=num; i<4; i++) digtrans[i]=(dig[i]==digold[i])?0:digHt; + } else del--; + clr(); + for(i=num; i<4; i++){ + if(digtrans[i]==0){ + dy=0; + showDigit(dig[i], digPos[i]+indent-(i==1&&num?2:0), fontCLOCK==8?dig5x8sec:dig5x7sec); + } else{ + dy=digHt-digtrans[i]; + showDigit(digold[i], digPos[i]+indent-(i==1&&num?2:0), fontCLOCK==8?dig5x8sec:dig5x7sec); + dy=-digtrans[i]; + showDigit(dig[i], digPos[i]+indent-(i==1&&num?2:0), fontCLOCK==8?dig5x8sec:dig5x7sec); + digtrans[i]--; + } + } + dy=0; + int flash=millis()%2000; + flash=flash/2; + if (!alarm_stat) { + if(flash<500) { + setCol(11+indent-(num?1:0), 0x80); + } else { + setCol(12+indent-(num?1:0), 0x80); + } + if(displayForecast && updateForecast && WIFI_connected) { + setCol(00, flash<500?0x80:0x00); + } + if(displayForecastTomorrow && displayForecast && updateForecasttomorrow && WIFI_connected) { + setCol((MAX7219_NUM*8-1), flash<500?0x80:0x00); + } + } else { + setCol(11+indent-(num?1:0), 0x80); + setCol(12+indent-(num?1:0), 0x80); + } + showDigit((second/10)%10, digPos[4]+indent+(num?1:0), fontCLOCK==8?dig3x7:dig3x6); + showDigit(second%10, digPos[5]+indent+(num?1:0), fontCLOCK==8?dig3x7:dig3x6); + refreshAll(); +} + +//================================================= +void showAnimWifi(byte probaWifi) { + byte digPos[2] = {18, 25}; + int digHt = 16; + int num = 2; + int ii; + if (del == 0) { + del = digHt; + for (ii = 0; ii < num; ii++) digold[ii] = dig[ii]; + dig[0] = probaWifi / 10; + dig[1] = probaWifi % 10; + for (ii = 0; ii < num; ii++) digtrans[ii] = (dig[ii] == digold[ii]) ? 0 : digHt; + } else del--; + clr(); + for (ii = 0; ii < num; ii++) { + if (digtrans[ii] == 0) { + dy = 0; + showDigit(dig[ii], digPos[ii], dig6x8); + } else { + dy = digHt - digtrans[ii]; + showDigit(digold[ii], digPos[ii], dig6x8); + dy = - digtrans[ii]; + showDigit(dig[ii], digPos[ii], dig6x8); + digtrans[ii]--; + } + } + dy = 0; + refreshAll(); +} + +//==========ДРУКУВАННЯ БІГУЧОЇ СТРОКИ *s - текст, shiftDelay - швидкість========================================== +void printStringWithShift(const char* s, int shiftDelay) { + endString=false; + while(*s){ // коли працює ця функція, основний цикл зупиняється + printCharWithShift(*s, shiftDelay); + s++; + if (updateOTAEnable) { + ArduinoOTA.handle(); + } + server.handleClient(); // зберігаемо можливість відповіді на HTML запити під час бігучої стоки + buttonInter(); + if (butMode != 0) { + clr(); + refreshAll(); + runningLine = 0; + return; + } + } + endString=true; +} + +//==========ДРУКУВАННЯ БІГУЧОГО СИМВОЛУ с - символ, shiftDelay - швидкість===================================== +void printCharWithShift(unsigned char c, int shiftDelay) { + c = convert_UA_RU_PL_DE(c); + if (c < ' ') return; + c -= 32; + int w = showChar(c, fontUA_RU_PL_DE); + for (int i = 0; i < w + 1; i++) { + delay(shiftDelay); + scrollLeft(); + refreshAll(); + } +} + +//====================================================================================== +int showChar(char ch, const uint8_t *data) { + int len = pgm_read_byte(data); + int i, w = pgm_read_byte(data + 1 + ch * len); + for (i = 0; i < w; i++) + scr[MAX7219_NUM * 8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i); + scr[MAX7219_NUM * 8 + i] = 0; + return w; +} + +//====================================================================================== +void showDigit(char ch, int col, const uint8_t *data) { + if (dy < -8 | dy > 8) return; + int len = pgm_read_byte(data); + int w = pgm_read_byte(data + 1 + ch * len); + col += dx; + for (int i = 0; i < w; i++) { + if (col + i >= 0 && col + i < 8 * MAX7219_NUM) { + byte v = pgm_read_byte(data + 1 + ch * len + 1 + i); + if (!dy) scr[col + i] = v; else scr[col + i] |= dy > 0 ? v >> dy : v << -dy; + } + } +} + +//====================================================================================== +void setCol(int col, byte v) { + if (dy < -8 | dy > 8) return; + col += dx; + if (col >= 0 && col < 8 * MAX7219_NUM) { + if (!dy) scr[col] = v; else scr[col] |= dy > 0 ? v >> dy : v << -dy; + } +} + +//========== +void saveChrMas(String string_t, byte lenght_off, byte number_s) { + byte lenght = string_t.length(); + if (lenght > lenght_off) return; + const char *s = string_t.c_str(); + + for (int i = 0; i < lenght; i++) { + //snprintf(*memory_date_mes[number_s], 1, "%s", *s); + s++; + } +} + +//==========ОНОВЛЕННЯ ЛОКАЛЬНОГО ЧАСУ (ЛОКАЛЬНІ ЧАСИ)=============================================================== +void updateTime(){ + long curEpoch=localEpoc+((millis()-localMillisAtUpdate)/1000); + long epoch=round(curEpoch+86400L); + epoch=(epoch%86400L); + hour=((epoch%86400L)/3600)%24; + minute=(epoch%3600)/60; + second=epoch%60; + if(second!=lastSecond) { + // на початку нової секунди скидаємо secFr в "0" + lastSecond=second; + secFr=0; + if(lastMinute!=minute){ + if(hour==0 && minute==0 && second==0){ + day++; + if(day==32 || (day==31 && (month==4 || month==6 || month==9 || month==11)) || (month==2 && ((day==29 && year%4!=0) || (day==30 && year%4==0)))){ + day=1; + month++; + if(month>12){ + month=1; + year++; + } + } + dayOfWeek++; + if(dayOfWeek>7) dayOfWeek=1; + } + lastMinute=minute; + } + } else secFr++; +} + +//==========ОНОВЛЕННЯ МЕРЕЖЕВОГО ЧАСУ (перевірка в три проходи)==================================================== +void timeUpdateNTP() { + if (!WIFI_connected) { + return; + } + if (printCom) { + printTime(); + } + statusUpdateNtpTime = 1; + for (int timeTest = 0; timeTest < 3; timeTest++) { + getNTPtime(); + if (printCom) { + if (timeTest) Serial.print(" "); + Serial.println("Proba #" + String(timeTest + 1) + " " + String(g_hour) + ":" + ((g_minute < 10) ? "0" : "") + String(g_minute) + ":" + ((g_second < 10) ? "0" : "") + String(g_second)); + } + // updateTime(); + + hourTest[timeTest] = g_hour; + minuteTest[timeTest] = (g_minute || (g_minute == 59 ? 0 : g_minute++)); + if (statusUpdateNtpTime == 0) { + if (printCom) { + printTime(); + Serial.print("ERROR TIME!!!\r\n"); + } + return; + } + if (timeTest > 0) { + if ((hourTest[timeTest] != hourTest[timeTest - 1] || minuteTest[timeTest] != minuteTest[timeTest - 1])) { + statusUpdateNtpTime = 0; + if (printCom) { + printTime(); + Serial.print("ERROR TIME!!!\r\n"); + } + return; + } + } + } + hour = g_hour; + minute = g_minute; + second = g_second; + day = g_day; + dayOfWeek = g_dayOfWeek; + month = g_month; + year = g_year; + if (rtcStat) { + rtcStruct.hour = hour; + rtcStruct.minute = minute; + rtcStruct.second = 0; + rtcStruct.year = year; + rtcStruct.month = month; + rtcStruct.day = day; + rtcStruct.dayOfWeek = dayOfWeek; + setRTCDateTime(); + } + localMillisAtUpdate = millis(); + localEpoc = (hour * 60 * 60 + minute * 60 + second); +// saveTime(); + if (printCom) { + printTime(); + Serial.println((day < 10 ? "0" : "") + String(day) + "." + (month < 10 ? "0" : "") + String(month) + "." + String(year) + " DW = " + String(dayOfWeek)); + Serial.println(" Time update OK."); + } +} + +//==========ОТРИМАННЯ ДАТИ ТА ЧАСУ ВІД СЕРВЕРА ТОЧНОГО ЧАСУ ============================================================= +void getNTPtime() { + WiFi.hostByName(ntpServerName.c_str(), timeServerIP); + int cb; + for (int i = 0; i < 3; i++) { + memset(packetBuffer, 0, NTP_PACKET_SIZE); + packetBuffer[0] = 0b11100011; + packetBuffer[1] = 0; + packetBuffer[2] = 6; + packetBuffer[3] = 0xEC; + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + udp.beginPacket(timeServerIP, 123); //NTP порт 123 + udp.write(packetBuffer, NTP_PACKET_SIZE); + udp.endPacket(); + delay(800); // чекаємо пів секуни + cb = udp.parsePacket(); + if (!cb && printCom) Serial.println(" no packet yet..." + String (i + 1)); + if (!cb && i == 2) { // якщо час не отримано + statusUpdateNtpTime = 0; + return; // вихіз з getNTPtime() + } + if (cb) i = 3; + } + if (cb) { // якщо отримали пакет з серверу + udp.read(packetBuffer, NTP_PACKET_SIZE); + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + unsigned long secsSince1900 = highWord << 16 | lowWord; + const unsigned long seventyYears = 2208988800UL; // Unix час станом на 1 січня 1970. в секундах, то 2208988800: + unsigned long epoch = secsSince1900 - seventyYears; + epochNM = epoch - (millis() / 1000); + boolean summerTime; + if (month < 3 || month > 10) summerTime = false; // не переходимо на літній час в січні, лютому, листопаді і грудню + if (month > 3 && month < 10) summerTime = true; // Sommerzeit лічимо в квіні, травні, червені, липні, серпені, вересені + if (month == 3 && (hour + 24 * day) >= (3 + 24 * (31 - (5 * year / 4 + 4) % 7)) || month == 10 && (hour + 24 * day) < (3 + 24 * (31 - (5 * year / 4 + 1) % 7))) summerTime = true; + epoch += (int)(timeZone * 3600 + (3600 * (isDayLightSaving && summerTime))); + hourCorr = timeZone + (isDayLightSaving && summerTime); + g_year = 0; + int days = 0; + uint32_t time; + time = epoch / 86400; + g_hour = (epoch % 86400L) / 3600; + g_minute = (epoch % 3600) / 60; + g_second = epoch % 60; + g_dayOfWeek = (((time) + 4) % 7) + 1; + while ((unsigned)(days += (LEAP_YEAR(g_year) ? 366 : 365)) <= time) { + g_year++; + } + days -= LEAP_YEAR(g_year) ? 366 : 365; + time -= days; + days = 0; + g_month = 0; + uint8_t monthLength = 0; + for (g_month = 0; g_month < 12; g_month++) { + if (g_month == 1) { + if (LEAP_YEAR(g_year)) monthLength = 29; + else monthLength = 28; + } + else monthLength = monthDays[g_month]; + if (time >= monthLength) time -= monthLength; + else break; + } + g_month++; + g_day = time + 1; + g_year += 1970; + return; + } + if (printCom) Serial.println("Nie ma czasu((("); +} + +//========================================================================================================= +void wifiConnect() { + if (printCom) { + printTime(); + Serial.print("Connecting WiFi (ssid0=" + String(ssid[0].c_str()) + " pass0=" + String(password[0].c_str()) + ") "); + } + if(WiFi.status() == WL_CONNECTED){ + WIFI_connected = true; + if(printCom) Serial.print(" IP adress : "); + if(printCom) Serial.println(WiFi.localIP()); + firstStart=1; + timeUpdateNTP(); + amountNotStarts=0; + return; + } + if (!firstStart) printStringWithShift("WiFi", 15); + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid[0].c_str(), password[0].c_str()); + for (int i = 1; i < 21; i++) { + if (WiFi.status() == WL_CONNECTED){ + WiFi.setAutoConnect(true); + WiFi.setAutoReconnect(true); + WIFI_connected = true; + if(printCom) Serial.print(" IP adress : "); + if(printCom) Serial.println(WiFi.localIP()); + if(!firstStart){ + String aaa=WiFi.localIP().toString() + space; + clr(); + printStringWithShift(" IP: ", 15); + printStringWithShift(aaa.c_str(), 25); + } + firstStart=1; + timeUpdateNTP(); + amountNotStarts=0; + return; + } + if(printCom) Serial.print("."); + if(!firstStart){ + int j=0; + while (j<500){ + if(j%10==0) showAnimWifi(i); + j++; + delay(1); + } + } + delay (800); + } + if (printCom) { + printTime(); + Serial.print("/nConnecting WiFi (ssid1=" + String(ssid[1].c_str()) + " pass1=" + String(password[1].c_str()) + ") "); + } + if (!firstStart) printStringWithShift("WiFi", 15); + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid[1].c_str(), password[1].c_str()); + for (int i = 1; i < 21; i++) { + if (WiFi.status() == WL_CONNECTED) { + WIFI_connected = true; + if(printCom) Serial.print(" IP adress : "); + if(printCom) Serial.println(WiFi.localIP()); + if(!firstStart){ + String aaa=WiFi.localIP().toString() + space; + clr(); + printStringWithShift(" IP: ", 15); + printStringWithShift(aaa.c_str(), 25); + } + firstStart=1; + timeUpdateNTP(); + amountNotStarts=0; + return; + } + if(printCom) Serial.print("."); + if(!firstStart){ + int j=0; + while (j<500){ + if(j%10==0) showAnimWifi(i); + j++; + delay(1); + } + } + delay (800); + } + WiFi.disconnect(); + if (printCom) Serial.println(" Not connected!!!"); + amountNotStarts++; + if (printCom) { + Serial.print("Amount of the unsuccessful connecting = "); + Serial.println(amountNotStarts); + } + if (amountNotStarts > 21) { + amountNotStarts = 0; + firstStart = 0; + ESP.reset(); + } + if (!firstStart) { + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); + WiFi.softAP(ssidAP.c_str(), passwordAP.c_str()); + if (printCom) { + printTime(); + Serial.println("Start AP mode!!!"); + Serial.print(" Wifi AP IP : "); + Serial.println(WiFi.softAPIP()); + } + updateTime(); + String aaa = tPoint + " " + ssidAP; + if(passwordAP != "") aaa += ", " + tPass + ": " + passwordAP; + aaa += ", " + tIp + ": 192.168.4.1"; + clr(); + printStringWithShift(aaa.c_str(), 35); + //firstStart=1; + } +} + +//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void sensorsAll() { + if(printCom) Serial.println("======== START GET SENSORS DATA ======================="); + sensorsDht(); + sensors(); + if(printCom) Serial.println("======== END =========================================="); +} + +//------------------------------------------------------------------------- +void sensors() { + data00 = (sensore[0]==0?0:sensore[0]==5?(param0>4?humiDht:tempDht):0); + if(sensore[0]) { + data00 += corr00; + } + data01 = (sensore[1]==0?0:sensore[1]==5?(param1>4?humiDht:tempDht):0); + if(sensore[1]) { + data01 += corr01; + } + if(printCom){ + if(sensore[0]){ + printTime(); + Serial.println("sensore[0](+corr) = " + String(data00)); + } + if(sensore[1]){ + printTime(); + Serial.println("sensore[1](+corr) = " + String(data01)); + } + } + if(setSgpCorr){ + if(setSgpCorr==4 && dhtFound){ + sgpCorrTemp = tempDht; + sgpCorrHumi = humiDht; + } + } + + if((sensore[0]==5 && sensore[2]==5 && dhtFound)){ + if(param4==21&&sensore[4]==99){ + float absH=(6.112*(pow(2.718281828,(17.67*data00)/(data00+243.5)))*data02*2.1674)/(273.15+data00); + if(printCom){ + printTime(); + Serial.println("Absolute Humi = " + String(absH) + "g/m3"); + } + data04=absH; + } + if(param3==3&&sensore[3]==99){ + float ans=(data00-(14.55+0.114*data00)*(1-(0.01*data02))-pow(((2.5+0.007*data00)*(1-(0.01*data02))),3)-(15.9+0.117*data00)*pow((1-(0.01*data02)),14)); + if(printCom){ + printTime(); + Serial.println("Dew point = " + String(ans) + "*C"); + } + data03=ans; + } + } +} + +//-------------------------------------------------------------------------- +void sensorsDht() { //5 + dhtFound=false; + int err = SimpleDHTErrSuccess; + int err2 = SimpleDHTErrSuccess; + byte temp11 = 0; + byte humi11 = 0; + if((err2 = dht22.read2(pinDHT, &tempDht, &humiDht, NULL)) == SimpleDHTErrSuccess) { + humiDht = int(humiDht); + if(printCom) { + printTime(); + Serial.println("Temperature DHT22: " + String(tempDht) + " *C, Humidity: " + String(humiDht) + " %"); + } + dhtFound=true; + } +} + +void printTime() { + if (printCom) { + Serial.print((hour < 10 ? "0" : "") + String(hour) + ":" + (minute < 10 ? "0" : "") + String(minute) + ":" + (second < 10 ? "0" : "") + String(second) + " "); + } +} + +//-------------------------------------------------------------------------- +byte alarms() { + for (byte i = 0; i < 5; i++) { + if (alarme[i][0] == hour && alarme[i][1] == minute && (alarme[i][2] == dayOfWeek || (alarme[i][2] == 8 && (dayOfWeek > 1 && dayOfWeek < 7)) || (alarme[i][2] == 9 && dayOfWeek > 1) || (alarme[i][2] == 10 && (dayOfWeek == 1 || dayOfWeek == 7)) || alarme[i][2] > 10)) { + alarm_numer = i; + return 1; + } + } + alarm_numer = 255; + return 0; +} + +//------------ function urlencode for weather parameters -------------------- +String urlencode(String str) { // функция взята от http://forum.amperka.ru/members/benben.19545/ + String encodedString = ""; + char c; + char code0; + char code1; + for (int i = 0; i < str.length(); i++) { + c = str.charAt(i); + if (c == ' ') { + encodedString += '+'; + } else if (isalnum(c)) { + encodedString += c; + } else { + code1 = (c & 0xf) + '0'; + if ((c & 0xf) > 9) { + code1 = (c & 0xf) - 10 + 'A'; + } + c = (c >> 4) & 0xf; + code0 = c + '0'; + if (c > 9) { + code0 = c - 10 + 'A'; + } + encodedString += '%'; + encodedString += code0; + encodedString += code1; + } + yield(); + } + return encodedString; +} + +//------------ function chr_to_str -------------------- +String chr_to_str(String str) { + String chr_to_str = ""; + for (int i = 0; i < str.length(); i++) { + chr_to_str += str.charAt(i); + } + return chr_to_str; +} + +//--------------------------------------------------------------------------- +void buttonInter() { + if (digitalRead(BUT_PIN) == butStat && butCount == 0 && butFlag == 0 && butMode == 0) { + butCount = millis(); + butFlag = 1; + } + if ((millis() - butCount) >= 30000 && butFlag == 1 && butMode == 0) { + butMode = 4; + butFlag = 0; + butCount = 0; + } + if (digitalRead(BUT_PIN) == !butStat && (millis() - butCount) >= 10000 && butFlag == 1 && butMode == 0) { + butMode = 3; + butFlag = 0; + butCount = 0; + } + if (digitalRead(BUT_PIN) == !butStat && (millis() - butCount) < 10000 && (millis() - butCount) > 800 && (butFlag == 1 || butFlag == 2) && butMode == 0) { + butMode = 1; + butFlag = 0; + butCount = 0; + } + if (digitalRead(BUT_PIN) == !butStat && (millis() - butCount) <= 800 && butFlag == 1 && butMode == 0) { + butFlag = 2; + } + if (digitalRead(BUT_PIN) == butStat && (millis() - butCount) <= 800 && butFlag == 2) { + butMode = 2; + butFlag = 0; + butCount = 0; + } +} + +//---------------------------------------------------------------- +void buttonHandling() { + if (alarm_stat && (butMode || stopAlarm)) { // если будильник работает, то любое нажатие выключает его + alarm_stat = 0; + alarm_hold = 1; + stopAlarm = false; + butMode = 0; + if (alarme[alarm_numer][2] == 11) { + alarme[alarm_numer][2] = 0; + } + } + if (butMode == 4) { // если кнопка нажата была более 30 секунд то возврат к заводским установкам + butMode = 0; + Serial.println("Ta to jest KAPUT!!!!!!"); + bip(); + bip(); + bip(); + bip(); + SPIFFS.remove("/config.json"); + if (printCom) { + printTime(); + Serial.println("ESP erase Config file"); + } + delay(3000); + ESP.reset(); + } + if (butMode == 3) { // если кнопка была нажата более 10 секунд но менее 30, то будет рестарт часов + butMode = 0; + Serial.println("Reset ESP!!!"); + bip(); + bip(); + bip(); + ESP.reset(); + } + if (butMode == 1) { + bip(); + clr(); + refreshAll(); + if(sensore[0]) { + showSimple(0); + delay(1500); + } + if(sensore[1]) { + showSimple(1); + delay(1500); + } + if(sensore[2]) { + showSimple(2); + delay(1500); + } + if(sensore[3]) { + showSimple(3); + delay(1500); + } + if(sensore[4]) { + showSimple(4); + delay(1500); + } + butMode = 0; + clr(); + refreshAll(); + } + if (butMode == 2) { // При двойном нажатии на кнопку выводится прогноз погоды + bip(); + bip(); + butMode = 0; + clr(); + refreshAll(); + printStringWithShift(weatherString.c_str(), timeScrollSpeed); + printStringWithShift(weatherStringZ.c_str(), timeScrollSpeed); + clr(); + refreshAll(); + } +} diff --git a/fileSystem.ino b/fileSystem.ino new file mode 100644 index 0000000..43dca32 --- /dev/null +++ b/fileSystem.ino @@ -0,0 +1,121 @@ +//====================================== Тут функції для роботи з файловою системою +String getContentType(String filename) { + if (server.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm") || filename.endsWith(".html") || filename.endsWith(".vz")) { + return "text/html"; + } else if (filename.endsWith(".json")) { + return "application/json"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; +} + +//======================================= Читання файлу +bool handleFileRead(String path) { + if (path.endsWith("/")) path += "index.htm"; + String contentType = getContentType(path); + String pathWithGz = path + ".gz"; + if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { + if (SPIFFS.exists(pathWithGz)) + path += ".gz"; + File file = SPIFFS.open(path, "r"); + size_t sent = server.streamFile(file, contentType); + file.close(); + return true; + } + return false; +} + +//======================================== Завантаження файлу +void handleFileUpload() { + if (server.uri() != "/edit") return; + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + String filename = upload.filename; + if (!filename.startsWith("/")) filename = "/" + filename; + fsUploadFile = SPIFFS.open(filename, "w"); + filename = String(); + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (fsUploadFile) + fsUploadFile.write(upload.buf, upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + if (fsUploadFile) + fsUploadFile.close(); + } +} + +//======================================== Видалення файлу +void handleFileDelete() { + if (server.args() == 0) return server.send(500, "text/plain", "BAD ARGS"); + String path = server.arg(0); + if (path == "/") + return server.send(500, "text/plain", "BAD PATH"); + if (!SPIFFS.exists(path)) + return server.send(404, "text/plain", "FileNotFound"); + SPIFFS.remove(path); + server.send(200, "text/plain", ""); + path = String(); +} + +//========================================= Створення файлу +void handleFileCreate() { + if (server.args() == 0) + return server.send(500, "text/plain", "BAD ARGS"); + String path = server.arg(0); + if (path == "/") + return server.send(500, "text/plain", "BAD PATH"); + if (SPIFFS.exists(path)) + return server.send(500, "text/plain", "FILE EXISTS"); + File file = SPIFFS.open(path, "w"); + if (file) + file.close(); + else + return server.send(500, "text/plain", "CREATE FAILED"); + server.send(200, "text/plain", ""); + path = String(); +} + +//========================================== Список файлів +void handleFileList() { + if (!server.hasArg("dir")){ + server.send(500, "text/plain", "BAD ARGS"); + return; + } + String path = server.arg("dir"); + Dir dir = SPIFFS.openDir(path); + path = String(); + String output = "["; + while (dir.next()) { + File entry = dir.openFile("r"); + if (output != "[") output += ','; + bool isDir = false; + output += "{\"type\":\""; + output += (isDir) ? "dir" : "file"; + output += "\",\"name\":\""; + output += String(entry.name()).substring(1); + output += "\"}"; + entry.close(); + } + output += "]"; + server.send(200, "text/json", output); +} diff --git a/fonts.h b/fonts.h new file mode 100644 index 0000000..040053a --- /dev/null +++ b/fonts.h @@ -0,0 +1,418 @@ +const uint8_t znaki5x8[] PROGMEM = { 6, +0x02, 0x03,0x03,0x00,0x00,0x00, // градус +0x05, 0x7E,0x81,0x81,0xc3,0x42, // C +0x01, 0xC0,0x00,0x00,0x00,0x00, // . +0x03, 0xF2,0x97,0x62,0x00,0x00, // +D +0x03, 0xF2,0x92,0x62,0x00,0x00, // -D +0x03, 0xB2,0xA7,0x72,0x00,0x00, // +U +0x03, 0xB2,0xA2,0x72,0x00,0x00, // -U +0x05, 0x78,0x84,0x83,0x84,0x78, // кропля +0x05, 0x83,0x63,0x18,0xC6,0xC1, // % +0x05, 0x88,0x90,0xbf,0x90,0x88, // тиск +0x04, 0xff,0x08,0x08,0xff,0x00, // H +0x03, 0x98,0xa4,0x78,0x00,0x00, // g +0x05, 0x20,0xBC,0x7E,0x3C,0x20, // колокол л +0x05, 0x20,0x3C,0x7E,0xBC,0x20, // колокол п +0x02, 0xC0,0xC0,0x00,0x00,0x00, // . жирная +0x05, 0x7F,0x08,0x70,0x00,0x00, // h +0x05, 0x7f,0x09,0x09,0x06,0x00, // P +0x03, 0xF2,0x27,0xF2,0x00,0x00, // +H +0x03, 0xF2,0x22,0xF2,0x00,0x00, // -H +0x03, 0x12,0xF7,0x12,0x00,0x00, // +T +0x03, 0x12,0xF2,0x12,0x00,0x00, // -T +0x03, 0x72,0x47,0x42,0x00,0x00, // +L +0x03, 0xF2,0x82,0x82,0x00,0x00, // -L +0x05, 0x78,0xFC,0xFF,0xFC,0x78, // кропля1 (полная) +0x05, 0x78,0xE3,0xE4,0xE3,0x78, // кропля2 (низ) +0x05, 0x78,0xFC,0xFF,0x84,0x78, // кропля3 (лева) +0x05, 0x78,0xFC,0xC7,0xFC,0x78, // кропля4 (центр) +0x05, 0xFE,0xF3,0xF3,0xF3,0xFE, // батарейка +0x05, 0xE8,0xCC,0xAA,0x19,0x08, // молния +0x05, 0x3F,0x40,0x80,0x40,0x3F, // вольты +0x05, 0xFC,0x12,0x11,0x12,0xFC, // амперы +0x03, 0x62,0x97,0x92,0x00,0x00, // +C +0x03, 0x62,0x92,0x92,0x00,0x00, // -C +}; +//========================================== +const uint8_t dig3x6[] PROGMEM = { 4, +0x03, 0x3C, 0x42, 0x3C, //0 +0x03, 0x44, 0x7E, 0x40, //1 +0x03, 0x64, 0x52, 0x4C, //2 +0x03, 0x22, 0x4A, 0x34, //3 +0x03, 0x1E, 0x10, 0x7E, //4 +0x03, 0x2E, 0x4A, 0x32, //5 +0x03, 0x3C, 0x4A, 0x32, //6 +0x03, 0x62, 0x12, 0x0E, //7 +0x03, 0x34, 0x4A, 0x34, //8 +0x03, 0x4C, 0x52, 0x3C //9 +}; +//========================================== +const uint8_t dig3x7[] PROGMEM = { 4, +0x03, 0x7C, 0x82, 0x7C, //0 +0x03, 0x84, 0xFE, 0x80, //1 +0x03, 0xE4, 0x92, 0x8C, //2 +0x03, 0x44, 0x92, 0x6C, //3 +0x03, 0x1E, 0x10, 0xFE, //4 +0x03, 0x4E, 0x8A, 0x72, //5 +0x03, 0x7C, 0x92, 0x64, //6 +0x03, 0xE2, 0x12, 0x0E, //7 +0x03, 0x6C, 0x92, 0x6C, //8 +0x03, 0x4C, 0x92, 0x7C //9 +}; +//========================================== +const uint8_t dig4x8[] PROGMEM = { 5, +0x04, 0x7E,0x81,0x81,0x7E, +0x04, 0x00,0x82,0xFF,0x80, +0x04, 0xC2,0xA1,0x91,0x8E, +0x04, 0x42,0x89,0x89,0x76, +0x04, 0x1C,0x12,0x11,0xFF, +0x04, 0x4F,0x89,0x89,0x71, +0x04, 0x7E,0x89,0x89,0x72, +0x04, 0x01,0xF1,0x09,0x07, +0x04, 0x76,0x89,0x89,0x76, +0x04, 0x46,0x89,0x89,0x7E +}; +//========================================== +const uint8_t dig4x8dig[] PROGMEM = { 5, +0x04, 0xFF,0x81,0x81,0xFF, +0x04, 0x00,0x00,0xFF,0x00, +0x04, 0xF9,0x89,0x89,0x8F, +0x04, 0x81,0x89,0x89,0xFF, +0x04, 0x0F,0x08,0x08,0xFF, +0x04, 0x8F,0x89,0x89,0xF9, +0x04, 0xFF,0x89,0x89,0xF9, +0x04, 0x01,0x01,0x01,0xFF, +0x04, 0xFF,0x89,0x89,0xFF, +0x04, 0x8F,0x89,0x89,0xFF +}; +//====================================================================================================== +const uint8_t dig5x8[] PROGMEM = { 6, +0x05, 0x7E,0x81,0x81,0x81,0x7E, //0 +0x05, 0x00,0x82,0xFF,0x80,0x00, //1 +0x05, 0xE2,0x91,0x91,0x89,0xC6, //2 +0x05, 0x42,0x89,0x89,0x89,0x76, //3 +0x05, 0x38,0x24,0x22,0xF1,0x20, //4 +0x05, 0x47,0x89,0x89,0x89,0x71, //5 +0x05, 0x7E,0x89,0x89,0x89,0x72, //6 +0x05, 0x01,0x01,0xF1,0x09,0x07, //7 +0x05, 0x76,0x89,0x89,0x89,0x76, //8 +0x05, 0x46,0x89,0x89,0x89,0x7E //9 +}; +//========================================== +const uint8_t dig5x8dig[] PROGMEM = { 6, +0x05, 0xFF,0x81,0x81,0x81,0xFF, +0x05, 0x00,0x00,0x00,0xFF,0x00, +0x05, 0xF9,0x89,0x89,0x89,0x8F, +0x05, 0x89,0x89,0x89,0x89,0xFF, +0x05, 0x0F,0x08,0x08,0x08,0xFF, +0x05, 0x8F,0x89,0x89,0x89,0xF9, +0x05, 0xFF,0x89,0x89,0x89,0xF9, +0x05, 0x01,0x01,0x01,0x01,0xFF, +0x05, 0xFF,0x89,0x89,0x89,0xFF, +0x05, 0x8F,0x89,0x89,0x89,0xFF +}; +//=========================================== +const uint8_t dig5x8rn[] PROGMEM = { 6, +0x05, 0x7E, 0x81, 0x81, 0xFF, 0x7E, //0 +0x05, 0x04, 0x02, 0xFF, 0xFF, 0x00, //1 +0x05, 0xF1, 0x89, 0x89, 0x8F, 0x86, //2 +0x05, 0x81, 0x89, 0x89, 0xFF, 0x76, //3 +0x05, 0x1F, 0x10, 0x10, 0xFE, 0xFE, //4 +0x05, 0x8F, 0x89, 0x89, 0xF9, 0x71, //5 +0x05, 0x7E, 0x89, 0x89, 0xF9, 0x70, //6 +0x05, 0x01, 0xC1, 0xF1, 0x3F, 0x0F, //7 +0x05, 0x76, 0x89, 0x89, 0xFF, 0x76, //8 +0x05, 0x0E, 0x91, 0x91, 0xFF, 0x7E //9 +}; +//========================================== +const uint8_t dig5x8rndig[] PROGMEM = { 6, +0x05, 0xFF,0x81,0x81,0xFF,0xFF, //0 +0x05, 0x00,0x00,0xFF,0xFF,0x00, //1 +0x05, 0xF9,0x89,0x89,0x8F,0x8F, //2 +0x05, 0x89,0x89,0x89,0xFF,0xFF, //3 +0x05, 0x0F,0x08,0x08,0xFF,0xFF, //4 +0x05, 0x8F,0x89,0x89,0xF9,0xF9, //5 +0x05, 0xFF,0x89,0x89,0xF9,0xF9, //6 +0x05, 0x01,0x01,0x01,0xFF,0xFF, //7 +0x05, 0xFF,0x89,0x89,0xFF,0xFF, //8 +0x05, 0x8F,0x89,0x89,0xFF,0xFF //9 +}; +//====================================================================================================== +const uint8_t dig5x7sec[] PROGMEM = { 6, +0x05, 0x3E,0x7F,0x41,0x7F,0x3E, // 0 +0x05, 0x00,0x42,0x7F,0x7F,0x40, // 1 +0x05, 0x62,0x73,0x59,0x4F,0x46, // 2 +0x05, 0x22,0x63,0x49,0x7F,0x36, // 3 +0x05, 0x18,0x1C,0x16,0x7F,0x7F, // 4 +0x05, 0x2F,0x6F,0x49,0x79,0x31, // 5 +0x05, 0x3E,0x7F,0x49,0x7B,0x32, // 6 +0x05, 0x01,0x71,0x79,0x0F,0x07, // 7 +0x05, 0x36,0x7F,0x49,0x7F,0x36, // 8 +0x05, 0x26,0x6F,0x49,0x7F,0x3E // 9 +}; +//====================================================================================================== +const uint8_t dig5x8sec[] PROGMEM = { 6, +0x05, 0x7E,0xFF,0x81,0xFF,0x7E, // 0 +0x05, 0x00,0x82,0xFF,0xFF,0x80, // 1 +0x05, 0xC2,0xE3,0xB1,0x9F,0x8E, // 2 +0x05, 0x42,0xC3,0x89,0xFF,0x76, // 3 +0x05, 0x38,0x3C,0x26,0xFF,0xFF, // 4 +0x05, 0x4F,0xCF,0x89,0xF9,0x71, // 5 +0x05, 0x7E,0xFF,0x89,0xFB,0x72, // 6 +0x05, 0x01,0xE1,0xF1,0x1F,0x0F, // 7 +0x05, 0x76,0xFF,0x89,0xFF,0x76, // 8 +0x05, 0x4E,0xDF,0x91,0xFF,0x7E // 9 +}; +//====================================================================================================== +const uint8_t dig6x8[] PROGMEM = { 7, +0x06, 0x7E,0xFF,0x81,0x81,0xFF,0x7E, +0x06, 0x00,0x82,0xFF,0xFF,0x80,0x00, +0x06, 0xC2,0xE3,0xB1,0x99,0x8F,0x86, +0x06, 0x42,0xC3,0x89,0x89,0xFF,0x76, +0x06, 0x38,0x3C,0x26,0x23,0xFF,0xFF, +0x06, 0x4F,0xCF,0x89,0x89,0xF9,0x71, +0x06, 0x7E,0xFF,0x89,0x89,0xFB,0x72, +0x06, 0x01,0x01,0xF1,0xF9,0x0F,0x07, +0x06, 0x76,0xFF,0x89,0x89,0xFF,0x76, +0x06, 0x4E,0xDF,0x91,0x91,0xFF,0x7E +}; +//========================================== +const uint8_t dig6x8dig[] PROGMEM = { 7, +0x06, 0xFF,0xFF,0x81,0x81,0xFF,0xFF, +0x06, 0x00,0x00,0xFF,0xFF,0x00,0x00, +0x06, 0xF9,0xF9,0x89,0x89,0x8F,0x8F, +0x06, 0x89,0x89,0x89,0x89,0xFF,0xFF, +0x06, 0x0F,0x0F,0x08,0x08,0xFF,0xFF, +0x06, 0x8F,0x8F,0x89,0x89,0xF9,0xF9, +0x06, 0xFF,0xFF,0x89,0x89,0xF9,0xF9, +0x06, 0x01,0x01,0x01,0x01,0xFF,0xFF, +0x06, 0xFF,0xFF,0x89,0x89,0xFF,0xFF, +0x06, 0x8F,0x8F,0x89,0x89,0xFF,0xFF +}; +//====================================================================================================== +const uint8_t fontUA_RU_PL_DE[] PROGMEM = {8, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 - 'Space' + 0x01, 0x5f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, // 33 - '!' + 0x03, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, // 34 - '"' + 0x05, 0x14, 0x7f, 0x14, 0x7f, 0x14, 0x00, 0x00, // 35 - '#' + 0x05, 0x24, 0x2a, 0x7f, 0x2a, 0x12, 0x00, 0x00, // 36 - '$' + 0x05, 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, 0x00, // 37 - '%' + 0x05, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x00, // 38 - '&' + 0x03, 0x08, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, // 39 - ''' + 0x03, 0x1c, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00, // 40 - '(' + 0x03, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, // 41 - ')' + 0x05, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, 0x00, 0x00, // 42 - '*' + 0x05, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, // 43 - '+' + 0x03, 0x80, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, // 44 - ',' + 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, // 45 - '-' + 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 46 - '.' + 0x05, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, // 47 - '/' + 0x05, 0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x00, // 48 - '0' + 0x05, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x00, 0x00, // 49 - '1' + 0x05, 0x72, 0x49, 0x49, 0x49, 0x46, 0x00, 0x00, // 50 - '2' + 0x05, 0x21, 0x41, 0x49, 0x4d, 0x33, 0x00, 0x00, // 51 - '3' + 0x05, 0x18, 0x14, 0x12, 0x7f, 0x10, 0x00, 0x00, // 52 - '4' + 0x05, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00, // 53 - '5' + 0x05, 0x3c, 0x4a, 0x49, 0x49, 0x31, 0x00, 0x00, // 54 - '6' + 0x05, 0x41, 0x21, 0x11, 0x09, 0x07, 0x00, 0x00, // 55 - '7' + 0x05, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00, // 56 - '8' + 0x05, 0x46, 0x49, 0x49, 0x29, 0x1e, 0x00, 0x00, // 57 - '9' + 0x02, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // 58 - ':' + 0x02, 0x80, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, // 59 - ';' + 0x04, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00, // 60 - '<' + 0x05, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, // 61 - '=' + 0x04, 0x41, 0x22, 0x14, 0x08, 0x00, 0x00, 0x00, // 62 - '>' + 0x05, 0x02, 0x01, 0x59, 0x09, 0x06, 0x00, 0x00, // 63 - '?' + 0x05, 0x3e, 0x41, 0x5d, 0x59, 0x4e, 0x00, 0x00, // 64 - '@' + 0x05, 0x7c, 0x12, 0x11, 0x12, 0x7c, 0x00, 0x00, // 65 - 'A' + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00, // 66 - 'B' + 0x05, 0x3e, 0x41, 0x41, 0x41, 0x22, 0x00, 0x00, // 67 - 'C' + 0x05, 0x7f, 0x41, 0x41, 0x41, 0x3e, 0x00, 0x00, // 68 - 'D' + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x00, 0x00, // 69 - 'E' + 0x05, 0x7f, 0x09, 0x09, 0x09, 0x01, 0x00, 0x00, // 70 - 'F' + 0x05, 0x3e, 0x41, 0x41, 0x51, 0x73, 0x00, 0x00, // 71 - 'G' + 0x05, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x00, // 72 - 'H' + 0x03, 0x41, 0x7f, 0x41, 0x00, 0x00, 0x00, 0x00, // 73 - 'I' + 0x05, 0x20, 0x40, 0x41, 0x3f, 0x01, 0x00, 0x00, // 74 - 'J' + 0x05, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, // 75 - 'K' + 0x05, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, // 76 - 'L' + 0x05, 0x7f, 0x02, 0x1c, 0x02, 0x7f, 0x00, 0x00, // 77 - 'M' + 0x05, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x00, 0x00, // 78 - 'N' + 0x05, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00, 0x00, // 79 - 'O' + 0x05, 0x7f, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00, // 80 - 'P' + 0x05, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x00, 0x00, // 81 - 'Q' + 0x05, 0x7f, 0x09, 0x19, 0x29, 0x46, 0x00, 0x00, // 82 - 'R' + 0x05, 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00, // 83 - 'S' + 0x05, 0x03, 0x01, 0x7f, 0x01, 0x03, 0x00, 0x00, // 84 - 'T' + 0x05, 0x3f, 0x40, 0x40, 0x40, 0x3f, 0x00, 0x00, // 85 - 'U' + 0x05, 0x1f, 0x20, 0x40, 0x20, 0x1f, 0x00, 0x00, // 86 - 'V' + 0x05, 0x3f, 0x40, 0x38, 0x40, 0x3f, 0x00, 0x00, // 87 - 'W' + 0x05, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x00, // 88 - 'X' + 0x05, 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, 0x00, // 89 - 'Y' + 0x05, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x00, // 90 - 'Z' + 0x03, 0x7f, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, // 91 - '[' + 0x05, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 92 - '\' + 0x03, 0x41, 0x41, 0x7f, 0x00, 0x00, 0x00, 0x00, // 93 - ']' + 0x05, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x00, // 94 - '^' + 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, // 95 - '_' + 0x02, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - '`' + 0x05, 0x20, 0x54, 0x54, 0x78, 0x40, 0x00, 0x00, // 97 - 'a' + 0x05, 0x7f, 0x28, 0x44, 0x44, 0x38, 0x00, 0x00, // 98 - 'b' + 0x05, 0x38, 0x44, 0x44, 0x44, 0x28, 0x00, 0x00, // 99 - 'c' + 0x05, 0x38, 0x44, 0x44, 0x28, 0x7f, 0x00, 0x00, // 100 - 'd' + 0x05, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00, // 101 - 'e' + 0x04, 0x08, 0x7e, 0x09, 0x02, 0x00, 0x00, 0x00, // 102 - 'f' + 0x05, 0x18, 0xa4, 0xa4, 0x9c, 0x78, 0x00, 0x00, // 103 - 'g' + 0x05, 0x7f, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00, // 104 - 'h' + 0x03, 0x44, 0x7d, 0x40, 0x00, 0x00, 0x00, 0x00, // 105 - 'i' + 0x04, 0x40, 0x80, 0x80, 0x7a, 0x00, 0x00, 0x00, // 106 - 'j' + 0x04, 0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, // 107 - 'k' + 0x03, 0x41, 0x7f, 0x40, 0x00, 0x00, 0x00, 0x00, // 108 - 'l' + 0x05, 0x7c, 0x04, 0x78, 0x04, 0x78, 0x00, 0x00, // 109 - 'm' + 0x05, 0x7c, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00, // 110 - 'n' + 0x05, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, // 111 - 'o' + 0x05, 0xfc, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00, // 112 - 'p' + 0x05, 0x18, 0x24, 0x24, 0x18, 0xfc, 0x00, 0x00, // 113 - 'q' + 0x05, 0x7c, 0x08, 0x04, 0x04, 0x08, 0x00, 0x00, // 114 - 'r' + 0x05, 0x48, 0x54, 0x54, 0x54, 0x24, 0x00, 0x00, // 115 - 's' + 0x04, 0x04, 0x3f, 0x44, 0x24, 0x00, 0x00, 0x00, // 116 - 't' + 0x05, 0x3c, 0x40, 0x40, 0x20, 0x7c, 0x00, 0x00, // 117 - 'u' + 0x05, 0x1c, 0x20, 0x40, 0x20, 0x1c, 0x00, 0x00, // 118 - 'v' + 0x05, 0x3c, 0x40, 0x30, 0x40, 0x3c, 0x00, 0x00, // 119 - 'w' + 0x05, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, // 120 - 'x' + 0x05, 0x4c, 0x90, 0x90, 0x90, 0x7c, 0x00, 0x00, // 121 - 'y' + 0x05, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x00, // 122 - 'z' + 0x03, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x00, // 123 - '{' + 0x01, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 124 - '|' + 0x03, 0x41, 0x36, 0x08, 0x00, 0x00, 0x00, 0x00, // 125 - '}' + 0x05, 0x02, 0x01, 0x02, 0x04, 0x02, 0x00, 0x00, // 126 - '~' + 0x05, 0x3c, 0x26, 0x23, 0x26, 0x3c, 0x00, 0x00, // 127 - 'Hollow Up Arrow' + 0x07, 0x0E, 0x1F, 0x3F, 0x7E, 0x3F, 0x1F, 0x0E, // 128 (200) - heart СЕРЦЕ + 0x07, 0x0E, 0x11, 0x04, 0x7A, 0x04, 0x11, 0x0E, // 129 (201)антена точки доступа + 0x04, 0x06, 0x09, 0x09, 0x06, 0x00, 0x00, 0x00, // 130 (202) - градус цельсия + 0x05, 0x7E, 0x81, 0x81, 0xC3, 0x42, 0x00, 0x00, // 131 (203) - C - большая + 0x05, 0x3e, 0x49, 0x49, 0x49, 0x22, 0x00, 0x00, // 132 - 'Є ukr' + 0x05, 0x7E, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, // 133 - 'Ґ ukr' + 0x03, 0x41, 0x7f, 0x41, 0x00, 0x00, 0x00, 0x00, // 134 - 'I ukr' + 0x05, 0x01, 0x40, 0x7E, 0x40, 0x01, 0x00, 0x00, // 135 - 'Ї ukr' + 0x05, 0x04, 0x02, 0x7F, 0x02, 0x04, 0x00, 0x00, // 136 (210) - 'стрелка вверх 0 градусов' + 0x05, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x00, 0x00, // 137 (211) - 'стрелка вниз 180 градусов' + 0x05, 0x60, 0xFE, 0xF9, 0xFE, 0x60, 0x00, 0x00, // 138 (212) - 'Градусник' + 0x05, 0x38, 0x44, 0x43, 0x44, 0x38, 0x00, 0x00, // 139 (213) - 'Капелька' + 0x06, 0x24, 0x12, 0x12, 0x24, 0x24, 0x12, 0x00, // 140 (214) - 'Ветер' + 0x05, 0x44, 0x48, 0x5F, 0x48, 0x44, 0x00, 0x00, // 141 (215) - 'Давление' + 0x07, 0x1C, 0x22, 0x22, 0x24, 0x28, 0x24, 0x18, // 142 (216) - 'Облачность' + 0x07, 0x3C, 0x42, 0x95, 0xA1, 0x95, 0x42, 0x3C, // 143 (217) - ' :) ' + 0x07, 0x3C, 0x42, 0xA5, 0x91, 0xA5, 0x42, 0x3C, // 144 (220) - ' :( ' + 0x05, 0x14, 0x3E, 0x55, 0x41, 0x22, 0x00, 0x00, // 145 (221) - 'Евро' + 0x05, 0x14, 0x35, 0x5D, 0x56, 0x14, 0x00, 0x00, // 146 (222) - 'Гривна' + 0x07, 0x64, 0x54, 0x4C, 0x00, 0x08, 0x7F, 0x04, // 147 (223) - 'Злотый' + 0x05, 0x38, 0x54, 0x54, 0x44, 0x28, 0x00, 0x00, // 148 (224) - 'є ukr' + 0x04, 0x7c, 0x04, 0x04, 0x02, 0x00, 0x00, 0x00, // 149 (225) - 'ґ ukr' + 0x03, 0x44, 0x7d, 0x40, 0x00, 0x00, 0x00, 0x00, // 150 (226) - 'i ukr' + 0x03, 0x4A, 0x78, 0x42, 0x00, 0x00, 0x00, 0x00, // 151 (227) - 'ї ukr' + 0x07, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, // 152 (230) - 'Стрелка влево 270 градусов' + 0x07, 0x08, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, // 153 (231) - 'Стрелка вправо 90 градусов' + 0x07, 0x40, 0x20, 0x10, 0x09, 0x05, 0x03, 0x0F, // 154 (232) - 'Стрелка 45 градусов' + 0x07, 0x01, 0x02, 0x04, 0x48, 0x50, 0x60, 0x78, // 155 (233) - 'Стрелка 135 градусов' + 0x07, 0x78, 0x60, 0x50, 0x48, 0x04, 0x02, 0x01, // 156 (234) - 'Стрелка 225 градусов' + 0x07, 0x0F, 0x03, 0x05, 0x09, 0x10, 0x20, 0x40, // 157 (235) - 'Стрелка 315 градусов' + 0x06, 0x7c, 0x12, 0x11, 0x12, 0x7c, 0x80, 0x00, // 158 - 'Ą pl' + 0x05, 0x20, 0x54, 0x54, 0x78, 0x80, 0x00, 0x00, // 159 - 'ą pl' + 0x05, 0x70, 0x88, 0x8a, 0x89, 0x50, 0x00, 0x00, // 160 - 'Ć pl' + 0x04, 0x30, 0x48, 0x4a, 0x49, 0x00, 0x00, 0x00, // 161 - 'ć pl' + 0x06, 0x08, 0x7f, 0x44, 0x40, 0x40, 0x40, 0x00, // 162 - 'Ł pl' + 0x03, 0x49, 0x7f, 0x44, 0x00, 0x00, 0x00, 0x00, // 163 - 'ł pl' + 0x05, 0xf8, 0x10, 0x22, 0x41, 0xf8, 0x00, 0x00, // 164 - 'Ń pl' + 0x05, 0x78, 0x10, 0x0a, 0x09, 0x70, 0x00, 0x00, // 165 - 'ń pl' + 0x05, 0x70, 0x88, 0x8a, 0x89, 0x70, 0x00, 0x00, // 166 - 'Ó pl' + 0x05, 0x30, 0x48, 0x4a, 0x49, 0x30, 0x00, 0x00, // 167 - 'ó pl' + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x80, 0x00, 0x00, // 168 - 'Ę pl' + 0x05, 0x38, 0x54, 0x54, 0x54, 0x98, 0x00, 0x00, // 169 - 'ę pl' + 0x05, 0x90, 0xa8, 0xaa, 0xa9, 0x48, 0x00, 0x00, // 170 - 'Ś pl' + 0x05, 0x08, 0x54, 0x56, 0x55, 0x20, 0x00, 0x00, // 171 - 'ś pl' + 0x05, 0x88, 0xc8, 0xaa, 0x99, 0x88, 0x00, 0x00, // 172 - 'Ź pl' + 0x04, 0x48, 0x6a, 0x59, 0x48, 0x00, 0x00, 0x00, // 173 - 'ź pl' + 0x05, 0x44, 0x64, 0x55, 0x4c, 0x44, 0x00, 0x00, // 174 - 'Ż pl' + 0x04, 0x48, 0x6a, 0x58, 0x48, 0x00, 0x00, 0x00, // 175 - 'ż pl' + 0x04, 0x06, 0x09, 0x09, 0x06, 0x00, 0x00, 0x00, // 176 - 'градус Цельсия'' + 0x05, 0xf8, 0x25, 0x22, 0x25, 0xf8, 0x00, 0x00, // 177 - 'Ä' Deutsch + 0x05, 0x20, 0x55, 0x54, 0x54, 0x79, 0x00, 0x00, // 178 - 'ä' Deutsch + 0x05, 0x78, 0x85, 0x84, 0x85, 0x78, 0x00, 0x00, // 179 - 'Ö' Deutsch + 0x05, 0x38, 0x45, 0x44, 0x45, 0x38, 0x00, 0x00, // 180 - 'ö' Deutsch + 0x05, 0x7c, 0x81, 0x80, 0x81, 0x7c, 0x00, 0x00, // 181 - 'Ü' Deutsch + 0x05, 0x3c, 0x41, 0x40, 0x21, 0x7c, 0x00, 0x00, // 182 - 'ü' Deutsch + 0x05, 0x85, 0xff, 0x49, 0x4e, 0x30, 0x00, 0x00, // 183 - 'ß' Deutsch + 0x05, 0x14, 0x14, 0xf7, 0x00, 0xff, 0x00, 0x00, // 184 - 'Right T double all' + 0x05, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, // 185 - 'Right side double' + 0x05, 0x14, 0x14, 0xf4, 0x04, 0xfc, 0x00, 0x00, // 186 - 'Top Right double' + 0x05, 0x14, 0x14, 0x17, 0x10, 0x1f, 0x00, 0x00, // 187 - 'Bot Right double' + 0x05, 0x10, 0x10, 0x1f, 0x10, 0x1f, 0x00, 0x00, // 188 - 'Bot Right double V' + 0x05, 0x14, 0x14, 0x14, 0x14, 0x1f, 0x00, 0x00, // 189 - 'Bot Right double H' + 0x05, 0x10, 0x10, 0x10, 0x10, 0xf0, 0x00, 0x00, // 190 - 'Top Right' + 0x05, 0x00, 0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, // 191 - 'Bot Left' + 0x05, 0x7e, 0x11, 0x11, 0x11, 0x7e, 0x00, 0x00, // 192 - 'А' x0c0 + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x31, 0x00, 0x00, // 193 - 'Б' x0c1 + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00, // 194 - 'B' x0c2 + 0x05, 0x7f, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, // 195 - 'Г' x0c3 + 0x06, 0xc0, 0x7e, 0x41, 0x41, 0x7e, 0xc0, 0x00, // 196 - 'Д' x0c4 + 0x05, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x00, 0x00, // 197 - 'E' x0c5 + 0x05, 0x77, 0x08, 0x7f, 0x08, 0x77, 0x00, 0x00, // 198 - 'Ж' x0c6 + 0x05, 0x41, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00, // 199 - 'З' x0c7 + 0x05, 0x7f, 0x10, 0x08, 0x04, 0x7f, 0x00, 0x00, // 200 - 'И' x0c8 + 0x05, 0x7f, 0x10, 0x09, 0x04, 0x7f, 0x00, 0x00, // 201 - 'Й' x0c9 + 0x05, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, // 202 - 'K' x0ca + 0x05, 0x40, 0x3e, 0x01, 0x01, 0x7f, 0x00, 0x00, // 203 - 'Л' x0cb + 0x05, 0x7f, 0x02, 0x0c, 0x02, 0x7f, 0x00, 0x00, // 204 - 'M' x0cc + 0x05, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x00, // 205 - 'H' x0cd + 0x05, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00, 0x00, // 206 - 'O' x0ce + 0x05, 0x7f, 0x01, 0x01, 0x01, 0x7f, 0x00, 0x00, // 207 - 'П' x0cf + 0x05, 0x7f, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00, // 208 - 'Р' x0d0 + 0x05, 0x3e, 0x41, 0x41, 0x41, 0x22, 0x00, 0x00, // 209 - 'C' x0d1 + 0x05, 0x03, 0x01, 0x7f, 0x01, 0x03, 0x00, 0x00, // 210 - 'T' x0d2 + 0x05, 0x27, 0x48, 0x48, 0x48, 0x3f, 0x00, 0x00, // 211 - 'У' x0d3 + 0x07, 0x1c, 0x22, 0x22, 0x7f, 0x22, 0x22, 0x1c, // 212 - 'Ф' x0d4 + 0x05, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x00, // 213 - 'X' x0d5 + 0x06, 0x7f, 0x40, 0x40, 0x40, 0x7f, 0xc0, 0x00, // 214 - 'Ц' x0d6 + 0x05, 0x07, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x00, // 215 - 'Ч' x0d7 + 0x05, 0x7f, 0x40, 0x7e, 0x40, 0x7f, 0x00, 0x00, // 216 - 'Ш' x0d8 + 0x06, 0x7f, 0x40, 0x7e, 0x40, 0x7f, 0xc0, 0x00, // 217 - 'Щ' x0d9 + 0x07, 0x03, 0x01, 0x7f, 0x48, 0x48, 0x30, 0x00, // 218 - 'Ъ' x0da + 0x07, 0x7f, 0x48, 0x48, 0x30, 0x00, 0x7F, 0x00, // 219 - 'Ы' x0db + 0x05, 0x7f, 0x48, 0x48, 0x48, 0x30, 0x00, 0x00, // 220 - 'Ь' x0dc + 0x05, 0x22, 0x41, 0x49, 0x49, 0x3e, 0x00, 0x00, // 221 - 'Э' x0dd + 0x06, 0x7f, 0x08, 0x3e, 0x41, 0x41, 0x3e, 0x00, // 222 - 'Ю' x0de + 0x05, 0x46, 0x29, 0x19, 0x09, 0x7f, 0x00, 0x00, // 223 - 'Я' x0df + 0x05, 0x20, 0x54, 0x54, 0x54, 0x78, 0x00, 0x00, // 224 - 'а' x0e0 + 0x05, 0x3c, 0x4a, 0x4a, 0x49, 0x31, 0x00, 0x00, // 225 - 'б' x0e1 + 0x05, 0x7c, 0x54, 0x54, 0x54, 0x28, 0x00, 0x00, // 226 - 'в' x0e2 + 0x04, 0x7c, 0x04, 0x04, 0x0c, 0x00, 0x00, 0x00, // 227 - 'г' x0e3 + 0x06, 0xc0, 0x78, 0x44, 0x44, 0x78, 0xc0, 0x00, // 228 - 'д' x0e4 + 0x05, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00, // 229 - 'e' x0e5 + 0x05, 0x6c, 0x10, 0x7c, 0x10, 0x6c, 0x00, 0x00, // 230 - 'ж' x0e6 + 0x05, 0x44, 0x54, 0x54, 0x54, 0x28, 0x00, 0x00, // 231 - 'з' x0e7 + 0x05, 0x7c, 0x20, 0x10, 0x08, 0x7c, 0x00, 0x00, // 232 - 'и' x0e8 + 0x05, 0x7c, 0x20, 0x12, 0x08, 0x7c, 0x00, 0x00, // 233 - 'й' x0e9 + 0x05, 0x7c, 0x10, 0x10, 0x28, 0x44, 0x00, 0x00, // 234 - 'к' x0ea + 0x05, 0x40, 0x38, 0x04, 0x04, 0x7c, 0x00, 0x00, // 235 - 'л' x0eb + 0x05, 0x7c, 0x08, 0x10, 0x08, 0x7c, 0x00, 0x00, // 236 - 'м' x0ec + 0x05, 0x7c, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, // 237 - 'н' x0ed + 0x05, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, // 238 - 'o' x0ee + 0x05, 0x7c, 0x04, 0x04, 0x04, 0x7c, 0x00, 0x00, // 239 - 'п' x0ef + 0x05, 0xfc, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, // 240 - 'р' x0f0 + 0x04, 0x38, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, // 241 - 'с' x0f1 + 0x05, 0x0c, 0x04, 0x7c, 0x04, 0x0c, 0x00, 0x00, // 242 - 'т' x0f2 + 0x05, 0x4c, 0x90, 0x90, 0x50, 0x3c, 0x00, 0x00, // 243 - 'у' x0f3 + 0x07, 0x10, 0x28, 0x28, 0xfc, 0x28, 0x28, 0x10, // 244 - 'ф' x0f4 + 0x05, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, // 245 - 'x' x0f5 + 0x05, 0x7c, 0x40, 0x40, 0x7c, 0xc0, 0x00, 0x00, // 246 - 'ц' x0f6 + 0x05, 0x0c, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, // 247 - 'ч' x0f7 + 0x05, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, // 248 - 'ш' x0f8 + 0x06, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0xc0, 0x00, // 249 - 'щ' x0f9 + 0x06, 0x04, 0x7c, 0x50, 0x50, 0x50, 0x20, 0x00, // 250 - 'ъ' x0fa + 0x05, 0x7c, 0x50, 0x50, 0x20, 0x7c, 0x00, 0x00, // 251 - 'ы' x0fb + 0x05, 0x7c, 0x50, 0x50, 0x50, 0x20, 0x00, 0x00, // 252 - 'ь' x0fc + 0x05, 0x28, 0x44, 0x54, 0x54, 0x38, 0x00, 0x00, // 253 - 'э' x0fd + 0x06, 0x7c, 0x10, 0x38, 0x44, 0x44, 0x38, 0x00, // 254 - 'ю' x0fe + 0x05, 0x48, 0x34, 0x14, 0x14, 0x7c, 0x00, 0x00 // 255 - 'я' x0ff +}; diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..a498661 --- /dev/null +++ b/globals.h @@ -0,0 +1,14 @@ + +bool printCom = true; + +int hour = 2, minute = 40, second = 42, month = 4, day = 6, dayOfWeek = 6, year = 2018; + + +int secFr, lastSecond, lastMinute; + +byte kuOn = 7; +byte kuOff = 23; + +bool isActiveBuzzer = 1; + +String tMes, tNow, tCurr, tPress, tPress0, tSpeed, tMin, tTom, tYour, tPoint, tIp, tPass, tWeatrTN, tWeatrNot, tAlarm; diff --git a/langModule.ino b/langModule.ino new file mode 100644 index 0000000..609f73c --- /dev/null +++ b/langModule.ino @@ -0,0 +1,257 @@ + + +//==========КОНВЕРТАЦІЯ СИМВОЛІВ В РАЗІ ВИКОРИСТАННЯ УКРАЇНСЬКИХ ЛІТЕР================== +byte dualChar = 0; +unsigned char convert_UA_RU_PL_DE(unsigned char _c) { + unsigned char c = _c; + // конвертирование латиницы + if (c == 208) { + dualChar = 1; + return 0; + } + else if (c == 209 || c == 210) { + dualChar = 2; + return 0; + } + if (c == 32 && dualChar != 3) { + dualChar = 3; + return c; + } + if (dualChar == 1) { + if (c >= 144 && c < 192) { + c += 48; + } + dualChar = 0; + return c; + } + if (dualChar == 2) { + if (c >= 128 && c < 144) { + c += 112; + } + switch (_c) { + case 144: c = 133; break; + case 145: c = 149; break; + } + dualChar = 0; + return c; + } + // конвертирование польского и немецкого + if (c == 195) { + dualChar = 4; + return 0; + } + if (c == 196) { + dualChar = 5; + return 0; + } + if (c == 197) { + dualChar = 6; + return 0; + } + if (dualChar == 4) { + switch (_c) { + case 132: c = 177; break; + case 147: c = 166; break; + case 150: c = 179; break; + case 156: c = 181; break; + case 159: c = 183; break; + case 164: c = 178; break; + case 179: c = 167; break; + case 182: c = 180; break; + case 188: c = 182; break; + } + dualChar = 0; + return c; + } + if (dualChar == 5) { + if (c >= 132 && c < 136) { + c += 26; + } + switch (_c) { + case 152: c = 168; break; + case 153: c = 169; break; + } + dualChar = 0; + return c; + } + if (dualChar == 6) { + if (c >= 129 && c < 133) { + c += 33; + } + if (c >= 154 && c < 156) { + c += 16; + } + if (c >= 185 && c < 189) { + c -= 13; + } + dualChar = 0; + return c; + } +} + +// ===========================КОНВЕРТАЦІЯ НАЗВ ДНІВ ТИЖНЯ НА УКРАЇНСЬКУ МОВУ============================================ +void convertDw(){ + switch(dayOfWeek){ + case 1 : dw = tSunday; break; + case 2 : dw = tMonday; break; + case 3 : dw = tTuesday; break; + case 4 : dw = tWednesday; break; + case 5 : dw = tThursday; break; + case 6 : dw = tFriday; break; + case 7 : dw = tSaturday; break; + } +} +// ===========================КОНВЕРТАЦІЯ НАЗВ МІСЯЦІВ НА УКРАЇНСЬКУ МОВУ============================================ +void convertMonth(){ + switch(month){ + case 1 : _month = tJanuary; break; + case 2 : _month = tFebruary; break; + case 3 : _month = tMarch; break; + case 4 : _month = tApril; break; + case 5 : _month = tMay; break; + case 6 : _month = tJune; break; + case 7 : _month = tJuly; break; + case 8 : _month = tAugust; break; + case 9 : _month = tSeptember; break; + case 10 : _month = tOctober; break; + case 11 : _month = tNovember; break; + case 12 : _month = tDecember; break; + } +} + +//----------------------------------------------------- +void initLang() { + if (weatherLang == "uk") { + tNow = "Зараз"; + tPress0 = "гПа"; + tPress = "ммРс"; + tSpeed = "м/с"; + tMin = "хв."; + tCurr = "Сьогодні"; + tTom = "Завтра"; + tYour = "Ваш"; + tPoint = "Підключіться до"; + tIp = "та перейдіть за адресою"; + tPass = "пароль"; + tWeatrNot = " Немає оновлень погоди більше 6 годин!!! "; + tWeatrTN = "Немає оновлень погоди."; + tJanuary = "січня"; + tFebruary = "лютого"; + tMarch = "березня"; + tApril = "квітня"; + tMay = "травня"; + tJune = "червня"; + tJuly = "липня"; + tAugust = "серпня"; + tSeptember = "вересня"; + tOctober = "жовтня"; + tNovember = "листопада"; + tDecember = "грудня"; + tMonday = "Понеділок"; + tTuesday = "Вівторок"; + tWednesday = "Середа"; + tThursday = "Четвер"; + tFriday = "П'ятниця"; + tSaturday = "Субота"; + tSunday = "Неділя"; + tAlarm = "УВАГА!!! "; + sgpCo2Message[0] = "Допустимий рівень повітря"; + sgpCo2Message[1] = "Важке повітря. Необхідно провітрювання"; + sgpCo2Message[2] = "Можлива сонливість, втома, головний біль"; + sgpCo2Message[3] = "Серйозне погіршення здоров'я !!!"; + sgpCo2Message[4] = "Гранично допустима концентрація протягом 8 годин!!!"; + sgpTvocMessage [0] = "Допустимий рівень ЛОС"; + sgpTvocMessage [1] = "Рекомендується вентиляція"; + sgpTvocMessage [2] = "Рекомендується інтенсивна вентиляція"; + sgpTvocMessage [3] = "Інтенсивна вентиляція і провітрювання необхідні"; + sgpTvocMessage [4] = "Дуже інтенсивна вентиляція суворо необхідна !!!"; + } else if (weatherLang == "ru") { + tNow = "Сейчас"; + tPress0 = "гПа"; + tPress = "ммРс"; + tSpeed = "м/с"; + tMin = "мин."; + tCurr = "Сегодня"; + tTom = "Завтра"; + tYour = "Ваш"; + tPoint = "Подключитесь к точке доступа"; + tIp = "и введите в браузере адрес"; + tPass = "пароль"; + tWeatrNot = " Нет обновления погоды более 6 часов!!! "; + tWeatrTN = "Нет обновления погоды."; + tJanuary = "января"; + tFebruary = "февраля"; + tMarch = "марта"; + tApril = "апреля"; + tMay = "мая"; + tJune = "июня"; + tJuly = "июля"; + tAugust = "августа"; + tSeptember = "сентября"; + tOctober = "октября"; + tNovember = "ноября"; + tDecember = "декабря"; + tMonday = "Понедельник"; + tTuesday = "Вторник"; + tWednesday = "Среда"; + tThursday = "Четверг"; + tFriday = "Пятница"; + tSaturday = "Суббота"; + tSunday = "Воскресенье"; + tAlarm = "ВНИМАНИЕ!!! "; + sgpCo2Message[0] = "Допустимый уровень возуха"; + sgpCo2Message[1] = "Тяжелый воздух. Необходимо проветривание"; + sgpCo2Message[2] = "Возможно сонливость, усталость, головная боль"; + sgpCo2Message[3] = "Серьезное ухудшение здоровья!!!"; + sgpCo2Message[4] = "Предельно допустимая концентрация в течении 8 часов"; + sgpTvocMessage[0] = "Допустимый уровень ЛОС"; + sgpTvocMessage[1] = "Рекомендуется вентиляция"; + sgpTvocMessage[2] = "Рекомендуется интенсивная вентиляция"; + sgpTvocMessage[3] = "Интенсивная вентиляция и проветривание необходимы"; + sgpTvocMessage[4] = "Очень интенсивная вентиляция строжайше необходима!!!"; + } else if (weatherLang == "en") { + tNow = "now"; + tPress0 = "gPa"; + tPress = "mmHg"; + tSpeed = "m/s"; + tMin = "min."; + tCurr = "Today"; + tTom = "Tomorrow"; + tYour = "Your"; + tPoint = "Connect to access point the"; + tIp = "and enter in the browser the address"; + tPass = "password"; + tWeatrNot = " There is no weather update for more than 6 hours !!! "; + tWeatrTN = "No weather updates."; + tJanuary = "January"; + tFebruary = "February"; + tMarch = "Martha"; + tApril = "April"; + tMay = "May"; + tJune = "June"; + tJuly = "July"; + tAugust = "August"; + tSeptember = "September"; + tOctober = "October"; + tNovember = "November"; + tDecember = "December"; + tMonday = "Monday"; + tTuesday = "Tuesday"; + tWednesday = "Wednesday"; + tThursday = "Thursday"; + tFriday = "Friday"; + tSaturday = "Saturday"; + tSunday = "Sunday"; + tAlarm = "ATTENTION!!! "; + sgpCo2Message[0] = "Permissible level of air"; + sgpCo2Message[1] = "Heavy air. Ventilation is necessary"; + sgpCo2Message[2] = "Drowsiness, fatigue, headache is possible"; + sgpCo2Message[3] = "Serious deterioration of health !!!"; + sgpCo2Message[4] = "Maximum permissible concentration within 8 hours"; + sgpTvocMessage [0] = "Allowable VOC Level"; + sgpTvocMessage [1] = "Recommended ventilation"; + sgpTvocMessage [2] = "Recommended intensive ventilation"; + sgpTvocMessage [3] = "Intensive ventilation and ventilation are necessary"; + sgpTvocMessage [4] = "Very intensive ventilation is strictly necessary !!!"; + } +} diff --git a/max7219.h b/max7219.h new file mode 100644 index 0000000..26d54a1 --- /dev/null +++ b/max7219.h @@ -0,0 +1,107 @@ +// MAX7219 commands: +#define CMD_NOOP 0 +#define CMD_DIGIT0 1 +#define CMD_DIGIT1 2 +#define CMD_DIGIT2 3 +#define CMD_DIGIT3 4 +#define CMD_DIGIT4 5 +#define CMD_DIGIT5 6 +#define CMD_DIGIT6 7 +#define CMD_DIGIT7 8 +#define CMD_DECODEMODE 9 +#define CMD_INTENSITY 10 +#define CMD_SCANLIMIT 11 +#define CMD_SHUTDOWN 12 +#define CMD_DISPLAYTEST 15 + +byte scr[136]; + +byte MAX7219_NUM = 4; +int MAX7219_ROTATE = 90; //0, 90, 180, 270 + + +//====================================================================================================== +void sendCmdAll(byte cmd, byte data) { + digitalWrite(CS_PIN, LOW); + for(int i = MAX7219_NUM - 1; i >= 0; i--) { + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, cmd); + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, data); + } + digitalWrite(CS_PIN, HIGH); +} +//====================================================================================================== +void refreshAll() { + byte mask = (MAX7219_ROTATE == 270 ? 0x01 : 0x80); + for(int c = 0; c < 8; c++) { + digitalWrite(CS_PIN, LOW); + for(int i = (MAX7219_ROTATE == 180 ? 0 : MAX7219_NUM - 1); (MAX7219_ROTATE == 180 ? (i < MAX7219_NUM) : (i >= 0)); (MAX7219_ROTATE == 180 ? i++ : i--)) { + byte bt = 0; + if(MAX7219_ROTATE == 270) { + for(int b = 0; b < 8; b++) { + bt <<= 1; + if(scr[i * 8 + b] & mask) { + bt |= 0x01; + } + } + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, CMD_DIGIT0 + c); + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, bt); + } else if(MAX7219_ROTATE == 90) { + for(int b = 0; b < 8; b++) { + bt >>= 1; + if(scr[i * 8 + b] & mask) { + bt |= 0x80; + } + } + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, CMD_DIGIT0 + c); + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, bt); + } else { + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, CMD_DIGIT0 + c); + shiftOut(DIN_PIN, CLK_PIN, MSBFIRST, scr[i * 8 + c]); + } + } + digitalWrite(CS_PIN, HIGH); + if(MAX7219_ROTATE == 270) { + mask <<= 1; + } + if(MAX7219_ROTATE == 90) { + mask >>= 1; + } + } +} + +//====================================================================================================== +void clr() { + for(int i = 0; i < MAX7219_NUM * 8; i++) { + scr[i] = 0; + } +} + +//====================================================================================================== +void scrollLeft() { + for(int i = 0; i < MAX7219_NUM * 8 + 7; i++) { + scr[i] = scr[i + 1]; + } +} + +//====================================================================================================== +void invert() { + for(int i = 0; i < MAX7219_NUM * 8; i++) { + scr[i] =~ scr[i]; + } +} + +//====================================================================================================== +void initMAX7219() { + pinMode(DIN_PIN, OUTPUT); + pinMode(CLK_PIN, OUTPUT); + pinMode(CS_PIN, OUTPUT); + digitalWrite(CS_PIN, HIGH); + delay(500); + sendCmdAll(CMD_DISPLAYTEST, 0); + sendCmdAll(CMD_SCANLIMIT, 7); + sendCmdAll(CMD_DECODEMODE, 0); + sendCmdAll(CMD_INTENSITY, 0); + sendCmdAll(CMD_SHUTDOWN, 0); + clr(); + refreshAll(); +} diff --git a/ntpModule.ino b/ntpModule.ino new file mode 100644 index 0000000..e69de29 diff --git a/rtc.h b/rtc.h new file mode 100644 index 0000000..3913502 --- /dev/null +++ b/rtc.h @@ -0,0 +1,59 @@ +int BCD2DEC(int x) { return ((x)>>4)*10+((x)&0xf); } +int DEC2BCD(int x) { return (((x)/10)<<4)+((x)%10); } + +#define I2CStart(x) Wire.beginTransmission(x) +#define I2CStop() Wire.endTransmission() +#define I2CWrite(x) Wire.write(x) +#define I2CRead() Wire.read() +#define I2CReq(x,y) Wire.requestFrom(x,y) +#define I2CReady while(!Wire.available()) {}; + +#define DS_RTC_TIME 0x00 +#define DS_RTC_DOW 0x03 +#define DS_RTC_DATE 0x04 +#define DS_RTC_MEM 0x08 + +int rtcAddr = 0x68; + +typedef struct { + int hour, minute, second, month, day, dayOfWeek, year; +} RtcStruct; + +RtcStruct rtcStruct; + +void setRTCDateTime() { + I2CStart(rtcAddr); + I2CWrite(DS_RTC_TIME); + I2CWrite(DEC2BCD(rtcStruct.second)); + I2CWrite(DEC2BCD(rtcStruct.minute)); + I2CWrite(DEC2BCD(rtcStruct.hour)); + I2CWrite(DEC2BCD(rtcStruct.dayOfWeek)); + I2CWrite(DEC2BCD(rtcStruct.day)); + I2CWrite(DEC2BCD(rtcStruct.month)); + I2CWrite(DEC2BCD(rtcStruct.year - 2000)); + I2CStop(); +} + +void getRTCDateTime(void) { + int v; + I2CStart(rtcAddr); + I2CWrite(DS_RTC_TIME); + I2CStop(); + I2CReq(rtcAddr, 7); + I2CReady; + v = I2CRead() & 0x7f; + rtcStruct.second = BCD2DEC(v); + v = I2CRead() & 0x7f; + rtcStruct.minute = BCD2DEC(v); + v = I2CRead() & 0x3f; + rtcStruct.hour = BCD2DEC(v); + v = I2CRead() & 0x07; + rtcStruct.dayOfWeek = BCD2DEC(v); + v = I2CRead() & 0x3f; + rtcStruct.day = BCD2DEC(v); + v = I2CRead() & 0x3f; + rtcStruct.month = BCD2DEC(v); + v = I2CRead() & 0xff; + rtcStruct.year = BCD2DEC(v) + 2000; + I2CStop(); +} diff --git a/sgpModule.h b/sgpModule.h new file mode 100644 index 0000000..d9866ba --- /dev/null +++ b/sgpModule.h @@ -0,0 +1,136 @@ + +Adafruit_SGP30 sgp; + +boolean sgpFound = false; +int sgpCo2 = 400; + +byte sgpCo2LivelAlarm = 2; +boolean eCo2AlarmEsp = true; + +boolean eCo2Led = true; +String sgpCo2Message[] = {"","","","",""}; +int sgpTvoc = 0; +byte sgpTvocLivelAlarm = 2; + +boolean tvocAlarmEsp = true; + +boolean tvocLed = true; + +String sgpTvocMessage[] = {"","","","",""}; + +byte setSgpCorr = 0; //0-нет коррекции, 1-BME-280, 2-Si7021, 3-AHTx0, 4-DHT11/22 + +float sgpCorrTemp = 22.1; // [°C] +float sgpCorrHumi = 45.2; // [%RH] + +typedef struct { + byte co2Livel = 0; //0=400-699, 1=700-999, 2=1000-2499, 3=2500-4999, 4=5000++ + int co2Livel1 = 700; + int co2Livel2 = 1000; + int co2Livel3 = 2500; + int co2Livel4 = 5000; + byte tvocLivel = 0; //0=0-64, 1=65-219, 2=220-659, 3=660-2199, 4=2200++ + int tvocLivel1 = 65; + int tvocLivel2 = 220; + int tvocLivel3 = 660; + int tvocLivel4 = 2200; +} SgpValues; + +SgpValues sgpValues; + +uint32_t getAbsoluteHumidity(float temperature, float humidity) { + // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 + const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] + const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3] + return absoluteHumidityScaled; +} + +void sgp30() { + // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals + if(setSgpCorr) { + sgp.setHumidity(getAbsoluteHumidity(sgpCorrTemp, sgpCorrHumi)); + } + printTime(); + if(!sgp.IAQmeasure()) { + if(printCom) { + Serial.println("Measurement failed"); + } + return; + } + sgpCo2 = sgp.eCO2; + sgpTvoc = sgp.TVOC; + if(printCom) { + Serial.print("TVOC = " + String(sgpTvoc) + " ppb, "); + } + if(printCom) { + Serial.print("eCO2 = " + String(sgpCo2) + " ppm, "); + } + if(sgpCo2 < sgpValues.co2Livel1) { + sgpValues.co2Livel = 0; + } else if(sgpCo2 < sgpValues.co2Livel2) { + sgpValues.co2Livel = 1; + } else if(sgpCo2 < sgpValues.co2Livel3) { + sgpValues.co2Livel = 2; + } else if(sgpCo2 < sgpValues.co2Livel4) { + sgpValues.co2Livel = 3; + } else { + sgpValues.co2Livel = 4; + } + if(sgpTvoc < sgpValues.tvocLivel1) { + sgpValues.tvocLivel = 0; + } else if(sgpTvoc < sgpValues.tvocLivel2) { + sgpValues.tvocLivel = 1; + } else if(sgpTvoc < sgpValues.tvocLivel3) { + sgpValues.tvocLivel = 2; + } else if(sgpTvoc < sgpValues.tvocLivel4) { + sgpValues.tvocLivel = 3; + } else { + sgpValues.tvocLivel = 4; + } + + if(!sgp.IAQmeasureRaw()) { + if(printCom) { + Serial.println("Raw Measurement failed"); + } + } else { + if(printCom) { + Serial.print("Raw H2 = " + String(sgp.rawH2) + ", "); + } + if(printCom) { + Serial.println("Raw Ethanol = " + String(sgp.rawEthanol)); + } + if (minute % 5 == 0) { + uint16_t TVOC_base, eCO2_base; + if(!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { + if(printCom){ + printTime(); + Serial.println("Failed to get baseline readings"); + } + return; + } + if(printCom) { + printTime(); + Serial.print("****Baseline values: eCO2: 0x" + String (eCO2_base, HEX)); + Serial.println(" & TVOC: 0x" + String(TVOC_base, HEX)); + } + } + } + + String livelCo2 = tAlarm + sgpCo2Message[sgpValues.co2Livel] + " eCO2 = " + String(sgpCo2) + " ppm "; + if(sgpValues.co2Livel >= sgpCo2LivelAlarm) { + if(eCo2AlarmEsp) { + bip(4); + clr(); + printStringWithShift((" " + livelCo2).c_str(), timeScrollSpeed); + } + } + + String livelTvoc = tAlarm + sgpTvocMessage[sgpValues.tvocLivel] + " TVOC = " + String(sgpTvoc) + " ppb "; + if(sgpValues.tvocLivel >= sgpTvocLivelAlarm) { + if(tvocAlarmEsp) { + bip(4); + clr(); + printStringWithShift((" " + livelTvoc).c_str(), timeScrollSpeed); + } + } +} diff --git a/soundModule.h b/soundModule.h new file mode 100644 index 0000000..056a876 --- /dev/null +++ b/soundModule.h @@ -0,0 +1,26 @@ + +void bip() { + if (!isActiveBuzzer) { + tone(buzzerPin, 2000, 40); + delay(250); + noTone(buzzerPin); + } else { + digitalWrite(buzzerPin, HIGH); + delay(120); + digitalWrite(buzzerPin, LOW); + delay(120); + } +} + +void bip(uint8_t count) { + for(uint8_t i = 0; i < count; i++) { + bip(); + } +} + +void checkNeedHourSound() { + if(minute == 0 && second == 0 && secFr == 0 && (hour >= kuOn && hour < kuOff)) { + bip(); + bip(); + } +} diff --git a/weatherModule.ino b/weatherModule.ino new file mode 100644 index 0000000..2f60f3e --- /dev/null +++ b/weatherModule.ino @@ -0,0 +1,501 @@ + +static const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Кількість днів у місяцях + +String weatherMain = ""; +String weatherDescription = ""; +String weatherLocation = ""; +String country; +String tempz; +int clouds; +int windDeg; +float windSpeed; +String dw, _month; + +//===============================================================================================================================// +// БЕРЕМО ПОГОДУ З САЙТУ https://www.weatherbit.io // +//===============================================================================================================================// +void getWeatherData0() { + if(weatherKey0=="" || !displayForecast) return; + if(!WIFI_connected) { + updateForecast++; + return; + } + if(printCom) { + Serial.println("======== START GET WEATHER FROM WEATHERBIT.IO ========="); + printTime(); + } + location_name = ""; + location_region = ""; + location_country = ""; + location_localtime = ""; + location_temp = 0; + location_app_temp = 0; + location_rh = 0; + location_pres = 0; + location_wind_spd = 0; + location_wind_cdir_full = ""; + location_sunrise = ""; + location_sunset = ""; + location_clouds = 0; + location_vis = 0; + location_uv = 0; + location_weather_description = ""; + if(ESPclient.connect(weatherHost0.c_str(), 80)){} + else { + if(printCom){ + Serial.println(" Not connection server!!!"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + HTTPClient http; + String line=""; + String reqline="http://"+weatherHost0+"/v2.0/current/daily?city="+urlencode(cityID0)+"&lang="+weatherLang+"&key="+weatherKey0; + if(printCom) Serial.print(reqline); + if(http.begin(ESPclient, reqline)){ + int httpCode = http.GET(); + if(httpCode > 0) { + if(printCom) Serial.printf(" [HTTP] GET... code: %d\n", httpCode); + if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + line = http.getString(); + } + } else { + if(printCom){ + Serial.printf(" [HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.println("======== END =========================================="); + } + http.end(); + updateForecast++; + return; + } + http.end(); + } else { + if(printCom){ + Serial.printf(" [HTTP] Unable to connect\n"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + if(line==""){ + if(printCom){ + printTime(); + Serial.printf("[HTTP] The answer is empty\n"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(37) + 1128; //https://arduinojson.org/v6/assistant/ + DynamicJsonDocument doc(capacity); + deserializeJson(doc, line); + if(!doc.capacity()){ + if(printCom){ + printTime(); + Serial.println("Parse weather forecast - FAILED!!!"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + JsonObject data = doc["data"][0]; + location_rh = data["rh"]; // 69 + location_pres = data["pres"]; // 999.3 + if(pressSys == 1) location_pres /= 1.3332239; + const char* data_timezone = data["timezone"]; // "Europe/Kiev" + location_region = data_timezone; + const char* data_ob_time = data["ob_time"]; // "2019-09-19 17:57" + location_localtime = data_ob_time; + const char* data_country_code = data["country_code"]; // "UA" + location_country = data_country_code; + location_clouds = data["clouds"]; // 4 + const char* data_city_name = data["city_name"]; // "Kiev" + location_name = data_city_name; + location_wind_spd = data["wind_spd"]; // 1 + const char* data_wind_cdir_full = data["wind_cdir_full"]; // "пі́вдень-пі́вдень-схід" + location_wind_cdir_full = data_wind_cdir_full; + location_vis = data["vis"]; // 5 + const char* data_sunset = data["sunset"]; // "16:01" + location_sunset = data_sunset; + location_uv = data["uv"]; // 0 + int data_wind_dir = data["wind_dir"]; // 166 + const char* data_sunrise = data["sunrise"]; // "03:39" + location_sunrise = data_sunrise; //int data_dni = data["dni"]; // 0 + JsonObject data_weather = data["weather"]; + const char* data_weather_description = data_weather["description"]; // "ясного неба" + location_weather_description = data_weather_description; + location_temp = data["temp"]; // 10.6 + location_app_temp = data["app_temp"]; // 10.6 + String windDegString; + if(data_wind_dir >= 345 || data_wind_dir <= 22) windDegString = "\211"; //"Північний"; + if(data_wind_dir >= 23 && data_wind_dir <= 68) windDegString = "\234"; //"Північно-східний"; + if(data_wind_dir >= 69 && data_wind_dir <= 114) windDegString = "\230"; //"Східний"; + if(data_wind_dir >= 115 && data_wind_dir <= 160) windDegString = "\235"; //"Південно-східний"; + if(data_wind_dir >= 161 && data_wind_dir <= 206) windDegString = "\210"; //"Південний"; + if(data_wind_dir >= 207 && data_wind_dir <= 252) windDegString = "\232"; //"Південно-західний"; + if(data_wind_dir >= 253 && data_wind_dir <= 298) windDegString = "\231"; //"Західний"; + if(data_wind_dir >= 299 && data_wind_dir <= 344) windDegString = "\233"; //"Північно-західний"; + weatherString = space; + if(displayCityName){ + String PCN=personalCityName; + if(PCN.length() > 0) weatherString += PCN; + else weatherString += String(location_name); + weatherString += ", "; + } + if(displayForecastNow){ + weatherString += tNow + ": \212 " + String(location_temp, 1)+" ("+String(location_app_temp,1)+")"+("\202")+"C"; + weatherString += " \213 " + String(location_rh) + "%"; + weatherString += " \215 " + String((location_pres), 0) + (pressSys == 1 ? tPress : tPress0); + weatherString += " \214 " + windDegString + String(location_wind_spd, 1) + tSpeed; + weatherString += " \216 " + String(location_clouds) + "% " + data_weather_description + space; + } + updateForecast = 0; + updateForecastNot = false; + if(printCom){ + printTime(); + Serial.println("line =" + line); + Serial.println("======== END =========================================="); + } +} + +//===============================================================================================================================// +// БЕРЕМО ПОГОДУ З САЙТУ openweathermap.org // +//===============================================================================================================================// +void getWeatherData1(){ + if(weatherKey1=="" || !displayForecast) return; + if(!WIFI_connected){ + updateForecast++; + return; + } + if(printCom){ // + Serial.println("======== START GET WEATHER FROM OPENWEATHERMAP.ORG ===="); + printTime(); + } + location_name = ""; + location_region = ""; + location_country = ""; + location_localtime = ""; + location_temp = 0; + location_app_temp = 0; + location_rh = 0; + location_pres = 0; + location_wind_spd = 0; + location_wind_cdir_full = ""; + location_sunrise = ""; + location_sunset = ""; + location_clouds = 0; + location_vis = 0; + location_uv = 0; + location_weather_description = ""; + if(ESPclient.connect(weatherHost1.c_str(), 80)){} + else{ + if(printCom){ + Serial.println(" Not connection server!!!"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + HTTPClient http; + String line=""; + String reqline="http://"+weatherHost1+"/data/2.5/weather?id="+urlencode(cityID1)+"&lang="+weatherLang+"&units=metric&appid="+weatherKey1; + if(printCom) Serial.print(reqline); + if(http.begin(ESPclient, reqline)){ + int httpCode = http.GET(); + if(httpCode > 0) { + if(printCom) Serial.printf(" [HTTP] GET... code: %d\n", httpCode); + if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY){ + line = http.getString(); + } + } else { + if(printCom){ + Serial.printf(" [HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.println("======== END =========================================="); + } + http.end(); + updateForecast++; + return; + } + http.end(); + } else { + if(printCom){ + Serial.printf(" [HTTP] Unable to connect\n"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + if(line==""){ + if(printCom){ + printTime(); + Serial.printf("[HTTP] The answer is empty\n"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(13) + 751; //https://arduinojson.org/v6/assistant/ + DynamicJsonDocument doc(capacity); + deserializeJson(doc, line); + if(!doc.capacity()){ + if(printCom){ + printTime(); + Serial.println("Parse weather forecast - FAILED!!!"); + Serial.println("======== END =========================================="); + } + updateForecast++; + return; + } + JsonObject weather_0 = doc["weather"][0]; + const char* data_weather_description = weather_0["description"]; // "fog" + location_weather_description = data_weather_description; + JsonObject main = doc["main"]; + location_temp = main["temp"]; // 10.34 + location_pres = main["pressure"]; // 1023 + if(pressSys == 1) location_pres /= 1.3332239; + location_rh = main["humidity"]; // 100 + float location_temp_min = main["temp_min"]; // 7 + float location_temp_max = main["temp_max"]; // 12.22 + location_vis = doc["visibility"]; // 1000 + location_vis /= 1000; + location_wind_spd = doc["wind"]["speed"]; // 1 + int data_wind_dir = doc["wind"]["deg"]; // 230 + location_clouds = doc["clouds"]["all"]; // 20 + JsonObject sys = doc["sys"]; + const char* data_country_code = sys["country"]; // "UA" + location_country = data_country_code; + const char* data_city_name = doc["name"]; // "Kyiv" + location_name = data_city_name; + String windDegString; + if(data_wind_dir >= 345 || data_wind_dir <= 22) windDegString = "\211"; //"Північний"; + if(data_wind_dir >= 23 && data_wind_dir <= 68) windDegString = "\234"; //"Північно-східний"; + if(data_wind_dir >= 69 && data_wind_dir <= 114) windDegString = "\230"; //"Східний"; + if(data_wind_dir >= 115 && data_wind_dir <= 160) windDegString = "\235"; //"Південно-східний"; + if(data_wind_dir >= 161 && data_wind_dir <= 206) windDegString = "\210"; //"Південний"; + if(data_wind_dir >= 207 && data_wind_dir <= 252) windDegString = "\232"; //"Південно-західний"; + if(data_wind_dir >= 253 && data_wind_dir <= 298) windDegString = "\231"; //"Західний"; + if(data_wind_dir >= 299 && data_wind_dir <= 344) windDegString = "\233"; //"Північно-західний"; + weatherString = space; + if(displayCityName){ + String PCN=personalCityName; + if(PCN.length() > 0) weatherString += PCN; + else weatherString += String(location_name); + weatherString += ", "; + } + if(displayForecastNow){ + weatherString += tNow + ": \212 "+String(location_temp, 1)+" ("+String(location_temp_min,1)+"..."+String(location_temp_max,1)+")"+("\202")+"C"; + weatherString += " \213 " + String(location_rh) + "%"; + weatherString += " \215 " + String((location_pres), 0) + (pressSys == 1 ? tPress : tPress0) ; + weatherString += " \214 " + windDegString + String(location_wind_spd, 1) + tSpeed; + weatherString += " \216 " + String(location_clouds) + "% " + data_weather_description + space; + } + updateForecast = 0; + updateForecastNot = false; + if(printCom){ + printTime(); + Serial.println("line =" + line); + Serial.println("======== END =========================================="); + } +} + +// ============================================================================// +// Беремо ПРОГНОЗ!!! погоди з сайту https://www.weatherbit.io // +// ============================================================================// +void getWeatherDataz0() { + if(weatherKey0=="" || !displayForecastTomorrow) return; + if(!WIFI_connected) { + updateForecasttomorrow++; + return; + } + if(printCom){ + Serial.println("======== START GET FORECAST FROM WEATHERBIT.IO ========"); + printTime(); + } + HTTPClient http; + String line=""; + String reqline="http://"+weatherHost0+"/v2.0/forecast/daily?city="+urlencode(cityID0)+"&lang="+weatherLang+"&days=2&key="+weatherKey0; + if(printCom) Serial.print(reqline); + if(http.begin(ESPclient, reqline)){ + int httpCode = http.GET(); + if(httpCode > 0) { + if(printCom) Serial.printf(" [HTTP] GET... code: %d\n", httpCode); + if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + line = http.getString(); + } + } else { + if(printCom){ + Serial.printf(" [HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.println("======== END =========================================="); + } + http.end(); + updateForecasttomorrow++; + return; + } + http.end(); + } else { + if(printCom){ + Serial.printf(" [HTTP] Unable to connect\n"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + if(line==""){ + if(printCom){ + printTime(); + Serial.printf("[HTTP] The answer is empty\n"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + const size_t capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(7) + 2*JSON_OBJECT_SIZE(37) + 2321; + DynamicJsonDocument doc(capacity); + deserializeJson(doc, line); + if(!doc.capacity()){ + if(printCom){ + printTime(); + Serial.println("Parse weather forecast - FAILED!!!"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + JsonObject data_0 = doc["data"][0]; + JsonObject data_0_weather = data_0["weather"]; + const char* data_0_weather_description = data_0_weather["description"]; // "Помірний дощ" + float data_0_max_temp = data_0["max_temp"]; // 13.4 + float data_0_min_temp = data_0["min_temp"]; // 10.9 + JsonObject data_1 = doc["data"][1]; + int data_1_rh = data_1["rh"]; // 75 + int data_1_clouds = data_1["clouds"]; // 58 + float data_1_wind_spd = data_1["wind_spd"]; // 3.75302 + JsonObject data_1_weather = data_1["weather"]; + const char* data_1_weather_description = data_1_weather["description"]; // "Світло душ дощ" + float data_1_max_temp = data_1["max_temp"]; // 16.3 + float data_1_min_temp = data_1["min_temp"]; // 10 + weatherStringZ = ""; + if(displayForecastToday){ + if(hour<18) weatherStringZ += tCurr + ":"; + if(hour<12) weatherStringZ += " \212" + String(data_0_min_temp, 1) + "...." + String(data_0_max_temp, 1) + "\202" + "C "; + if(hour<18) weatherStringZ += " " + String(data_0_weather_description) + " "; + } + if(displayForecastTomorrow) { + weatherStringZ += tTom + ": \212" + String(data_1_min_temp, 1) + "...." + String(data_1_max_temp, 1) + "\202" + "C"; + weatherStringZ += " \213 " + String(data_1_rh) + "%"; + weatherStringZ += " \214 " + String(data_1_wind_spd, 1) + tSpeed; + weatherStringZ += " " + String(data_1_weather_description); + weatherStringZ += space; + } + if(printCom) Serial.println(" Getting weather forecast for tomorrow - is OK."); + updateForecasttomorrow = 0; + updateForecastNot = false; + if(printCom){ + printTime(); + Serial.println("line =" + line); + Serial.println("======== END =========================================="); + } +} + +// =======================================================================// +// Беремо ПРОГНОЗ!!! погоди з сайту openweathermap.org // +// =======================================================================// +void getWeatherDataz1(){ + if(weatherKey1=="" || !displayForecastTomorrow) return; + if(!WIFI_connected) { + updateForecasttomorrow++; + return; + } + if(printCom){ + Serial.println("======== START GET FORECAST FROM OPENWEATHERMAP.ORG ==="); + printTime(); + } + HTTPClient http; + String line=""; + String reqline="http://"+weatherHost1+"/data/2.5/forecast/daily?id="+urlencode(cityID1)+"&units=metric&appid="+weatherKey1+"&lang="+weatherLang+"&cnt=2"; + if(printCom) Serial.print(reqline); + if(http.begin(ESPclient, reqline)){ + int httpCode = http.GET(); + if (httpCode > 0) { + if(printCom) Serial.printf(" [HTTP] GET... code: %d\n", httpCode); + if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + line = http.getString(); + } + } else { + if(printCom){ + Serial.printf(" [HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.println("======== END =========================================="); + } + http.end(); + updateForecasttomorrow++; + return; + } + http.end(); + } else { + if(printCom){ + Serial.printf(" [HTTP] Unable to connect\n"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + if(line==""){ + if(printCom){ + printTime(); + Serial.printf("[HTTP] The answer is empty\n"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + const size_t capacity = 2*JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(2) + JSON_OBJECT_SIZE(2) + 2*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 3*JSON_OBJECT_SIZE(6) + 2*JSON_OBJECT_SIZE(10) + 1281; + DynamicJsonDocument doc(capacity); + deserializeJson(doc, line); + if(!doc.capacity()){ + if(printCom){ + printTime(); + Serial.println("Parse weather forecast - FAILED!!!"); + Serial.println("======== END =========================================="); + } + updateForecasttomorrow++; + return; + } + JsonObject city = doc["city"]; + JsonObject list_0 = doc["list"][0]; + JsonObject list_0_temp = list_0["temp"]; + float data_0_min_temp = list_0_temp["min"]; // 9.63 + float data_0_max_temp = list_0_temp["max"]; // 9.69 + JsonObject list_0_weather_0 = list_0["weather"][0]; + const char* data_0_weather_description = list_0_weather_0["description"]; // "облачно" + JsonObject list_1 = doc["list"][1]; + JsonObject list_1_temp = list_1["temp"]; + float data_1_day_temp = list_1_temp["day"]; // 18.13 + float data_1_min_temp = list_1_temp["min"]; // 10.67 + float data_1_max_temp = list_1_temp["max"]; // 20.32 + int data_1_rh = list_1["humidity"]; // 56 + JsonObject list_1_weather_0 = list_1["weather"][0]; + const char* data_1_weather_description = list_1_weather_0["description"]; // "слегка облачно" + float data_1_wind_spd = list_1["speed"]; // 0.7 + int data_1_clouds = list_1["clouds"]; // 38 + weatherStringZ = ""; + if(displayForecastToday){ + if(hour<18) weatherStringZ += tCurr + ":"; + if(hour<12) weatherStringZ += " \212" + String(data_0_min_temp, 1) + "...." + String(data_0_max_temp, 1) + "\202" + "C "; + if(hour<18) weatherStringZ += " " + String(data_0_weather_description) + " "; + } + if(displayForecastTomorrow) { + weatherStringZ += tTom + ": \212" + String(data_1_min_temp, 1) + "...." + String(data_1_day_temp, 1) + "...." + String(data_1_max_temp, 1) + "\202" + "C"; + weatherStringZ += " \213 " + String(data_1_rh) + "%"; + weatherStringZ += " \214 " + String(data_1_wind_spd, 1) + tSpeed; + weatherStringZ += " \216 " + String(data_1_clouds) + "% " + String(data_1_weather_description); + weatherStringZ += space; + } + updateForecasttomorrow = 0; + updateForecastNot = false; + if(printCom){ + printTime(); + Serial.println("line =" + line); + Serial.println("======== END =========================================="); + } +}