diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..293347b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +##### Visual Studio Code ##### +.pio +.vscode +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +build_output +src/GUI-Generic.ino.cpp diff --git a/SuplaCommonPROGMEM.cpp b/SuplaCommonPROGMEM.cpp deleted file mode 100644 index 592861f4..00000000 --- a/SuplaCommonPROGMEM.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "SuplaCommonPROGMEM.h" -#include "SuplaTemplateBoard.h" - -static char buffer[15]; - -String GIPOString(uint8_t gpio) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(GPIO_P[gpio]))); - return buffer; -} - -String BME280String(uint8_t adr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(BME280_P[adr]))); - return buffer; -} - -String SHT30String(uint8_t adr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(SHT30_P[adr]))); - return buffer; -} - -String StateString(uint8_t adr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(STATE_P[adr]))); - return buffer; -} - -String LevelString(uint8_t nr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(LEVEL_P[nr]))); - return buffer; -} - -String MemoryString(uint8_t nr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(MEMORY_P[nr]))); - return buffer; -} - -String TriggerString(uint8_t nr) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(TRIGGER_P[nr]))); - return buffer; -} - -String BoardString(uint8_t board) { - strcpy_P(buffer, (char*)pgm_read_ptr(&(BOARD_P[board]))); - return buffer; -} diff --git a/SuplaConfigESP.cpp b/SuplaConfigESP.cpp deleted file mode 100644 index e4e50c0b..00000000 --- a/SuplaConfigESP.cpp +++ /dev/null @@ -1,507 +0,0 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include - -#include "SuplaConfigESP.h" -#include "SuplaConfigManager.h" -#include "SuplaDeviceGUI.h" - -SuplaConfigESP::SuplaConfigESP() { - configModeESP = NORMAL_MODE; - - if (ConfigManager->isDeviceConfigured()) { - ConfigManager->setGUIDandAUTHKEY(); - if (String(ConfigManager->get(KEY_LOGIN)->getValue()) == 0) { - ConfigManager->set(KEY_LOGIN, DEFAULT_LOGIN); - } - if (String(ConfigManager->get(KEY_LOGIN_PASS)->getValue()) == 0) { - ConfigManager->set(KEY_LOGIN_PASS, DEFAULT_LOGIN_PASS); - } - if (String(ConfigManager->get(KEY_HOST_NAME)->getValue()) == 0) { - ConfigManager->set(KEY_HOST_NAME, DEFAULT_HOSTNAME); - } - if (String(ConfigManager->get(KEY_SUPLA_SERVER)->getValue()) == 0) { - ConfigManager->set(KEY_SUPLA_SERVER, DEFAULT_SERVER); - } - if (String(ConfigManager->get(KEY_SUPLA_EMAIL)->getValue()) == 0) { - ConfigManager->set(KEY_SUPLA_EMAIL, DEFAULT_EMAIL); - } - ConfigManager->save(); - - configModeInit(); - } - - // if(String(ConfigManager->get(KEY_WIFI_SSID)->getValue()) == 0 || - // String(ConfigManager->get(KEY_WIFI_PASS)->getValue()) == 0 || - // String(ConfigManager->get(KEY_SUPLA_SERVER)->getValue()) == - // DEFAULT_SERVER || - // String(ConfigManager->get(KEY_SUPLA_EMAIL)->getValue()) == - // DEFAULT_EMAIL){ - // configModeInit(); - // return; - // } - SuplaDevice.setStatusFuncImpl(&status_func); -} - -void SuplaConfigESP::addConfigESP(int _pinNumberConfig, int _pinLedConfig, int _modeConfigButton, bool _highIsOn) { - pinNumberConfig = _pinNumberConfig; - pinLedConfig = _pinLedConfig; - modeConfigButton = _modeConfigButton; - highIsOn = _highIsOn; - - if (pinLedConfig <= 0) { - Serial.println(F("ESP - status LED disabled")); - } - else { - pinMode(pinLedConfig, OUTPUT); - digitalWrite(pinLedConfig, pinOffValue()); - } - - Supla::Control::Button *buttonConfig = new Supla::Control::Button(pinNumberConfig, true, true); - - if (modeConfigButton == CONFIG_MODE_10_ON_PRESSES) { - buttonConfig->addAction(CONFIG_MODE_10_ON_PRESSES, *ConfigESP, Supla::ON_PRESS); - } - if (modeConfigButton == CONFIG_MODE_5SEK_HOLD) { - buttonConfig->addAction(CONFIG_MODE_5SEK_HOLD, *ConfigESP, Supla::ON_PRESS); - buttonConfig->addAction(CONFIG_MODE_5SEK_HOLD, *ConfigESP, Supla::ON_RELEASE); - } -} - -void SuplaConfigESP::runAction(int event, int action) { - if (action == CONFIG_MODE_10_ON_PRESSES) { - if (millis() - cnfigChangeTimeMs > 10000UL) { - cnfigChangeTimeMs = millis(); - countPresses = 0; - } - countPresses++; - - if (countPresses == 10) { - // Serial.println(F("CONFIG_MODE_3_PRESSES")); - configModeInit(); - countPresses = 0; - return; - } - } - - if (action == CONFIG_MODE_5SEK_HOLD) { - if (event == Supla::ON_PRESS) { - cnfigChangeTimeMs = millis(); - } - if (event == Supla::ON_RELEASE) { - if (millis() - cnfigChangeTimeMs > 5000UL) { - if (!digitalRead(pinNumberConfig)) { - // Serial.println(F("CONFIG_MODE_5SEK_HOLD")); - configModeInit(); - } - } - cnfigChangeTimeMs = 0; - } - } - - if (configModeESP == CONFIG_MODE) { - if (event == Supla::ON_PRESS) { - rebootESP(); - } - } -} - -void SuplaConfigESP::rebootESP() { - delay(1000); - WiFi.forceSleepBegin(); - wdt_reset(); - ESP.restart(); - while (1) wdt_reset(); -} - -void WiFiEvent(WiFiEvent_t event) { - switch (event) { - case WIFI_EVENT_STAMODE_CONNECTED: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_STAMODE_CONNECTED")); - break; - case WIFI_EVENT_STAMODE_DISCONNECTED: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WiFi lost connection")); - break; - case WIFI_EVENT_STAMODE_AUTHMODE_CHANGE: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_STAMODE_AUTHMODE_CHANGE")); - break; - case WIFI_EVENT_STAMODE_GOT_IP: - // Serial.print(millis()); - // Serial.print(" => "); - // Serial.println(F("WIFI_EVENT_STAMODE_GOT_IP")); - // Serial.println(WiFi.localIP()); - break; - case WIFI_EVENT_STAMODE_DHCP_TIMEOUT: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_STAMODE_DHCP_TIMEOUT")); - break; - case WIFI_EVENT_SOFTAPMODE_STACONNECTED: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_SOFTAPMODE_STACONNECTED")); - break; - case WIFI_EVENT_SOFTAPMODE_STADISCONNECTED: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_SOFTAPMODE_STADISCONNECTED")); - break; - case WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED: - // Serial.print(" => "); - // Serial.println("WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED")); - break; - case WIFI_EVENT_MAX: - // Serial.print(millis()); - // Serial.print(" => "); - // - // Serial.println(F("WIFI_EVENT_MAX")); - break; - } -} - -void SuplaConfigESP::configModeInit() { - configModeESP = CONFIG_MODE; - ledBlinking(100); - - WiFi.onEvent(WiFiEvent); - // Serial.print(F("Creating Access Point")); - // Serial.print(F("Setting mode ... ")); - // Serial.println(WiFi.mode(WIFI_AP_STA) ? "Ready" : "Failed!"); - WiFi.softAPdisconnect(true); - WiFi.disconnect(true); - WiFi.mode(WIFI_AP_STA); - - String CONFIG_WIFI_NAME = "SUPLA-ESP8266-" + getMacAddress(false); - while (!WiFi.softAP(CONFIG_WIFI_NAME, "")) { - // Serial.println(F(".")); - delay(100); - } - - // Serial.println(F("Network Created!")); - // Serial.print(F("Soft-AP IP address = ")); - // Serial.println(WiFi.softAPIP()); -} - -const char *SuplaConfigESP::getLastStatusSupla() { - return supla_status.msg; -} - -void SuplaConfigESP::ledBlinking(int time) { - os_timer_disarm(&led_timer); - os_timer_setfn(&led_timer, ledBlinking_func, NULL); - os_timer_arm(&led_timer, time, true); -} - -void SuplaConfigESP::ledBlinkingStop(void) { - os_timer_disarm(&led_timer); - digitalWrite(pinLedConfig, pinOffValue()); -} - -int SuplaConfigESP::getPinLedConfig() { - return pinLedConfig; -} - -String SuplaConfigESP::getMacAddress(bool formating) { - byte mac[6]; - WiFi.macAddress(mac); - char baseMacChr[18] = {0}; - - if (formating) - sprintf(baseMacChr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - else - sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - - return String(baseMacChr); -} - -void ledBlinking_func(void *timer_arg) { - int val = digitalRead(ConfigESP->getPinLedConfig()); - digitalWrite(ConfigESP->getPinLedConfig(), val == HIGH ? 0 : 1); -} - -void status_func(int status, const char *msg) { - switch (status) { - case 2: - ConfigESP->supla_status.msg = "Już zainicjalizowane"; - break; - case 3: - ConfigESP->supla_status.msg = "Nie przypisane CB"; - break; - case 4: - ConfigESP->supla_status.msg = - "Nieprawidłowy identyfikator GUID lub rejestracja urządzeń " - "NIEAKTYWNA"; - break; - case 5: - ConfigESP->supla_status.msg = "Nieznany adres serwera"; - break; - case 6: - ConfigESP->supla_status.msg = "Nieznany identyfikator ID"; - break; - case 7: - ConfigESP->supla_status.msg = "Zainicjowany"; - break; - case 8: - ConfigESP->supla_status.msg = "Przekroczono limit kanału"; - break; - case 9: - ConfigESP->supla_status.msg = "Rozłączony"; - break; - case 10: - ConfigESP->supla_status.msg = "Rejestracja w toku"; - break; - case 11: - ConfigESP->supla_status.msg = "Błąd zmiennej"; - break; - case 12: - ConfigESP->supla_status.msg = "Błąd wersji protokołu"; - break; - case 13: - ConfigESP->supla_status.msg = "Złe poświadczenia"; - break; - case 14: - ConfigESP->supla_status.msg = "Tymczasowo niedostępne"; - break; - case 15: - ConfigESP->supla_status.msg = "Konflikt lokalizacji"; - break; - case 16: - ConfigESP->supla_status.msg = "Konflikt kanałów"; - break; - case 17: - ConfigESP->supla_status.msg = "Zarejestrowany i gotowy"; - break; - case 18: - ConfigESP->supla_status.msg = "Urządzenie jest rozłączone"; - break; - case 19: - ConfigESP->supla_status.msg = "Lokalizacja jest wyłączona"; - break; - case 20: - ConfigESP->supla_status.msg = "Przekroczono limit urządzeń"; - } - - static int lock; - if (status == 17 && ConfigESP->configModeESP == NORMAL_MODE) { - ConfigESP->ledBlinkingStop(); - lock = 0; - } - else if (status != 17 && lock == 0 && ConfigESP->configModeESP == NORMAL_MODE) { - ConfigESP->ledBlinking(500); - lock = 1; - } - - if (ConfigESP->supla_status.old_msg != ConfigESP->supla_status.msg) { - ConfigESP->supla_status.old_msg = ConfigESP->supla_status.msg; - ConfigESP->supla_status.status = status; - // Serial.println(ConfigESP->supla_status.msg); - } -} -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -int SuplaConfigESP::getMemoryRelay(int nr) { - uint8_t gpio; - for (gpio = 0; gpio <= OFF_GPIO; gpio++) { - String key = GPIO; - key += gpio; - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_RELAY) { - if (ConfigManager->get(key.c_str())->getElement(NR).toInt() == nr) { - uint8_t memory = ConfigManager->get(key.c_str())->getElement(MEMORY).toInt(); - return memory; - } - } - } - return OFF_GPIO; -} -#endif - -int SuplaConfigESP::getGpio(int nr, int function) { - uint8_t gpio; - for (gpio = 0; gpio <= OFF_GPIO; gpio++) { - String key = GPIO; - key += gpio; - if (function == FUNCTION_CFG_BUTTON) { - if (checkBusyCfg(gpio)) { - return gpio; - } - } - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == function) { - if (ConfigManager->get(key.c_str())->getElement(NR).toInt() == nr) { - return gpio; - } - } - } - return OFF_GPIO; -} - -int SuplaConfigESP::getLevel(int nr, int function) { - uint8_t gpio; - for (gpio = 0; gpio <= OFF_GPIO; gpio++) { - String key = GPIO; - key += gpio; - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == function) { - if (ConfigManager->get(key.c_str())->getElement(NR).toInt() == nr) { - uint8_t level = ConfigManager->get(key.c_str())->getElement(LEVEL).toInt(); - return level; - } - } - } - return OFF_GPIO; -} - -bool SuplaConfigESP::checkBusyCfg(int gpio) { - String key = GPIO; - key += gpio; - if (ConfigManager->get(key.c_str())->getElement(FUNCTION_CFG_LED).toInt() == 1) { - return true; - } - return false; -} - -int SuplaConfigESP::checkBusyGpio(int gpio, int function) { - if (gpio == 6 || gpio == 7 || gpio == 8 || gpio == 11) { - return true; - } - else { - String key = GPIO; - key += gpio; - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_BUTTON) { - if (function == FUNCTION_CFG_BUTTON) { - return false; - } - } - if (checkBusyCfg(gpio)) { - if (function != FUNCTION_BUTTON) { - return true; - } - } - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() != FUNCTION_OFF) { - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() != function) { - return true; - } - } - return false; - } -} - -void SuplaConfigESP::setGpio(uint8_t gpio, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory) { - String key = GPIO; - key += gpio; - if (function == FUNCTION_CFG_BUTTON) { - ConfigManager->setElement(key.c_str(), CFG, 1); - return; - } - - ConfigManager->setElement(key.c_str(), NR, nr); - ConfigManager->setElement(key.c_str(), FUNCTION, function); - ConfigManager->setElement(key.c_str(), LEVEL, level); - ConfigManager->setElement(key.c_str(), MEMORY, memory); - - // ConfigManager->setElement(key.c_str(), MEMORY, memory); - // ConfigManager->setElement(key.c_str(), CFG, cfg); -} - -void SuplaConfigESP::clearGpio(uint8_t gpio, uint8_t function) { - String key = GPIO; - key += gpio; - if (function == FUNCTION_CFG_BUTTON) { - ConfigManager->setElement(key.c_str(), CFG, 0); - return; - } - ConfigManager->setElement(key.c_str(), NR, 0); - ConfigManager->setElement(key.c_str(), FUNCTION, FUNCTION_OFF); - ConfigManager->setElement(key.c_str(), LEVEL, 0); - ConfigManager->setElement(key.c_str(), MEMORY, 0); -} - -uint8_t SuplaConfigESP::countFreeGpio(uint8_t exception) { - uint8_t count = 0; - for (uint8_t gpio = 0; gpio < OFF_GPIO; gpio++) { - if (gpio != 6 && gpio != 7 && gpio != 8 && gpio != 11) { - String key = GPIO; - key += gpio; - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == exception) { - count++; - } - } - } - return count; -} - -void SuplaConfigESP::factoryReset() { - delay(1000); - pinMode(0, INPUT); - if (!digitalRead(0)) { - Serial.println("FACTORY RESET!!!"); - - ConfigManager->set(KEY_WIFI_SSID, ""); - ConfigManager->set(KEY_WIFI_PASS, ""); - ConfigManager->set(KEY_SUPLA_SERVER, DEFAULT_SERVER); - ConfigManager->set(KEY_SUPLA_EMAIL, DEFAULT_EMAIL); - ConfigManager->set(KEY_HOST_NAME, DEFAULT_HOSTNAME); - ConfigManager->set(KEY_LOGIN, DEFAULT_LOGIN); - ConfigManager->set(KEY_LOGIN_PASS, DEFAULT_LOGIN_PASS); - ConfigManager->set(KEY_MAX_ROLLERSHUTTER, "0"); - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigManager->set(KEY_MAX_LIMIT_SWITCH, "0"); - ConfigManager->set(KEY_MAX_DHT22, "1"); - ConfigManager->set(KEY_MAX_DHT11, "1"); - ConfigManager->set(KEY_MULTI_MAX_DS18B20, "1"); - ConfigManager->set(KEY_ALTITUDE_BME280, "0"); - - int nr; - String key; - - for (nr = 0; nr <= 17; nr++) { - key = GPIO; - key += nr; - ConfigManager->set(key.c_str(), "0,0,0,0,0"); - } - - ConfigManager->set(KEY_ACTIVE_SENSOR, "0,0,0,0,0"); - - for (nr = 0; nr <= MAX_DS18B20; nr++) { - key = KEY_DS; - key += nr; - ConfigManager->set(key.c_str(), ""); - key = KEY_DS_NAME; - key += nr; - ConfigManager->set(key.c_str(), ""); - } - - ConfigManager->save(); - - delay(3000); - WiFi.forceSleepBegin(); - wdt_reset(); - ESP.restart(); - while (1) wdt_reset(); - } -} diff --git a/SuplaTemplateBoard.cpp b/SuplaTemplateBoard.cpp deleted file mode 100644 index 59c22ba3..00000000 --- a/SuplaTemplateBoard.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "SuplaTemplateBoard.h" -#include "SuplaWebPageRelay.h" -#include - -void chooseTemplateBoard(uint8_t board) { - switch (board) { - case BOARD_ELECTRODRAGON: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(16, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "2"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(2, 2, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "2"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(13, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_INCAN3: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(2, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "2"); - ConfigESP->setGpio(14, 1, FUNCTION_BUTTON, Supla::ON_CHANGE); - ConfigESP->setGpio(12, 2, FUNCTION_BUTTON, Supla::ON_CHANGE); - - ConfigManager->set(KEY_MAX_RELAY, "2"); - ConfigESP->setGpio(5, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(13, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - - ConfigManager->set(KEY_MAX_LIMIT_SWITCH, "2"); - ConfigESP->setGpio(4, 1, FUNCTION_LIMIT_SWITCH, 0); - ConfigESP->setGpio(16, 2, FUNCTION_LIMIT_SWITCH, 0); - break; - case BOARD_MELINK: - ConfigESP->setGpio(5, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(12, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(5, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(4, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_NEO_COOLCAM: - ConfigESP->setGpio(13, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(4, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(13, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SHELLY1: - ConfigESP->setGpio(5, FUNCTION_CFG_BUTTON); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(5, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(4, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SHELLY2: - ConfigESP->setGpio(12, FUNCTION_CFG_BUTTON); - - ConfigManager->set(KEY_MAX_BUTTON, "2"); - ConfigESP->setGpio(12, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(14, 2, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "2"); - ConfigESP->setGpio(4, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(5, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_BASIC: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_DUAL_R2: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "2"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(9, 2, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "2"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(5, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_S2X: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - - ConfigESP->setGpio(14, FUNCTION_SI7021_SONOFF); - break; - case BOARD_SONOFF_TH: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - - ConfigESP->setGpio(14, FUNCTION_SI7021_SONOFF); - break; - case BOARD_SONOFF_TOUCH: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_TOUCH_2CH: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "2"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(9, 2, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "2"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(5, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_TOUCH_3CH: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "3"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(9, 2, FUNCTION_BUTTON, Supla::ON_PRESS); - ConfigESP->setGpio(10, 3, FUNCTION_BUTTON, Supla::ON_PRESS); - - ConfigManager->set(KEY_MAX_RELAY, "3"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(5, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(4, 3, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - case BOARD_SONOFF_4CH: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(13, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "4"); - ConfigESP->setGpio(0, 1, FUNCTION_BUTTON, Supla::ON_CHANGE); - ConfigESP->setGpio(9, 2, FUNCTION_BUTTON, Supla::ON_CHANGE); - ConfigESP->setGpio(10, 3, FUNCTION_BUTTON, Supla::ON_CHANGE); - ConfigESP->setGpio(14, 4, FUNCTION_BUTTON, Supla::ON_CHANGE); - - ConfigManager->set(KEY_MAX_RELAY, "4"); - ConfigESP->setGpio(12, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(5, 2, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(4, 3, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - ConfigESP->setGpio(15, 4, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - - break; - case BOARD_YUNSHAN: - ConfigESP->setGpio(0, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(2, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(3, 1, FUNCTION_BUTTON, Supla::ON_CHANGE); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(4, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - - case BOARD_YUNTONG_SMART: - ConfigESP->setGpio(12, FUNCTION_CFG_BUTTON); - ConfigESP->setGpio(15, FUNCTION_CFG_LED, HIGH); - - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigESP->setGpio(12, 1, FUNCTION_BUTTON, Supla::ON_CHANGE); - - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigESP->setGpio(4, 1, FUNCTION_RELAY, HIGH, MEMORY_RELAY_RESTORE); - break; - - default: - ConfigManager->set(KEY_MAX_BUTTON, "1"); - ConfigManager->set(KEY_MAX_RELAY, "1"); - ConfigManager->set(KEY_MAX_LIMIT_SWITCH, "0"); - break; - } -} diff --git a/SuplaWebPageConfig.cpp b/SuplaWebPageConfig.cpp deleted file mode 100644 index c613d939..00000000 --- a/SuplaWebPageConfig.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "SuplaWebPageConfig.h" -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" -#include "SuplaCommonPROGMEM.h" -#include "GUIGenericCommon.h" - -SuplaWebPageConfig *WebPageConfig = new SuplaWebPageConfig(); - -SuplaWebPageConfig::SuplaWebPageConfig() { -} - -void SuplaWebPageConfig::createWebPageConfig() { - String path; - path = PATH_START; - path += PATH_CONFIG; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageConfig::handleConfig, this)); - path = PATH_START; - path += PATH_SAVE_CONFIG; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageConfig::handleConfigSave, this)); -} - -void SuplaWebPageConfig::handleConfig() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_config(0)); -} - -void SuplaWebPageConfig::handleConfigSave() { - // Serial.println(F("HTTP_POST - metoda handleConfigSave")); - - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String key, input; - input = INPUT_CFG_LED_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_CFG_LED) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_CFG_LED)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), 1, FUNCTION_CFG_LED, 1); - } - else if (ConfigESP->getGpio(FUNCTION_CFG_LED) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_CFG_LED) { - key = GPIO; - key += ConfigESP->getGpio(FUNCTION_CFG_LED); - input = INPUT_CFG_LED_LEVEL; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); - } - else { - WebServer->sendContent(supla_webpage_config(6)); - return; - } - } - - input = INPUT_CFG_BTN_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_CFG_BUTTON) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_CFG_BUTTON)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_CFG_BUTTON) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_CFG_BUTTON)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_CFG_BUTTON); - } - else if (ConfigESP->checkBusyGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_BUTTON) == false) { - // ConfigManager->setElement(key.c_str(), CFG, 1); - ConfigESP->setGpio(key.toInt(), FUNCTION_CFG_BUTTON); - } - else { - WebServer->sendContent(supla_webpage_config(6)); - return; - } - } - - if (ConfigESP->getGpio(FUNCTION_CFG_BUTTON) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_CFG_BUTTON), FUNCTION_CFG_BUTTON); - } - - /*#ifdef SUPLA_BUTTON - for (int i = 1; i <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); i++) { - key = GPIO; - key += ConfigESP->getGpio(i, FUNCTION_BUTTON); - if (ConfigESP->getGpio(i, FUNCTION_BUTTON) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) - { if (ConfigManager->get(key.c_str())->getElement(CFG).toInt() == 1) { ConfigManager->setElement(key.c_str(), CFG, 0); - } - } - } - #endif*/ - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_config(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_config(2)); - break; - } -} - -String SuplaWebPageConfig::supla_webpage_config(int save) { - uint8_t selected, suported; - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_CONFIG); - page += F("

"); - page += S_GPIO_SETTINGS_FOR_CONFIG; - page += F("

"); - page += F(""); - page += WebServer->selectGPIO(INPUT_CFG_LED_GPIO, FUNCTION_CFG_LED); - page += F(""); - - if (selected != 17) { - page += F(""); - } - page += F(""); - page += WebServer->selectGPIO(INPUT_CFG_BTN_GPIO, FUNCTION_CFG_BUTTON); - page += F(""); - page += F("
"); - page += F("
"); - page += F(""); - return page; -} diff --git a/SuplaWebPageControl.cpp b/SuplaWebPageControl.cpp deleted file mode 100644 index 0635389b..00000000 --- a/SuplaWebPageControl.cpp +++ /dev/null @@ -1,326 +0,0 @@ -#include "SuplaWebPageControl.h" -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" -#include "SuplaCommonPROGMEM.h" -#include "GUIGenericCommon.h" - -SuplaWebPageControl *WebPageControl = new SuplaWebPageControl(); - -void SuplaWebPageControl::createWebPageControl() { - String path; - path += PATH_START; - path += PATH_CONTROL; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControl, this)); - path = PATH_START; - path += PATH_SAVE_CONTROL; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControlSave, this)); - -#ifdef SUPLA_BUTTON - for (uint8_t i = 1; i <= MAX_GPIO; i++) { - path = PATH_START; - path += PATH_BUTTON_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSet, this)); - - path = PATH_START; - path += PATH_SAVE_BUTTON_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSaveSet, this)); - } -#endif -} - -void SuplaWebPageControl::handleControl() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_control(0)); -} - -void SuplaWebPageControl::handleControlSave() { - // Serial.println(F("HTTP_POST - metoda handleControlSave")); - - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - String key, input; - uint8_t nr, current_value, last_value; -#ifdef SUPLA_BUTTON - last_value = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); - current_value = WebServer->httpServer.arg(INPUT_MAX_BUTTON).toInt(); - - if (last_value > 0) { - for (nr = 1; nr <= last_value; nr++) { - input = INPUT_BUTTON_GPIO; - input += nr; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(nr, FUNCTION_BUTTON) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO || ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, FUNCTION_BUTTON)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_BUTTON, 2); - } - else if (ConfigESP->getGpio(nr, FUNCTION_BUTTON) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_BUTTON) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_BUTTON, ConfigESP->getLevel(nr, FUNCTION_BUTTON)); - } - else { - WebServer->sendContent(supla_webpage_control(6)); - return; - } - } - } - } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_BUTTON, WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str()); - } -#endif - -#ifdef SUPLA_LIMIT_SWITCH - last_value = ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); - current_value = WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).toInt(); - - if (last_value > 0) { - for (nr = 1; nr <= last_value; nr++) { - input = INPUT_LIMIT_SWITCH_GPIO; - input += nr; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO || ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_LIMIT_SWITCH, 0); - } - else if (ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_LIMIT_SWITCH) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_LIMIT_SWITCH, ConfigESP->getLevel(nr, FUNCTION_LIMIT_SWITCH)); - } - else { - WebServer->sendContent(supla_webpage_control(6)); - return; - } - } - } - } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_LIMIT_SWITCH, WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str()); - } -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_control(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_control(2)); - break; - } -} - -String SuplaWebPageControl::supla_webpage_control(int save) { - uint8_t nr, suported, selected; - String pagebutton, key; - - pagebutton += WebServer->SuplaSaveResult(save); - pagebutton += WebServer->SuplaJavaScript(PATH_CONTROL); - pagebutton += F("

"); - pagebutton += S_GPIO_SETTINGS_FOR_BUTTONS; - pagebutton += F("

"); - pagebutton += F(""); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); nr++) { - pagebutton += F(""); - pagebutton += WebServer->selectGPIO(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr); - pagebutton += F(""); - } - pagebutton += F("
"); -#endif - -#ifdef SUPLA_LIMIT_SWITCH - pagebutton += F("

"); - pagebutton += S_GPIO_SETTINGS_FOR_LIMIT_SWITCH; - pagebutton += F("

"); - pagebutton += F(""); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { - pagebutton += F(""); - pagebutton += WebServer->selectGPIO(INPUT_LIMIT_SWITCH_GPIO, FUNCTION_LIMIT_SWITCH, nr); - pagebutton += F(""); - } - pagebutton += F("
"); -#endif - - pagebutton += F("
"); - pagebutton += F("
"); - pagebutton += F(""); - return pagebutton; -} - -#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_RSUPLA_BUTTONELAY) || defined(SUPLA_ROLLERSHUTTER)) -void SuplaWebPageControl::handleButtonSet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_button_set(0)); -} - -void SuplaWebPageControl::handleButtonSaveSet() { - // Serial.println(F("HTTP_POST - metoda handleRelaySaveSet")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String readUrl, nr_button, key, input; - uint8_t place; - - String path = PATH_START; - path += PATH_SAVE_BUTTON_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); - key = GPIO; - key += ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON); - - input = INPUT_BUTTON_LEVEL; - input += nr_button; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - WebServer->sendContent(supla_webpage_control(1)); - break; - - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_control(2)); - break; - } -} - -String SuplaWebPageControl::supla_webpage_button_set(int save) { - String readUrl, nr_button, key; - uint8_t place, selected, suported; - - String path = PATH_START; - path += PATH_BUTTON_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); - - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_CONTROL); - uint8_t buttons = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); - if (nr_button.toInt() <= buttons && ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON) != OFF_GPIO) { - page += F("

"); - page += S_BUTTON_NR_SETTINGS; - page += F(" "); - page += nr_button; - page += F("

"); - page += F(""); - page += F("
"); - } - else { - page += F("

"); - page += S_NO_BUTTON_NR; - page += F("

"); - page += nr_button; - page += F(""); - } - page += F("
"); - page += F("
"); - - return page; -} -#endif diff --git a/SuplaWebPageControl.h b/SuplaWebPageControl.h deleted file mode 100644 index ab47ee94..00000000 --- a/SuplaWebPageControl.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SuplaWebPageControl_h -#define SuplaWebPageControl_h - -#include "SuplaWebServer.h" -#include "SuplaDeviceGUI.h" - -#define PATH_CONTROL "control" -#define PATH_SAVE_CONTROL "savecontrol" -#define PATH_BUTTON_SET "setbutton" -#define PATH_SAVE_BUTTON_SET "savesetbutton" -#define INPUT_TRIGGER "trs" -#define INPUT_BUTTON_SET "bts" -#define INPUT_BUTTON_GPIO "btg" -#define INPUT_BUTTON_LEVEL "icl" -#define INPUT_LIMIT_SWITCH_GPIO "lsg" -#define INPUT_MAX_BUTTON "mbt" -#define INPUT_MAX_LIMIT_SWITCH "mls" - -/*enum _trigger_button { - TRIGGER_PRESS, - TRIGGER_RELEASE, - TRIGGER_CHANGE -};*/ - -class SuplaWebPageControl { - public: - void createWebPageControl(); - void handleControl(); - void handleControlSave(); - -#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_RSUPLA_BUTTONELAY) || defined(SUPLA_ROLLERSHUTTER)) - void handleButtonSet(); - void handleButtonSaveSet(); -#endif - - private: - String supla_webpage_control(int save); - -#ifdef SUPLA_BUTTON - String supla_webpage_button_set(int save); -#endif - -}; - -extern SuplaWebPageControl* WebPageControl; -#endif // SuplaWebPageControl_h diff --git a/SuplaWebPageRelay.cpp b/SuplaWebPageRelay.cpp deleted file mode 100644 index a1b453fe..00000000 --- a/SuplaWebPageRelay.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "SuplaWebPageRelay.h" -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" -#include "SuplaCommonPROGMEM.h" -#include "GUIGenericCommon.h" - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -SuplaWebPageRelay *WebPageRelay = new SuplaWebPageRelay(); - -SuplaWebPageRelay::SuplaWebPageRelay() { -} - -void SuplaWebPageRelay::createWebPageRelay() { - String path; - path += PATH_START; - path += PATH_RELAY; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelay, this)); - path = PATH_START; - path += PATH_SAVE_RELAY; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySave, this)); - - for (uint8_t i = 1; i <= MAX_GPIO; i++) { - path = PATH_START; - path += PATH_RELAY_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySet, this)); - - path = PATH_START; - path += PATH_SAVE_RELAY_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySaveSet, this)); - } -} - -void SuplaWebPageRelay::handleRelay() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_relay(0)); -} - -void SuplaWebPageRelay::handleRelaySave() { - // Serial.println(F("HTTP_POST - metoda handleRelaySave")); - - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - String key, input; - uint8_t nr, current_value, last_value; - - last_value = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); - current_value = WebServer->httpServer.arg(INPUT_MAX_RELAY).toInt(); - - if (last_value > 0) { - for (nr = 1; nr <= last_value; nr++) { - input = INPUT_RELAY_GPIO; - input += nr; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(nr, FUNCTION_RELAY) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO || ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, FUNCTION_RELAY)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_RELAY, 1); - } - else if (ConfigESP->getGpio(nr, FUNCTION_RELAY) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_RELAY) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_RELAY, ConfigESP->getLevel(nr, FUNCTION_RELAY)); - } - else { - WebServer->sendContent(supla_webpage_relay(6)); - return; - } - } - } - } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_RELAY, WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str()); - } - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_relay(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_relay(2)); - break; - } -} - -String SuplaWebPageRelay::supla_webpage_relay(int save) { - String key; - uint8_t selected, suported, nr; - - String pagerelay = ""; - pagerelay += WebServer->SuplaSaveResult(save); - pagerelay += WebServer->SuplaJavaScript(PATH_RELAY); - pagerelay += F("

"); - pagerelay += S_GPIO_SETTINGS_FOR_RELAYS; - pagerelay += F("

"); - pagerelay += F(""); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { - pagerelay += F(""); - pagerelay += WebServer->selectGPIO(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr); - pagerelay += F(""); - } - pagerelay += F("
"); - pagerelay += F("
"); - pagerelay += F(""); - return pagerelay; -} - -void SuplaWebPageRelay::handleRelaySet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_relay_set(0)); -} - -void SuplaWebPageRelay::handleRelaySaveSet() { - // Serial.println(F("HTTP_POST - metoda handleRelaySaveSet")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String readUrl, nr_relay, key, input; - uint8_t place; - - String path = PATH_START; - path += PATH_SAVE_RELAY_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); - key = GPIO; - key += ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY); - - input = INPUT_RELAY_MEMORY; - input += nr_relay; - ConfigManager->setElement(key.c_str(), MEMORY, WebServer->httpServer.arg(input).toInt()); - - input = INPUT_RELAY_LEVEL; - input += nr_relay; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - WebServer->sendContent(supla_webpage_relay(1)); - break; - - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_relay(2)); - break; - } -} - -String SuplaWebPageRelay::supla_webpage_relay_set(int save) { - String readUrl, nr_relay, key; - uint8_t place, selected, suported; - - String path = PATH_START; - path += PATH_RELAY_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); - - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_RELAY); - uint8_t relays = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); - if (nr_relay.toInt() <= relays && ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY) != OFF_GPIO) { - page += F("

"); - page += S_RELAY_NR_SETTINGS; - page += F(" "); - page += nr_relay; - page += F("

"); - page += F(""); - page += F(""); - page += F("
"); - } - else { - page += F("

"); - page += S_NO_RELAY_NR; - page += F(" "); - page += nr_relay; - page += F("

"); - } - page += F("
"); - page += F("
"); - - return page; -} -#endif diff --git a/SuplaWebPageRelay.h b/SuplaWebPageRelay.h deleted file mode 100644 index dfeeaad4..00000000 --- a/SuplaWebPageRelay.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SuplaWebPageRelay_h -#define SuplaWebPageRelay_h - -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" - -#define PATH_RELAY "relay" -#define PATH_SAVE_RELAY "saverelay" -#define PATH_RELAY_SET "setrelay" -#define PATH_SAVE_RELAY_SET "savesetrelay" -#define INPUT_MAX_RELAY "mrl" -#define INPUT_RELAY_GPIO "rlg" -#define INPUT_RELAY_LEVEL "irl" -#define INPUT_RELAY_MEMORY "irm" -#define INPUT_RELAY_DURATION "ird" -#define INPUT_ROLLERSHUTTER "irsr" - -enum _memory_relay { - MEMORY_RELAY_OFF, - MEMORY_RELAY_ON, - MEMORY_RELAY_RESTORE -}; - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) - -class SuplaWebPageRelay { - public: - SuplaWebPageRelay(); - void createWebPageRelay(); - void handleRelay(); - void handleRelaySave(); - void handleRelaySet(); - void handleRelaySaveSet(); - - private: - String supla_webpage_relay_set(int save); - String supla_webpage_relay(int save); -}; - -extern SuplaWebPageRelay* WebPageRelay; -#endif -#endif // SuplaWebPageRelay_h diff --git a/SuplaWebPageSensor.cpp b/SuplaWebPageSensor.cpp deleted file mode 100644 index c1f46178..00000000 --- a/SuplaWebPageSensor.cpp +++ /dev/null @@ -1,933 +0,0 @@ -#include "SuplaWebPageSensor.h" - -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" -#include "SuplaCommonPROGMEM.h" -#include "GUIGenericCommon.h" - -SuplaWebPageSensor *WebPageSensor = new SuplaWebPageSensor(); - -SuplaWebPageSensor::SuplaWebPageSensor() { -} - -void SuplaWebPageSensor::createWebPageSensor() { - String path; - -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - path = PATH_START; - path += PATH_1WIRE; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1Wire, this)); - path = PATH_START; - path += PATH_SAVE_1WIRE; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1WireSave, this)); -#ifdef SUPLA_DS18B20 - path = PATH_START; - path += PATH_MULTI_DS; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSearchDS, this)); - path = PATH_START; - path += PATH_SAVE_MULTI_DS; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleDSSave, this)); -#endif -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - path = PATH_START; - path += PATH_I2C; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2c, this)); - path = PATH_START; - path += PATH_SAVE_I2C; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2cSave, this)); -#endif - -#if defined(SUPLA_MAX6675) - path = PATH_START; - path += PATH_SPI; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpi, this)); - path = PATH_START; - path += PATH_SAVE_SPI; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpiSave, this)); -#endif -} - -#ifdef SUPLA_DS18B20 -void SuplaWebPageSensor::handleSearchDS() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_search(0)); -} - -void SuplaWebPageSensor::handleDSSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { - String ds_key = KEY_DS; - String ds_name_key = KEY_DS_NAME; - ds_key += i; - ds_name_key += i; - - String ds = F("dschlid"); - String ds_name = F("dsnameid"); - ds += i; - ds_name += i; - - ConfigManager->set(ds_key.c_str(), WebServer->httpServer.arg(ds).c_str()); - ConfigManager->set(ds_name_key.c_str(), WebServer->httpServer.arg(ds_name).c_str()); - - Supla::GUI::sensorDS[i]->setDeviceAddress(ConfigManager->get(ds_key.c_str())->getValueBin(MAX_DS18B20_ADDRESS)); - } - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_search(1)); - // WebServer->rebootESP(); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_search(2)); - break; - } -} - -String SuplaWebPageSensor::supla_webpage_search(int save) { - String content = ""; - uint8_t count = 0; - uint8_t pin = ConfigESP->getGpio(FUNCTION_DS18B20); - - OneWire ow(pin); - DallasTemperature sensors; - DeviceAddress address; - char strAddr[64]; - uint8_t i; - - content += WebServer->SuplaSaveResult(save); - content += WebServer->SuplaJavaScript(PATH_MULTI_DS); - content += F("
"); - if (ConfigESP->getGpio(FUNCTION_DS18B20) < OFF_GPIO || !Supla::GUI::sensorDS.empty()) { - content += F("
"); - this->showDS18B20(content); - content += F("
"); - content += F("
"); - } - content += F("
"); - content += F("
"); - content += F("

"); - content += S_FOUND; - content += F(" DS18b20

"); - sensors.setOneWire(&ow); - sensors.begin(); - if (sensors.isParasitePowerMode()) { - supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is ON", pin); - } - else { - supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is OFF", pin); - } - // report parasite power requirements - for (i = 0; i < sensors.getDeviceCount(); i++) { - if (!sensors.getAddress(address, i)) { - supla_log(LOG_DEBUG, "Unable to find address for Device %d", i); - } - else { - sprintf(strAddr, "%02X%02X%02X%02X%02X%02X%02X%02X", address[0], address[1], address[2], address[3], address[4], address[5], address[6], - address[7]); - supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr); - - content += F(""); - - count++; - } - delay(0); - } - - if (count == 0) { - content += F(""); - } - content += F("
"); - content += F("
"); - content += F(""); - content += F("

"); - content += F(""); - - return content; -} - -void SuplaWebPageSensor::showDS18B20(String &content, bool readonly) { - if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { - content += F("
"); - content += F("

"); - content += S_TEMPERATURE; - content += F("

"); - for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { - String ds_key = KEY_DS; - String ds_name_key = KEY_DS_NAME; - ds_key += i; - ds_name_key += i; - - double temp = Supla::GUI::sensorDS[i]->getValue(); - content += F(""); - content += F(""); - delay(0); - } - content += F("
"); - } -} -#endif - -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) -void SuplaWebPageSensor::handle1Wire() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_1wire(0)); -} - -void SuplaWebPageSensor::handle1WireSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String key, input; - uint8_t nr, current_value, last_value; - -#ifdef SUPLA_DHT11 - last_value = ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); - current_value = WebServer->httpServer.arg(INPUT_MAX_DHT11).toInt(); - - if (last_value > 0) { - for (nr = 1; nr <= last_value; nr++) { - input = INPUT_DHT11_GPIO; - input += nr; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(nr, FUNCTION_DHT11) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO || ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, FUNCTION_DHT11)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(nr, FUNCTION_DHT11) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_DHT11)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_DHT11, 0); - } - else { - WebServer->sendContent(supla_webpage_1wire(6)); - return; - } - } - } - } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_DHT11, WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str()); - } -#endif - -#ifdef SUPLA_DHT22 - last_value = ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); - current_value = WebServer->httpServer.arg(INPUT_MAX_DHT22).toInt(); - - if (last_value > 0) { - for (nr = 1; nr <= last_value; nr++) { - input = INPUT_DHT22_GPIO; - input += nr; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(nr, FUNCTION_DHT22) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO || ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, FUNCTION_DHT22)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(nr, FUNCTION_DHT22) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_DHT22)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, FUNCTION_DHT22, 0); - } - else { - WebServer->sendContent(supla_webpage_1wire(6)); - return; - } - } - } - } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_DHT22, WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str()); - } -#endif - -#ifdef SUPLA_DS18B20 - input = INPUT_MULTI_DS_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_DS18B20) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_DS18B20)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_DS18B20) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_DS18B20)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_DS18B20); - } - else { - WebServer->sendContent(supla_webpage_1wire(6)); - return; - } - } - - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str(), "") > 0) { - ConfigManager->set(KEY_MULTI_MAX_DS18B20, WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str()); - } -#endif - -#ifdef SUPLA_SI7021_SONOFF - input = INPUT_SI7021_SONOFF; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_SI7021_SONOFF) != WebServer->httpServer.arg(input).toInt() || - WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_SI7021_SONOFF)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_SI7021_SONOFF) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_SI7021_SONOFF)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_SI7021_SONOFF); - } - else { - WebServer->sendContent(supla_webpage_1wire(6)); - return; - } - } -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_1wire(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_1wire(2)); - break; - } -} - -String SuplaWebPageSensor::supla_webpage_1wire(int save) { - uint8_t nr, suported, selected; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_1WIRE); - page += F("
"); -#ifdef SUPLA_DHT11 - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" DHT11

"); - page += F(""); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { - page += F(""); - page += WebServer->selectGPIO(INPUT_DHT11_GPIO, FUNCTION_DHT11, nr); - page += F(""); - } - page += F("
"); -#endif - -#ifdef SUPLA_DHT22 - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" DHT22

"); - page += F(""); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { - page += F(""); - page += WebServer->selectGPIO(INPUT_DHT22_GPIO, FUNCTION_DHT22, nr); - page += F(""); - } - page += F("
"); -#endif - -#ifdef SUPLA_SI7021_SONOFF - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" Si7021 Sonoff

"); - page += F(""); - page += WebServer->selectGPIO(INPUT_SI7021_SONOFF, FUNCTION_SI7021_SONOFF); - page += F(""); - page += F("
"); -#endif - -#ifdef SUPLA_DS18B20 - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" Multi DS18B20

"); - page += F(""); - page += F(""); - if (ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt() > 1) { - page += F(""); - } - page += WebServer->selectGPIO(INPUT_MULTI_DS_GPIO, FUNCTION_DS18B20); - page += F(""); - page += F("
"); -#endif - - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; -} -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) -void SuplaWebPageSensor::handlei2c() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_i2c(0)); -} - -void SuplaWebPageSensor::handlei2cSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String key, input; - uint8_t nr, current_value, last_value; - -#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT30) - input = INPUT_SDA_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_SDA) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_SDA)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_SDA) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_SDA)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_SDA); - } - else { - WebServer->sendContent(supla_webpage_i2c(6)); - return; - } - } - - input = INPUT_SCL_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_SCL) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_SCL)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_SCL) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_SCL)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_SCL); - } - else { - WebServer->sendContent(supla_webpage_i2c(6)); - return; - } - } -#endif - -#ifdef SUPLA_BME280 - key = KEY_ACTIVE_SENSOR; - input = INPUT_BME280; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_BME280, WebServer->httpServer.arg(input).toInt()); - } - - key = KEY_ALTITUDE_BME280; - input = INPUT_ALTITUDE_BME280; - if (strcmp(WebServer->httpServer.arg(INPUT_ALTITUDE_BME280).c_str(), "") != 0) { - ConfigManager->set(key.c_str(), WebServer->httpServer.arg(input).c_str()); - } -#endif - -#ifdef SUPLA_SHT30 - key = KEY_ACTIVE_SENSOR; - input = INPUT_SHT30; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SHT30, WebServer->httpServer.arg(input).toInt()); - } -#endif - -#ifdef SUPLA_SI7021 - key = KEY_ACTIVE_SENSOR; - input = INPUT_SI7021; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SI7021, WebServer->httpServer.arg(input).toInt()); - } -#endif - -#ifdef SUPLA_HC_SR04 - input = INPUT_TRIG_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_TRIG) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_TRIG)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_TRIG) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_TRIG)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_TRIG); - } - else { - WebServer->sendContent(supla_webpage_i2c(6)); - return; - } - } - - input = INPUT_ECHO_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_ECHO) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_ECHO)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_ECHO) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_ECHO)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_ECHO); - } - else { - WebServer->sendContent(supla_webpage_i2c(6)); - return; - } - } - -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_i2c(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_i2c(2)); - break; - } -} - -String SuplaWebPageSensor::supla_webpage_i2c(int save) { - uint8_t nr, suported, selected; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_I2C); - page += F("
"); - -#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT30) - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" i2c

"); - page += F(""); - page += WebServer->selectGPIO(INPUT_SDA_GPIO, FUNCTION_SDA); - page += F(""); - page += F(""); - page += WebServer->selectGPIO(INPUT_SCL_GPIO, FUNCTION_SCL); - page += F(""); - - if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { -#ifdef SUPLA_BME280 - page += F(""); - page += F(""); -#endif - -#ifdef SUPLA_SHT30 - page += F(""); -#endif - -#ifdef SUPLA_SI7021 - page += F(""); -#endif - } - page += F("
"); -#endif - -#ifdef SUPLA_HC_SR04 - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" HC-SR04

"); - page += F(""); - page += WebServer->selectGPIO(INPUT_TRIG_GPIO, FUNCTION_TRIG); - page += F(""); - page += F(""); - page += WebServer->selectGPIO(INPUT_ECHO_GPIO, FUNCTION_ECHO); - page += F(""); - page += F("
"); -#endif - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; -} -#endif - -#if defined(SUPLA_MAX6675) -void SuplaWebPageSensor::handleSpi() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_spi(0)); -} - -void SuplaWebPageSensor::handleSpiSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String key, input; - uint8_t nr, current_value, last_value; - -#if defined(SUPLA_MAX6675) - input = INPUT_CLK_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_CLK) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_CLK)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_CLK) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_CLK)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_CLK); - } - else { - WebServer->sendContent(supla_webpage_spi(6)); - return; - } - } - - input = INPUT_CS_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_CS) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_CS)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_CS) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_CS)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_CS); - } - else { - WebServer->sendContent(supla_webpage_spi(6)); - return; - } - } - - input = INPUT_D0_GPIO; - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigESP->getGpio(FUNCTION_D0) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(FUNCTION_D0)); - } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF || - (ConfigESP->getGpio(FUNCTION_D0) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_D0)) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_D0); - } - else { - WebServer->sendContent(supla_webpage_spi(6)); - return; - } - } -#endif - -#ifdef SUPLA_MAX6675 - key = KEY_ACTIVE_SENSOR; - input = INPUT_MAX6675; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_MAX6675, WebServer->httpServer.arg(input).toInt()); - } -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_spi(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_spi(2)); - break; - } -} - -String SuplaWebPageSensor::supla_webpage_spi(int save) { - uint8_t nr, suported, selected; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_SPI); - page += F("
"); - -#if defined(SUPLA_MAX6675) - page += F("

"); - page += S_GPIO_SETTINGS_FOR; - page += F(" SPI

"); - page += F(""); - page += WebServer->selectGPIO(INPUT_CLK_GPIO, FUNCTION_CLK); - page += F(""); - page += F(""); - page += WebServer->selectGPIO(INPUT_CS_GPIO, FUNCTION_CS); - page += F(""); - page += F(""); - page += WebServer->selectGPIO(INPUT_D0_GPIO, FUNCTION_D0); - page += F(""); - - if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { -#ifdef SUPLA_MAX6675 - page += F(""); -#endif - } - page += F("
"); -#endif - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; -} -#endif diff --git a/SuplaWebPageSensor.h b/SuplaWebPageSensor.h deleted file mode 100644 index 478d397e..00000000 --- a/SuplaWebPageSensor.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef SuplaWebPageSensor_h -#define SuplaWebPageSensor_h - -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" - -#define PATH_MULTI_DS "multids" -#define PATH_SAVE_MULTI_DS "savemultids" -#define PATH_1WIRE "1wire" -#define PATH_SAVE_1WIRE "save1wire" -#define PATH_I2C "i2c" -#define PATH_SAVE_I2C "savei2c" -#define PATH_SPI "spi" -#define PATH_SAVE_SPI "savespi" - -#define INPUT_MULTI_DS_GPIO "mdsg" -#define INPUT_DHT11_GPIO "dht11" -#define INPUT_DHT22_GPIO "dht22" -#define INPUT_SDA_GPIO "sdag" -#define INPUT_SCL_GPIO "sclg" -#define INPUT_BME280 "bme280" -#define INPUT_ALTITUDE_BME280 "abme280" -#define INPUT_SHT30 "sht30" -#define INPUT_SI7021 "si7021" -#define INPUT_SI7021_SONOFF "si7021sonoff" -#define INPUT_TRIG_GPIO "trig" -#define INPUT_ECHO_GPIO "echo" -#define INPUT_MAX_DHT11 "mdht11" -#define INPUT_MAX_DHT22 "mdht22" -#define INPUT_MAX_DS18B20 "maxds" -#define INPUT_CLK_GPIO "clk" -#define INPUT_CS_GPIO "cs" -#define INPUT_D0_GPIO "d0" -#define INPUT_MAX6675 "max6675" - -enum _sensorI2C -{ - SENSOR_BME280, - SENSOR_SHT30, - SENSOR_SI7021 -}; - -enum _sensorSPI -{ - SENSOR_MAX6675 -}; - -#ifdef SUPLA_BME280 -enum _bmeAdress -{ - BME280_ADDRESS_0X76 = 1, - BME280_ADDRESS_0X77, - BME280_ADDRESS_0X76_AND_0X77 -}; -#endif - -#ifdef SUPLA_SHT30 -enum _shtAdress -{ - SHT30_ADDRESS_0X44 = 1, - SHT30_ADDRESS_0X45, - SHT30_ADDRESS_0X44_AND_0X45 -}; -#endif - -class SuplaWebPageSensor { - public: - SuplaWebPageSensor(); - void createWebPageSensor(); - -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - void handle1Wire(); - void handle1WireSave(); -#endif - -#ifdef SUPLA_DS18B20 - void handleSearchDS(); - void handleDSSave(); - void showDS18B20(String& content, bool readonly = false); -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - void handlei2c(); - void handlei2cSave(); -#endif - -#if defined(SUPLA_MAX6675) - void handleSpi(); - void handleSpiSave(); -#endif - - private: -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - String supla_webpage_1wire(int save); -#ifdef SUPLA_DS18B20 - String supla_webpage_search(int save); -#endif -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - String supla_webpage_i2c(int save); -#endif -#if defined(SUPLA_MAX6675) - String supla_webpage_spi(int save); -#endif -}; - -extern SuplaWebPageSensor* WebPageSensor; -#endif // SuplaWebPageSensor_h diff --git a/SuplaWebServer.cpp b/SuplaWebServer.cpp deleted file mode 100644 index 7f9a97ec..00000000 --- a/SuplaWebServer.cpp +++ /dev/null @@ -1,633 +0,0 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "SuplaWebServer.h" -#include "SuplaDeviceGUI.h" -#include "SuplaWebPageConfig.h" -#include "SuplaWebPageControl.h" -#include "SuplaWebPageRelay.h" -#include "SuplaWebPageSensor.h" -#include "SuplaCommonPROGMEM.h" -#include "SuplaTemplateBoard.h" -#include "GUIGenericCommon.h" - -SuplaWebServer::SuplaWebServer() { -} - -void SuplaWebServer::begin() { - this->createWebServer(); - - strcpy(this->www_username, ConfigManager->get(KEY_LOGIN)->getValue()); - strcpy(this->www_password, ConfigManager->get(KEY_LOGIN_PASS)->getValue()); - - httpUpdater.setup(&httpServer, UPDATE_PATH, www_username, www_password); - httpServer.begin(); -} - -void SuplaWebServer::iterateAlways() { - httpServer.handleClient(); -} - -void SuplaWebServer::createWebServer() { - String path = PATH_START; - httpServer.on(path, HTTP_GET, std::bind(&SuplaWebServer::handle, this)); - path = PATH_START; - httpServer.on(path, std::bind(&SuplaWebServer::handleSave, this)); - path = PATH_START; - path += PATH_UPDATE; - httpServer.on(path, std::bind(&SuplaWebServer::handleFirmwareUp, this)); - path = PATH_START; - path += PATH_REBOT; - httpServer.on(path, std::bind(&SuplaWebServer::supla_webpage_reboot, this)); - path = PATH_START; - path += PATH_DEVICE_SETTINGS; - httpServer.on(path, std::bind(&SuplaWebServer::handleDeviceSettings, this)); - path = PATH_START; - path += PATH_SAVE_BOARD; - httpServer.on(path, std::bind(&SuplaWebServer::handleBoardSave, this)); - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) - WebPageRelay->createWebPageRelay(); -#endif -#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) - WebPageControl->createWebPageControl(); -#endif -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) - WebPageSensor->createWebPageSensor(); -#endif -#ifdef SUPLA_CONFIG - WebPageConfig->createWebPageConfig(); -#endif -} - -void SuplaWebServer::handle() { - // Serial.println(F("HTTP_GET - metoda handle")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); - } - this->sendContent(supla_webpage_start(0)); -} - -void SuplaWebServer::handleSave() { - // Serial.println(F("HTTP_POST - metoda handleSave")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); - } - - if (strcmp(httpServer.arg(PATH_REBOT).c_str(), "1") == 0) { - this->rebootESP(); - return; - } - - ConfigManager->set(KEY_WIFI_SSID, httpServer.arg(INPUT_WIFI_SSID).c_str()); - ConfigManager->set(KEY_WIFI_PASS, httpServer.arg(INPUT_WIFI_PASS).c_str()); - ConfigManager->set(KEY_SUPLA_SERVER, httpServer.arg(INPUT_SERVER).c_str()); - ConfigManager->set(KEY_SUPLA_EMAIL, httpServer.arg(INPUT_EMAIL).c_str()); - ConfigManager->set(KEY_HOST_NAME, httpServer.arg(INPUT_HOSTNAME).c_str()); - ConfigManager->set(KEY_LOGIN, httpServer.arg(INPUT_MODUL_LOGIN).c_str()); - ConfigManager->set(KEY_LOGIN_PASS, httpServer.arg(INPUT_MODUL_PASS).c_str()); - -#ifdef SUPLA_ROLLERSHUTTER - if (strcmp(WebServer->httpServer.arg(INPUT_ROLLERSHUTTER).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_ROLLERSHUTTER, httpServer.arg(INPUT_ROLLERSHUTTER).c_str()); - } -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - this->sendContent(supla_webpage_start(5)); - this->rebootESP(); - } - else { - this->sendContent(supla_webpage_start(7)); - } - break; - - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - this->sendContent(supla_webpage_start(4)); - break; - } -} - -void SuplaWebServer::handleFirmwareUp() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(www_username, www_password)) - return httpServer.requestAuthentication(); - } - this->sendContent(supla_webpage_upddate()); -} - -void SuplaWebServer::handleDeviceSettings() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(www_username, www_password)) - return httpServer.requestAuthentication(); - } - this->sendContent(deviceSettings(0)); -} - -String SuplaWebServer::supla_webpage_start(int save) { - String content = F(""); - content += SuplaSaveResult(save); - content += SuplaJavaScript(); - content += F("
"); - content += F("
"); - content += F("

"); - content += S_SETTING_WIFI_SSID; - content += F("

"); - content += F(" "); - content += F("get(KEY_WIFI_PASS)->getValue() != 0) { - content += F("required>"); - } - else { - content += F("'minlength='"); - content += MIN_PASSWORD; - content += F("' length="); - content += MAX_PASSWORD; - content += F(" required>"); - } - content += F(" "); - content += F(" "); - content += F("
"); - content += F("
"); - content += F("

"); - content += S_SETTING_SUPLA; - content += F("

"); - content += F(" "); - content += F(""); - content += F("
"); - - content += F("
"); - content += F("

"); - content += S_SETTING_ADMIN; - content += F("

"); - content += F(""); - content += F(""); - content += F(""); - content += F("
"); - -#ifdef SUPLA_ROLLERSHUTTER - uint8_t maxrollershutter = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); - if (maxrollershutter >= 2) { - content += F("
"); - content += F("

"); - content += S_ROLLERSHUTTERS; - content += F("

"); - content += F(""); - content += F("
"); - } -#endif - -#ifdef SUPLA_DS18B20 - WebPageSensor->showDS18B20(content, true); -#endif - - content += F("
"); - content += F("
"); - content += F(""); - content += F("

"); - content += F(""); - content += F("

"); - content += F("
"); - content += F("
"); - return content; -} - -String SuplaWebServer::supla_webpage_upddate() { - String content = ""; - content += F("
"); - content += F("

"); - content += S_SOFTWARE_UPDATE; - content += F("

"); - content += F("
"); - content += F("
"); - content += F(""); - content += F("
"); - content += F("
"); - content += F(""); - - return content; -} - -void SuplaWebServer::supla_webpage_reboot() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(www_username, www_password)) - return httpServer.requestAuthentication(); - } - this->sendContent(supla_webpage_start(2)); - this->rebootESP(); -} - -String SuplaWebServer::deviceSettings(int save) { - String content = ""; - - content += WebServer->SuplaSaveResult(save); - content += WebServer->SuplaJavaScript(PATH_DEVICE_SETTINGS); - content += F("
"); - content += F("

"); - content += S_TEMPLATE_BOARD; - content += F("

"); - content += F(""); - content += F("


"); - content += F("
"); - content += F("

"); - content += S_DEVICE_SETTINGS; - content += F("

"); - content += F("
"); - content += F("
"); - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) - content += F(""); - content += F("

"); -#endif - -#ifdef SUPLA_BUTTON - content += F(""); - content += F("

"); -#endif - -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - content += F(""); - content += F("

"); -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_HC_SR04) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - content += F(""); - content += F("

"); -#endif - -#if defined(SUPLA_MAX6675) - content += F(""); - content += F("

"); -#endif - -#ifdef SUPLA_CONFIG - content += F(""); - content += F("

"); -#endif - content += F("
"); - content += F(""); - content += F(""); - - return content; -} - -void SuplaWebServer::handleBoardSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); - } - String input = INPUT_BOARD; - - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->set(KEY_BOARD, httpServer.arg(input).c_str()); - - int nr; - String key; - for (nr = 0; nr <= 17; nr++) { - key = GPIO; - key += nr; - ConfigManager->set(key.c_str(), "0,0,0,0,0"); - } - - chooseTemplateBoard(WebServer->httpServer.arg(input).toInt()); - } - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - WebServer->sendContent(deviceSettings(1)); - break; - case E_CONFIG_FILE_OPEN: - WebServer->sendContent(deviceSettings(2)); - break; - } -} - -String SuplaWebServer::selectGPIO(const char* input, uint8_t function, uint8_t nr) { - String page = ""; - page += F(""); - return page; -} - -const String SuplaWebServer::SuplaFavicon() { - // return F("\n"); - return F(""); -} - -const String SuplaWebServer::SuplaIconEdit() { - return F( - ""); -} - -const String SuplaWebServer::SuplaJavaScript(String java_return) { - String java_script = - F("\n"); - return java_script; -} - -const String SuplaWebServer::SuplaSaveResult(int save) { - if (save == 0) - return F(""); - String saveresult = ""; - saveresult += F("
"); - if (save == 1) { - saveresult += S_DATA_SAVED; - } - else if (save == 2) { - saveresult += S_RESTART_MODULE; - } - else if (save == 3) { - saveresult += S_DATA_ERASED_RESTART_DEVICE; - } - else if (save == 4) { - saveresult += S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING; - } - else if (save == 5) { - saveresult += S_DATA_SAVED_RESTART_MODULE; - } - else if (save == 6) { - saveresult += S_WRITE_ERROR_BAD_DATA; - } - else if (save == 7) { - saveresult += F("data saved"); - } - saveresult += F("
"); - return saveresult; -} - -void SuplaWebServer::rebootESP() { - wdt_reset(); - ESP.restart(); - while (1) wdt_reset(); -} - -void SuplaWebServer::sendContent(const String content) { - // httpServer.send(200, "text/html", ""); - const int bufferSize = 1000; - String _buffer; - int bufferCounter = 0; - int fileSize = content.length(); - -#ifdef DEBUG_MODE - Serial.print("Content size: "); - Serial.println(fileSize); -#endif - - httpServer.setContentLength(fileSize); - httpServer.chunkedResponseModeStart(200, "text/html"); - - httpServer.sendContent_P(HTTP_META); - httpServer.sendContent_P(HTTP_STYLE); - httpServer.sendContent_P(HTTP_LOGO); - - String summary = FPSTR(HTTP_SUMMARY); - summary.replace("{h}", ConfigManager->get(KEY_HOST_NAME)->getValue()); - summary.replace("{s}", ConfigESP->getLastStatusSupla()); - summary.replace("{v}", Supla::Channel::reg_dev.SoftVer); - summary.replace("{g}", ConfigManager->get(KEY_SUPLA_GUID)->getValueHex(SUPLA_GUID_SIZE)); - summary.replace("{m}", ConfigESP->getMacAddress(true)); - httpServer.sendContent(summary); - - // httpServer.send(200, "text/html", ""); - for (int i = 0; i < fileSize; i++) { - _buffer += content[i]; - bufferCounter++; - - if (bufferCounter >= bufferSize) { - httpServer.sendContent(_buffer); - yield(); - bufferCounter = 0; - _buffer = ""; - } - } - if (bufferCounter > 0) { - httpServer.sendContent(_buffer); - yield(); - bufferCounter = 0; - _buffer = ""; - } - httpServer.sendContent_P(HTTP_COPYRIGHT); - - httpServer.chunkedResponseFinalize(); -} - -void SuplaWebServer::redirectToIndex() { - httpServer.sendHeader("Location", "/", true); - httpServer.send(302, "text/plain", ""); - httpServer.client().stop(); -} diff --git a/lib/SuplaDevice/.drone.yml b/lib/SuplaDevice/.drone.yml new file mode 100644 index 00000000..688641c7 --- /dev/null +++ b/lib/SuplaDevice/.drone.yml @@ -0,0 +1,17 @@ +kind: pipeline +name: default + +steps: +- name: compilation + image: rikorose/gcc-cmake + commands: + - mkdir extras/test/build + - cd extras/test/build + - cmake .. + - make + +- name: test + image: rikorose/gcc-cmake + commands: + - cd extras/test/build + - ./supladevicetests diff --git a/lib/SuplaDevice/.gitignore b/lib/SuplaDevice/.gitignore new file mode 100644 index 00000000..dcd241ea --- /dev/null +++ b/lib/SuplaDevice/.gitignore @@ -0,0 +1 @@ +extras/test/build/ diff --git a/lib/SuplaDevice/.travis.yml b/lib/SuplaDevice/.travis.yml new file mode 100644 index 00000000..bd33cea0 --- /dev/null +++ b/lib/SuplaDevice/.travis.yml @@ -0,0 +1,18 @@ +os: linux +dist: xenial +sudo: false +language: cpp + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + +script: + - mkdir extras/test/build + - cd extras/test/build + - CXX=/usr/bin/g++-6 CC=/usr/bin/gcc-6 cmake .. + - make + - ./supladevicetests diff --git a/lib/SuplaDevice/LICENSE b/lib/SuplaDevice/LICENSE new file mode 100644 index 00000000..23cb7903 --- /dev/null +++ b/lib/SuplaDevice/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/lib/SuplaDevice/_clang-format b/lib/SuplaDevice/_clang-format new file mode 100644 index 00000000..5915a3e9 --- /dev/null +++ b/lib/SuplaDevice/_clang-format @@ -0,0 +1,91 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never +... + diff --git a/lib/SuplaDevice/_config.yml b/lib/SuplaDevice/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/lib/SuplaDevice/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/lib/SuplaDevice/examples/Afore/Afore.ino b/lib/SuplaDevice/examples/Afore/Afore.ino new file mode 100644 index 00000000..859636c9 --- /dev/null +++ b/lib/SuplaDevice/examples/Afore/Afore.ino @@ -0,0 +1,74 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0 + // Put IP address of your Afore inverter, then port, and last parametere is base64 encoded "login:password" + // You can use any online base64 encoder to convert your login and password, i.e. https://www.base64encode.org/ + new Supla::PV::Afore(IPAddress(192, 168, 0, 59), 80, "bG9naW46cGFzc3dvcmQ="); + + /* + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key +} + +void loop() { + SuplaDevice.iterate(); +} + diff --git a/lib/SuplaDevice/examples/DHT/DHT.ino b/lib/SuplaDevice/examples/DHT/DHT.ino new file mode 100644 index 00000000..e0c85e2a --- /dev/null +++ b/lib/SuplaDevice/examples/DHT/DHT.ino @@ -0,0 +1,89 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +/* + * This example requires DHT sensor library installed. + * https://github.com/adafruit/DHT-sensor-library + */ + +#define DHT1PIN 24 +#define DHT1TYPE DHT22 +#define DHT2PIN 25 +#define DHT2TYPE DHT22 + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // This example adds two DHT22 sensors. + + // CHANNEL0 - DHT22 Sensor + new Supla::Sensor::DHT(DHT1PIN, DHT1TYPE); + + // CHANNEL1 - DHT22 Sensor + new Supla::Sensor::DHT(DHT2PIN, DHT2TYPE); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino b/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino new file mode 100644 index 00000000..81eb8fbc --- /dev/null +++ b/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino @@ -0,0 +1,91 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +/* + * This example requires Dallas Temperature Control library installed. + * https://github.com/milesburton/Arduino-Temperature-Control-Library + */ +// Add include to DS sensor +#include + + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0-3 - Thermometer DS18B20 + // 4 DS18B20 thermometers at pin 23. DS address can be omitted when there is only one device at a pin + DeviceAddress ds1addr = {0x28, 0xFF, 0xC8, 0xAB, 0x6E, 0x18, 0x01, 0xFC}; + DeviceAddress ds2addr = {0x28, 0xFF, 0x54, 0x73, 0x6E, 0x18, 0x01, 0x77}; + DeviceAddress ds3addr = {0x28, 0xFF, 0x55, 0xCA, 0x6B, 0x18, 0x01, 0x8D}; + DeviceAddress ds4addr = {0x28, 0xFF, 0x4F, 0xAB, 0x6E, 0x18, 0x01, 0x66}; + + new Supla::Sensor::DS18B20(23, ds1addr); + new Supla::Sensor::DS18B20(23, ds2addr); + new Supla::Sensor::DS18B20(23, ds3addr); + new Supla::Sensor::DS18B20(23, ds4addr); + + + /* + * SuplaDevice Initialization. + * Server address, LocationID and LocationPassword are available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/Fronius/Fronius.ino b/lib/SuplaDevice/examples/Fronius/Fronius.ino new file mode 100644 index 00000000..27cc0e75 --- /dev/null +++ b/lib/SuplaDevice/examples/Fronius/Fronius.ino @@ -0,0 +1,73 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0 + // Put IP address of your Fronius inverter, then port (deafult is 80) + new Supla::PV::Fronius(IPAddress(192, 168, 0, 59)); + + /* + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key +} + +void loop() { + SuplaDevice.iterate(); +} + diff --git a/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino new file mode 100644 index 00000000..0bb45c39 --- /dev/null +++ b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino @@ -0,0 +1,78 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +// Add include to HC_SR04 sensor +#include + + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + + new Supla::Sensor::HC_SR04(12,13);//(trigPin, echoPin) + + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino new file mode 100644 index 00000000..eb0d8503 --- /dev/null +++ b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino @@ -0,0 +1,85 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +// Add include to HC_SR04 sensor +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +int8_t trigPin = 13; +int8_t echoPin = 12; +int16_t minSensorRead = 5; //minimum sensor reading distance in centimeters +int16_t maxSensorRead = 55; //maximum sensor reading distance in centimeters +int16_t minAppReading = 50; //minimum distance shown by the App in centimeters when sensor read <= min_sensor_read +int16_t maxAppReading = 0; //maximum distance shown by the App in centimeters when sensor read >= max_sensor_read + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + new Supla::Sensor::HC_SR04(trigPin,echoPin, minSensorRead, maxSensorRead, minAppReading, maxAppReading); + + // Supla::Sensor::HC_SR04(trigPin, echoPin) // sends sensor reading to cloud unchanged + + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svrX.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino b/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino new file mode 100644 index 00000000..e2949220 --- /dev/null +++ b/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino @@ -0,0 +1,98 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +// Remove below line if you don't want to have internal LED blinking on each impulse +#include +#include + +// Choose where Supla should store counter data in persistant memory +// We recommend to use external FRAM memory +#define STORAGE_OFFSET 100 +#include +Supla::Eeprom eeprom(STORAGE_OFFSET); +// #include +// Supla::FramSpi fram(STORAGE_OFFSET); + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0 - Impulse Counter on pin 34, counting raising edge (from LOW to HIGH), no pullup on pin, and 10 ms debounce timeout + auto ic1 = new Supla::Sensor::ImpulseCounter(34, true, false, 10); + + // CHANNEL1 - Impulse Counter on pin 35, counting falling edge (from HIGH to LOW), with pullup on pin, and 50 ms debounce timeout + auto ic2 = new Supla::Sensor::ImpulseCounter(35, false, true, 50); + + // Configuring internal LED to notify each change of impulse counter + auto led1 = new Supla::Control::InternalPinOutput(24); // LED on pin 24 + auto led2 = new Supla::Control::InternalPinOutput(25); // LED on pin 25 + + // LED1 will blink (100 ms) on each change of ic1 + led1->setDurationMs(100); + ic1->addAction(Supla::TURN_ON, led1, Supla::ON_CHANGE); + + // LED2 will toggle it's state on each change of ic2 + ic2->addAction(Supla::TOGGLE, led2, Supla::ON_CHANGE); + + /* + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} + diff --git a/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino b/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino new file mode 100644 index 00000000..0b8c4918 --- /dev/null +++ b/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino @@ -0,0 +1,76 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + //dependence: Arduino communication library for Peacefair PZEM-004T Energy monitor https://github.com/olehs/PZEM004T + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + new Supla::Sensor::PZEMv2(5, 4); // (RX,TX) + + + /* + * SuplaDevice Initialization. + * Server address, is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino b/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino new file mode 100644 index 00000000..85fe0bb6 --- /dev/null +++ b/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino @@ -0,0 +1,78 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// dependence: Arduino library for the Updated PZEM-004T v3.0 Power and Energy +// meter https://github.com/mandulaj/PZEM-004T-v30 + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR +// Arduino Mega with EthernetShield W5100: +#include +// Ethernet MAC address +uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +Supla::EthernetShield ethernet(mac); + +// Arduino Mega with ENC28J60: +// #include +// Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +// ESP8266 and ESP32 based board: +#include +Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from + // https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: + // https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + new Supla::Sensor::PZEMv3(5, 4); // (RX,TX) + + /* + * SuplaDevice Initialization. + * Server address, is available at https://cloud.supla.org + * If you do not have an account, you can create it at + * https://cloud.supla.org/account/create SUPLA and SUPLA CLOUD are free of + * charge + * + */ + + SuplaDevice.begin( + GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/RGBW/RGBW.ino b/lib/SuplaDevice/examples/RGBW/RGBW.ino new file mode 100644 index 00000000..fb446e08 --- /dev/null +++ b/lib/SuplaDevice/examples/RGBW/RGBW.ino @@ -0,0 +1,120 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +/* + * Youtube: https://youtu.be/FE9tqzTjmA4 + * Youtube example was done on older version of SuplaDevice library + */ + +#define RED_PIN 44 +#define GREEN_PIN 45 +#define BLUE_PIN 46 +#define BRIGHTNESS_PIN 7 +#define COLOR_BRIGHTNESS_PIN 8 + +class RgbwLeds : public Supla::Control::RGBWBase { + public: + RgbwLeds(int redPin, + int greenPin, + int bluePin, + int colorBrightnessPin, + int brightnessPin) + : redPin(redPin), + greenPin(greenPin), + bluePin(bluePin), + colorBrightnessPin(colorBrightnessPin), + brightnessPin(brightnessPin) { + } + + void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) { + analogWrite(brightnessPin, (brightness * 255) / 100); + analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); + analogWrite(redPin, red); + analogWrite(greenPin, green); + analogWrite(bluePin, blue); + } + + protected: + int redPin; + int greenPin; + int bluePin; + int brightnessPin; + int colorBrightnessPin; +}; + +void setup() { + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0 - RGB controller and dimmer (RGBW) + new RgbwLeds( + RED_PIN, GREEN_PIN, BLUE_PIN, COLOR_BRIGHTNESS_PIN, BRIGHTNESS_PIN); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at + * https://cloud.supla.org/account/create SUPLA and SUPLA CLOUD are free of + * charge + * + */ + + SuplaDevice.begin( + GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino b/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino new file mode 100644 index 00000000..777b47bd --- /dev/null +++ b/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino @@ -0,0 +1,94 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include + +// Choose where Supla should store roller shutter data in persistant memory +// We recommend to use external FRAM memory +#define STORAGE_OFFSET 100 +#include +Supla::Eeprom eeprom(STORAGE_OFFSET); +// #include +// Supla::FramSpi fram(STORAGE_OFFSET); + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + Supla::Control::RollerShutter *rs = new Supla::Control::RollerShutter(30, 31, false); + Supla::Control::Button *buttonOpen = new Supla::Control::Button(28, true, true); + Supla::Control::Button *buttonClose = new Supla::Control::Button(29, true, true); + + // Add two LEDs to inform about roller shutter relay status + // If inverted value is required, please add third parameter with true value + new Supla::Control::PinStatusLed(30, 24); // pin 30 status to be informed on pin 24 + new Supla::Control::PinStatusLed(31, 25); // pin 31 status to be informed on pin 25 + + + buttonOpen->addAction(Supla::OPEN_OR_STOP, *rs, Supla::ON_PRESS); + buttonClose->addAction(Supla::CLOSE_OR_STOP, *rs, Supla::ON_PRESS); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino b/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino new file mode 100644 index 00000000..75f81134 --- /dev/null +++ b/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino @@ -0,0 +1,89 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + auto secretRelay = new Supla::Control::Relay(30, false); // Low level trigger relay on pin 30 + auto alarmRelay = new Supla::Control::Relay(31, false); // Low level trigger relay on pin 31 + auto seqButton = new Supla::Control::SequenceButton(28, true, true); // Button on pin 28 with internal pullUp + // and LOW is considered as "pressed" state + + // Sequence of lenghts [ms] of button being presset, released, pressed, released, etc. + // Aplication will print on Serial recorded sequence, so use it to record your rhythm and put it here + uint16_t sequence[30] = {90, 590, 90, 140, 90, 290, 90, 230, 90, 140, 90}; + + seqButton->setSequence(sequence); + seqButton->setMargin(0.5); // we allow +- 50% deviation of state length compared to matching sequence + + // Button will trigger secretRelay when correct rhythm will be detected or alarmRelay otherwise + seqButton->addAction(Supla::TURN_ON, secretRelay, Supla::ON_SEQUENCE_MATCH); + seqButton->addAction(Supla::TURN_ON, alarmRelay, Supla::ON_SEQUENCE_DOESNT_MATCH); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/extras/test/CMakeLists.txt b/lib/SuplaDevice/extras/test/CMakeLists.txt new file mode 100644 index 00000000..381d42d8 --- /dev/null +++ b/lib/SuplaDevice/extras/test/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.11) + +project(supladevice) + +enable_testing() + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_BUILD_TYPE Debug) + +include_directories(../../src) +include_directories(doubles) + +add_subdirectory(../../src/ build) + +mark_as_advanced( +BUILD_GMOCK +BUILD_GTEST +) + +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.10.0 + ) + +FetchContent_GetProperties(googletest) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) +endif() + +file(GLOB TEST_SRC + UptimeTests/*.cpp + ChannelTests/*cpp + IoTests/*.cpp + ElementTests/*.cpp + LocalActionTests/*.cpp + SensorTests/*.cpp + ChannelElementTests/*.cpp + InternalPinOutputTests/*.cpp + PinStatusLedTests/*.cpp + ) + +file(GLOB DOUBLE_SRC doubles/*.cpp) + +add_executable(supladevicetests ${TEST_SRC} ${DOUBLE_SRC}) + +target_link_libraries(supladevicetests + gmock + gtest + gtest_main + supladevicelib + ) + +add_test(NAME supladevicetests + COMMAND supladevicetests) diff --git a/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp b/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp new file mode 100644 index 00000000..4cb78137 --- /dev/null +++ b/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp @@ -0,0 +1,54 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +TEST(ChannelElementTests, ActionDelegationToChannel) { + Supla::ChannelElement element; + + ActionHandlerMock mock1; + ActionHandlerMock mock2; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)).Times(2); + EXPECT_CALL(mock2, handleAction(Supla::ON_TURN_OFF, action1)).Times(2); + + element.addAction(action1, mock1, Supla::ON_TURN_ON); + element.addAction(action1, &mock2, Supla::ON_TURN_OFF); + + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(true); + element.getChannel()->setNewValue(true); + + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(true); + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(false); +} + diff --git a/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp b/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp new file mode 100644 index 00000000..136316dd --- /dev/null +++ b/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp @@ -0,0 +1,492 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Args; +using ::testing::ElementsAre; + +TEST(ChannelTests, ChannelMethods) { + Supla::Channel first; + Supla::Channel second; + + EXPECT_EQ(first.getChannelNumber(), 0); + EXPECT_EQ(second.getChannelNumber(), 1); + + EXPECT_EQ(first.isExtended(), false); + EXPECT_EQ(first.isUpdateReady(), false); + EXPECT_EQ(first.getChannelType(), 0); + EXPECT_EQ(first.getExtValue(), nullptr); + + int number = first.getChannelNumber(); + char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + EXPECT_EQ(number, Supla::Channel::reg_dev.channels[number].Number); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Type, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].FuncList, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Default, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, emptyArray, SUPLA_CHANNELVALUE_SIZE)); + + first.setType(10); + EXPECT_EQ(first.getChannelType(), 10); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Type, 10); + + first.setDefault(14); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Default, 14); + + first.setFlag(2); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 2); + + first.setFlag(4); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 2 | 4); + + first.unsetFlag(2); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 4); + + first.unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, 4); + + first.setFuncList(11); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].FuncList, 11); + +} + +TEST(ChannelTests, SetNewValue) { + Supla::Channel channel; + int number = channel.getChannelNumber(); + char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, emptyArray, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_FALSE(channel.isUpdateReady()); + + char array[SUPLA_CHANNELVALUE_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7}; + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + + channel.clearUpdateReady(); + EXPECT_FALSE(channel.isUpdateReady()); + + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_FALSE(channel.isUpdateReady()); + + array[4] = 15; + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + + ASSERT_EQ(sizeof(double), 8); + double temp = 3.1415; + channel.setNewValue(temp); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &temp, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + char arrayBool[SUPLA_CHANNELVALUE_SIZE] = {}; + arrayBool[0] = true; + channel.setNewValue(true); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, arrayBool, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + channel.setNewValue(false); + arrayBool[0] = false; + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, arrayBool, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + int value = 1234; + ASSERT_EQ(sizeof(int), 4); + channel.setNewValue(value); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &value, sizeof(int))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + _supla_int64_t value64 = 124346; + ASSERT_EQ(sizeof(value64), 8); + channel.setNewValue(value64); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &value64, sizeof(value64))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + double humi = 95.2234123; + temp = 23.443322; + + int expectedTemp = temp * 1000; + int expectedHumi = humi * 1000; + + channel.setNewValue(temp, humi); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedTemp, sizeof(expectedTemp))); + EXPECT_TRUE(0 == memcmp(&(Supla::Channel::reg_dev.channels[number].value[4]), &expectedHumi, sizeof(expectedHumi))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + // RGBW channel setting + channel.setNewValue(1, 2, 3, 4, 5); + char rgbwArray[SUPLA_CHANNELVALUE_SIZE] = {5, 4, 3, 2, 1, 0, 0, 0}; + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, rgbwArray, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + TElectricityMeter_ExtendedValue_V2 emVal = {}; + TElectricityMeter_Value expectedValue = {}; + + emVal.m_count = 1; + emVal.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; + emVal.total_forward_active_energy[0] = 1000; + emVal.total_forward_active_energy[1] = 2000; + emVal.total_forward_active_energy[2] = 4000; + + expectedValue.total_forward_active_energy = (1000 + 2000 + 4000) / 1000; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.measured_values |= EM_VAR_VOLTAGE; + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 20; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE2_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 300; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE3_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 230; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON | EM_VALUE_FLAG_PHASE2_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); +} + +TEST(ChannelTests, ExtendedChannelMethods) { + Supla::ChannelExtended extChannel; + + EXPECT_TRUE(extChannel.isExtended()); + EXPECT_NE(nullptr, extChannel.getExtValue() ); + +} + +TEST(ChannelTests, ChannelValueGetters) { + Supla::Channel channel; + + EXPECT_DOUBLE_EQ(channel.getValueDouble(), 0); + + double pi = 3.1415; + channel.setNewValue(pi); + EXPECT_DOUBLE_EQ(channel.getValueDouble(), pi); + + double e = 2.71828; + channel.setNewValue(pi, e); + EXPECT_NEAR(channel.getValueDoubleFirst(), pi, 0.001); + EXPECT_NEAR(channel.getValueDoubleSecond(), e, 0.001); + + int valueInt = 2021; + channel.setNewValue(valueInt); + EXPECT_EQ(channel.getValueInt32(), valueInt); + + _supla_int64_t valueInt64 = 202013012021000; + channel.setNewValue(valueInt64); + EXPECT_EQ(channel.getValueInt64(), valueInt64); + + channel.setNewValue(true); + EXPECT_TRUE(channel.getValueBool()); + + channel.setNewValue(false); + EXPECT_FALSE(channel.getValueBool()); + + uint8_t red = 10, green = 20, blue = 30, colorBright = 50, bright = 90; + channel.setNewValue(red, green, blue, colorBright, bright); + EXPECT_EQ(channel.getValueRed(), red); + EXPECT_EQ(channel.getValueGreen(), green); + EXPECT_EQ(channel.getValueBlue(), blue); + EXPECT_EQ(channel.getValueColorBrightness(), colorBright); + EXPECT_EQ(channel.getValueBrightness(), bright); +} + +TEST(ChannelTests, SendUpdateTest) { + Supla::Channel channel; + ::testing::InSequence seq; + SrpcMock srpc; + + const char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + char array[SUPLA_CHANNELVALUE_SIZE] = {}; + array[0] = 1; + + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(emptyArray))); + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array))); + + EXPECT_FALSE(channel.isUpdateReady()); + channel.sendUpdate(nullptr); + channel.setNewValue(true); + EXPECT_TRUE(channel.isUpdateReady()); + channel.sendUpdate(nullptr); + EXPECT_FALSE(channel.isUpdateReady()); +} + +TEST(ChannelTests, BoolChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_OFF, action2)); + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action3)); + EXPECT_CALL(mock2, handleAction).Times(0); + + + ch1.addAction(action1, mock1, Supla::ON_TURN_ON); + ch1.addAction(action2, mock1, Supla::ON_TURN_OFF); + + ch1.setNewValue(true); + ch1.setNewValue(false); + ch1.setNewValue(false); // nothing should be called on mocks + ch1.setNewValue(false); // nothing should be called on mocks + + ch1.addAction(action3, mock1, Supla::ON_CHANGE); + ch1.setNewValue(true); + ch1.setNewValue(true); // nothing should be called on mocks + ch1.setNewValue(true); // nothing should be called on mocks + +} + +TEST(ChannelTests, Int32ChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + _supla_int_t value = 15; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, Int64ChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + _supla_int64_t value = 15; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, DoubleChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + double value = 3.1415; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value += 1.2; + ch1.setNewValue(value); + + value += 1.2; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, DoubleFloatChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + float value1 = 3.1415; + float value2 = 2.5; + + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); + + value1 += 1.2; + ch1.setNewValue(value1, value2); + + value2 += 1.2; + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); +} + +TEST(ChannelTests, RgbwChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + ch1.setNewValue(10, 20, 30, 90, 80); + ch1.setNewValue(10, 20, 30, 90, 80); + + ch1.setNewValue(10, 21, 30, 90, 80); + ch1.setNewValue(10, 20, 30, 90, 81); + ch1.setNewValue(10, 20, 30, 90, 81); +} diff --git a/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp b/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp new file mode 100644 index 00000000..8526dc0a --- /dev/null +++ b/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp @@ -0,0 +1,180 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include + +using ::testing::Return; +using ::testing::ElementsAreArray; + +class ElementWithChannel : public Supla::Element { + public: + Supla::Channel *getChannel() { + return &channel; + } + Supla::Channel channel; +}; + +TEST(ElementTests, ElementEmptyListTests) { + EXPECT_EQ(Supla::Element::begin(), nullptr); + EXPECT_EQ(Supla::Element::last(), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); +} + +TEST(ElementTests, ElementListAdding) { + auto el1 = new Supla::Element; + + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el1); + + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + // Element without channel number acts as channel with -1 number + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), el1); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); + + auto el2 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el2); + + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + // Element without channel number acts as channel with -1 number + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), el1); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); + + auto el3 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el3); + + delete el2; + + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el3); + + el2 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el2); + + delete el1; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el2); + + el1 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el1); + + delete el1; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el2); + + delete el2; + delete el3; + + EXPECT_EQ(Supla::Element::begin(), nullptr); + EXPECT_EQ(Supla::Element::last(), nullptr); + +} + +TEST(ElementTests, NoChannelElementMethods) { + Supla::Element el1; + + // those methods are empty, so just call to make sure that they do nothing and don't crash + el1.onInit(); + el1.onLoadState(); + el1.onSaveState(); + el1.iterateAlways(); + el1.onTimer(); + el1.onFastTimer(); + + TDSC_ChannelState channelState; + el1.handleGetChannelState(channelState); + + EXPECT_EQ(el1.getChannelNumber(), -1); + EXPECT_EQ(el1.getChannel(), nullptr); + EXPECT_EQ(el1.getSecondaryChannel(), nullptr); + EXPECT_EQ(&(el1.disableChannelState()), &el1); + EXPECT_EQ(el1.next(), nullptr); + + EXPECT_EQ(el1.iterateConnected(nullptr), true); + EXPECT_EQ(el1.iterateConnected(&el1), true); + EXPECT_EQ(el1.handleNewValueFromServer(nullptr), -1); + EXPECT_EQ(el1.handleCalcfgFromServer(nullptr), SUPLA_CALCFG_RESULT_NOT_SUPPORTED); +} + +TEST(ElementTests, ChannelElementMethods) { + ElementWithChannel el1; + TimeInterfaceMock time; + SrpcMock srpc; + + // those methods are empty, so just call to make sure that they do nothing and don't crash + el1.onInit(); + el1.onLoadState(); + el1.onSaveState(); + el1.iterateAlways(); + el1.onTimer(); + el1.onFastTimer(); + + TDSC_ChannelState channelState; + el1.handleGetChannelState(channelState); + + EXPECT_EQ(el1.getChannelNumber(), 0); + EXPECT_EQ(el1.getChannel(), &(el1.channel)); + EXPECT_EQ(el1.getSecondaryChannel(), nullptr); + EXPECT_EQ(&(el1.disableChannelState()), &el1); + EXPECT_EQ(el1.next(), nullptr); + + EXPECT_FALSE(el1.channel.isUpdateReady()); + EXPECT_EQ(el1.iterateConnected(nullptr), true); + EXPECT_EQ(el1.iterateConnected(&el1), true); + EXPECT_EQ(el1.handleNewValueFromServer(nullptr), -1); + EXPECT_EQ(el1.handleCalcfgFromServer(nullptr), SUPLA_CALCFG_RESULT_NOT_SUPPORTED); + + EXPECT_FALSE(el1.channel.isUpdateReady()); + el1.channel.setNewValue(true); + EXPECT_TRUE(el1.channel.isUpdateReady()); + + EXPECT_CALL(time, millis) + .WillOnce(Return(0)) // #1 first call after value changed to true + .WillOnce(Return(200)) // #2 two calls after value changed to true and 100 ms passed + .WillOnce(Return(200)) // #2 + .WillOnce(Return(250)) // #3 value changed, however not enough time passed + .WillOnce(Return(250)) // #4 value changed, however not enough time passed + .WillOnce(Return(400)) // #5 two calls after value changed and another >100 ms passed + .WillOnce(Return(400)); + + char array0[SUPLA_CHANNELVALUE_SIZE] = {}; + char array1[SUPLA_CHANNELVALUE_SIZE] = {}; + array1[0] = 1; + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array1))); // value at #2 + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array0))); // value at #5 + + + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #1 + EXPECT_EQ(el1.iterateConnected(nullptr), false); // #2 + + el1.channel.setNewValue(false); + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #3 + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #4 + EXPECT_EQ(el1.iterateConnected(nullptr), false); // #5 +} + + + diff --git a/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp b/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp new file mode 100644 index 00000000..fe661d4d --- /dev/null +++ b/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp @@ -0,0 +1,353 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include +#include +#include + +using ::testing::Return; + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + +TEST(InternalPinOutputTests, BasicMethodsTests) { + const int pin = 5; + const int pin2 = 6; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(pin2)).WillOnce(Return(HIGH)); + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + Supla::Control::InternalPinOutput ipo2(pin2, false); + + + EXPECT_EQ(ipo.pinOnValue(), HIGH); + EXPECT_EQ(ipo.pinOffValue(), LOW); + + EXPECT_EQ(ipo2.pinOnValue(), LOW); + EXPECT_EQ(ipo2.pinOffValue(), HIGH); + + ipo.onInit(); + ipo2.onInit(); + + EXPECT_EQ(ipo.isOn(), false); + EXPECT_EQ(ipo2.isOn(), false); + + ipo.turnOn(); + ipo2.turnOn(); + + ipo.turnOff(); + + ipo.toggle(); +} + +TEST(InternalPinOutputTests, DefaultInitialState) { + const int pin = 5; + const int pin2 = 6; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin2, OUTPUT)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + Supla::Control::InternalPinOutput ipo2(pin2, false); + + ipo.setDefaultStateOn(); + ipo2.setDefaultStateOff(); + + ipo.onInit(); + ipo2.onInit(); + +} + +TEST(InternalPinOutputTests, TurnOnWithIterations) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOnWithDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(200); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOffWithDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOff(200); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + + +TEST(InternalPinOutputTests, TurnOnWithStoredDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(300)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + EXPECT_CALL(timeMock, millis).WillOnce(Return(400)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(600)); + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(600)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.setDurationMs(200); + + ipo.turnOn(); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, HandleActionTests) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); // onInit + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); // Turn on + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); // Turn off + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); // Toggle + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.handleAction(0, Supla::TURN_ON); + + ipo.iterateAlways(); + ipo.handleAction(0, Supla::TURN_OFF); + + ipo.iterateAlways(); + ipo.handleAction(0, Supla::TOGGLE); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOnWithAction) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + ActionHandlerMock ahMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_TURN_OFF, Supla::TURN_OFF)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, 33)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_TURN_ON, Supla::TURN_ON)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, 33)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + ipo.addAction(Supla::TURN_ON, ahMock, Supla::ON_TURN_ON); + ipo.addAction(Supla::TURN_OFF, ahMock, Supla::ON_TURN_OFF); + ipo.addAction(33, ahMock, Supla::ON_CHANGE); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + diff --git a/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp b/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp new file mode 100644 index 00000000..0c9fc938 --- /dev/null +++ b/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp @@ -0,0 +1,146 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +using ::testing::Return; + +class CustomIoMock : public Supla::Io { + public: + MOCK_METHOD( + void, customPinMode, (int channelNumber, uint8_t pin, uint8_t mode)); + MOCK_METHOD(int, customDigitalRead, (int channelNumber, uint8_t pin)); + MOCK_METHOD( + void, customDigitalWrite, (int channelNumber, uint8_t pin, uint8_t val)); +}; + +TEST(IoTests, PinMode) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, pinMode(3, INPUT)); + EXPECT_CALL(ioMock, pinMode(0, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, INPUT_PULLUP)); + + Supla::Io::pinMode(3, INPUT); + Supla::Io::pinMode(0, OUTPUT); + Supla::Io::pinMode(3, INPUT_PULLUP); +} + +TEST(IoTests, PinModeWithChannelNumber) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, pinMode(3, INPUT)); + EXPECT_CALL(ioMock, pinMode(0, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, INPUT_PULLUP)); + + Supla::Io::pinMode(10, 3, INPUT); + Supla::Io::pinMode(11, 0, OUTPUT); + Supla::Io::pinMode(12, 3, INPUT_PULLUP); +} + +TEST(IoTests, DigitalWrite) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(3, HIGH)); + EXPECT_CALL(ioMock, digitalWrite(3, LOW)); + EXPECT_CALL(ioMock, digitalWrite(99, LOW)); + + Supla::Io::digitalWrite(3, HIGH); + Supla::Io::digitalWrite(3, LOW); + Supla::Io::digitalWrite(99, LOW); +} + +TEST(IoTests, DigitalWriteWithChannel) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(3, HIGH)); + EXPECT_CALL(ioMock, digitalWrite(3, LOW)); + EXPECT_CALL(ioMock, digitalWrite(99, LOW)); + + Supla::Io::digitalWrite(3, HIGH); + Supla::Io::digitalWrite(3, LOW); + Supla::Io::digitalWrite(99, LOW); +} + +TEST(IoTests, DigitalRead) { + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, digitalRead(3)) + .WillOnce(Return(LOW)) + .WillOnce(Return(HIGH)) + .WillOnce(Return(LOW)); + + EXPECT_EQ(Supla::Io::digitalRead(3), LOW); + EXPECT_EQ(Supla::Io::digitalRead(3), HIGH); + EXPECT_EQ(Supla::Io::digitalRead(3), LOW); +} + +TEST(IoTests, DigitalReadWithChannel) { + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, digitalRead(3)) + .WillOnce(Return(LOW)) + .WillOnce(Return(HIGH)) + .WillOnce(Return(LOW)); + + EXPECT_EQ(Supla::Io::digitalRead(100, 3), LOW); + EXPECT_EQ(Supla::Io::digitalRead(100, 3), HIGH); + EXPECT_EQ(Supla::Io::digitalRead(-1, 3), LOW); +} + +TEST(IoTest, OperationsWithCustomIoInteface) { + DigitalInterfaceMock hwInterfaceMock; + CustomIoMock ioMock; + + EXPECT_CALL(hwInterfaceMock, pinMode).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalWrite).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalRead).Times(0); + + EXPECT_CALL(ioMock, customPinMode(-1, 12, INPUT)); + EXPECT_CALL(ioMock, customDigitalRead(-1, 11)) + .WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, customDigitalWrite(-1, 13, HIGH)); + + Supla::Io::pinMode(12, INPUT); + EXPECT_EQ(Supla::Io::digitalRead(11), HIGH); + Supla::Io::digitalWrite(13, HIGH); + +} +TEST(IoTest, OperationsWithCustomIoIntefaceWithChannel) { + DigitalInterfaceMock hwInterfaceMock; + CustomIoMock ioMock; + + // Custom io interface should not call arduino's methods + EXPECT_CALL(hwInterfaceMock, pinMode).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalWrite).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalRead).Times(0); + + EXPECT_CALL(ioMock, customPinMode(6, 12, INPUT)); + EXPECT_CALL(ioMock, customDigitalRead(6, 11)) + .WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, customDigitalWrite(6, 13, HIGH)); + + Supla::Io::pinMode(6, 12, INPUT); + EXPECT_EQ(Supla::Io::digitalRead(6, 11), HIGH); + Supla::Io::digitalWrite(6, 13, HIGH); + +} diff --git a/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp b/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp new file mode 100644 index 00000000..bbd03614 --- /dev/null +++ b/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + +TEST(LocalActionTests, TwoItemsTests) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + + EXPECT_CALL(mock1, handleAction(event1, action1)); + EXPECT_CALL(mock1, handleAction(event1, action3)); + EXPECT_CALL(mock2, handleAction(event3, action1)); + EXPECT_CALL(mock1, handleAction(event2, action2)); + EXPECT_CALL(mock1, handleAction(event2, action4)); + EXPECT_CALL(mock1, handleAction(event2, action5)); + EXPECT_CALL(mock2, handleAction(event1, action1)); + EXPECT_CALL(mock2, handleAction(event2, action2)); + + b1->addAction(action1, mock1, event1); + b1->addAction(action2, mock1, event2); + b1->addAction(action3, mock1, event1); + b1->addAction(action4, mock1, event2); + b1->addAction(action5, mock1, event2); + b1->addAction(action1, mock2, event3); + b2->addAction(action1, mock2, event1); + b2->addAction(action2, mock2, event2); + b1->runAction(event1); + b1->runAction(event3); + b1->runAction(event2); + + b2->runAction(event1); + + delete b1; + + b2->runAction(event2); + + delete b2; +} + +TEST(LocalActionTests, FourItemsTestsNoCalls) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + auto b3 = new Supla::LocalAction; + auto b4 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + ActionHandlerMock mock3; + ActionHandlerMock mock4; + + EXPECT_CALL(mock1, handleAction).Times(0); + EXPECT_CALL(mock2, handleAction).Times(0); + EXPECT_CALL(mock3, handleAction).Times(0); + EXPECT_CALL(mock4, handleAction).Times(0); + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + int event4 = 14; + + b4->addAction(action4, mock4, event4); + b3->addAction(action3, mock3, event3); + b1->addAction(action1, mock1, event1); + b2->addAction(action2, mock2, event2); + + b1->runAction(event2); + b1->runAction(event3); + b1->runAction(event4); + + b2->runAction(event1); + b2->runAction(event3); + b2->runAction(event4); + + b3->runAction(event1); + b3->runAction(event2); + b3->runAction(event4); + + b4->runAction(event1); + b4->runAction(event2); + b4->runAction(event3); + + delete b1; + delete b2; + delete b3; + delete b4; +} + +TEST(LocalActionTests, FourItemsTestsWithCalls) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + auto b3 = new Supla::LocalAction; + auto b4 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + ActionHandlerMock mock3; + ActionHandlerMock mock4; + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + int event4 = 14; + + EXPECT_CALL(mock1, handleAction(event1, action1)); + EXPECT_CALL(mock2, handleAction(event2, action2)); + EXPECT_CALL(mock3, handleAction(event3, action3)); + EXPECT_CALL(mock4, handleAction(event4, action4)); + + b4->addAction(action4, mock4, event4); + b3->addAction(action3, mock3, event3); + b1->addAction(action1, mock1, event1); + b2->addAction(action2, mock2, event2); + + b1->runAction(event1); + b1->runAction(event2); + b1->runAction(event3); + b1->runAction(event4); + + b2->runAction(event1); + b2->runAction(event2); + b2->runAction(event3); + b2->runAction(event4); + + b3->runAction(event3); + b3->runAction(event1); + b3->runAction(event2); + b3->runAction(event4); + + b4->runAction(event1); + b4->runAction(event2); + b4->runAction(event3); + b4->runAction(event4); + + delete b1; + delete b2; + delete b3; + delete b4; +} diff --git a/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp b/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp new file mode 100644 index 00000000..c9118731 --- /dev/null +++ b/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include + + +using ::testing::Return; + +TEST(PinStatusLedTest, ReplicationTest) { + EXPECT_EQ(DigitalInterface::instance, nullptr); + + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + Supla::Control::PinStatusLed led(1, 2); + led.onInit(); + led.iterateAlways(); // LOW->LOW + led.iterateAlways(); // HIGH->HIGH + led.iterateAlways(); // LOW->LOW + +} + +TEST(PinStatusLedTest, ReplicationTestWithInvertedLogic) { + EXPECT_EQ(DigitalInterface::instance, nullptr); + + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // onInit + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // iterateAlways + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); +// EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // disable inverted logic + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + Supla::Control::PinStatusLed led(1, 2, true); + led.onInit(); + led.iterateAlways(); // LOW->HIGH + led.iterateAlways(); // HIGH->LOW + led.iterateAlways(); // LOW->HIGH + + led.setInvertedLogic(false); // LOW->LOW + led.iterateAlways(); // HIGH->HIGH + +} diff --git a/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp b/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp new file mode 100644 index 00000000..cea47588 --- /dev/null +++ b/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp @@ -0,0 +1,24 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +TEST(ThermometerTests, BasicTest) { + EXPECT_TRUE(true); +} + diff --git a/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp b/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp new file mode 100644 index 00000000..59241f9b --- /dev/null +++ b/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp @@ -0,0 +1,76 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +TEST(UptimeTests, LastResetCauseSetAndGet) { + Supla::Uptime uptime; + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + // setter should not work unless first resetConnectionUptime is called + uptime.setConnectionLostCause(SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + uptime.resetConnectionUptime(); + uptime.setConnectionLostCause(SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); +} + +TEST(UptimeTests, IterateShouldIncreaseUptimeCounters) { + Supla::Uptime uptime; + unsigned long millis = 0; + + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 999; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 1500; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 2); + EXPECT_EQ(uptime.getConnectionUptime(), 2); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 20000; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 22); + EXPECT_EQ(uptime.getConnectionUptime(), 22); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + uptime.resetConnectionUptime(); + EXPECT_EQ(uptime.getUptime(), 22); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 2500; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 24); + EXPECT_EQ(uptime.getConnectionUptime(), 2); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); +} + diff --git a/lib/SuplaDevice/extras/test/doubles/Arduino.h b/lib/SuplaDevice/extras/test/doubles/Arduino.h new file mode 100644 index 00000000..518485d2 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/Arduino.h @@ -0,0 +1,115 @@ +#ifndef _test_double_arduino_h +#define _test_double_arduino_h + +#include + +#include + +typedef std::string String; + +#define LSBFIRST 0 +#define INPUT 0 +#define INPUT_PULLUP 2 +#define OUTPUT 1 +#define HIGH 1 +#define LOW 0 + +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); +void pinMode(uint8_t pin, uint8_t mode); +unsigned long millis(); + + +class SerialStub { + public: + SerialStub() { + } + + virtual ~SerialStub() { + } + + int printf(const char *format, ...) { + return 0; + } + int print(const String &) { + return 0; + } + + int print(const char[]) { + return 0; + } + + int print(char) { + return 0; + } + + int print(unsigned char) { + return 0; + } + + int print(int) { + return 0; + } + + int print(unsigned int) { + return 0; + } + + int print(long) { + return 0; + } + + int print(unsigned long) { + return 0; + } + + int print(double) { + return 0; + } + + + int println(const String &s) { + return 0; + } + + int println(const char[]) { + return 0; + } + + int println(char) { + return 0; + } + + int println(unsigned char) { + return 0; + } + + int println(int) { + return 0; + } + + int println(unsigned int) { + return 0; + } + + int println(long) { + return 0; + } + + int println(unsigned long) { + return 0; + } + + int println(double) { + return 0; + } + + int println(void) { + return 0; + } + +}; + +extern SerialStub Serial; + +#endif diff --git a/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp b/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp new file mode 100644 index 00000000..f64576a2 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include "Arduino.h" +#include +#include "arduino_mock.h" + +SerialStub Serial; + +DigitalInterface::DigitalInterface() { + instance = this; +} + +DigitalInterface::~DigitalInterface() { + instance = nullptr; +} + +DigitalInterface *DigitalInterface::instance = nullptr; + +TimeInterface::TimeInterface() { + instance = this; +} + +TimeInterface::~TimeInterface() { + instance = nullptr; +} + +TimeInterface *TimeInterface::instance = nullptr; + +void digitalWrite(uint8_t pin, uint8_t val) { + DigitalInterface::instance->digitalWrite(pin, val); +} + +int digitalRead(uint8_t pin) { + return DigitalInterface::instance->digitalRead(pin); +} + +void pinMode(uint8_t pin, uint8_t mode) { + DigitalInterface::instance->pinMode(pin, mode); +} + +unsigned long millis() { + return TimeInterface::instance->millis(); +} + + diff --git a/lib/SuplaDevice/extras/test/doubles/arduino_mock.h b/lib/SuplaDevice/extras/test/doubles/arduino_mock.h new file mode 100644 index 00000000..8daf5a1e --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/arduino_mock.h @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _arduino_mock_h +#define _arduino_mock_h + +#include "Arduino.h" +#include + +class DigitalInterface { + public: + DigitalInterface(); + virtual ~DigitalInterface(); + virtual void digitalWrite(uint8_t, uint8_t) = 0; + virtual int digitalRead(uint8_t) = 0; + virtual void pinMode(uint8_t, uint8_t) = 0; + + static DigitalInterface *instance; +}; + +class TimeInterface { + public: + TimeInterface(); + virtual ~TimeInterface(); + virtual unsigned long millis() = 0; + + static TimeInterface *instance; +}; + + +class DigitalInterfaceMock : public DigitalInterface { + public: + MOCK_METHOD(void, digitalWrite, (uint8_t, uint8_t), (override)); + MOCK_METHOD(int, digitalRead, (uint8_t), (override)); + MOCK_METHOD(void, pinMode, (uint8_t, uint8_t), (override)); + +}; + +class TimeInterfaceMock : public TimeInterface { + public: + MOCK_METHOD(unsigned long, millis, (), (override)); +}; + +#endif diff --git a/lib/SuplaDevice/extras/test/doubles/log.cpp b/lib/SuplaDevice/extras/test/doubles/log.cpp new file mode 100644 index 00000000..e117cc36 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/log.cpp @@ -0,0 +1,22 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +void supla_log(int __pri, const char *__fmt, ...) { + return; +} + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc.cpp b/lib/SuplaDevice/extras/test/doubles/srpc.cpp new file mode 100644 index 00000000..5ac9386a --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc.cpp @@ -0,0 +1,29 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value) { + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value) { + return 0; +} + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp b/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp new file mode 100644 index 00000000..46d5e16c --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include "srpc_mock.h" + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value) { + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value) { + std::vector vec(value, value + 8); + return SrpcInterface::instance->valueChanged(_srpc, channel_number, vec); +} + +SrpcInterface::SrpcInterface() { + instance = this; +} + +SrpcInterface::~SrpcInterface() { + instance = nullptr; +} + +SrpcInterface *SrpcInterface::instance = nullptr; + + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc_mock.h b/lib/SuplaDevice/extras/test/doubles/srpc_mock.h new file mode 100644 index 00000000..1c7d677c --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc_mock.h @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _srpc_mock_h +#define _srpc_mock_h + +#include +#include +#include + +class SrpcInterface { + public: + SrpcInterface(); + virtual ~SrpcInterface(); + + virtual _supla_int_t valueChanged(void *srpc, unsigned char channelNumber, std::vector value) = 0; + + static SrpcInterface *instance; +}; + +class SrpcMock : public SrpcInterface { + public: + MOCK_METHOD(_supla_int_t, valueChanged, (void *, unsigned char, std::vector), (override)); +}; + +#endif diff --git a/lib/SuplaDevice/library.properties b/lib/SuplaDevice/library.properties new file mode 100644 index 00000000..87d96835 --- /dev/null +++ b/lib/SuplaDevice/library.properties @@ -0,0 +1,11 @@ +name=SuplaDevice +author=AC SOFTWARE SP. Z O.O. +maintainer=Krzysztof Lewandowski +sentence=Library enables you to connect the device to the SUPLA automation system. +paragraph=It provides easy interface for adding various sensors, relays, buttons, roller shutters, etc. that can be controlled via SUPLA Cloud and application on mobile device. +url=https://github.com/SUPLA/arduino +architectures=avr,esp32,esp8266 +version=2.3.2 +dependencies= +core-dependencies=arduino (>=1.5.0) +category=Communication diff --git a/lib/SuplaDevice/readme.md b/lib/SuplaDevice/readme.md new file mode 100644 index 00000000..a80f3eca --- /dev/null +++ b/lib/SuplaDevice/readme.md @@ -0,0 +1,248 @@ +# SUPLA project + +[SUPLA](https://www.supla.org) is an open source project for home automation. + +# SuplaDevice library for Arduino IDE + +SuplaDevice is a library for [Arduino IDE](https://www.arduino.cc/en/main/software) that allows to implement devices working with [Supla](https://www.supla.org). + +## Library installation + +There are few options how to install library in Arduino IDE. Here is one example: +1. Download SuplaDevice repository as a zip file (click green "Code" button on github repository) +2. Extract downloaded zip file +3. Copy whole SuplaDevice subfolder to a location where Arduino keeps libraries (in Arduino IDE open File->Preferences and there is a sketch location folder - libraries are kept in "libraries" subfolder) +4. You should be able to open SuplaDevice exmaples in Arduino IDE + +## Hardware requirements + +### Arduino Mega +SuplaDevice works with Arduino Mega boards. Currently Arduino Uno is not supported because of RAM limitations. It should work on other Arduino boards with at least 8 kB of RAM. +Following network interfaces are supported: +* Ethernet Shield with W5100 chipset +* ENC28J60 (not recommended - see Supported hardware section) + +Warning: WiFi shields are currently not supported + +### ESP8266 +ESP8266 boards are supported. Network connection is done via internal WiFi. Tested with ESP8266 boards 2.6.3. +Most probably it will work with other ESP8266 compatible boards. + +### ESP32 +Experimental support for ESP32 boards is provided. Some issues seen with reconnection to WiFi router which requires further analysis. + +## Installation + +Before you start, you will need to: +1. install Arduino IDE, +2. install support for your board +3. install driver for your USB to serial converter device (it can be external device, or build in on your board) +4. make sure that communication over serial interface with your board is working (i.e. check some example Arduino application) +5. download and install this librarary by copying SuplaDevice folder into your Arduino library folder + +Steps 1-4 are standard Arudino IDE setup procedures not related to SuplaDevice library. You can find many tutorials on Internet with detailed instructions. Tutorials doesn't have to be related in any way with Supla. + +After step 5 you should see Supla example applications in Arduino IDE examples. Select one and have fun! Example file requires adjustments before you compile them and upload to your board. Please read all comments in example and make proper adjustments. + +## Usage + +### Network interfaces +Supported network interfaces for Arduino Mega: +* Ethernet Shield - with W5100 chipset. Include `` and add `Supla::EthernetShield ethernet;` as a global variable. +* ENC28J60 - it requires additional UIPEthenet library (https://github.com/ntruchsess/arduino_uip). Include `` and +add `Supla::ENC28J60 ethernet;` as a global variable. Warning: network initialization on this library is blocking. In case of missing ENC28J60 board +or some other problem with network, program will stuck on initialization and will not work until connection is properly esablished. +Second warning: UIPEthernet library is consuming few hundred of bytes of RAM memory more, compared to standard Ethernet library. + +Supported network interface for ESP8266: +* There is a native WiFi controller. Include `` and add `Supla::ESPWifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. +Warning: by default connection with Supla server is encrypted. Default settings of SSL consumes big amount of RAM. +To disable SSL connection, use: + `wifi.enableSSL(false);` + + + +SSL certificate verification. +If you specify Supla's server certificate thumbprint there will be additional verification proceeded. Please use this method to configure fingerprint for validation: + `wifi.setServersCertFingerprint("9ba818295ec60652f8221500e15288d7a611177");' + + + +Supported network interface for ESP32: +* There is a native WiFi controller. Include `` and add `Supla::ESP32Wifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. + +### Exmaples + +Each example can run on Arduino Mega, ESP8266, or ESP32 board - unless mentioned otherwise in comments. Please read comments in example files and uncomment proper library for your network interface. + +SuplaSomfy - this example is not updated yet. + +### Folder structure + +* `supla-common` - Supla protocol definitions and structures. There are also methods to handle low level communication with Supla server, like message coding, decoding, sending and receiving. Those files are common with `supla-core` and the same code is run on multiple Supla platforms and services +* `supla/network` - implementation of network interfaces for supported boards +* `supla/sensor` - implementation of Supla sensor channels (thermometers, open/close sensors, etc.) +* `supla/control` - implementation of Supla control channels (various combinations of relays, buttons, action triggers) +* `supla/clock` - time services used in library (i.e. RTC) +* `supla` - all common classes are defined in main `supla` folder. You can find there classes that create framework on which all other components work. + +Some functions from above folders have dependencies to external libraries. Please check documentation included in header files. + +### How does it work? + +Everything that is visible in Supla (in Cloud, on API, mobile application) is called "channel". Supla channels are used to control relays, read temperature, check if garage door is open. + +SuplaDevice implements support for channels in `Channel` and `ChannelExtended` classes. Instances of those classes are part of objects called `Element` which are building blocks for any SuplaDevice application. + +All sensors, relays, buttons objects inherits from `Element` class. Each instance of such object will automatically register in SuplaDevice and proper virtual methods will be called by SuplaDevice in a specified way. + +All elements have to be constructed before `SuplaDevice.begin()` method is called. + +Supla channel number is assigned to each elemement with channel in an order of creation of objects. First channel will get number 0, second 1, etc. Supla server will not accept registration of device when order of channels is changed, or some channel is removed. In such case, you should remove device from Supla Cloud and register it again from scratch. + +`Element` class defines follwoing virtual methods that are called by SuplaDevice: +1. `onInit` - called first within `SuplaDevice.begin()` method. It should initialize your element. +2. `onLoadState` - called second within `SuplaDevice.begin()` method. It reads configuration data from persistent memory storage. +3. `onSaveState` - called in `SuplaDevice.iterate()` - it saves state data to persistant storage. It is not called on each iteration. `Storage` class makes sure that storing to memory does not happen to often and time delay between saves depends on implementation. +3. `iterateAlways` - called on each iteration of `SuplaDevice.iterate()` method, regardless of network/connection status. Be careful - some other methods called in `SuplaDevice.iterate()` method may block program execution for some time (even few seconds) - i.e. trying to establish connection with Supla server is blocking - in case server is not accessible, it will iterfere with `iterateAlways` method. So time critical functions should not be put here. +4. `iterateConnected` - called on each iterateion of `SuplaDevice.iterate()` method when device is connected and properly registered in Supla server. This method usually checks if there is some new data to be send to server (i.e. new temperature reading) and sends it. +5. `onTimer` - called every 10 ms after enabling in `SuplaDevice.begin()` +6. `onFastTimer` - called every 1 ms (0.5 ms in case of Arudino Mega) after enabling in `SuplaDevice.begin()` + +## How to migrate programs written in SuplaDevice libraray versions 1.6 and older + +For Arduino Mega applications include proper network interface header: +``` +// Choose proper network interface for your card: +// Arduino Mega with EthernetShield W5100: +#include +// Ethernet MAC address +uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +Supla::EthernetShield ethernet(mac); +// +// Arduino Mega with ENC28J60: +// #include +// Supla::ENC28J60 ethernet(mac); + +``` + +For ESP8266 based applications include wifi header and provide WIFI SSID and password: +``` +// ESP8266 based board: +#include +Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +``` + +In case of ESP8266 remove all methods that defined network interface. They were usually added on the bottom of ino file, after end of loop() method, i.e.: +``` +// Supla.org ethernet layer + int supla_arduino_tcp_read(void *buf, int count) { +... +SuplaDeviceCallbacks supla_arduino_get_callbacks(void) { +... +``` + +Remove also those lines: +``` +#define SUPLADEVICE_CPP +WiFiClient client; +``` +You may also remove all WIFI related includes from ino file. + +Common instruction for all boards: + +If you use local IP address, please provide it in constructor of your network inteface class, i.e.: +``` +Supla::EthernetShield ethernet(mac, localIp); +``` + +After that go to SuplaDevice.begin() method. Old code looked like this +``` +SuplaDevice.begin(GUID, // Global Unique Identifier + mac, // Ethernet MAC address + "svr1.supla.org", // SUPLA server address + locationId, // Location ID + locationPassword); // Location Password +``` +This method requires now different set of parameters: +``` + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "mail@address.pl", // Email address + AUTHKEY); // Authentication key +``` +What is different? Well, GUID and Supla server address it the same as previously. MAC address, location ID, location password are removed. +MAC address was moved to network interface class. Location ID and password were replaced with new authentication method - via email address +and authentication key. You can generate your authentication key in the same way as GUID (it is actually in exactly the same format): +``` +// Generate AUTHKEY from https://www.supla.org/arduino/get-authkey +char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; +``` + +Next change is for custom digitalWrite and digitalRead methods. Those can be used to create virtual digital pins. Instead of adding custom +callback method that overrides digitalWrite/Read method, you should create a new class which inhertis from Supla::Io base class and define +your own customDigitalRead/Write methods. Here is short example (you can put this code in ino file, before setup()): +``` +#include + +class MyDigitalRead : public Supla::Io { + public: + int customDigitalRead(int channelNumber, uint8_t pin) { + if (channelNumber == MY_EXTRA_VIRTUAL_CHANNEL) { + return someCustomSourceOfData.getValue(); + } else { + return ::digitalRead(pin); + } + } +} + +MyDigitalRead instanceMyDigitalRead; +``` + +All channels from old version of library should be removed and created again in a new format. Please check instructions below how to add each type of channel. + +## Supported channels +### Sensors +Sensor category is for all elements/channels that reads something and provides data to Supla serwer. + + +### Control +Control category is for all elements/channels that are used to control something, i.e. relays, buttons, RGBW. + +## Supported persistant memory storage +Storage class is used as an abstraction for different persistant memory devices. Some elements/channels will not work properly without storage and some will have limitted functionalities. I.e. `ImpulseCounter` requires storage to save counter value, so it could be restored after reset, or `RollerShutter` requires storage to keep openin/closing times and current shutter possition. Currently two variants of storage classes are supported. +### EEPROM/Flash memory +EEPROM (in case of Arduino Mega) and Flash (in case of ESP) are build into most of boards. Usually writing should be safe for 100 000 write operations (on each bit). So in order to limit those operations, this kind of Storage will save data in longer time periods (every few minutes). Supla will not write data if there is no change. +``` +#include +Supla::Eeprom eeprom(SUPLA_STORAGE_OFFSET); +``` +Offset parameter is optional - use it if you already use saving to EEPROM in your application and you want SuplaDevice to use some other area of memory. + +### Adafruit FRAM SPI +FRAM is recommended for storage in Supla. It allows almost limitless writing cycles and it is very fast memory. +Currently only Adafruit FRAM SPI is supported. +``` +#include +// Hardware SPI +Supla::FramSpi fram(FRAM_CS, SUPLA_STORAGE_OFFSET); +``` +or with SW SPI: +``` +// Software SPI +Supla::FramSpi fram(SCK_PIN, MISO_PIN, MOSI_PIN, FRAM_CS, SUPLA_STORAGE_OFFSET); +``` + +## History + +Version 2.3.0 + + +## Credits + + +## License + + +Please check it [here](https://github.com/SUPLA/supla-cloud/blob/master/LICENSE). + diff --git a/lib/SuplaDevice/src/CMakeLists.txt b/lib/SuplaDevice/src/CMakeLists.txt new file mode 100644 index 00000000..0a9d65ff --- /dev/null +++ b/lib/SuplaDevice/src/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SRCS + supla/uptime.cpp + supla/channel.cpp + supla/channel_extended.cpp + supla/io.cpp + supla/tools.cpp + supla/element.cpp + supla/local_action.cpp + supla/channel_element.cpp + supla/control/internal_pin_output.cpp + supla/control/pin_status_led.cpp +) + +add_library(supladevicelib SHARED ${SRCS}) diff --git a/lib/SuplaDevice/src/SuplaDevice.cpp b/lib/SuplaDevice/src/SuplaDevice.cpp new file mode 100644 index 00000000..de04f397 --- /dev/null +++ b/lib/SuplaDevice/src/SuplaDevice.cpp @@ -0,0 +1,519 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "SuplaDevice.h" +#include "supla-common/IEEE754tools.h" +#include "supla-common/log.h" +#include "supla-common/srpc.h" +#include "supla/channel.h" +#include "supla/element.h" +#include "supla/io.h" +#include "supla/storage/storage.h" +#include "supla/timer.h" + +void SuplaDeviceClass::status(int status, const char *msg) { + if (impl_arduino_status != NULL) { + impl_arduino_status(status, msg); + } else { + if (currentStatus != status) { + currentStatus = status; + supla_log(LOG_DEBUG, "Current status: [%d] %s", status, msg); + } + } +} + +SuplaDeviceClass::SuplaDeviceClass() + : port(-1), + connectionFailCounter(0), + networkIsNotReadyCounter(0), + currentStatus(STATUS_UNKNOWN) { + srpc = NULL; + registered = 0; + last_iterate_time = 0; + wait_for_iterate = 0; +} + +SuplaDeviceClass::~SuplaDeviceClass() { +} + +void SuplaDeviceClass::setStatusFuncImpl( + _impl_arduino_status impl_arduino_status) { + this->impl_arduino_status = impl_arduino_status; +} + +bool SuplaDeviceClass::isInitialized(bool msg) { + if (srpc != NULL) { + if (msg) + status(STATUS_ALREADY_INITIALIZED, "SuplaDevice is already initialized"); + + return true; + } + + return false; +} + +bool SuplaDeviceClass::begin(char GUID[SUPLA_GUID_SIZE], + const char *Server, + const char *email, + char authkey[SUPLA_AUTHKEY_SIZE], + unsigned char version) { + setGUID(GUID); + setServer(Server); + setEmail(email); + setAuthKey(authkey); + + return begin(version); +} + +bool SuplaDeviceClass::begin(unsigned char version) { + if (isInitialized(true)) return false; + supla_log(LOG_DEBUG, "Supla - starting initialization"); + + Supla::Storage::Init(); + + if (Supla::Network::Instance() == NULL) { + status(STATUS_MISSING_NETWORK_INTERFACE, "Network Interface not defined!"); + return false; + } + + // Supla::Storage::LoadDeviceConfig(); + // Supla::Storage::LoadElementConfig(); + + // Pefrorm dry run of write state to validate stored state section with current + // device configuration + Serial.println(F("Validating storage state section with current device configuration")); + Supla::Storage::PrepareState(true); + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onSaveState(); + } + // If state storage validation was successful, perform read state + if (Supla::Storage::FinalizeSaveState()) { + Serial.println(F("Storage state section validation completed. Loading elements state...")); + // Iterate all elements and load state + Supla::Storage::PrepareState(); + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onLoadState(); + } + } + + // Initialize elements + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onInit(); + } + + // Enable timers + Supla::initTimers(); + + + bool emptyGuidDetected = true; + for (int i = 0; i < SUPLA_GUID_SIZE; i++) { + if (Supla::Channel::reg_dev.GUID[i] != 0) { + emptyGuidDetected = false; + } + } + if (emptyGuidDetected) { + status(STATUS_INVALID_GUID, "Invalid GUID"); + return false; + } + + if (Supla::Channel::reg_dev.ServerName[0] == '\0') { + status(STATUS_UNKNOWN_SERVER_ADDRESS, "Unknown server address"); + return false; + } + + if (Supla::Channel::reg_dev.Email[0] == '\0') { + status(STATUS_MISSING_CREDENTIALS, "Unknown email address"); + return false; + } + + bool emptyAuthKeyDetected = true; + for (int i = 0; i < SUPLA_AUTHKEY_SIZE; i++) { + if (Supla::Channel::reg_dev.AuthKey[i] != 0) { + emptyAuthKeyDetected = false; + break; + } + } + if (emptyAuthKeyDetected) { + status(STATUS_MISSING_CREDENTIALS, "Unknown AuthKey"); + return false; + } + + if (strnlen(Supla::Channel::reg_dev.Name, SUPLA_DEVICE_NAME_MAXSIZE) == 0) { +#if defined(ARDUINO_ARCH_ESP8266) + setString( + Supla::Channel::reg_dev.Name, "ESP8266", SUPLA_DEVICE_NAME_MAXSIZE); +#elif defined(ARDUINO_ARCH_ESP32) + setString(Supla::Channel::reg_dev.Name, "ESP32", SUPLA_DEVICE_NAME_MAXSIZE); +#else + setString( + Supla::Channel::reg_dev.Name, "ARDUINO", SUPLA_DEVICE_NAME_MAXSIZE); +#endif + } + + if (strnlen(Supla::Channel::reg_dev.SoftVer, SUPLA_SOFTVER_MAXSIZE) == 0) { + setString(Supla::Channel::reg_dev.SoftVer, + "User SW, lib 2.3.2", + SUPLA_SOFTVER_MAXSIZE); + } + + Serial.println(F("Initializing network layer")); + Supla::Network::Setup(); + + TsrpcParams srpc_params; + srpc_params_init(&srpc_params); + srpc_params.data_read = &Supla::data_read; + srpc_params.data_write = &Supla::data_write; + srpc_params.on_remote_call_received = &Supla::message_received; + srpc_params.user_params = this; + + srpc = srpc_init(&srpc_params); + Supla::Network::SetSrpc(srpc); + + // Set Supla protocol interface version + srpc_set_proto_version(srpc, version); + + supla_log(LOG_DEBUG, "Using Supla protocol version %d", version); + + status(STATUS_INITIALIZED, "SuplaDevice initialized"); + return true; +} + +void SuplaDeviceClass::setName(const char *Name) { + if (isInitialized(true)) return; + setString(Supla::Channel::reg_dev.Name, Name, SUPLA_DEVICE_NAME_MAXSIZE); +} + +void SuplaDeviceClass::setString(char *dst, const char *src, int max_size) { + if (src == NULL) { + dst[0] = 0; + return; + } + + int size = strlen(src); + + if (size + 1 > max_size) size = max_size - 1; + + memcpy(dst, src, size); +} + +void SuplaDeviceClass::onTimer(void) { + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onTimer(); + } +} + +void SuplaDeviceClass::onFastTimer(void) { + // Iteration over all impulse counters will count incomming impulses. It is + // after SuplaDevice initialization (because we have to read stored counter + // values) and before any other operation like connection to Supla cloud + // (because we want to count impulses even when we have connection issues. + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onFastTimer(); + } +} + +void SuplaDeviceClass::iterate(void) { + if (!isInitialized(false)) return; + + unsigned long _millis = millis(); + unsigned long time_diff = abs(_millis - last_iterate_time); + + uptime.iterate(_millis); + + // Iterate all elements + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->iterateAlways(); + } + + // Iterate all elements and saves state + if (Supla::Storage::SaveStateAllowed(_millis)) { + Supla::Storage::PrepareState(); + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onSaveState(); + } + Supla::Storage::FinalizeSaveState(); + } + + if (wait_for_iterate != 0 && _millis < wait_for_iterate) { + return; + + } else { + wait_for_iterate = 0; + } + + // Restart network after >1 min of failed connection attempts + if (connectionFailCounter > 30) { + connectionFailCounter = 0; + supla_log(LOG_DEBUG, + "Connection fail counter overflow. Trying to setup network " + "interface again"); + Supla::Network::Setup(); + return; + } + + if (!Supla::Network::IsReady()) { + uptime.setConnectionLostCause( + SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); + wait_for_iterate = millis() + 100; + status(STATUS_NETWORK_DISCONNECTED, "No connection to network"); + networkIsNotReadyCounter++; + if (networkIsNotReadyCounter > 20) { + networkIsNotReadyCounter = 0; + connectionFailCounter++; + } + return; + } + networkIsNotReadyCounter = 0; + + if (!Supla::Network::Connected()) { + status(STATUS_SERVER_DISCONNECTED, "Not connected to Supla server"); + + uptime.setConnectionLostCause( + SUPLA_LASTCONNECTIONRESETCAUSE_SERVER_CONNECTION_LOST); + + registered = 0; + + int result = + Supla::Network::Connect(Supla::Channel::reg_dev.ServerName, port); + if (1 == result) { + uptime.resetConnectionUptime(); + connectionFailCounter = 0; + supla_log(LOG_DEBUG, "Connected to Supla Server"); + } else { + supla_log(LOG_DEBUG, + "Connection fail (%d). Server: %s", + result, + Supla::Channel::reg_dev.ServerName); + + Supla::Network::Disconnect(); + wait_for_iterate = millis() + 2000; + connectionFailCounter++; + return; + } + } + + Supla::Network::Iterate(); + + if (srpc_iterate(srpc) == SUPLA_RESULT_FALSE) { + status(STATUS_ITERATE_FAIL, "Iterate fail"); + Supla::Network::Disconnect(); + + wait_for_iterate = millis() + 5000; + return; + } + + if (registered == 0) { + registered = -1; + status(STATUS_REGISTER_IN_PROGRESS, "Register in progress"); + if (!srpc_ds_async_registerdevice_e(srpc, &Supla::Channel::reg_dev)) { + supla_log(LOG_DEBUG, "Fatal SRPC failure!"); + } + + } else if (registered == 1) { + if (Supla::Network::Ping() == false) { + uptime.setConnectionLostCause( + SUPLA_LASTCONNECTIONRESETCAUSE_ACTIVITY_TIMEOUT); + supla_log(LOG_DEBUG, "TIMEOUT - lost connection with server"); + Supla::Network::Disconnect(); + } + + if (time_diff > 0) { + // Iterate all elements + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + if (!element->iterateConnected(srpc)) { + break; + } + } + + last_iterate_time = millis(); + } + } +} + +void SuplaDeviceClass::onVersionError(TSDC_SuplaVersionError *version_error) { + status(STATUS_PROTOCOL_VERSION_ERROR, "Protocol version error"); + Serial.print(F("Protocol version error. Server min: ")); + Serial.print(version_error->server_version_min); + Serial.print(F("; Server version: ")); + Serial.println(version_error->server_version); + + Supla::Network::Disconnect(); + + wait_for_iterate = millis() + 5000; +} + +void SuplaDeviceClass::onRegisterResult( + TSD_SuplaRegisterDeviceResult *register_device_result) { + _supla_int_t activity_timeout = 0; + + switch (register_device_result->result_code) { + // OK scenario + case SUPLA_RESULTCODE_TRUE: + activity_timeout = register_device_result->activity_timeout; + Supla::Network::Instance()->setActivityTimeout(activity_timeout); + registered = 1; + supla_log(LOG_DEBUG, + "Device registered (activity timeout %d s, server version: %d, " + "server min version: %d)", + register_device_result->activity_timeout, + register_device_result->version, + register_device_result->version_min); + last_iterate_time = millis(); + status(STATUS_REGISTERED_AND_READY, "Registered and ready."); + + if (activity_timeout != ACTIVITY_TIMEOUT) { + supla_log( + LOG_DEBUG, "Changing activity timeout to %d", ACTIVITY_TIMEOUT); + TDCS_SuplaSetActivityTimeout at; + at.activity_timeout = ACTIVITY_TIMEOUT; + srpc_dcs_async_set_activity_timeout(srpc, &at); + } + + return; + + // NOK scenarios + case SUPLA_RESULTCODE_BAD_CREDENTIALS: + status(STATUS_BAD_CREDENTIALS, "Bad credentials!"); + break; + + case SUPLA_RESULTCODE_TEMPORARILY_UNAVAILABLE: + status(STATUS_TEMPORARILY_UNAVAILABLE, "Temporarily unavailable!"); + break; + + case SUPLA_RESULTCODE_LOCATION_CONFLICT: + status(STATUS_LOCATION_CONFLICT, "Location conflict!"); + break; + + case SUPLA_RESULTCODE_CHANNEL_CONFLICT: + status(STATUS_CHANNEL_CONFLICT, "Channel conflict!"); + break; + case SUPLA_RESULTCODE_DEVICE_DISABLED: + status(STATUS_DEVICE_IS_DISABLED, "Device is disabled!"); + break; + + case SUPLA_RESULTCODE_LOCATION_DISABLED: + status(STATUS_LOCATION_IS_DISABLED, "Location is disabled!"); + break; + + case SUPLA_RESULTCODE_DEVICE_LIMITEXCEEDED: + status(STATUS_DEVICE_LIMIT_EXCEEDED, "Device limit exceeded!"); + break; + + case SUPLA_RESULTCODE_GUID_ERROR: + status(STATUS_INVALID_GUID, "Incorrect device GUID!"); + break; + + case SUPLA_RESULTCODE_AUTHKEY_ERROR: + status(STATUS_INVALID_GUID, "Incorrect AuthKey!"); + break; + + case SUPLA_RESULTCODE_REGISTRATION_DISABLED: + status(STATUS_INVALID_GUID, "Registration disabled!"); + break; + + case SUPLA_RESULTCODE_NO_LOCATION_AVAILABLE: + status(STATUS_INVALID_GUID, "No location available!"); + break; + + case SUPLA_RESULTCODE_USER_CONFLICT: + status(STATUS_INVALID_GUID, "User conflict!"); + break; + + default: + supla_log(LOG_ERR, + "Register result code %i", + register_device_result->result_code); + break; + } + + Supla::Network::Disconnect(); + wait_for_iterate = millis() + 5000; +} + +void SuplaDeviceClass::channelSetActivityTimeoutResult( + TSDC_SuplaSetActivityTimeoutResult *result) { + Supla::Network::Instance()->setActivityTimeout(result->activity_timeout); + supla_log( + LOG_DEBUG, "Activity timeout set to %d s", result->activity_timeout); +} + +void SuplaDeviceClass::setServerPort(int value) { + port = value; +} + +void SuplaDeviceClass::setSwVersion(const char *swVersion) { + setString(Supla::Channel::reg_dev.SoftVer, swVersion, SUPLA_SOFTVER_MAXSIZE); +} + +int SuplaDeviceClass::getCurrentStatus() { + return currentStatus; +} + +void SuplaDeviceClass::fillStateData(TDSC_ChannelState &channelState) { + channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_UPTIME | + SUPLA_CHANNELSTATE_FIELD_CONNECTIONUPTIME; + + channelState.Uptime = uptime.getUptime(); + channelState.ConnectionUptime = uptime.getConnectionUptime(); + if (uptime.getLastResetCause() > 0) { + channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_LASTCONNECTIONRESETCAUSE; + channelState.LastConnectionResetCause = uptime.getLastResetCause(); + } +} + +void SuplaDeviceClass::setGUID(char GUID[SUPLA_GUID_SIZE]) { + memcpy(Supla::Channel::reg_dev.GUID, GUID, SUPLA_GUID_SIZE); +} + +void SuplaDeviceClass::setAuthKey(char authkey[SUPLA_AUTHKEY_SIZE]) { + memcpy(Supla::Channel::reg_dev.AuthKey, authkey, SUPLA_AUTHKEY_SIZE); +} + +void SuplaDeviceClass::setEmail(const char *email) { + setString(Supla::Channel::reg_dev.Email, email, SUPLA_EMAIL_MAXSIZE); +} + +void SuplaDeviceClass::setServer(const char *server) { + setString( + Supla::Channel::reg_dev.ServerName, server, SUPLA_SERVER_NAME_MAXSIZE); +} + +void SuplaDeviceClass::onGetUserLocaltimeResult(TSDC_UserLocalTimeResult *result) { + if (clock) { + clock->parseLocaltimeFromServer(result); + } +} + +void SuplaDeviceClass::addClock(Supla::Clock *_clock) { + Serial.println(F("Clock class added")); + clock = _clock; +} + +Supla::Clock * SuplaDeviceClass::getClock() { + return clock; +} + +SuplaDeviceClass SuplaDevice; diff --git a/lib/SuplaDevice/src/SuplaDevice.h b/lib/SuplaDevice/src/SuplaDevice.h new file mode 100644 index 00000000..5c1067fe --- /dev/null +++ b/lib/SuplaDevice/src/SuplaDevice.h @@ -0,0 +1,120 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SUPLADEVICE_H +#define SUPLADEVICE_H + +#include + +#include "supla-common/proto.h" +#include "supla/network/network.h" +#include "supla/uptime.h" +#include "supla/clock/clock.h" + +#define ACTIVITY_TIMEOUT 30 + +#define STATUS_UNKNOWN -1 +#define STATUS_ALREADY_INITIALIZED 2 +#define STATUS_MISSING_NETWORK_INTERFACE 3 +#define STATUS_INVALID_GUID 4 +#define STATUS_UNKNOWN_SERVER_ADDRESS 5 +#define STATUS_UNKNOWN_LOCATION_ID 6 +#define STATUS_INITIALIZED 7 +#define STATUS_CHANNEL_LIMIT_EXCEEDED 8 +#define STATUS_SERVER_DISCONNECTED 9 +#define STATUS_REGISTER_IN_PROGRESS 10 +#define STATUS_ITERATE_FAIL 11 +#define STATUS_PROTOCOL_VERSION_ERROR 12 +#define STATUS_BAD_CREDENTIALS 13 +#define STATUS_TEMPORARILY_UNAVAILABLE 14 +#define STATUS_LOCATION_CONFLICT 15 +#define STATUS_CHANNEL_CONFLICT 16 +#define STATUS_REGISTERED_AND_READY 17 +#define STATUS_DEVICE_IS_DISABLED 18 +#define STATUS_LOCATION_IS_DISABLED 19 +#define STATUS_DEVICE_LIMIT_EXCEEDED 20 +#define STATUS_NETWORK_DISCONNECTED 21 +#define STATUS_REGISTRATION_DISABLED 22 +#define STATUS_MISSING_CREDENTIALS 23 + +typedef void (*_impl_arduino_status)(int status, const char *msg); + +class SuplaDeviceClass { + protected: + void *srpc; + char registered; + int port; + int connectionFailCounter; + int networkIsNotReadyCounter; + + unsigned long last_iterate_time; + unsigned long wait_for_iterate; + + _impl_arduino_status impl_arduino_status; + int currentStatus; + + Supla::Uptime uptime; + Supla::Clock *clock; + + bool isInitialized(bool msg); + void setString(char *dst, const char *src, int max_size); + + private: + void status(int status, const char *msg); + + public: + SuplaDeviceClass(); + ~SuplaDeviceClass(); + + void fillStateData(TDSC_ChannelState &channelState); + void addClock(Supla::Clock *clock); + Supla::Clock *getClock(); + + bool begin(char GUID[SUPLA_GUID_SIZE], + const char *Server, + const char *email, + char authkey[SUPLA_AUTHKEY_SIZE], + unsigned char version = 12); + + bool begin(unsigned char version = 12); + + void setName(const char *Name); + void setGUID(char GUID[SUPLA_GUID_SIZE]); + void setAuthKey(char authkey[SUPLA_AUTHKEY_SIZE]); + void setEmail(const char *email); + void setServer(const char *server); + + // Timer with 100 Hz frequency (10 ms) + void onTimer(void); + // TImer with 2000 Hz frequency (0.5 ms) + void onFastTimer(void); + void iterate(void); + + void setStatusFuncImpl(_impl_arduino_status impl_arduino_status); + void setServerPort(int value); + + void onVersionError(TSDC_SuplaVersionError *version_error); + void onRegisterResult(TSD_SuplaRegisterDeviceResult *register_device_result); + void channelSetActivityTimeoutResult( + TSDC_SuplaSetActivityTimeoutResult *result); + void onGetUserLocaltimeResult(TSDC_UserLocalTimeResult *result); + + void setSwVersion(const char *); + int getCurrentStatus(); +}; + +extern SuplaDeviceClass SuplaDevice; +#endif diff --git a/lib/SuplaDevice/src/SuplaSomfy.cpp b/lib/SuplaDevice/src/SuplaSomfy.cpp new file mode 100644 index 00000000..a5969ccf --- /dev/null +++ b/lib/SuplaDevice/src/SuplaSomfy.cpp @@ -0,0 +1,173 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SuplaSomfy.h" + +/* + Library for Somfy RTS remote controller + Author: Maciej Królewski + Using documantation: https://pushstack.wordpress.com/somfy-rts-protocol/ +*/ + +SuplaSomfy::SuplaSomfy(uint8_t dataPin) { + pinMode(dataPin, OUTPUT); + digitalWrite(dataPin, LOW); + + _dataPin = dataPin; +} + +SuplaSomfy::~SuplaSomfy(void) { +} + +void SuplaSomfy::SendBitZero(void) { + digitalWrite(_dataPin, HIGH); + delayMicroseconds(BASIC_TIME); + digitalWrite(_dataPin, LOW); + delayMicroseconds(BASIC_TIME); +} + +void SuplaSomfy::SendBitOne(void) { + digitalWrite(_dataPin, LOW); + delayMicroseconds(BASIC_TIME); + digitalWrite(_dataPin, HIGH); + delayMicroseconds(BASIC_TIME); +} + +uint8_t SuplaSomfy::Checksum(somfy_frame_t *frame) { + uint8_t checksum = 0; + for (uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + + return checksum & 0xF; +} + +void SuplaSomfy::Obfuscation(somfy_frame_t *frame) { + for (uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } +} + +void SuplaSomfy::SendCommand(somfy_frame_t *frame, uint8_t sync) { + // Only with the first frame + if (sync == 2) { + // Wake-up pulse + digitalWrite(_dataPin, HIGH); + delayMicroseconds(9415); + // Silence pulse + digitalWrite(_dataPin, LOW); + delayMicroseconds((unsigned int)89565); + } + + // Hardware sync (Two sync for the first frame, seven for the next frame) + for (uint8_t i = 0; i < sync; i++) { + digitalWrite(_dataPin, HIGH); + delayMicroseconds(4 * BASIC_TIME); + digitalWrite(_dataPin, LOW); + delayMicroseconds(4 * BASIC_TIME); + } + + // Software sync + digitalWrite(_dataPin, HIGH); + delayMicroseconds(4550); + digitalWrite(_dataPin, LOW); + delayMicroseconds(BASIC_TIME); + + // Send data bits one by one, starting with MSB + for (uint8_t i = 0; i < FRAME_SIZE; i++) { + for (int8_t j = 7; j >= 0; j--) { + if (bitRead(frame[i], j) == 1) { + SendBitOne(); + } else { + SendBitZero(); + } + } + } + + digitalWrite(_dataPin, LOW); + // Between frame silence + delayMicroseconds(30415); +} + +void SuplaSomfy::SetRemote(somfy_remote_t remote) { + _remote = remote; +} + +somfy_remote_t SuplaSomfy::GetRemote() { + return _remote; +} + +void SuplaSomfy::PushButton(ControlButtons pushButton) { + somfy_frame_t *frame = (somfy_frame_t *)malloc(FRAME_SIZE); + + frame[0] = 0xA7; // Encryption key + frame[1] = pushButton << 4; // MSB - Button pressed, LSB - Checksum + frame[2] = _remote.rollingCode.svalue.byte1; // Rolling code (big endian) + frame[3] = _remote.rollingCode.svalue.byte2; // Rolling code + frame[4] = _remote.remoteControl.svalue.byte1; // Remote address + frame[5] = _remote.remoteControl.svalue.byte2; // Remote address + frame[6] = _remote.remoteControl.svalue.byte3; // Remote address + +#if defined DEBUG_SOMFY + Serial.print(F("Frame : ")); + PrintHex8(frame, FRAME_SIZE); +#endif + + // Calculate checksum frame + frame[1] |= Checksum(frame); + +#if defined DEBUG_SOMFY + Serial.println(""); + Serial.print(F("With checksum : ")); + PrintHex8(frame, FRAME_SIZE); +#endif + + // Obfuscation frame + Obfuscation(frame); + +#if defined DEBUG_SOMFY + Serial.println(""); + Serial.print(F("Obfuscated : ")); + PrintHex8(frame, FRAME_SIZE); +#endif + +#if defined DEBUG_SOMFY + Serial.println(""); + Serial.print(F("Rolling Code : ")); + Serial.println(_remote.rollingCode.ivalue); + Serial.println(""); +#endif + + SendCommand(frame, 2); + for (uint8_t i = 0; i < 2; i++) { + SendCommand(frame, 7); + } + + free(frame); + _remote.rollingCode.ivalue++; +} + +#if defined DEBUG_SOMFY +void SuplaSomfy::PrintHex8(uint8_t *data, uint8_t length) { + Serial.print("0x"); + for (int i = 0; i < length; i++) { + if (data[i] < 0x10) { + Serial.print("0"); + } + Serial.print(data[i], HEX); + Serial.print(" "); + } +} +#endif diff --git a/lib/SuplaDevice/src/SuplaSomfy.h b/lib/SuplaDevice/src/SuplaSomfy.h new file mode 100644 index 00000000..c47d9004 --- /dev/null +++ b/lib/SuplaDevice/src/SuplaSomfy.h @@ -0,0 +1,93 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Library for Somfy RTS remote controller + Author: Maciej Królewski + Using documantation: https://pushstack.wordpress.com/somfy-rts-protocol/ +*/ + +#ifndef SuplaSomfy_h +#define SuplaSomfy_h + +#include "Arduino.h" + +#define DEBUG_SOMFY + +// ----- DO NOT CHANGE ----- +#define FRAME_SIZE 7 // Somfy RTS frame size +#define BASIC_TIME 604 // Basic time [us] +// ------------------------- + +typedef uint8_t somfy_frame_t; +typedef union { + struct { + uint8_t byte1; + uint8_t byte2; + } svalue; + uint8_t tvalue[2]; + uint16_t ivalue; +} somfy_rollingcode_t; +typedef union { + struct { + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + } svalue; + uint8_t tvalue[3]; + uint32_t ivalue : 24; +} somfy_remotesn_t; + +enum /*class*/ ControlButtons { + STOP = 0x1, //[My] Stop or move to favourite position + UP = 0x2, //[Up] Move Up + MYUP = 0x3, //[My + Up] Set upper motor limit in initial programming mode + DOWN = 0x4, //[Down] Move Down + MYDOWN = + 0x5, //[My + Down] Set lower motor limit in initial programming mode + UPDOWN = 0x6, //[Up + Down] Change motor limit and initial programming mode + PROG = 0x8, //[Prog] Registering / Deregistering remotes + SUN = 0x9, //[Sun + Flag] Enable sun and wind detector (SUN and FLAG TIME on + //the Telis Soliris RC) + FLAG = 0xA //[Flag] Disable sun detector (FLAG TIME on the Telis + //Soliris RC) +}; + +struct somfy_remote_t { + somfy_rollingcode_t rollingCode; + somfy_remotesn_t remoteControl; +}; + +class SuplaSomfy { + private: + uint8_t _dataPin; + somfy_remote_t _remote; + + void SendBitZero(void); + void SendBitOne(void); + uint8_t Checksum(somfy_frame_t *frame); + void Obfuscation(somfy_frame_t *frame); + void SendCommand(somfy_frame_t *frame, uint8_t sync); + + public: + SuplaSomfy(uint8_t dataPin); + ~SuplaSomfy(void); + void SetRemote(somfy_remote_t remote); + somfy_remote_t GetRemote(void); + void PushButton(ControlButtons pushButton); + +#if defined DEBUG_SOMFY + void PrintHex8(uint8_t *data, uint8_t length); +#endif +}; + +#endif diff --git a/lib/SuplaDevice/src/crc16.h b/lib/SuplaDevice/src/crc16.h new file mode 100644 index 00000000..5713f7bd --- /dev/null +++ b/lib/SuplaDevice/src/crc16.h @@ -0,0 +1,14 @@ + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} diff --git a/lib/SuplaDevice/src/supla-common/IEEE754tools.h b/lib/SuplaDevice/src/supla-common/IEEE754tools.h new file mode 100644 index 00000000..c38cfbc1 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/IEEE754tools.h @@ -0,0 +1,62 @@ +// +// FILE: IEEE754tools.h +// AUTHOR: Rob Tillaart +// VERSION: 0.1.00 +// PURPOSE: IEEE754 tools +// +// http://playground.arduino.cc//Main/IEEE754tools +// +// Released to the public domain +// not tested, use with care +// + +#ifndef IEEE754tools_h +#define IEEE754tools_h + +// IEEE754 float layout; +struct IEEEfloat +{ + uint32_t m:23; + uint8_t e:8; + uint8_t s:1; +}; + +// for packing and unpacking a float +typedef union _FLOATCONV +{ + IEEEfloat p; + float f; + uint8_t b[4]; +} _FLOATCONV; + +// Arduino UNO double layout: +// the UNO has no 64 bit double, it is only able to map 23 bits of the mantisse +// a filler is added. +struct _DBL +{ + uint32_t filler:29; + uint32_t m:23; + uint16_t e:11; + uint8_t s:1; +}; + + +// for packing and unpacking a double +typedef union _DBLCONV +{ + // IEEEdouble p; + _DBL p; + double d; // !! is a 32bit float for UNO. + uint8_t b[8]; +} _DBLCONV; + +// +// converts a float to a packed array of 8 bytes representing a 64 bit double +// restriction exponent and mantisse. +// +// float; array of 8 bytes; LSBFIRST; MSBFIRST +// +//void float2DoublePacked(float number, byte* bar, int byteOrder=LSBFIRST); + + +#endif diff --git a/lib/SuplaDevice/src/supla-common/eh.h b/lib/SuplaDevice/src/supla-common/eh.h new file mode 100644 index 00000000..dd5e14b6 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/eh.h @@ -0,0 +1,68 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef EH_H_ +#define EH_H_ + +#if !defined(ESP8266) && !defined(__AVR__) && !defined(_WIN32) && \ + !defined(ESP32) +#include +#endif + +#if !defined(__AVR__) && !defined(_WIN32) +#include +#endif + +#ifdef __AVR__ +#include "proto.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nfds; + +#ifndef _WIN32 + +#ifdef __linux__ + int epoll_fd; + int fd1; +#else + int fd1[2]; +#endif + + int fd2; + int fd3; + + struct timeval tv; + +#endif +} TEventHandler; + +TEventHandler *eh_init(void); +void eh_add_fd(TEventHandler *eh, int fd); +void eh_raise_event(TEventHandler *eh); +int eh_wait(TEventHandler *eh, int usec); +void eh_free(TEventHandler *eh); + +#ifdef __cplusplus +} +#endif + +#endif /* EH_H_ */ diff --git a/lib/SuplaDevice/src/supla-common/lck.c b/lib/SuplaDevice/src/supla-common/lck.c new file mode 100644 index 00000000..18cbeb69 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/lck.c @@ -0,0 +1,198 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "lck.h" + +#ifdef __LCK_DEBUG +#include +#include +#endif /*__LCK_DEBUG*/ + +#if defined(__AVR__) || defined(ARDUINO_ARCH_ESP8266) || \ + defined(ARDUINO_ARCH_ESP32) +#define __SINGLE_THREAD +#else + +#ifdef _WIN32 +#include +#else +#include +#include +#endif /*_WIN32*/ + +#endif // defined(__AVR__) || defined(ARDUINO_ARCH_ESP8266) + // || defined(ARDUINO_ARCH_ESP32) + +#include + +#define MUTEX_COUNT 4 + +#ifndef __SINGLE_THREAD + +typedef struct { +#ifdef _WIN32 + CRITICAL_SECTION critSec; +#else + pthread_mutex_t mutex; +#ifdef __LCK_DEBUG + + pthread_t thread; + int count; + int lineNumber; + char fileName[100]; + +#endif /*__LCK_DEBUG*/ +#endif /*_WIN32*/ +} TLckData; +#endif + +#ifdef __LCK_DEBUG +void *ptrs[500]; + +void lck_debug_init(void) { memset(ptrs, 0, sizeof(ptrs)); } + +void lck_debug_dump(void) { + printf("LCK DEBUG DUMP\n"); + int a; + int n = sizeof(ptrs) / sizeof(void *); + TLckData *l = 0; + + for (a = 0; a < n; a++) { + if ((l = (TLckData *)ptrs[a]) != 0 && l->count != 0) { + printf("%p:%p %s:%i count=%i\n", (void *)l, (void *)l->thread, + l->fileName, l->lineNumber, l->count); + } + } + + printf("<<-----\n"); +} + +#endif /*__LCK_DEBUG*/ + +void *lck_init(void) { +#ifdef __SINGLE_THREAD + return NULL; +#else + TLckData *lck = malloc(sizeof(TLckData)); + + if (lck != NULL) { +#ifdef _WIN32 + InitializeCriticalSectionEx(&lck->critSec, 4000, + CRITICAL_SECTION_NO_DEBUG_INFO); +#else + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&lck->mutex, &attr); + +#endif /*_WIN32*/ + } + +#ifdef __LCK_DEBUG + memset(lck, 0, sizeof(TLckData)); + int a; + int n = sizeof(ptrs) / sizeof(void *); + for (a = 0; a < n; a++) { + if (ptrs[a] == 0) { + ptrs[a] = lck; + break; + } + } +#endif /*__LCK_DEBUG*/ + + return lck; +#endif /*__SINGLE_THREAD*/ +} + +#ifdef __LCK_DEBUG + +void __lck_lock(void *lck, const char *file, int line) { + _lck_lock(lck); + + ((TLckData *)lck)->thread = pthread_self(); + ((TLckData *)lck)->count++; + if (((TLckData *)lck)->count == 1) { + snprintf(((TLckData *)lck)->fileName, sizeof(((TLckData *)lck)->fileName), + "%s", file); + ((TLckData *)lck)->lineNumber = line; + } +} + +void _lck_lock(void *lck) { +#else +void lck_lock(void *lck) { +#endif /*__LCK_DEBUG*/ +#ifndef __SINGLE_THREAD + if (lck != NULL) { +#ifdef _WIN32 + EnterCriticalSection(&((TLckData *)lck)->critSec); // NOLINT +#else + pthread_mutex_lock(&((TLckData *)lck)->mutex); // NOLINT +#endif /*_WIN32*/ + } + +#endif /*__SINGLE_THREAD*/ +} + +void lck_unlock(void *lck) { +#ifdef __LCK_DEBUG + ((TLckData *)lck)->count--; +#endif /*__LCK_DEBUG*/ +#ifndef __SINGLE_THREAD + if (lck != NULL) { +#ifdef _WIN32 + LeaveCriticalSection(&((TLckData *)lck)->critSec); // NOLINT +#else + pthread_mutex_unlock(&((TLckData *)lck)->mutex); // NOLINT +#endif /*_WIN32*/ + } + +#endif /*__SINGLE_THREAD*/ +} + +int lck_unlock_r(void *lck, int result) { +#ifndef __SINGLE_THREAD + lck_unlock(lck); +#endif /*__SINGLE_THREAD*/ + return result; +} + +void lck_free(void *lck) { +#ifdef __LCK_DEBUG + int a; + int n = sizeof(ptrs) / sizeof(void *); + for (a = 0; a < n; a++) { + if (ptrs[a] == lck) { + ptrs[a] = 0; + break; + } + } +#endif /*__LCK_DEBUG*/ + +#ifndef __SINGLE_THREAD + if (lck != NULL) { +#ifdef _WIN32 + DeleteCriticalSection(&((TLckData *)lck)->critSec); // NOLINT +#else + pthread_mutex_destroy(&((TLckData *)lck)->mutex); // NOLINT +#endif /*_WIN32*/ + free(lck); + } +#endif /*__SINGLE_THREAD*/ +} diff --git a/lib/SuplaDevice/src/supla-common/lck.h b/lib/SuplaDevice/src/supla-common/lck.h new file mode 100644 index 00000000..ce655366 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/lck.h @@ -0,0 +1,50 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LCK_H_ +#define LCK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __DEBUG +// #define __LCK_DEBUG +#endif + +#ifdef __LCK_DEBUG +#define lck_lock(ptr) __lck_lock(ptr, __FILE__, __LINE__) +void _lck_lock(void *lck); +void __lck_lock(void *lck, const char *file, int line); +void lck_debug_init(void); +void lck_debug_dump(void); +#else +void lck_lock(void *lck); +#endif /*__LCK_DEBUG*/ + +char lck_lock_with_timeout(void *lck, int timeout_sec); +void lck_unlock(void *lck); +int lck_unlock_r(void *lck, int result); +void *lck_init(void); +void lck_free(void *lck); + +#ifdef __cplusplus +} +#endif + +#endif /* LCK_H_ */ diff --git a/lib/SuplaDevice/src/supla-common/log.cpp b/lib/SuplaDevice/src/supla-common/log.cpp new file mode 100644 index 00000000..4dc045b6 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/log.cpp @@ -0,0 +1,293 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "log.h" +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#elif defined(ARDUINO_ARCH_ESP8266) || defined(__AVR__) || defined(ARDUINO_ARCH_ESP32) +#include +#else +#include +#include +#include +#include +#include +#endif /*defined(_WIN32)*/ + +#include + +#if defined(ESP8266) || defined(ESP32) + +#include +#if !defined(ARDUINO_ARCH_ESP32) +#include +#endif + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#include +#if defined(ARDUINO_ARCH_ESP266) +#include +#endif +#else +#include +#include "espmissingincludes.h" +#endif /*ARDUINO_ARCH_ESP8266 || ARDUINO_ARCH_ESP8266 */ + +#else + +#ifndef __AVR__ +#include "cfg.h" +#endif /*__AVR__*/ + +#endif /*ESP8266 || ESP32 */ + +#ifdef __ANDROID__ +#include +#endif /*__ANDROID__*/ + +#ifdef __LOG_CALLBACK +_supla_log_callback __supla_log_callback = NULL; + +void supla_log_set_callback(_supla_log_callback callback) { + __supla_log_callback = callback; +} +#endif /*__LOG_CALLBACK*/ + +char supla_log_string(char **buffer, int *size, va_list va, const char *__fmt) { + char *nb; + int n; + + if (*size == 0) { + *size = strnlen(__fmt, 10240) + 10; + *buffer = (char *)malloc(*size); + } + + if (*buffer == NULL) { + *size = 0; + return 0; + } + +#ifdef _WIN32 + n = vsnprintf_s(*buffer, *size, _TRUNCATE, __fmt, va); +#else + n = vsnprintf(*buffer, *size, __fmt, va); +#endif /*_WIN32*/ + + if (n < 0 || n >= *size) { + if (n > -1) /* glibc 2.1 */ + *size = n + 1; /* precisely what is needed */ + else /* glibc 2.0 */ + (*size) *= 2; /* twice the old size */ + + if ((nb = (char *)realloc(*buffer, *size)) == NULL) { + free(*buffer); + *buffer = NULL; + *size = 0; + } else { + *buffer = nb; + return 1; + } + } + + if (*buffer != NULL) { + (*buffer)[(*size) - 1] = 0; + } + + return 0; +} + +#ifdef _WIN32 + +void supla_vlog(int __pri, const char *message) { + WCHAR wstr[2048]; + size_t convertedChars; + + mbstowcs_s(&convertedChars, wstr, 2048, message, strnlen(message, 10240)); + + OutputDebugStringW(wstr); + OutputDebugStringW(L"\n"); +} + +#elif defined(ESP8266) || defined(__AVR__) || defined(ESP32) +void supla_vlog(int __pri, const char *message) { +#if (defined(ESP8266) || defined(ESP32)) && !defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32) +#ifndef ESP8266_LOG_DISABLED + os_printf("%s\r\n", message); +#endif +#else + Serial.println(message); +#endif +} +#else +void supla_vlog(int __pri, const char *message) { +#ifdef __ANDROID__ + switch (__pri) { + case LOG_CRIT: + case LOG_EMERG: + __pri = ANDROID_LOG_FATAL; + break; + case LOG_ERR: + __pri = ANDROID_LOG_ERROR; + break; + case LOG_ALERT: + case LOG_WARNING: + __pri = ANDROID_LOG_WARN; + break; + case LOG_NOTICE: + __pri = ANDROID_LOG_DEFAULT; + break; + case LOG_INFO: + __pri = ANDROID_LOG_INFO; + break; + case LOG_DEBUG: + __pri = ANDROID_LOG_DEBUG; + break; + } + __android_log_write(ANDROID_LOG_DEBUG, "LibSuplaClient", message); +#else + +#ifndef __LOG_CALLBACK + struct timeval now; +#endif + +#if defined(ESP8266) || defined(__AVR__) || defined(ESP32) + if (message == NULL) return; +#else + if (message == NULL || (debug_mode == 0 && __pri == LOG_DEBUG)) return; +#endif + +#ifdef __LOG_CALLBACK + if (__supla_log_callback) __supla_log_callback(__pri, message); +#else + if (run_as_daemon == 1) { + syslog(__pri, "%s", message); + } else { + switch (__pri) { + case LOG_EMERG: + printf("EMERG"); + break; + case LOG_ALERT: + printf("ALERT"); + break; + case LOG_CRIT: + printf("CRIT"); + break; + case LOG_ERR: + printf("ERR"); + break; + case LOG_WARNING: + printf("WARNING"); + break; + case LOG_NOTICE: + printf("NOTICE"); + break; + case LOG_INFO: + printf("INFO"); + break; + case LOG_DEBUG: + printf("DEBUG"); + break; + } + +#if defined(ESP8266) || defined(__AVR__) || defined(ESP32) + os_printf("%s\r\n", message); +#else + gettimeofday(&now, NULL); + printf("[%li.%li] ", (unsigned long)now.tv_sec, (unsigned long)now.tv_usec); + printf("%s", message); + printf("\n"); + fflush(stdout); +#endif + } +#endif + +#endif +} +#endif + +void supla_log(int __pri, const char *__fmt, ...) { + va_list ap; + char *buffer = NULL; + int size = 0; + +#if defined(ESP8266) || defined(__AVR__) || defined(_WIN32) || defined(ESP32) + if (__fmt == NULL) return; +#else + if (__fmt == NULL || (debug_mode == 0 && __pri == LOG_DEBUG)) return; +#endif + + while (1) { + va_start(ap, __fmt); + if (0 == supla_log_string(&buffer, &size, ap, __fmt)) { + va_end(ap); + break; + } else { + va_end(ap); + } + va_end(ap); + } + + if (buffer == NULL) return; + + supla_vlog(__pri, buffer); + free(buffer); +} + +void supla_write_state_file(const char *file, int __pri, const char *__fmt, + ...) { + char *buffer = NULL; + int size = 0; + + va_list ap; + + while (1) { + va_start(ap, __fmt); + if (0 == supla_log_string(&buffer, &size, ap, __fmt)) { + va_end(ap); + break; + } else { + va_end(ap); + } + } + + if (buffer == NULL) return; + + if (__pri > -1) { + supla_vlog(__pri, buffer); + } + +#if !defined(ESP8266) && !defined(__AVR__) && !defined(WIN32) && !defined(ESP32) + + int fd; + + if (file != 0 && strnlen(file, 1024) > 0) { + fd = open(file, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (fd != -1) { + write(fd, buffer, strnlen(buffer, size)); + close(fd); + } + } +#endif + + free(buffer); +} diff --git a/lib/SuplaDevice/src/supla-common/log.h b/lib/SuplaDevice/src/supla-common/log.h new file mode 100644 index 00000000..dc28158a --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/log.h @@ -0,0 +1,58 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef suplalog_H_ +#define suplalog_H_ + +#if defined(ESP8266) || defined(__AVR__) || defined(_WIN32) || defined(ESP32) + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#else + +#include + +#endif // defined(ESP8266) || defined(__AVR__) + // || defined(_WIN32) || defined(ESP32) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __LOG_CALLBACK +typedef int (*_supla_log_callback)(int __pri, const char *message); + +void supla_log_set_callback(_supla_log_callback callback); +#endif /*__LOG_CALLBACK*/ + +void supla_log(int __pri, const char *__fmt, ...); +void supla_write_state_file(const char *file, int __pri, const char *__fmt, + ...); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /* suplalog_H_ */ diff --git a/lib/SuplaDevice/src/supla-common/proto.c b/lib/SuplaDevice/src/supla-common/proto.c new file mode 100644 index 00000000..26d19a5c --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/proto.c @@ -0,0 +1,409 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "proto.h" +#include +#include +#include +#include "log.h" + +#if defined(ESP8266) || defined(ESP32) + +#include +#if !defined(ARDUINO_ARCH_ESP32) +#include +#endif +#define BUFFER_MIN_SIZE 512 +#define BUFFER_MAX_SIZE 2048 + +#if !defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32) +#include +#include "espmissingincludes.h" +#endif + +#elif defined(__AVR__) + +#define BUFFER_MIN_SIZE 32 +#define BUFFER_MAX_SIZE 1024 + +#endif /*ESP8266*/ + +#ifndef BUFFER_MIN_SIZE +#define BUFFER_MIN_SIZE 0 +#endif /*BUFFER_MIN_SIZE*/ + +#ifndef BUFFER_MAX_SIZE +#define BUFFER_MAX_SIZE 131072 +#endif /*BUFFER_MAX_SIZE*/ + +char sproto_tag[SUPLA_TAG_SIZE] = {'S', 'U', 'P', 'L', 'A'}; + +typedef struct { + unsigned char begin_tag; + unsigned _supla_int_t size; + unsigned _supla_int_t data_size; + + char *buffer; +} TSuplaProtoInBuffer; + +#ifndef SPROTO_WITHOUT_OUT_BUFFER +typedef struct { + unsigned _supla_int_t size; + unsigned _supla_int_t data_size; + + char *buffer; +} TSuplaProtoOutBuffer; +#endif + +typedef struct { + unsigned _supla_int_t next_rr_id; + unsigned char version; + TSuplaProtoInBuffer in; +#ifndef SPROTO_WITHOUT_OUT_BUFFER + TSuplaProtoOutBuffer out; +#endif +} TSuplaProtoData; + +void *sproto_init(void) { + TSuplaProtoData *spd = malloc(sizeof(TSuplaProtoData)); + if (spd) { + memset(spd, 0, sizeof(TSuplaProtoData)); + spd->version = SUPLA_PROTO_VERSION; + return (spd); + } + + return (NULL); +} + +void sproto_free(void *spd_ptr) { + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + if (spd != NULL) { + if (spd->in.buffer != NULL) free(spd->in.buffer); +#ifndef SPROTO_WITHOUT_OUT_BUFFER + if (spd->out.buffer != NULL) free(spd->out.buffer); +#endif + + free(spd); + } +} + +unsigned char sproto_buffer_append(void *spd_ptr, char **buffer, + unsigned _supla_int_t *buffer_size, + unsigned _supla_int_t *buffer_data_size, + char *data, + unsigned _supla_int_t data_size) { + unsigned _supla_int_t size = *buffer_size; + + if (size < BUFFER_MIN_SIZE) { + size = BUFFER_MIN_SIZE; + } + + if (data_size > size - (*buffer_data_size)) { + size += data_size - (size - (*buffer_data_size)); + } + + if (size >= BUFFER_MAX_SIZE) return (SUPLA_RESULT_BUFFER_OVERFLOW); + + if (size != (*buffer_size)) { + char *new_buffer = (char *)realloc(*buffer, size); + + if (size > 0 && new_buffer == NULL) { + return (SUPLA_RESULT_FALSE); + } + +#ifndef ESP8266 +#ifndef ESP32 +#ifndef __AVR__ + if (errno == ENOMEM) return (SUPLA_RESULT_FALSE); +#endif +#endif +#endif + + *buffer = new_buffer; + } + + memcpy(&(*buffer)[(*buffer_data_size)], data, data_size); + + (*buffer_size) = size; + (*buffer_data_size) += data_size; + + return (SUPLA_RESULT_TRUE); +} + +char sproto_in_buffer_append(void *spd_ptr, char *data, + unsigned _supla_int_t data_size) { + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + return sproto_buffer_append(spd_ptr, &spd->in.buffer, &spd->in.size, + &spd->in.data_size, data, data_size); +} + +#ifndef SPROTO_WITHOUT_OUT_BUFFER +char sproto_out_buffer_append(void *spd_ptr, TSuplaDataPacket *sdp) { + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + unsigned _supla_int_t sdp_size = sizeof(TSuplaDataPacket); + unsigned _supla_int_t packet_size = + sdp_size - SUPLA_MAX_DATA_SIZE + sdp->data_size; + + if (packet_size > sdp_size) return SUPLA_RESULT_DATA_TOO_LARGE; + + if (SUPLA_RESULT_TRUE == + sproto_buffer_append(spd_ptr, &spd->out.buffer, &spd->out.size, + &spd->out.data_size, (char *)sdp, packet_size)) { + return sproto_buffer_append(spd_ptr, &spd->out.buffer, &spd->out.size, + &spd->out.data_size, sproto_tag, + SUPLA_TAG_SIZE); + } + + return (SUPLA_RESULT_FALSE); +} + +unsigned _supla_int_t sproto_pop_out_data(void *spd_ptr, char *buffer, + unsigned _supla_int_t buffer_size) { + unsigned _supla_int_t a; + unsigned _supla_int_t b; + + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + + if (spd->out.data_size <= 0 || buffer_size == 0 || buffer == NULL) return (0); + + if (spd->out.data_size < buffer_size) buffer_size = spd->out.data_size; + + memcpy(buffer, spd->out.buffer, buffer_size); + + b = 0; + + for (a = buffer_size; a < spd->out.data_size; a++) { + spd->out.buffer[b] = spd->out.buffer[a]; + b++; + } + + spd->out.data_size -= buffer_size; + + if (spd->out.data_size < spd->out.size) { + b = spd->out.size; + + spd->out.size = spd->out.data_size; + if (spd->out.size < BUFFER_MIN_SIZE) spd->out.size = BUFFER_MIN_SIZE; + + if (b != spd->out.size) { + char *new_out_buffer = (char *)realloc(spd->out.buffer, spd->out.size); + + if (new_out_buffer == NULL && spd->out.size > 0) { + spd->out.size = b; + } else { + spd->out.buffer = new_out_buffer; + } + } + } + + return (buffer_size); +} +#endif /*SPROTO_WITHOUT_OUT_BUFFER*/ + +char sproto_out_dataexists(void *spd_ptr) { +#ifdef SPROTO_WITHOUT_OUT_BUFFER + return SUPLA_RESULT_FALSE; +#else + return ((TSuplaProtoData *)spd_ptr)->out.data_size > 0 ? SUPLA_RESULT_TRUE + : SUPLA_RESULT_FALSE; +#endif +} + +char sproto_in_dataexists(void *spd_ptr) { + return ((TSuplaProtoData *)spd_ptr)->in.data_size > 0 ? SUPLA_RESULT_TRUE + : SUPLA_RESULT_FALSE; +} + +void sproto_shrink_in_buffer(TSuplaProtoInBuffer *in, + unsigned _supla_int_t size) { + unsigned _supla_int_t old_size = in->size; + _supla_int_t a, b; + + in->begin_tag = 0; + + if (size > in->data_size) size = in->data_size; + + b = 0; + + for (a = size; a < in->data_size; a++) { + in->buffer[b] = in->buffer[a]; + b++; + } + + in->data_size -= size; + + if (in->data_size < in->size) { + in->size = in->data_size; + + if (in->size < BUFFER_MIN_SIZE) in->size = BUFFER_MIN_SIZE; + + if (old_size != in->size) { + char *new_in_buffer = (char *)realloc(in->buffer, in->size); + + if (new_in_buffer == NULL && in->size > 0) { + in->size = old_size; + } else { + in->buffer = new_in_buffer; + } + } + } +} + +char sproto_pop_in_sdp(void *spd_ptr, TSuplaDataPacket *sdp) { + unsigned _supla_int_t header_size; + TSuplaDataPacket *_sdp; + + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + + if (spd->in.begin_tag == 0 && spd->in.data_size >= SUPLA_TAG_SIZE) { + if (memcmp(spd->in.buffer, sproto_tag, SUPLA_TAG_SIZE) == 0) { + spd->in.begin_tag = 1; + } else { + sproto_shrink_in_buffer(&spd->in, spd->in.data_size); + return SUPLA_RESULT_DATA_ERROR; + } + } + + if (spd->in.begin_tag == 1) { + header_size = sizeof(TSuplaDataPacket) - SUPLA_MAX_DATA_SIZE; + if ((spd->in.data_size - SUPLA_TAG_SIZE) >= header_size) { + _sdp = (TSuplaDataPacket *)spd->in.buffer; + + if (_sdp->version > SUPLA_PROTO_VERSION || + _sdp->version < SUPLA_PROTO_VERSION_MIN) { + sdp->version = _sdp->version; + sproto_shrink_in_buffer(&spd->in, spd->in.data_size); + + return SUPLA_RESULT_VERSION_ERROR; + } + + if ((header_size + _sdp->data_size) > sizeof(TSuplaDataPacket)) { + sproto_shrink_in_buffer(&spd->in, spd->in.data_size); + return SUPLA_RESULT_DATA_ERROR; + } + + if ((header_size + _sdp->data_size + SUPLA_TAG_SIZE) > spd->in.data_size) + return SUPLA_RESULT_FALSE; + + if (header_size + _sdp->data_size >= spd->in.size || + memcmp(&spd->in.buffer[header_size + _sdp->data_size], sproto_tag, + SUPLA_TAG_SIZE) != 0) { + sproto_shrink_in_buffer(&spd->in, spd->in.data_size); + + return SUPLA_RESULT_DATA_ERROR; + } + + memcpy(sdp, spd->in.buffer, header_size + _sdp->data_size); + sproto_shrink_in_buffer(&spd->in, + header_size + _sdp->data_size + SUPLA_TAG_SIZE); + + return (SUPLA_RESULT_TRUE); + } + } + + return (SUPLA_RESULT_FALSE); +} + +void sproto_set_version(void *spd_ptr, unsigned char version) { + if (version >= SUPLA_PROTO_VERSION_MIN && version <= SUPLA_PROTO_VERSION) { + ((TSuplaProtoData *)spd_ptr)->version = version; + } else { + ((TSuplaProtoData *)spd_ptr)->version = SUPLA_PROTO_VERSION; + } +} + +unsigned char sproto_get_version(void *spd_ptr) { + return ((TSuplaProtoData *)spd_ptr)->version; +} + +void sproto_sdp_init(void *spd_ptr, TSuplaDataPacket *sdp) { + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + + memset(sdp, 0, sizeof(TSuplaDataPacket)); + memcpy(sdp->tag, sproto_tag, SUPLA_TAG_SIZE); + + spd->next_rr_id++; + + if (spd->next_rr_id == 0) spd->next_rr_id++; + + sdp->rr_id = spd->next_rr_id; + sdp->version = spd->version; +} + +TSuplaDataPacket *sproto_sdp_malloc(void *spd_ptr) { + TSuplaDataPacket *result = malloc(sizeof(TSuplaDataPacket)); + + if (result) sproto_sdp_init(spd_ptr, result); + + return result; +} + +void sproto_sdp_free(TSuplaDataPacket *sdp) { free(sdp); } + +char sproto_set_data(TSuplaDataPacket *sdp, char *data, + unsigned _supla_int_t data_size, + unsigned _supla_int_t call_type) { + if (data_size > SUPLA_MAX_DATA_SIZE || (data_size > 0 && data == 0)) + return SUPLA_RESULT_FALSE; + + if (data_size > 0) memcpy(sdp->data, data, data_size); + + sdp->data_size = data_size; + sdp->call_type = call_type; + return SUPLA_RESULT_TRUE; +} + +void sproto_log_summary(void *spd_ptr) { + if (spd_ptr == NULL) { + supla_log(LOG_DEBUG, "SPROTO - Not initialized!"); + return; + } + + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + + supla_log(LOG_DEBUG, "BUFFER IN"); + supla_log(LOG_DEBUG, " size: %i", spd->in.size); + supla_log(LOG_DEBUG, " data_size: %i", spd->in.data_size); + supla_log(LOG_DEBUG, " begin_tag: %i", spd->in.begin_tag); +#ifndef SPROTO_WITHOUT_OUT_BUFFER + supla_log(LOG_DEBUG, "BUFFER OUT"); + supla_log(LOG_DEBUG, " size: %i", spd->out.size); + supla_log(LOG_DEBUG, " data_size: %i", spd->out.data_size); +#endif /*SPROTO_WITHOUT_OUT_BUFFER*/ +} + +void sproto_buffer_dump(void *spd_ptr, unsigned char in) { + _supla_int_t a; + char *buffer = NULL; + _supla_int_t size = 0; + + TSuplaProtoData *spd = (TSuplaProtoData *)spd_ptr; + + if (in != 0) { + buffer = spd->in.buffer; + size = spd->in.data_size; +#ifndef SPROTO_WITHOUT_OUT_BUFFER + } else { + buffer = spd->out.buffer; + size = spd->out.data_size; +#endif /*SPROTO_WITHOUT_OUT_BUFFER*/ + } + + for (a = 0; a < size; a++) + supla_log(LOG_DEBUG, "%c [%i]", buffer[a], buffer[a]); +} diff --git a/lib/SuplaDevice/src/supla-common/proto.h b/lib/SuplaDevice/src/supla-common/proto.h new file mode 100644 index 00000000..84bfc8b0 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/proto.h @@ -0,0 +1,1676 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef supla_proto_H_ +#define supla_proto_H_ + +#ifdef _WIN32 + +#include +#define _supla_int_t int +#define _supla_int16_t short +#define _supla_int64_t __int64 +#define _supla_timeval timeval + +#elif defined(__AVR__) + +#define SPROTO_WITHOUT_OUT_BUFFER + +struct _supla_timeval { + long tv_sec[2]; + long tv_usec[2]; +}; + +#define timeval _supla_timeval + +#define _supla_int16_t int +#define _supla_int_t long +#define _supla_int64_t long long + +#elif defined(ESP8266) || defined(ESP32) + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#define SPROTO_WITHOUT_OUT_BUFFER +#endif /*ARDUINO_ARCH_ESP8266*/ + +struct _supla_timeval { + long long tv_sec; + long long tv_usec; +}; + +#define _supla_int16_t short +#define _supla_int_t int +#define _supla_int64_t long long +#elif defined(__arm__) + +struct _supla_timeval { + long long tv_sec; + long long tv_usec; +}; + +#include +#define _supla_int16_t short +#define _supla_int_t int +#define _supla_int64_t long long + +#else /*__arm__*/ +#include +#define _supla_int16_t short +#define _supla_int_t int +#define _supla_int64_t long long +#define _supla_timeval timeval +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUPLA_TAG_SIZE 5 +extern char sproto_tag[SUPLA_TAG_SIZE]; + +// DCS - device/client -> server +// SDC - server -> device/client +// DS - device -> server +// SD - server -> device +// CS - client -> server +// SC - server -> client + +#define SUPLA_PROTO_VERSION 13 +#define SUPLA_PROTO_VERSION_MIN 1 +#if defined(ARDUINO_ARCH_AVR) // Arduino IDE for Arduino HW +#define SUPLA_MAX_DATA_SIZE 1248 // Registration header + 32 channels x 21 B +#elif defined(ARDUINO_ARCH_ESP8266) || \ + defined(ARDUINO_ARCH_ESP32) // Arduino IDE for ESP8266 +#define SUPLA_MAX_DATA_SIZE 3264 // Registration header + 128 channels x 21 B +#elif defined(ESP8266) +#define SUPLA_MAX_DATA_SIZE 1536 +#else +#define SUPLA_MAX_DATA_SIZE 10240 +#endif +#define SUPLA_RC_MAX_DEV_COUNT 50 +#define SUPLA_SOFTVER_MAXSIZE 21 + +#define SUPLA_GUID_SIZE 16 +#define SUPLA_GUID_HEXSIZE 33 +#define SUPLA_LOCATION_PWD_MAXSIZE 33 +#define SUPLA_ACCESSID_PWD_MAXSIZE 33 +#define SUPLA_LOCATION_CAPTION_MAXSIZE 401 +#define SUPLA_LOCATIONPACK_MAXCOUNT 20 +#define SUPLA_CHANNEL_CAPTION_MAXSIZE 401 +#define SUPLA_CHANNELPACK_MAXCOUNT 20 +#define SUPLA_URL_HOST_MAXSIZE 101 +#define SUPLA_URL_PATH_MAXSIZE 101 +#define SUPLA_SERVER_NAME_MAXSIZE 65 +#define SUPLA_EMAIL_MAXSIZE 256 // ver. >= 7 +#define SUPLA_PASSWORD_MAXSIZE 64 // ver. >= 10 +#define SUPLA_AUTHKEY_SIZE 16 // ver. >= 7 +#define SUPLA_AUTHKEY_HEXSIZE 33 // ver. >= 7 +#define SUPLA_OAUTH_TOKEN_MAXSIZE 256 // ver. >= 10 +#define SUPLA_CHANNELGROUP_PACK_MAXCOUNT 20 // ver. >= 9 +#define SUPLA_CHANNELGROUP_CAPTION_MAXSIZE 401 // ver. >= 9 +#define SUPLA_CHANNELVALUE_PACK_MAXCOUNT 20 // ver. >= 9 +#define SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXCOUNT 5 // ver. >= 10 +#define SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXDATASIZE \ + (SUPLA_MAX_DATA_SIZE - 50) // ver. >= 10 +#define SUPLA_CALCFG_DATA_MAXSIZE 128 // ver. >= 10 +#define SUPLA_TIMEZONE_MAXSIZE 51 // ver. >= 11 + +#ifndef SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT +#define SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT 100 // ver. >= 9 +#endif /*SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT*/ + +#define SUPLA_DCS_CALL_GETVERSION 10 +#define SUPLA_SDC_CALL_GETVERSION_RESULT 20 +#define SUPLA_SDC_CALL_VERSIONERROR 30 +#define SUPLA_DCS_CALL_PING_SERVER 40 +#define SUPLA_SDC_CALL_PING_SERVER_RESULT 50 +#define SUPLA_DS_CALL_REGISTER_DEVICE 60 +#define SUPLA_DS_CALL_REGISTER_DEVICE_B 65 // ver. >= 2 +#define SUPLA_DS_CALL_REGISTER_DEVICE_C 67 // ver. >= 6 +#define SUPLA_DS_CALL_REGISTER_DEVICE_D 68 // ver. >= 7 +#define SUPLA_DS_CALL_REGISTER_DEVICE_E 69 // ver. >= 10 +#define SUPLA_SD_CALL_REGISTER_DEVICE_RESULT 70 +#define SUPLA_CS_CALL_REGISTER_CLIENT 80 +#define SUPLA_CS_CALL_REGISTER_CLIENT_B 85 // ver. >= 6 +#define SUPLA_CS_CALL_REGISTER_CLIENT_C 86 // ver. >= 7 +#define SUPLA_CS_CALL_REGISTER_CLIENT_D 87 // ver. >= 12 +#define SUPLA_SC_CALL_REGISTER_CLIENT_RESULT 90 +#define SUPLA_SC_CALL_REGISTER_CLIENT_RESULT_B 92 // ver. >= 9 +#define SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED 100 +#define SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_B 102 // ver. >= 12 +#define SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_C 103 // ver. >= 12 +#define SUPLA_DS_CALL_DEVICE_CHANNEL_EXTENDEDVALUE_CHANGED 105 // ver. >= 10 +#define SUPLA_SD_CALL_CHANNEL_SET_VALUE 110 +#define SUPLA_SD_CALL_CHANNELGROUP_SET_VALUE 115 // ver. >= 13 +#define SUPLA_DS_CALL_CHANNEL_SET_VALUE_RESULT 120 +#define SUPLA_SC_CALL_LOCATION_UPDATE 130 +#define SUPLA_SC_CALL_LOCATIONPACK_UPDATE 140 +#define SUPLA_SC_CALL_CHANNEL_UPDATE 150 +#define SUPLA_SC_CALL_CHANNELPACK_UPDATE 160 +#define SUPLA_SC_CALL_CHANNEL_VALUE_UPDATE 170 +#define SUPLA_CS_CALL_GET_NEXT 180 +#define SUPLA_SC_CALL_EVENT 190 +#define SUPLA_CS_CALL_CHANNEL_SET_VALUE 200 +#define SUPLA_CS_CALL_CHANNEL_SET_VALUE_B 205 // ver. >= 3 +#define SUPLA_DCS_CALL_SET_ACTIVITY_TIMEOUT 210 // ver. >= 2 +#define SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT 220 // ver. >= 2 +#define SUPLA_DS_CALL_GET_FIRMWARE_UPDATE_URL 300 // ver. >= 5 +#define SUPLA_SD_CALL_GET_FIRMWARE_UPDATE_URL_RESULT 310 // ver. >= 5 +#define SUPLA_DCS_CALL_GET_REGISTRATION_ENABLED 320 // ver. >= 7 +#define SUPLA_SDC_CALL_GET_REGISTRATION_ENABLED_RESULT 330 // ver. >= 7 +#define SUPLA_CS_CALL_OAUTH_TOKEN_REQUEST 340 // ver. >= 10 +#define SUPLA_SC_CALL_OAUTH_TOKEN_REQUEST_RESULT 350 // ver. >= 10 +#define SUPLA_SC_CALL_CHANNELPACK_UPDATE_B 360 // ver. >= 8 +#define SUPLA_SC_CALL_CHANNELPACK_UPDATE_C 361 // ver. >= 10 +#define SUPLA_SC_CALL_CHANNEL_UPDATE_B 370 // ver. >= 8 +#define SUPLA_SC_CALL_CHANNEL_UPDATE_C 371 // ver. >= 10 +#define SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE 380 // ver. >= 9 +#define SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE_B 381 // ver. >= 10 +#define SUPLA_SC_CALL_CHANNELGROUP_RELATION_PACK_UPDATE 390 // ver. >= 9 +#define SUPLA_SC_CALL_CHANNELVALUE_PACK_UPDATE 400 // ver. >= 9 +#define SUPLA_SC_CALL_CHANNELEXTENDEDVALUE_PACK_UPDATE 405 // ver. >= 10 +#define SUPLA_CS_CALL_SET_VALUE 410 // ver. >= 9 +#define SUPLA_CS_CALL_SUPERUSER_AUTHORIZATION_REQUEST 420 // ver. >= 10 +#define SUPLA_CS_CALL_GET_SUPERUSER_AUTHORIZATION_RESULT 425 // ver. >= 12 +#define SUPLA_SC_CALL_SUPERUSER_AUTHORIZATION_RESULT 430 // ver. >= 10 +#define SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST 440 // ver. >= 10 +#define SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST_B 445 // ver. >= 11 +#define SUPLA_SC_CALL_DEVICE_CALCFG_RESULT 450 // ver. >= 10 +#define SUPLA_SD_CALL_DEVICE_CALCFG_REQUEST 460 // ver. >= 10 +#define SUPLA_DS_CALL_DEVICE_CALCFG_RESULT 470 // ver. >= 10 +#define SUPLA_DCS_CALL_GET_USER_LOCALTIME 480 // ver. >= 11 +#define SUPLA_DCS_CALL_GET_USER_LOCALTIME_RESULT 490 // ver. >= 11 +#define SUPLA_CSD_CALL_GET_CHANNEL_STATE 500 // ver. >= 12 +#define SUPLA_DSC_CALL_CHANNEL_STATE_RESULT 510 // ver. >= 12 +#define SUPLA_CS_CALL_GET_CHANNEL_BASIC_CFG 520 // ver. >= 12 +#define SUPLA_SC_CALL_CHANNEL_BASIC_CFG_RESULT 530 // ver. >= 12 +#define SUPLA_CS_CALL_SET_CHANNEL_FUNCTION 540 // ver. >= 12 +#define SUPLA_SC_CALL_SET_CHANNEL_FUNCTION_RESULT 550 // ver. >= 12 +#define SUPLA_CS_CALL_CLIENTS_RECONNECT_REQUEST 560 // ver. >= 12 +#define SUPLA_SC_CALL_CLIENTS_RECONNECT_REQUEST_RESULT 570 // ver. >= 12 +#define SUPLA_CS_CALL_SET_REGISTRATION_ENABLED 580 // ver. >= 12 +#define SUPLA_SC_CALL_SET_REGISTRATION_ENABLED_RESULT 590 // ver. >= 12 +#define SUPLA_CS_CALL_DEVICE_RECONNECT_REQUEST 600 // ver. >= 12 +#define SUPLA_SC_CALL_DEVICE_RECONNECT_REQUEST_RESULT 610 // ver. >= 12 +#define SUPLA_DS_CALL_GET_CHANNEL_FUNCTIONS 620 // ver. >= 12 +#define SUPLA_SD_CALL_GET_CHANNEL_FUNCTIONS_RESULT 630 // ver. >= 12 +#define SUPLA_CS_CALL_SET_CHANNEL_CAPTION 640 // ver. >= 12 +#define SUPLA_SC_CALL_SET_CHANNEL_CAPTION_RESULT 650 // ver. >= 12 + +#define SUPLA_RESULT_CALL_NOT_ALLOWED -5 +#define SUPLA_RESULT_DATA_TOO_LARGE -4 +#define SUPLA_RESULT_BUFFER_OVERFLOW -3 +#define SUPLA_RESULT_DATA_ERROR -2 +#define SUPLA_RESULT_VERSION_ERROR -1 +#define SUPLA_RESULT_FALSE 0 +#define SUPLA_RESULT_TRUE 1 + +#define SUPLA_RESULTCODE_NONE 0 +#define SUPLA_RESULTCODE_UNSUPORTED 1 +#define SUPLA_RESULTCODE_FALSE 2 +#define SUPLA_RESULTCODE_TRUE 3 +#define SUPLA_RESULTCODE_TEMPORARILY_UNAVAILABLE 4 +#define SUPLA_RESULTCODE_BAD_CREDENTIALS 5 +#define SUPLA_RESULTCODE_LOCATION_CONFLICT 6 +#define SUPLA_RESULTCODE_CHANNEL_CONFLICT 7 +#define SUPLA_RESULTCODE_DEVICE_DISABLED 8 +#define SUPLA_RESULTCODE_ACCESSID_DISABLED 9 +#define SUPLA_RESULTCODE_LOCATION_DISABLED 10 +#define SUPLA_RESULTCODE_CLIENT_DISABLED 11 +#define SUPLA_RESULTCODE_CLIENT_LIMITEXCEEDED 12 +#define SUPLA_RESULTCODE_DEVICE_LIMITEXCEEDED 13 +#define SUPLA_RESULTCODE_GUID_ERROR 14 +#define SUPLA_RESULTCODE_HOSTNOTFOUND 15 // ver. >= 5 +#define SUPLA_RESULTCODE_CANTCONNECTTOHOST 16 // ver. >= 5 +#define SUPLA_RESULTCODE_REGISTRATION_DISABLED 17 // ver. >= 7 +#define SUPLA_RESULTCODE_ACCESSID_NOT_ASSIGNED 18 // ver. >= 7 +#define SUPLA_RESULTCODE_AUTHKEY_ERROR 19 // ver. >= 7 +#define SUPLA_RESULTCODE_NO_LOCATION_AVAILABLE 20 // ver. >= 7 +#define SUPLA_RESULTCODE_USER_CONFLICT 21 // Deprecated +#define SUPLA_RESULTCODE_UNAUTHORIZED 22 // ver. >= 10 +#define SUPLA_RESULTCODE_AUTHORIZED 23 // ver. >= 10 +#define SUPLA_RESULTCODE_NOT_ALLOWED 24 // ver. >= 12 +#define SUPLA_RESULTCODE_CHANNELNOTFOUND 25 // ver. >= 12 +#define SUPLA_RESULTCODE_UNKNOWN_ERROR 26 // ver. >= 12 +#define SUPLA_RESULTCODE_DENY_CHANNEL_BELONG_TO_GROUP 27 // ver. >= 12 +#define SUPLA_RESULTCODE_DENY_CHANNEL_HAS_SCHEDULE 28 // ver. >= 12 +#define SUPLA_RESULTCODE_DENY_CHANNEL_IS_ASSOCIETED_WITH_SCENE 29 // ver. >= 12 + +#define SUPLA_OAUTH_RESULTCODE_ERROR 0 // ver. >= 10 +#define SUPLA_OAUTH_RESULTCODE_SUCCESS 1 // ver. >= 10 +#define SUPLA_OAUTH_TEMPORARILY_UNAVAILABLE 2 // ver. >= 10 + +#define SUPLA_DEVICE_NAME_MAXSIZE 201 +#define SUPLA_CLIENT_NAME_MAXSIZE 201 +#define SUPLA_SENDER_NAME_MAXSIZE 201 + +#ifdef __AVR__ +#ifdef __AVR_ATmega2560__ +#define SUPLA_CHANNELMAXCOUNT 32 +#else +#define SUPLA_CHANNELMAXCOUNT 1 +#endif +#else +#define SUPLA_CHANNELMAXCOUNT 128 +#endif + +#define SUPLA_CHANNELVALUE_SIZE 8 + +#ifdef __AVR__ +#define SUPLA_CHANNELEXTENDEDVALUE_SIZE 256 +#else +#define SUPLA_CHANNELEXTENDEDVALUE_SIZE 1024 +#endif + +#define SUPLA_CHANNELTYPE_SENSORNO 1000 +#define SUPLA_CHANNELTYPE_SENSORNC 1010 // DEPRECATED +#define SUPLA_CHANNELTYPE_DISTANCESENSOR 1020 // ver. >= 5 +#define SUPLA_CHANNELTYPE_CALLBUTTON 1500 // ver. >= 4 +#define SUPLA_CHANNELTYPE_RELAYHFD4 2000 +#define SUPLA_CHANNELTYPE_RELAYG5LA1A 2010 +#define SUPLA_CHANNELTYPE_2XRELAYG5LA1A 2020 +#define SUPLA_CHANNELTYPE_RELAY 2900 +#define SUPLA_CHANNELTYPE_THERMOMETERDS18B20 3000 +#define SUPLA_CHANNELTYPE_DHT11 3010 // ver. >= 4 +#define SUPLA_CHANNELTYPE_DHT22 3020 // ver. >= 4 +#define SUPLA_CHANNELTYPE_DHT21 3022 // ver. >= 5 +#define SUPLA_CHANNELTYPE_AM2302 3030 // ver. >= 4 +#define SUPLA_CHANNELTYPE_AM2301 3032 // ver. >= 5 + +#define SUPLA_CHANNELTYPE_THERMOMETER 3034 // ver. >= 8 +#define SUPLA_CHANNELTYPE_HUMIDITYSENSOR 3036 // ver. >= 8 +#define SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR 3038 // ver. >= 8 +#define SUPLA_CHANNELTYPE_WINDSENSOR 3042 // ver. >= 8 +#define SUPLA_CHANNELTYPE_PRESSURESENSOR 3044 // ver. >= 8 +#define SUPLA_CHANNELTYPE_RAINSENSOR 3048 // ver. >= 8 +#define SUPLA_CHANNELTYPE_WEIGHTSENSOR 3050 // ver. >= 8 +#define SUPLA_CHANNELTYPE_WEATHER_STATION 3100 // ver. >= 8 + +#define SUPLA_CHANNELTYPE_DIMMER 4000 // ver. >= 4 +#define SUPLA_CHANNELTYPE_RGBLEDCONTROLLER 4010 // ver. >= 4 +#define SUPLA_CHANNELTYPE_DIMMERANDRGBLED 4020 // ver. >= 4 + +#define SUPLA_CHANNELTYPE_ELECTRICITY_METER 5000 // ver. >= 10 +#define SUPLA_CHANNELTYPE_IMPULSE_COUNTER 5010 // ver. >= 10 + +#define SUPLA_CHANNELTYPE_THERMOSTAT 6000 // ver. >= 11 +#define SUPLA_CHANNELTYPE_THERMOSTAT_HEATPOL_HOMEPLUS 6010 // ver. >= 11 + +#define SUPLA_CHANNELTYPE_VALVE_OPENCLOSE 7000 // ver. >= 12 +#define SUPLA_CHANNELTYPE_VALVE_PERCENTAGE 7010 // ver. >= 12 +#define SUPLA_CHANNELTYPE_BRIDGE 8000 // ver. >= 12 +#define SUPLA_CHANNELTYPE_GENERAL_PURPOSE_MEASUREMENT 9000 // ver. >= 12 +#define SUPLA_CHANNELTYPE_ENGINE 10000 // ver. >= 12 +#define SUPLA_CHANNELTYPE_ACTIONTRIGGER 11000 // ver. >= 12 +#define SUPLA_CHANNELTYPE_DIGIGLASS 12000 // ver. >= 12 + +#define SUPLA_CHANNELDRIVER_MCP23008 2 + +#define SUPLA_CHANNELFNC_NONE 0 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEGATEWAYLOCK 10 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEGATE 20 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEGARAGEDOOR 30 +#define SUPLA_CHANNELFNC_THERMOMETER 40 +#define SUPLA_CHANNELFNC_HUMIDITY 42 +#define SUPLA_CHANNELFNC_HUMIDITYANDTEMPERATURE 45 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_GATEWAY 50 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_GATE 60 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_GARAGEDOOR 70 +#define SUPLA_CHANNELFNC_NOLIQUIDSENSOR 80 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEDOORLOCK 90 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_DOOR 100 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEROLLERSHUTTER 110 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEROOFWINDOW 115 // ver. >= 13 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_ROLLERSHUTTER 120 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_ROOFWINDOW 125 // ver. >= 13 +#define SUPLA_CHANNELFNC_POWERSWITCH 130 +#define SUPLA_CHANNELFNC_LIGHTSWITCH 140 +#define SUPLA_CHANNELFNC_RING 150 +#define SUPLA_CHANNELFNC_ALARM 160 +#define SUPLA_CHANNELFNC_NOTIFICATION 170 +#define SUPLA_CHANNELFNC_DIMMER 180 +#define SUPLA_CHANNELFNC_RGBLIGHTING 190 +#define SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING 200 +#define SUPLA_CHANNELFNC_DEPTHSENSOR 210 // ver. >= 5 +#define SUPLA_CHANNELFNC_DISTANCESENSOR 220 // ver. >= 5 +#define SUPLA_CHANNELFNC_OPENINGSENSOR_WINDOW 230 // ver. >= 8 +#define SUPLA_CHANNELFNC_MAILSENSOR 240 // ver. >= 8 +#define SUPLA_CHANNELFNC_WINDSENSOR 250 // ver. >= 8 +#define SUPLA_CHANNELFNC_PRESSURESENSOR 260 // ver. >= 8 +#define SUPLA_CHANNELFNC_RAINSENSOR 270 // ver. >= 8 +#define SUPLA_CHANNELFNC_WEIGHTSENSOR 280 // ver. >= 8 +#define SUPLA_CHANNELFNC_WEATHER_STATION 290 // ver. >= 8 +#define SUPLA_CHANNELFNC_STAIRCASETIMER 300 // ver. >= 8 +#define SUPLA_CHANNELFNC_ELECTRICITY_METER 310 // ver. >= 10 +#define SUPLA_CHANNELFNC_IC_ELECTRICITY_METER 315 // ver. >= 12 +#define SUPLA_CHANNELFNC_IC_GAS_METER 320 // ver. >= 10 +#define SUPLA_CHANNELFNC_IC_WATER_METER 330 // ver. >= 10 +#define SUPLA_CHANNELFNC_IC_HEAT_METER 340 // ver. >= 10 +#define SUPLA_CHANNELFNC_THERMOSTAT 400 // ver. >= 11 +#define SUPLA_CHANNELFNC_THERMOSTAT_HEATPOL_HOMEPLUS 410 // ver. >= 11 +#define SUPLA_CHANNELFNC_VALVE_OPENCLOSE 500 // ver. >= 12 +#define SUPLA_CHANNELFNC_VALVE_PERCENTAGE 510 // ver. >= 12 +#define SUPLA_CHANNELFNC_GENERAL_PURPOSE_MEASUREMENT 520 // ver. >= 12 +#define SUPLA_CHANNELFNC_CONTROLLINGTHEENGINESPEED 600 // ver. >= 12 +#define SUPLA_CHANNELFNC_ACTIONTRIGGER 700 // ver. >= 12 +#define SUPLA_CHANNELFNC_DIGIGLASS 800 // ver. >= 12 + +#define SUPLA_BIT_FUNC_CONTROLLINGTHEGATEWAYLOCK 0x00000001 +#define SUPLA_BIT_FUNC_CONTROLLINGTHEGATE 0x00000002 +#define SUPLA_BIT_FUNC_CONTROLLINGTHEGARAGEDOOR 0x00000004 +#define SUPLA_BIT_FUNC_CONTROLLINGTHEDOORLOCK 0x00000008 +#define SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER 0x00000010 +#define SUPLA_BIT_FUNC_POWERSWITCH 0x00000020 +#define SUPLA_BIT_FUNC_LIGHTSWITCH 0x00000040 +#define SUPLA_BIT_FUNC_STAIRCASETIMER 0x00000080 // ver. >= 8 +#define SUPLA_BIT_FUNC_THERMOMETER 0x00000100 // ver. >= 12 +#define SUPLA_BIT_FUNC_HUMIDITYANDTEMPERATURE 0x00000200 // ver. >= 12 +#define SUPLA_BIT_FUNC_HUMIDITY 0x00000400 // ver. >= 12 +#define SUPLA_BIT_FUNC_WINDSENSOR 0x00000800 // ver. >= 12 +#define SUPLA_BIT_FUNC_PRESSURESENSOR 0x00001000 // ver. >= 12 +#define SUPLA_BIT_FUNC_RAINSENSOR 0x00002000 // ver. >= 12 +#define SUPLA_BIT_FUNC_WEIGHTSENSOR 0x00004000 // ver. >= 12 +#define SUPLA_BIT_FUNC_CONTROLLINGTHEROOFWINDOW 0x00008000 // ver. >= 13 + +#define SUPLA_EVENT_CONTROLLINGTHEGATEWAYLOCK 10 +#define SUPLA_EVENT_CONTROLLINGTHEGATE 20 +#define SUPLA_EVENT_CONTROLLINGTHEGARAGEDOOR 30 +#define SUPLA_EVENT_CONTROLLINGTHEDOORLOCK 40 +#define SUPLA_EVENT_CONTROLLINGTHEROLLERSHUTTER 50 +#define SUPLA_EVENT_CONTROLLINGTHEROOFWINDOW 55 +#define SUPLA_EVENT_POWERONOFF 60 +#define SUPLA_EVENT_LIGHTONOFF 70 +#define SUPLA_EVENT_STAIRCASETIMERONOFF 80 // ver. >= 9 +#define SUPLA_EVENT_VALVEOPENCLOSE 90 // ver. >= 12 +#define SUPLA_EVENT_SET_BRIDGE_VALUE_FAILED 100 // ver. >= 12 + +#define SUPLA_URL_PROTO_HTTP 0x01 +#define SUPLA_URL_PROTO_HTTPS 0x02 + +#define SUPLA_PLATFORM_UNKNOWN 0 +#define SUPLA_PLATFORM_ESP8266 1 + +#define SUPLA_TARGET_CHANNEL 0 +#define SUPLA_TARGET_GROUP 1 +#define SUPLA_TARGET_IODEVICE 2 + +#define SUPLA_MFR_UNKNOWN 0 +#define SUPLA_MFR_ACSOFTWARE 1 +#define SUPLA_MFR_TRANSCOM 2 +#define SUPLA_MFR_LOGI 3 +#define SUPLA_MFR_ZAMEL 4 +#define SUPLA_MFR_NICE 5 +#define SUPLA_MFR_ITEAD 6 +#define SUPLA_MFR_DOYLETRATT 7 +#define SUPLA_MFR_HEATPOL 8 +#define SUPLA_MFR_FAKRO 9 +#define SUPLA_MFR_PEVEKO 10 +#define SUPLA_MFR_WEKTA 11 +#define SUPLA_MFR_STA_SYSTEM 12 + +#define SUPLA_CHANNEL_FLAG_ZWAVE_BRIDGE 0x0001 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_IR_BRIDGE 0x0002 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_RF_BRIDGE 0x0004 // ver. >= 12 +// Free bit for future use: 0x0008 +#define SUPLA_CHANNEL_FLAG_CHART_TYPE_BAR 0x0010 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CHART_DS_TYPE_DIFFERENTAL 0x0020 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CHART_INTERPOLATE_MEASUREMENTS 0x0040 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CAP_ACTION1 0x0080 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CAP_ACTION2 0x0100 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CAP_ACTION3 0x0200 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CAP_ACTION4 0x0400 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_CAP_ACTION5 0x0800 // ver. >= 12 +// Free bits for future use: 0x1000, 0x2000, 0x4000, 0x8000 +#define SUPLA_CHANNEL_FLAG_CHANNELSTATE 0x00010000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_PHASE1_UNSUPPORTED 0x00020000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_PHASE2_UNSUPPORTED 0x00040000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_PHASE3_UNSUPPORTED 0x00080000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_TIME_SETTING_NOT_AVAILABLE 0x00100000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_RSA_ENCRYPTED_PIN_REQUIRED 0x00200000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_OFFLINE_DURING_REGISTRATION 0x00400000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_ZIGBEE_BRIDGE 0x00800000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_COUNTDOWN_TIMER_SUPPORTED 0x01000000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_LIGHTSOURCELIFESPAN_SETTABLE \ + 0x02000000 // ver. >= 12 +#define SUPLA_CHANNEL_FLAG_POSSIBLE_SLEEP_MODE 0x04000000 // ver. >= 12 + +#pragma pack(push, 1) + +typedef struct { + char tag[SUPLA_TAG_SIZE]; + unsigned char version; + unsigned _supla_int_t rr_id; // Request/Response ID + unsigned _supla_int_t call_type; + unsigned _supla_int_t data_size; + char data[SUPLA_MAX_DATA_SIZE]; // Last variable in struct! +} TSuplaDataPacket; + +typedef struct { + // server -> device|client + unsigned char proto_version_min; + unsigned char proto_version; + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; +} TSDC_SuplaGetVersionResult; + +typedef struct { + // server -> device|client + unsigned char server_version_min; + unsigned char server_version; +} TSDC_SuplaVersionError; + +typedef struct { + // device|client -> server + struct _supla_timeval now; +} TDCS_SuplaPingServer; + +// Compatibility with ESP8266 +struct timeval_compat { + _supla_int_t tv_sec; + _supla_int_t tv_usec; +}; + +typedef struct { + // device|client -> server + struct timeval_compat now; +} TDCS_SuplaPingServer_COMPAT; + +typedef struct { + // server -> device|client + struct _supla_timeval now; +} TSDC_SuplaPingServerResult; + +typedef struct { + // device|client -> server + unsigned char activity_timeout; +} TDCS_SuplaSetActivityTimeout; + +typedef struct { + // server -> device|client + unsigned char activity_timeout; + unsigned char min; + unsigned char max; +} TSDC_SuplaSetActivityTimeoutResult; + +typedef struct { + char value[SUPLA_CHANNELVALUE_SIZE]; + char sub_value[SUPLA_CHANNELVALUE_SIZE]; // For example sensor value +} TSuplaChannelValue; + +#ifdef USE_DEPRECATED_EMEV_V1 +#define EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V1 10 +#endif /*USE_DEPRECATED_EMEV_V1*/ +#define EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V2 12 +#define EV_TYPE_IMPULSE_COUNTER_DETAILS_V1 20 +#define EV_TYPE_THERMOSTAT_DETAILS_V1 30 +#define EV_TYPE_CHANNEL_STATE_V1 40 +#define EV_TYPE_TIMER_STATE_V1 50 +#define EV_TYPE_CHANNEL_AND_TIMER_STATE_V1 60 + +#define CALCFG_TYPE_THERMOSTAT_DETAILS_V1 10 + +typedef struct { + char type; // EV_TYPE_ + unsigned _supla_int_t size; + char value[SUPLA_CHANNELEXTENDEDVALUE_SIZE]; // Last variable in struct! +} TSuplaChannelExtendedValue; // v. >= 10 + +typedef struct { + // server -> client + char EOL; // End Of List + _supla_int_t Id; + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_LOCATION_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaLocation; + +typedef struct { + // server -> client + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaLocation + items[SUPLA_LOCATIONPACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaLocationPack; + +typedef struct { + // device -> server + unsigned char Number; + _supla_int_t Type; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannel; + +typedef struct { + // device -> server + + _supla_int_t LocationID; + char LocationPWD[SUPLA_LOCATION_PWD_MAXSIZE]; // UTF8 + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + unsigned char channel_count; + TDS_SuplaDeviceChannel + channels[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! +} TDS_SuplaRegisterDevice; + +typedef struct { + // device -> server + + unsigned char Number; + _supla_int_t Type; + + _supla_int_t FuncList; + _supla_int_t Default; + + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannel_B; // ver. >= 2 + +typedef struct { + // device -> server + + unsigned char Number; + _supla_int_t Type; + + _supla_int_t FuncList; + _supla_int_t Default; + _supla_int_t Flags; + + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannel_C; // ver. >= 10 + +typedef struct { + // device -> server + + _supla_int_t LocationID; + char LocationPWD[SUPLA_LOCATION_PWD_MAXSIZE]; // UTF8 + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + unsigned char channel_count; + TDS_SuplaDeviceChannel_B + channels[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! +} TDS_SuplaRegisterDevice_B; // ver. >= 2 + +typedef struct { + // device -> server + + _supla_int_t LocationID; + char LocationPWD[SUPLA_LOCATION_PWD_MAXSIZE]; // UTF8 + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; + + unsigned char channel_count; + TDS_SuplaDeviceChannel_B + channels[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! +} TDS_SuplaRegisterDevice_C; // ver. >= 6 + +typedef struct { + // device -> server + + char Email[SUPLA_EMAIL_MAXSIZE]; // UTF8 + char AuthKey[SUPLA_AUTHKEY_SIZE]; + + char GUID[SUPLA_GUID_SIZE]; + + char Name[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; + + unsigned char channel_count; + TDS_SuplaDeviceChannel_B + channels[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! +} TDS_SuplaRegisterDevice_D; // ver. >= 7 + +typedef struct { + // device -> server + + char Email[SUPLA_EMAIL_MAXSIZE]; // UTF8 + char AuthKey[SUPLA_AUTHKEY_SIZE]; + + char GUID[SUPLA_GUID_SIZE]; + + char Name[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; + + _supla_int_t Flags; + _supla_int16_t ManufacturerID; + _supla_int16_t ProductID; + + unsigned char channel_count; + TDS_SuplaDeviceChannel_C + channels[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! +} TDS_SuplaRegisterDevice_E; // ver. >= 10 + +typedef struct { + // server -> device + + _supla_int_t result_code; + unsigned char activity_timeout; + unsigned char version; + unsigned char version_min; +} TSD_SuplaRegisterDeviceResult; + +typedef struct { + // device -> server + + unsigned char ChannelNumber; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannelValue; + +typedef struct { + // device -> server + + unsigned char ChannelNumber; + unsigned char Offline; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannelValue_B; // v. >= 12 + +typedef struct { + // device -> server + + unsigned char ChannelNumber; + unsigned char Offline; + unsigned _supla_int_t ValidityTimeSec; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TDS_SuplaDeviceChannelValue_C; // v. >= 12 + +typedef struct { + // device -> server + + unsigned char ChannelNumber; + TSuplaChannelExtendedValue value; // Last variable in struct! +} TDS_SuplaDeviceChannelExtendedValue; // v. >= 10 + +typedef struct { + // server -> device + _supla_int_t SenderID; + unsigned char ChannelNumber; + unsigned _supla_int_t DurationMS; + + char value[SUPLA_CHANNELVALUE_SIZE]; +} TSD_SuplaChannelNewValue; + +typedef struct { + // server -> device + _supla_int_t SenderID; + _supla_int_t GroupID; + unsigned char EOL; // End Of List + unsigned char ChannelNumber; + unsigned _supla_int_t DurationMS; + + char value[SUPLA_CHANNELVALUE_SIZE]; +} TSD_SuplaChannelGroupNewValue; // v. >= 13 + +typedef struct { + // device -> server + unsigned char ChannelNumber; + _supla_int_t SenderID; + char Success; +} TDS_SuplaChannelNewValueResult; + +typedef struct { + // server -> client + + char EOL; // End Of List + _supla_int_t Id; + char online; + + TSuplaChannelValue value; +} TSC_SuplaChannelValue; + +typedef struct { + // server -> client + _supla_int_t Id; + + TSuplaChannelExtendedValue value; // Last variable in struct! +} TSC_SuplaChannelExtendedValue; + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + + TSC_SuplaChannelValue + items[SUPLA_CHANNELVALUE_PACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelValuePack; // ver. >= 9 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + unsigned _supla_int_t pack_size; + + char pack[SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXDATASIZE]; // Last variable in + // struct! +} TSC_SuplaChannelExtendedValuePack; // ver. >= 10 + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t Id; + _supla_int_t LocationID; + _supla_int_t Func; + char online; + + TSuplaChannelValue value; + + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaChannel; + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannel + items[SUPLA_CHANNELPACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelPack; + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t Id; + _supla_int_t LocationID; + _supla_int_t Func; + _supla_int_t AltIcon; + unsigned _supla_int_t Flags; + unsigned char ProtocolVersion; + char online; + + TSuplaChannelValue value; + + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaChannel_B; // ver. >= 8 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannel_B + items[SUPLA_CHANNELPACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelPack_B; // ver. >= 8 + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t Id; + _supla_int_t DeviceID; + _supla_int_t LocationID; + _supla_int_t Type; + _supla_int_t Func; + _supla_int_t AltIcon; + _supla_int_t UserIcon; + _supla_int16_t ManufacturerID; + _supla_int16_t ProductID; + + unsigned _supla_int_t Flags; + unsigned char ProtocolVersion; + char online; + + TSuplaChannelValue value; + + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaChannel_C; // ver. >= 10 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannel_C + items[SUPLA_CHANNELPACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelPack_C; // ver. >= 10 + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t Id; + _supla_int_t LocationID; + _supla_int_t Func; + _supla_int_t AltIcon; + unsigned _supla_int_t Flags; + + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNELGROUP_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaChannelGroup; // ver. >= 9 + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t Id; + _supla_int_t LocationID; + _supla_int_t Func; + _supla_int_t AltIcon; + _supla_int_t UserIcon; + unsigned _supla_int_t Flags; + + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNELGROUP_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SuplaChannelGroup_B; // ver. >= 10 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannelGroup + items[SUPLA_CHANNELGROUP_PACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelGroupPack; // ver. >= 9 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannelGroup_B + items[SUPLA_CHANNELGROUP_PACK_MAXCOUNT]; // Last variable in struct! +} TSC_SuplaChannelGroupPack_B; // ver. >= 10 + +typedef struct { + // server -> client + char EOL; // End Of List + + _supla_int_t ChannelGroupID; + _supla_int_t ChannelID; +} TSC_SuplaChannelGroupRelation; // ver. >= 9 + +typedef struct { + // server -> client + + _supla_int_t count; + _supla_int_t total_left; + TSC_SuplaChannelGroupRelation + items[SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT]; // Last variable in + // struct! +} TSC_SuplaChannelGroupRelationPack; // ver. >= 9 + +typedef struct { + // client -> server + + _supla_int_t AccessID; + char AccessIDpwd[SUPLA_ACCESSID_PWD_MAXSIZE]; // UTF8 + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_CLIENT_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; +} TCS_SuplaRegisterClient; + +typedef struct { + // client -> server + + _supla_int_t AccessID; + char AccessIDpwd[SUPLA_ACCESSID_PWD_MAXSIZE]; // UTF8 + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_CLIENT_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; +} TCS_SuplaRegisterClient_B; // ver. >= 6 + +typedef struct { + // client -> server + + char Email[SUPLA_EMAIL_MAXSIZE]; // UTF8 + char AuthKey[SUPLA_AUTHKEY_SIZE]; + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_CLIENT_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; +} TCS_SuplaRegisterClient_C; // ver. >= 7 + +typedef struct { + // client -> server + + char Email[SUPLA_EMAIL_MAXSIZE]; // UTF8 + char Password[SUPLA_PASSWORD_MAXSIZE]; // Optional - UTF8 + char AuthKey[SUPLA_AUTHKEY_SIZE]; + + char GUID[SUPLA_GUID_SIZE]; + char Name[SUPLA_CLIENT_NAME_MAXSIZE]; // UTF8 + char SoftVer[SUPLA_SOFTVER_MAXSIZE]; + + char ServerName[SUPLA_SERVER_NAME_MAXSIZE]; +} TCS_SuplaRegisterClient_D; // ver. >= 12 + +typedef struct { + // server -> client + + _supla_int_t result_code; + _supla_int_t ClientID; + _supla_int_t LocationCount; + _supla_int_t ChannelCount; + unsigned char activity_timeout; + unsigned char version; + unsigned char version_min; +} TSC_SuplaRegisterClientResult; + +typedef struct { + // server -> client + + _supla_int_t result_code; + _supla_int_t ClientID; + _supla_int_t LocationCount; + _supla_int_t ChannelCount; + _supla_int_t ChannelGroupCount; + _supla_int_t Flags; + unsigned char activity_timeout; + unsigned char version; + unsigned char version_min; +} TSC_SuplaRegisterClientResult_B; // ver. >= 9 + +typedef struct { + // client -> server + unsigned char ChannelId; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TCS_SuplaChannelNewValue; // Deprecated + +typedef struct { + // client -> server + _supla_int_t ChannelId; + char value[SUPLA_CHANNELVALUE_SIZE]; +} TCS_SuplaChannelNewValue_B; + +typedef struct { + // client -> server + _supla_int_t Id; + char Target; // SUPLA_TARGET_ + char value[SUPLA_CHANNELVALUE_SIZE]; +} TCS_SuplaNewValue; // ver. >= 9 + +typedef struct { + // server -> client + _supla_int_t Event; + _supla_int_t ChannelID; + unsigned _supla_int_t DurationMS; + + _supla_int_t SenderID; + unsigned _supla_int_t + SenderNameSize; // including the terminating null byte ('\0') + char + SenderName[SUPLA_SENDER_NAME_MAXSIZE]; // Last variable in struct! | UTF8 +} TSC_SuplaEvent; + +typedef struct { + char Platform; + + _supla_int_t Param1; + _supla_int_t Param2; + _supla_int_t Param3; + _supla_int_t Param4; +} TDS_FirmwareUpdateParams; + +typedef struct { + char available_protocols; + char host[SUPLA_URL_HOST_MAXSIZE]; + _supla_int_t port; + char path[SUPLA_URL_PATH_MAXSIZE]; +} TSD_FirmwareUpdate_Url; + +typedef struct { + char exists; + TSD_FirmwareUpdate_Url url; +} TSD_FirmwareUpdate_UrlResult; + +typedef struct { + unsigned _supla_int_t client_timestamp; // time >= now == enabled + unsigned _supla_int_t iodevice_timestamp; // time >= now == enabled +} TSDC_RegistrationEnabled; + +typedef struct { + unsigned _supla_int_t ExpiresIn; + unsigned _supla_int_t + TokenSize; // including the terminating null byte ('\0') + char Token[SUPLA_OAUTH_TOKEN_MAXSIZE]; // Last variable in struct! +} TSC_OAuthToken; // ver. >= 10 + +typedef struct { + unsigned char ResultCode; + TSC_OAuthToken Token; +} TSC_OAuthTokenRequestResult; // ver. >= 10 + +typedef struct { + // 3 phases + unsigned _supla_int16_t freq; // * 0.01 Hz + unsigned _supla_int16_t voltage[3]; // * 0.01 V + unsigned _supla_int16_t + current[3]; // * 0.001 A (0.01A FOR EM_VAR_CURRENT_OVER_65A) + _supla_int_t power_active[3]; // * 0.00001 kW + _supla_int_t power_reactive[3]; // * 0.00001 kvar + _supla_int_t power_apparent[3]; // * 0.00001 kVA + _supla_int16_t power_factor[3]; // * 0.001 + _supla_int16_t phase_angle[3]; // * 0.1 degree +} TElectricityMeter_Measurement; // v. >= 10 + +#define EM_VAR_FREQ 0x0001 +#define EM_VAR_VOLTAGE 0x0002 +#define EM_VAR_CURRENT 0x0004 +#define EM_VAR_POWER_ACTIVE 0x0008 +#define EM_VAR_POWER_REACTIVE 0x0010 +#define EM_VAR_POWER_APPARENT 0x0020 +#define EM_VAR_POWER_FACTOR 0x0040 +#define EM_VAR_PHASE_ANGLE 0x0080 +#define EM_VAR_FORWARD_ACTIVE_ENERGY 0x0100 +#define EM_VAR_REVERSE_ACTIVE_ENERGY 0x0200 +#define EM_VAR_FORWARD_REACTIVE_ENERGY 0x0400 +#define EM_VAR_REVERSE_REACTIVE_ENERGY 0x0800 +#define EM_VAR_CURRENT_OVER_65A 0x1000 +#define EM_VAR_FORWARD_ACTIVE_ENERGY_BALANCED 0x2000 +#define EM_VAR_REVERSE_ACTIVE_ENERGY_BALANCED 0x4000 +#define EM_VAR_ALL 0xFFFF + +#ifdef __AVR__ +#define EM_MEASUREMENT_COUNT 1 +#else +#define EM_MEASUREMENT_COUNT 5 +#endif + +#ifdef USE_DEPRECATED_EMEV_V1 +// [IODevice->Server->Client] +typedef struct { + unsigned _supla_int64_t total_forward_active_energy[3]; // * 0.00001 kWh + unsigned _supla_int64_t total_reverse_active_energy[3]; // * 0.00001 kWh + unsigned _supla_int64_t total_forward_reactive_energy[3]; // * 0.00001 kvarh + unsigned _supla_int64_t total_reverse_reactive_energy[3]; // * 0.00001 kvarh + + // The price per unit, total cost and currency is overwritten by the server + // total_cost == SUM(total_forward_active_energy[n] * price_per_unit + _supla_int_t total_cost; // * 0.01 + _supla_int_t price_per_unit; // * 0.0001 + // Currency Code A https://www.nationsonline.org/oneworld/currencies.htm + char currency[3]; + + _supla_int_t measured_values; + _supla_int_t period; // Approximate period between measurements in seconds + _supla_int_t m_count; + TElectricityMeter_Measurement m[EM_MEASUREMENT_COUNT]; // Last variable in + // struct! +} TElectricityMeter_ExtendedValue; // v. >= 10 +#endif /*USE_DEPRECATED_EMEV_V1*/ + +// [IODevice->Server->Client] +typedef struct { + unsigned _supla_int64_t total_forward_active_energy[3]; // * 0.00001 kWh + unsigned _supla_int64_t total_reverse_active_energy[3]; // * 0.00001 kWh + unsigned _supla_int64_t total_forward_reactive_energy[3]; // * 0.00001 kvarh + unsigned _supla_int64_t total_reverse_reactive_energy[3]; // * 0.00001 kvarh + unsigned _supla_int64_t + total_forward_active_energy_balanced; // * 0.00001 kWh + // Vector phase-to-phase balancing + unsigned _supla_int64_t + total_reverse_active_energy_balanced; // * 0.00001 kWh + // Vector phase-to-phase balancing + + // The price per unit, total cost and currency is overwritten by the server + // total_cost == SUM(total_forward_active_energy[n] * price_per_unit + _supla_int_t total_cost; // * 0.01 + _supla_int_t total_cost_balanced; // * 0.01 + _supla_int_t price_per_unit; // * 0.0001 + // Currency Code A https://www.nationsonline.org/oneworld/currencies.htm + char currency[3]; + + _supla_int_t measured_values; + _supla_int_t period; // Approximate period between measurements in seconds + _supla_int_t m_count; + TElectricityMeter_Measurement m[EM_MEASUREMENT_COUNT]; // Last variable in + // struct! +} TElectricityMeter_ExtendedValue_V2; // v. >= 12 + +#define EM_VALUE_FLAG_PHASE1_ON 0x01 +#define EM_VALUE_FLAG_PHASE2_ON 0x02 +#define EM_VALUE_FLAG_PHASE3_ON 0x04 + +// [IODevice->Server->Client] +typedef struct { + char flags; + unsigned _supla_int_t total_forward_active_energy; // * 0.01 kW +} TElectricityMeter_Value; // v. >= 10 + +typedef struct { + // The price per unit, total cost and currency is overwritten by the server + // total_cost = calculated_value * price_per_unit + _supla_int_t total_cost; // * 0.01 + _supla_int_t price_per_unit; // * 0.0001 + // Currency Code A https://www.nationsonline.org/oneworld/currencies.htm + char currency[3]; + char custom_unit[9]; // UTF8 including the terminating null byte ('\0') + + _supla_int_t impulses_per_unit; + unsigned _supla_int64_t counter; + _supla_int64_t calculated_value; // * 0.001 +} TSC_ImpulseCounter_ExtendedValue; // v. >= 10 + +typedef struct { + unsigned _supla_int64_t counter; +} TDS_ImpulseCounter_Value; + +typedef struct { + unsigned _supla_int64_t calculated_value; // * 0.001 +} TSC_ImpulseCounter_Value; // v. >= 10 + +typedef struct { + char Email[SUPLA_EMAIL_MAXSIZE]; // UTF8 + char Password[SUPLA_PASSWORD_MAXSIZE]; // UTF8 +} TCS_SuperUserAuthorizationRequest; // v. >= 10 + +typedef struct { + _supla_int_t Result; +} TSC_SuperUserAuthorizationResult; // v. >= 10 + +#define SUPLA_CALCFG_RESULT_FALSE 0 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_TRUE 1 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_DONE 2 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_IN_PROGRESS 3 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_NODE_FOUND 4 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_SENDER_CONFLICT 100 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_TIMEOUT 101 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_NOT_SUPPORTED 102 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_ID_NOT_EXISTS 103 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_UNAUTHORIZED 104 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_DEBUG 105 // ver. >= 12 +#define SUPLA_CALCFG_RESULT_NOT_SUPPORTED_IN_SLAVE_MODE 106 // ver. >= 12 + +#define SUPLA_CALCFG_CMD_GET_CHANNEL_FUNCLIST 1000 // v. >= 11 +#define SUPLA_CALCFG_CMD_CANCEL_ALL_CLIENT_COMMANDS 1010 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_RESET_AND_CLEAR 2000 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_ADD_NODE 2010 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_REMOVE_NODE 2020 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_GET_NODE_LIST 2030 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_GET_ASSIGNED_NODE_ID 2040 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_ASSIGN_NODE_ID 2050 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_GET_WAKE_UP_SETTINGS 2060 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_SET_WAKE_UP_TIME 2070 // v. >= 12 +#define SUPLA_CALCFG_CMD_ZWAVE_CONFIG_MODE_ACTIVE 4000 // v. >= 12 +#define SUPLA_CALCFG_CMD_DEBUG_STRING 5000 // v. >= 12 +#define SUPLA_CALCFG_CMD_PROGRESS_REPORT 5001 // v. >= 12 +#define SUPLA_CALCFG_CMD_SET_LIGHTSOURCE_LIFESPAN 6000 // v. >= 12 + +#define CALCFG_ZWAVE_SCREENTYPE_UNKNOWN 0 +#define CALCFG_ZWAVE_SCREENTYPE_MULTILEVEL 1 +#define CALCFG_ZWAVE_SCREENTYPE_BINARY 2 +#define CALCFG_ZWAVE_SCREENTYPE_MULTILEVEL_AUTOSHADE 3 +#define CALCFG_ZWAVE_SCREENTYPE_MULTILEVEL_COLOR_CONTROL 4 +#define CALCFG_ZWAVE_SCREENTYPE_BINARY_COLOR_CONTROL 5 +#define CALCFG_ZWAVE_SCREENTYPE_SENSOR 6 + +#define ZWAVE_NODE_NAME_MAXSIZE 50 + +#define ZWAVE_NODE_FLAG_CHANNEL_ASSIGNED 0x1 +#define ZWAVE_NODE_FLAG_WAKEUP_TIME_SETTABLE 0x2 + +typedef struct { + unsigned char Id; + unsigned char Flags; + union { + unsigned char ChannelNumber; + _supla_int_t ChannelID; + }; + unsigned char ScreenType; + unsigned char NameSize; // including the terminating null byte ('\0') + char Name[ZWAVE_NODE_NAME_MAXSIZE]; // UTF8. Last variable in struct! +} TCalCfg_ZWave_Node; // v. >= 12 + +typedef struct { + unsigned _supla_int_t MinimumSec : 24; + unsigned _supla_int_t MaximumSec : 24; + unsigned _supla_int_t ValueSec : 24; + unsigned _supla_int_t IntervalStepSec : 24; +} TCalCfg_ZWave_WakeupSettingsReport; + +typedef struct { + unsigned _supla_int_t TimeSec : 24; +} TCalCfg_ZWave_WakeUpTime; + +typedef struct { + _supla_int_t Command; + unsigned char Progress; // 0 - 100% +} TCalCfg_ProgressReport; + +typedef struct { + unsigned char ResetCounter; // 0 - NO, 1 - YES + unsigned char SetTime; // 0 - NO, 1 - YES + unsigned short LightSourceLifespan; // 0 - 65535 hours +} TCalCfg_LightSourceLifespan; + +// CALCFG == CALIBRATION / CONFIG +typedef struct { + _supla_int_t ChannelID; + _supla_int_t Command; + _supla_int_t DataType; + unsigned _supla_int_t DataSize; + char Data[SUPLA_CALCFG_DATA_MAXSIZE]; // Last variable in struct! +} TCS_DeviceCalCfgRequest; // v. >= 10 + +// CALCFG == CALIBRATION / CONFIG +typedef struct { + _supla_int_t Id; + char Target; // SUPLA_TARGET_ + _supla_int_t Command; + _supla_int_t DataType; + unsigned _supla_int_t DataSize; + char Data[SUPLA_CALCFG_DATA_MAXSIZE]; // Last variable in struct! +} TCS_DeviceCalCfgRequest_B; // v. >= 11 + +typedef struct { + _supla_int_t ChannelID; + _supla_int_t Command; + _supla_int_t Result; + unsigned _supla_int_t DataSize; + char Data[SUPLA_CALCFG_DATA_MAXSIZE]; // Last variable in struct! +} TSC_DeviceCalCfgResult; // v. >= 10 + +typedef struct { + _supla_int_t SenderID; + _supla_int_t ChannelNumber; + _supla_int_t Command; + char SuperUserAuthorized; + _supla_int_t DataType; + unsigned _supla_int_t DataSize; + char Data[SUPLA_CALCFG_DATA_MAXSIZE]; // Last variable in struct! +} TSD_DeviceCalCfgRequest; // v. >= 10 + +typedef struct { + _supla_int_t ReceiverID; + _supla_int_t ChannelNumber; + _supla_int_t Command; + _supla_int_t Result; + unsigned _supla_int_t DataSize; + char Data[SUPLA_CALCFG_DATA_MAXSIZE]; // Last variable in struct! +} TDS_DeviceCalCfgResult; // v. >= 10 + +#define RGBW_BRIGHTNESS_ONOFF 0x1 +#define RGBW_COLOR_ONOFF 0x2 + +typedef struct { + char brightness; + char colorBrightness; + char R; + char G; + char B; + char onOff; +} TRGBW_Value; // v. >= 10 + +#define DIGIGLASS_FLAG_HORIZONATAL 0x1 + +typedef struct { + unsigned char sectionCount; // 1 - 16 + unsigned char flags; + unsigned short opaqueSections; +} TDigiglass_Value; + +typedef struct { + unsigned char sec; // 0-59 + unsigned char min; // 0-59 + unsigned char hour; // 0-24 + unsigned char dayOfWeek; // 1 = Sunday, 2 = Monday, …, 7 = Saturday +} TThermostat_Time; // v. >= 11 + +#define THERMOSTAT_SCHEDULE_DAY_SUNDAY 0x01 +#define THERMOSTAT_SCHEDULE_DAY_MONDAY 0x02 +#define THERMOSTAT_SCHEDULE_DAY_TUESDAY 0x04 +#define THERMOSTAT_SCHEDULE_DAY_WEDNESDAY 0x08 +#define THERMOSTAT_SCHEDULE_DAY_THURSDAY 0x10 +#define THERMOSTAT_SCHEDULE_DAY_FRIDAY 0x20 +#define THERMOSTAT_SCHEDULE_DAY_SATURDAY 0x40 +#define THERMOSTAT_SCHEDULE_DAY_ALL 0xFF + +#define THERMOSTAT_SCHEDULE_HOURVALUE_TYPE_TEMPERATURE 0 +#define THERMOSTAT_SCHEDULE_HOURVALUE_TYPE_PROGRAM 1 + +typedef struct { + unsigned char ValueType; // THERMOSTAT_SCHEDULE_HOURVALUE_TYPE_ + char HourValue[7][24]; // 7 days x 24h + // 0 = Sunday, 1 = Monday, …, 6 = Saturday +} TThermostat_Schedule; // v. >= 11 + +typedef struct { + unsigned char ValueType; // THERMOSTAT_SCHEDULE_HOURVALUE_TYPE_ + unsigned char WeekDays; // THERMOSTAT_SCHEDULE_DAY_ + char HourValue[24]; +} TThermostatValueGroup; // v. >= 11 + +typedef struct { + TThermostatValueGroup Group[4]; +} TThermostat_ScheduleCfg; // v. >= 11 + +#define TEMPERATURE_INDEX1 0x0001 +#define TEMPERATURE_INDEX2 0x0002 +#define TEMPERATURE_INDEX3 0x0004 +#define TEMPERATURE_INDEX4 0x0008 +#define TEMPERATURE_INDEX5 0x0010 +#define TEMPERATURE_INDEX6 0x0020 +#define TEMPERATURE_INDEX7 0x0040 +#define TEMPERATURE_INDEX8 0x0080 +#define TEMPERATURE_INDEX9 0x0100 +#define TEMPERATURE_INDEX10 0x0200 + +typedef struct { + _supla_int16_t Index; // BIT0 Temperature[0], BIT1 Temperature[1] etc... + unsigned _supla_int16_t Temperature[10]; +} TThermostatTemperatureCfg; + +// Thermostat configuration commands - ver. >= 11 +#define SUPLA_THERMOSTAT_CMD_TURNON 1 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_AUTO 2 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_COOL 3 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_HEAT 4 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_NORMAL 5 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_ECO 6 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_TURBO 7 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_DRY 8 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_FANONLY 9 +#define SUPLA_THERMOSTAT_CMD_SET_MODE_PURIFIER 10 +#define SUPLA_THERMOSTAT_CMD_SET_SCHEDULE 11 +#define SUPLA_THERMOSTAT_CMD_SET_TIME 12 +#define SUPLA_THERMOSTAT_CMD_SET_TEMPERATURE 13 + +// Thermostat capability flags - ver. >= 11 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_ONOFF 0x0001 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_AUTO 0x0002 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_COOL 0x0004 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_HEAT 0x0008 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_ECO 0x0010 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_DRY 0x0020 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_FANONLY 0x0040 +#define SUPLA_THERMOSTAT_CAP_FLAG_MODE_PURIFIER 0x0080 +#define SUPLA_THERMOSTAT_CAP_FLAG_SCHEDULE 0x0100 + +// Thermostat value flags - ver. >= 11 +#define SUPLA_THERMOSTAT_VALUE_FLAG_ON 0x0001 +#define SUPLA_THERMOSTAT_VALUE_FLAG_AUTO_MODE 0x0002 +#define SUPLA_THERMOSTAT_VALUE_FLAG_COOL_MODE 0x0004 +#define SUPLA_THERMOSTAT_VALUE_FLAG_HEAT_MODE 0x0008 +#define SUPLA_THERMOSTAT_VALUE_FLAG_ECO_MODE 0x0010 +#define SUPLA_THERMOSTAT_VALUE_FLAG_DRY_MODE 0x0020 +#define SUPLA_THERMOSTAT_VALUE_FLAG_FANONLY_MODE 0x0040 +#define SUPLA_THERMOSTAT_VALUE_FLAG_PURIFIER_MODE 0x0080 + +// Thermostat fields - ver. >= 11 +#define THERMOSTAT_FIELD_MeasuredTemperatures 0x01 +#define THERMOSTAT_FIELD_PresetTemperatures 0x02 +#define THERMOSTAT_FIELD_Flags 0x04 +#define THERMOSTAT_FIELD_Values 0x08 +#define THERMOSTAT_FIELD_Time 0x10 +#define THERMOSTAT_FIELD_Schedule 0x20 + +typedef struct { + unsigned char Fields; + _supla_int16_t MeasuredTemperature[10]; // * 0.01 + _supla_int16_t PresetTemperature[10]; // * 0.01 + _supla_int16_t Flags[8]; + _supla_int16_t Values[8]; + TThermostat_Time Time; + TThermostat_Schedule Schedule; // 7 days x 24h (4bit/hour) +} TThermostat_ExtendedValue; // v. >= 11 + +typedef struct { + unsigned char IsOn; + unsigned char Flags; + _supla_int16_t MeasuredTemperature; // * 0.01 + _supla_int16_t PresetTemperature; // * 0.01 +} TThermostat_Value; // v. >= 11 + +typedef struct { + unsigned _supla_int16_t year; + unsigned char month; + unsigned char day; + unsigned char dayOfWeek; // 1 = Sunday, 2 = Monday, …, 7 = Saturday + unsigned char hour; + unsigned char min; + unsigned char sec; + unsigned _supla_int_t + timezoneSize; // including the terminating null byte ('\0') + char timezone[SUPLA_TIMEZONE_MAXSIZE]; // Last variable in struct! +} TSDC_UserLocalTimeResult; + +typedef struct { + _supla_int_t SenderID; + union { + _supla_int_t ChannelID; // Client -> Server + unsigned char ChannelNumber; // Server -> Device + }; +} TCSD_ChannelStateRequest; // v. >= 12 Client -> Server -> Device + +#define SUPLA_CHANNELSTATE_FIELD_IPV4 0x0001 +#define SUPLA_CHANNELSTATE_FIELD_MAC 0x0002 +#define SUPLA_CHANNELSTATE_FIELD_BATTERYLEVEL 0x0004 +#define SUPLA_CHANNELSTATE_FIELD_BATTERYPOWERED 0x0008 +#define SUPLA_CHANNELSTATE_FIELD_WIFIRSSI 0x0010 +#define SUPLA_CHANNELSTATE_FIELD_WIFISIGNALSTRENGTH 0x0020 +#define SUPLA_CHANNELSTATE_FIELD_BRIDGENODESIGNALSTRENGTH 0x0040 +#define SUPLA_CHANNELSTATE_FIELD_UPTIME 0x0080 +#define SUPLA_CHANNELSTATE_FIELD_CONNECTIONUPTIME 0x0100 +#define SUPLA_CHANNELSTATE_FIELD_BATTERYHEALTH 0x0200 +#define SUPLA_CHANNELSTATE_FIELD_BRIDGENODEONLINE 0x0400 +#define SUPLA_CHANNELSTATE_FIELD_LASTCONNECTIONRESETCAUSE 0x0800 +#define SUPLA_CHANNELSTATE_FIELD_LIGHTSOURCELIFESPAN 0x1000 +#define SUPLA_CHANNELSTATE_FIELD_LIGHTSOURCEOPERATINGTIME 0x2000 + +#define SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN 0 +#define SUPLA_LASTCONNECTIONRESETCAUSE_ACTIVITY_TIMEOUT 1 +#define SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST 2 +#define SUPLA_LASTCONNECTIONRESETCAUSE_SERVER_CONNECTION_LOST 3 + +typedef struct { + _supla_int_t ReceiverID; // Not used in extended values + union { + // Not used in extended values + _supla_int_t ChannelID; // Server -> Client + unsigned char ChannelNumber; // Device -> Server + }; + _supla_int_t Fields; + _supla_int_t defaultIconField; // SUPLA_CHANNELSTATE_FIELD_* + unsigned _supla_int_t IPv4; + unsigned char MAC[6]; + unsigned char BatteryLevel; // 0 - 100% + unsigned char BatteryPowered; // true(1)/false(0) + char WiFiRSSI; + unsigned char WiFiSignalStrength; // 0 - 100% + unsigned char BridgeNodeOnline; // 0/1 + unsigned char BridgeNodeSignalStrength; // 0 - 100% + unsigned _supla_int_t Uptime; // sec. + unsigned _supla_int_t ConnectionUptime; // sec. + unsigned char BatteryHealth; + unsigned char LastConnectionResetCause; // SUPLA_LASTCONNECTIONRESETCAUSE_* + unsigned short LightSourceLifespan; // 0 - 65535 hours + union { + short LightSourceLifespanLeft; // -327,67 - 100.00% LightSourceLifespan * + // 0.01 + _supla_int_t LightSourceOperatingTime; // -3932100sec. - 3932100sec. + }; + char EmptySpace[2]; // Empty space for future use +} TDSC_ChannelState; // v. >= 12 Device -> Server -> Client + +#define TChannelState_ExtendedValue TDSC_ChannelState + +typedef struct { + _supla_int_t ChannelID; +} TCS_ChannelBasicCfgRequest; // v. >= 12 + +typedef struct { + union { + // Remaining time to turn off + unsigned _supla_int_t RemainingTimeMs; + unsigned _supla_int_t RemainingTimeTs; // Unix timestamp - Filled by server + }; + + unsigned char TargetValue[SUPLA_CHANNELVALUE_SIZE]; + + _supla_int_t SenderID; + unsigned _supla_int_t + SenderNameSize; // including the terminating null byte ('\0') + char SenderName[SUPLA_SENDER_NAME_MAXSIZE]; // Last variable in struct! + // UTF8 | Filled by server +} TTimerState_ExtendedValue; + +typedef struct { + TChannelState_ExtendedValue Channel; + TTimerState_ExtendedValue Timer; // Last variable in struct! +} TChannelAndTimerState_ExtendedValue; + +typedef struct { + char DeviceName[SUPLA_DEVICE_NAME_MAXSIZE]; // UTF8 + char DeviceSoftVer[SUPLA_SOFTVER_MAXSIZE]; + _supla_int_t DeviceID; + _supla_int_t DeviceFlags; + _supla_int16_t ManufacturerID; + _supla_int16_t ProductID; + + _supla_int_t ID; + unsigned char Number; + _supla_int_t Type; + _supla_int_t Func; + _supla_int_t FuncList; + + unsigned _supla_int_t ChannelFlags; + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_ChannelBasicCfg; // v. >= 12 + +typedef struct { + _supla_int_t ChannelID; + _supla_int_t Func; +} TCS_SetChannelFunction; // v. >= 12 + +typedef struct { + _supla_int_t ChannelID; + _supla_int_t Func; + unsigned char ResultCode; +} TSC_SetChannelFunctionResult; // v. >= 12 + +typedef struct { + _supla_int_t ChannelID; + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TCS_SetChannelCaption; // v. >= 12 + +typedef struct { + _supla_int_t ChannelID; + unsigned char ResultCode; + unsigned _supla_int_t + CaptionSize; // including the terminating null byte ('\0') + char Caption[SUPLA_CHANNEL_CAPTION_MAXSIZE]; // Last variable in struct! +} TSC_SetChannelCaptionResult; // v. >= 12 + +typedef struct { + unsigned char ResultCode; +} TSC_ClientsReconnectRequestResult; // v. >= 12 + +typedef struct { + // Disabled: 0 + // Ignore: <0 + _supla_int_t IODeviceRegistrationTimeSec; + _supla_int_t ClientRegistrationTimeSec; +} TCS_SetRegistrationEnabled; // v. >= 12 + +typedef struct { + unsigned char ResultCode; +} TSC_SetRegistrationEnabledResult; // v. >= 12 + +typedef struct { + int DeviceID; +} TCS_DeviceReconnectRequest; // v. >= 12 + +typedef struct { + int DeviceID; + unsigned char ResultCode; +} TSC_DeviceReconnectRequestResult; // v. >= 12 + +typedef struct { + // server -> device + + unsigned char ChannelCount; + _supla_int_t Functions[SUPLA_CHANNELMAXCOUNT]; // Last variable in struct! + // Functions[ChannelNumber] +} TSD_ChannelFunctions; // ver. >= 12 + +#define SUPLA_VALVE_FLAG_FLOODING 0x1 +#define SUPLA_VALVE_FLAG_MANUALLY_CLOSED 0x2 + +typedef struct { + union { + unsigned char closed; + unsigned char closed_percent; + }; + + unsigned char flags; +} TValve_Value; + +#pragma pack(pop) + +void *sproto_init(void); +void sproto_free(void *spd_ptr); + +#ifndef SPROTO_WITHOUT_OUT_BUFFER +char sproto_out_buffer_append(void *spd_ptr, TSuplaDataPacket *sdp); +unsigned _supla_int_t sproto_pop_out_data(void *spd_ptr, char *buffer, + unsigned _supla_int_t buffer_size); +#endif /*SPROTO_WITHOUT_OUT_BUFFER*/ +char sproto_out_dataexists(void *spd_ptr); +char sproto_in_buffer_append(void *spd_ptr, char *data, + unsigned _supla_int_t data_size); + +char sproto_pop_in_sdp(void *spd_ptr, TSuplaDataPacket *sdp); +char sproto_in_dataexists(void *spd_ptr); + +unsigned char sproto_get_version(void *spd_ptr); +void sproto_set_version(void *spd_ptr, unsigned char version); +void sproto_sdp_init(void *spd_ptr, TSuplaDataPacket *sdp); +char sproto_set_data(TSuplaDataPacket *sdp, char *data, + unsigned _supla_int_t data_size, + unsigned _supla_int_t call_type); +TSuplaDataPacket *sproto_sdp_malloc(void *spd_ptr); +void sproto_sdp_free(TSuplaDataPacket *sdp); + +void sproto_log_summary(void *spd_ptr); +void sproto_buffer_dump(void *spd_ptr, unsigned char in); + +#ifdef __cplusplus +} +#endif + +#endif /* supla_proto_H_ */ diff --git a/lib/SuplaDevice/src/supla-common/srpc.c b/lib/SuplaDevice/src/supla-common/srpc.c new file mode 100644 index 00000000..66b9e7d8 --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/srpc.c @@ -0,0 +1,2566 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "srpc.h" +#include +#include +#include "lck.h" +#include "log.h" +#include "proto.h" + +#if defined(ESP8266) || defined(ESP32) + +#include +#if !defined(ESP32) +#include +#endif + +#if defined(ARDUINO_ARCH_ESP8266) +#include +#define __EH_DISABLED +#elif defined(ARDUINO_ARCH_ESP32) +#define __EH_DISABLED +#else +#include +#include "espmissingincludes.h" +#endif + +#ifndef SRPC_BUFFER_SIZE +#define SRPC_BUFFER_SIZE 1024 +#endif /*SRPC_BUFFER_SIZE*/ + +#ifndef SRPC_QUEUE_SIZE +#define SRPC_QUEUE_SIZE 2 +#endif /*SRPC_QUEUE_SIZE*/ + +#ifndef SRPC_QUEUE_MIN_ALLOC_COUNT +#define SRPC_QUEUE_MIN_ALLOC_COUNT 2 +#endif /* SRPC_QUEUE_MIN_ALLOC_COUNT */ + +#elif defined(__AVR__) + +#define SRPC_BUFFER_SIZE 32 +#define SRPC_QUEUE_SIZE 1 +#define SRPC_QUEUE_MIN_ALLOC_COUNT 1 +#define __EH_DISABLED + +#else +#include +#endif + +#ifndef SRPC_BUFFER_SIZE +#define SRPC_BUFFER_SIZE 32768 +#endif /*SRPC_BUFFER_SIZE*/ + +#ifndef SRPC_QUEUE_SIZE +#define SRPC_QUEUE_SIZE 10 +#endif /*SRPC_QUEUE_SIZE*/ + +#ifndef SRPC_QUEUE_MIN_ALLOC_COUNT +#define SRPC_QUEUE_MIN_ALLOC_COUNT 0 +#endif /*SRPC_QUEUE_MIN_ALLOC_COUNT*/ + +typedef struct { + unsigned char item_count; + unsigned char alloc_count; + + TSuplaDataPacket *item[SRPC_QUEUE_SIZE]; +} Tsrpc_Queue; + +typedef struct { + void *proto; + TsrpcParams params; + + TSuplaDataPacket sdp; + +#ifndef SRPC_WITHOUT_IN_QUEUE + Tsrpc_Queue in_queue; +#endif /*SRPC_WITHOUT_IN_QUEUE*/ + +#ifndef SRPC_WITHOUT_OUT_QUEUE + Tsrpc_Queue out_queue; +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ + + void *lck; +} Tsrpc; + +void SRPC_ICACHE_FLASH srpc_params_init(TsrpcParams *params) { + memset(params, 0, sizeof(TsrpcParams)); +} + +void *SRPC_ICACHE_FLASH srpc_init(TsrpcParams *params) { + Tsrpc *srpc = (Tsrpc *)malloc(sizeof(Tsrpc)); + + if (srpc == NULL) return NULL; + + memset(srpc, 0, sizeof(Tsrpc)); + srpc->proto = sproto_init(); + +#ifndef ESP8266 +#ifndef ESP32 +#ifndef __AVR__ + assert(params != 0); + assert(params->data_read != 0); + assert(params->data_write != 0); +#endif +#endif +#endif + + memcpy(&srpc->params, params, sizeof(TsrpcParams)); + + srpc->lck = lck_init(); + + return srpc; +} + +void SRPC_ICACHE_FLASH srpc_queue_free(Tsrpc_Queue *queue) { + _supla_int_t a; + for (a = 0; a < SRPC_QUEUE_SIZE; a++) { + if (queue->item[a] != NULL) { + free(queue->item[a]); + } + } + + queue->item_count = 0; + queue->alloc_count = 0; +} + +void SRPC_ICACHE_FLASH srpc_free(void *_srpc) { + if (_srpc) { + Tsrpc *srpc = (Tsrpc *)_srpc; + + sproto_free(srpc->proto); + +#ifndef SRPC_WITHOUT_IN_QUEUE + srpc_queue_free(&srpc->in_queue); +#endif /*SRPC_WITHOUT_IN_QUEUE*/ + +#ifndef SRPC_WITHOUT_OUT_QUEUE + srpc_queue_free(&srpc->out_queue); +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ + lck_free(srpc->lck); + + free(srpc); + } +} + +char SRPC_ICACHE_FLASH srpc_queue_push(Tsrpc_Queue *queue, + TSuplaDataPacket *sdp) { + if (queue->item_count >= SRPC_QUEUE_SIZE) { + return SUPLA_RESULT_FALSE; + } + + if (queue->item[queue->item_count] == NULL) { + queue->item[queue->item_count] = + (TSuplaDataPacket *)malloc(sizeof(TSuplaDataPacket)); + } + + if (queue->item[queue->item_count] == NULL) { + return SUPLA_RESULT_FALSE; + } else { + queue->alloc_count++; + } + + memcpy(queue->item[queue->item_count], sdp, sizeof(TSuplaDataPacket)); + queue->item_count++; + + return SUPLA_RESULT_TRUE; +} + +char SRPC_ICACHE_FLASH srpc_queue_pop(Tsrpc_Queue *queue, TSuplaDataPacket *sdp, + unsigned _supla_int_t rr_id) { + _supla_int_t a, b; + + for (a = 0; a < queue->item_count; a++) + if (rr_id == 0 || queue->item[a]->rr_id == rr_id) { + memcpy(sdp, queue->item[a], sizeof(TSuplaDataPacket)); + + if (queue->alloc_count > SRPC_QUEUE_MIN_ALLOC_COUNT) { + queue->alloc_count--; + free(queue->item[a]); + queue->item[a] = NULL; + } + + TSuplaDataPacket *item = queue->item[a]; + + for (b = a; b < queue->item_count - 1; b++) { + queue->item[b] = queue->item[b + 1]; + } + + queue->item_count--; + queue->item[queue->item_count] = item; + + return SUPLA_RESULT_TRUE; + } + + return SUPLA_RESULT_FALSE; +} + +char SRPC_ICACHE_FLASH srpc_in_queue_pop(Tsrpc *srpc, TSuplaDataPacket *sdp, + unsigned _supla_int_t rr_id) { +#ifdef SRPC_WITHOUT_IN_QUEUE + return 1; +#else + return srpc_queue_pop(&srpc->in_queue, sdp, rr_id); +#endif /*SRPC_WITHOUT_IN_QUEUE*/ +} + +#ifndef SRPC_WITHOUT_IN_QUEUE +char SRPC_ICACHE_FLASH srpc_in_queue_push(Tsrpc *srpc, TSuplaDataPacket *sdp) { + return srpc_queue_push(&srpc->in_queue, sdp); +} +#endif /*SRPC_WITHOUT_IN_QUEUE*/ + +char SRPC_ICACHE_FLASH srpc_out_queue_push(Tsrpc *srpc, TSuplaDataPacket *sdp) { +#ifdef SRPC_WITHOUT_OUT_QUEUE + unsigned _supla_int_t data_size = sizeof(TSuplaDataPacket); + if (sdp->data_size < SUPLA_MAX_DATA_SIZE) { + data_size -= SUPLA_MAX_DATA_SIZE - sdp->data_size; + } + srpc->params.data_write((char *)sdp, data_size, srpc->params.user_params); + srpc->params.data_write(sproto_tag, SUPLA_TAG_SIZE, srpc->params.user_params); + return 1; +#else + return srpc_queue_push(&srpc->out_queue, sdp); +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ +} + +#ifndef SRPC_WITHOUT_OUT_QUEUE +char SRPC_ICACHE_FLASH srpc_out_queue_pop(Tsrpc *srpc, TSuplaDataPacket *sdp, + unsigned _supla_int_t rr_id) { + return srpc_queue_pop(&srpc->out_queue, sdp, rr_id); +} +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ + +unsigned char SRPC_ICACHE_FLASH srpc_out_queue_item_count(void *srpc) { +#ifdef SRPC_WITHOUT_OUT_QUEUE + return 0; +#else + return ((Tsrpc *)srpc)->out_queue.item_count; +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ +} + +char SRPC_ICACHE_FLASH srpc_input_dataexists(void *_srpc) { + int result = SUPLA_RESULT_FALSE; + Tsrpc *srpc = (Tsrpc *)_srpc; + lck_lock(srpc->lck); + result = sproto_in_dataexists(srpc->proto); + return lck_unlock_r(srpc->lck, result); +} + +char SRPC_ICACHE_FLASH srpc_output_dataexists(void *_srpc) { + int result = SUPLA_RESULT_FALSE; + Tsrpc *srpc = (Tsrpc *)_srpc; + lck_lock(srpc->lck); + result = sproto_out_dataexists(srpc->proto); + return lck_unlock_r(srpc->lck, result); +} + +char SRPC_ICACHE_FLASH srpc_iterate(void *_srpc) { + Tsrpc *srpc = (Tsrpc *)_srpc; + char data_buffer[SRPC_BUFFER_SIZE]; + char result; + unsigned char version; +#ifndef __EH_DISABLED + unsigned char raise_event = 0; +#endif /*__EH_DISABLED*/ + + // --------- IN --------------- + _supla_int_t data_size = srpc->params.data_read(data_buffer, SRPC_BUFFER_SIZE, + srpc->params.user_params); + + if (data_size == 0) return SUPLA_RESULT_FALSE; + + lck_lock(srpc->lck); + + if (data_size > 0 && + SUPLA_RESULT_TRUE != (result = sproto_in_buffer_append( + srpc->proto, data_buffer, data_size))) { + supla_log(LOG_DEBUG, "sproto_in_buffer_append: %i, datasize: %i", result, + data_size); + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); + } + + if (SUPLA_RESULT_TRUE == + (result = sproto_pop_in_sdp(srpc->proto, &srpc->sdp))) { +#ifndef __EH_DISABLED + raise_event = sproto_in_dataexists(srpc->proto) == 1 ? 1 : 0; +#endif /*__EH_DISABLED*/ + +#ifdef SRPC_WITHOUT_IN_QUEUE + if (srpc->params.on_remote_call_received) { + lck_unlock(srpc->lck); + srpc->params.on_remote_call_received( + srpc, srpc->sdp.rr_id, srpc->sdp.call_type, srpc->params.user_params, + srpc->sdp.version); + lck_lock(srpc->lck); + } +#else + if (SUPLA_RESULT_TRUE == srpc_in_queue_push(srpc, &srpc->sdp)) { + if (srpc->params.on_remote_call_received) { + lck_unlock(srpc->lck); + srpc->params.on_remote_call_received( + srpc, srpc->sdp.rr_id, srpc->sdp.call_type, + srpc->params.user_params, srpc->sdp.version); + lck_lock(srpc->lck); + } + + } else { + supla_log(LOG_DEBUG, "ssrpc_in_queue_push error"); + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); + } +#endif /*SRPC_WITHOUT_IN_QUEUE*/ + + } else if (result != SUPLA_RESULT_FALSE) { + if (result == (char)SUPLA_RESULT_VERSION_ERROR) { + if (srpc->params.on_version_error) { + version = srpc->sdp.version; + lck_unlock(srpc->lck); + + srpc->params.on_version_error(srpc, version, srpc->params.user_params); + return SUPLA_RESULT_FALSE; + } + } else { + supla_log(LOG_DEBUG, "sproto_pop_in_sdp error: %i", result); + } + + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); + } + + // --------- OUT --------------- +#ifndef SRPC_WITHOUT_OUT_QUEUE + if (srpc_out_queue_pop(srpc, &srpc->sdp, 0) == SUPLA_RESULT_TRUE && + SUPLA_RESULT_TRUE != + (result = sproto_out_buffer_append(srpc->proto, &srpc->sdp)) && + result != SUPLA_RESULT_FALSE) { + supla_log(LOG_DEBUG, "sproto_out_buffer_append error: %i", result); + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); + } + + data_size = sproto_pop_out_data(srpc->proto, data_buffer, SRPC_BUFFER_SIZE); + + if (data_size != 0) { + lck_unlock(srpc->lck); + srpc->params.data_write(data_buffer, data_size, srpc->params.user_params); + lck_lock(srpc->lck); + } + +#ifndef __EH_DISABLED + if (srpc->params.eh != 0 && + (sproto_out_dataexists(srpc->proto) == 1 || + srpc_out_queue_item_count(srpc) || raise_event)) { + eh_raise_event(srpc->params.eh); + } +#endif /*__EH_DISABLED*/ + +#endif /*SRPC_WITHOUT_OUT_QUEUE*/ + return lck_unlock_r(srpc->lck, SUPLA_RESULT_TRUE); +} + +typedef unsigned _supla_int_t (*_func_srpc_pack_get_caption_size)( + void *pack, _supla_int_t idx); +typedef void *(*_func_srpc_pack_get_item_ptr)(void *pack, _supla_int_t idx); +typedef _supla_int_t (*_func_srpc_pack_get_pack_count)(void *pack); +typedef void (*_func_srpc_pack_set_pack_count)(void *pack, _supla_int_t count, + unsigned char increment); +typedef unsigned _supla_int_t (*_func_srpc_pack_get_item_caption_size)( + void *item); +typedef unsigned _supla_int_t (*_func_srpc_pack_get_caption_size)( + void *pack, _supla_int_t idx); + +void SRPC_ICACHE_FLASH srpc_getpack( + Tsrpc *srpc, TsrpcReceivedData *rd, unsigned _supla_int_t pack_sizeof, + unsigned _supla_int_t item_sizeof, unsigned _supla_int_t pack_max_count, + unsigned _supla_int_t caption_max_size, + _func_srpc_pack_get_pack_count pack_get_count, + _func_srpc_pack_set_pack_count pack_set_count, + _func_srpc_pack_get_item_ptr get_item_ptr, + _func_srpc_pack_get_item_caption_size get_item_caption_size) { + _supla_int_t header_size = pack_sizeof - (item_sizeof * pack_max_count); + _supla_int_t c_header_size = item_sizeof - caption_max_size; + _supla_int_t a, count, size, offset, pack_size; + void *pack = NULL; + + if (srpc->sdp.data_size < header_size || srpc->sdp.data_size > pack_sizeof) { + return; + } + + count = pack_get_count(srpc->sdp.data); + + if (count < 0 || count > pack_max_count) { + return; + } + + pack_size = header_size + (item_sizeof * count); + pack = (TSC_SuplaChannelPack *)malloc(pack_size); + + if (pack == NULL) return; + + memset(pack, 0, pack_size); + memcpy(pack, srpc->sdp.data, header_size); + + offset = header_size; + pack_set_count(pack, 0, 0); + + for (a = 0; a < count; a++) + if (srpc->sdp.data_size - offset >= c_header_size) { + size = get_item_caption_size(&srpc->sdp.data[offset]); + + if (size >= 0 && size <= caption_max_size && + srpc->sdp.data_size - offset >= c_header_size + size) { + memcpy(get_item_ptr(pack, a), &srpc->sdp.data[offset], + c_header_size + size); + offset += c_header_size + size; + pack_set_count(pack, 1, 1); + + } else { + break; + } + } + + if (count == pack_get_count(pack)) { + srpc->sdp.data_size = 0; + // dcs_ping is 1st variable in union + rd->data.dcs_ping = pack; + + } else { + free(pack); + } +} + +void *srpc_channelpack_get_item_ptr(void *pack, _supla_int_t idx) { + return &((TSC_SuplaChannelPack *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_channelpack_get_pack_count(void *pack) { + return ((TSC_SuplaChannelPack *)pack)->count; +} + +void srpc_channelpack_set_pack_count(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaChannelPack *)pack)->count = count; + } else { + ((TSC_SuplaChannelPack *)pack)->count += count; + } +} + +unsigned _supla_int_t srpc_channelpack_get_item_caption_size(void *item) { + return ((TSC_SuplaChannel *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getchannelpack(Tsrpc *srpc, TsrpcReceivedData *rd) { + srpc_getpack(srpc, rd, sizeof(TSC_SuplaChannelPack), sizeof(TSC_SuplaChannel), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + &srpc_channelpack_get_pack_count, + &srpc_channelpack_set_pack_count, &srpc_channelpack_get_item_ptr, + &srpc_channelpack_get_item_caption_size); +} + +void *srpc_channelpack_get_item_ptr_b(void *pack, _supla_int_t idx) { + return &((TSC_SuplaChannelPack_B *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_channelpack_get_pack_count_b(void *pack) { + return ((TSC_SuplaChannelPack_B *)pack)->count; +} + +void srpc_channelpack_set_pack_count_b(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaChannelPack_B *)pack)->count = count; + } else { + ((TSC_SuplaChannelPack_B *)pack)->count += count; + } +} + +unsigned _supla_int_t srpc_channelpack_get_item_caption_size_b(void *item) { + return ((TSC_SuplaChannel_B *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getchannelpack_b(Tsrpc *srpc, + TsrpcReceivedData *rd) { + srpc_getpack( + srpc, rd, sizeof(TSC_SuplaChannelPack_B), sizeof(TSC_SuplaChannel_B), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + &srpc_channelpack_get_pack_count_b, &srpc_channelpack_set_pack_count_b, + &srpc_channelpack_get_item_ptr_b, + &srpc_channelpack_get_item_caption_size_b); +} + +void *srpc_channelpack_get_item_ptr_c(void *pack, _supla_int_t idx) { + return &((TSC_SuplaChannelPack_C *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_channelpack_get_pack_count_c(void *pack) { + return ((TSC_SuplaChannelPack_C *)pack)->count; +} + +void srpc_channelpack_set_pack_count_c(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaChannelPack_C *)pack)->count = count; + } else { + ((TSC_SuplaChannelPack_C *)pack)->count += count; + } +} + +unsigned _supla_int_t srpc_channelpack_get_item_caption_size_c(void *item) { + return ((TSC_SuplaChannel_C *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getchannelpack_c(Tsrpc *srpc, + TsrpcReceivedData *rd) { + srpc_getpack( + srpc, rd, sizeof(TSC_SuplaChannelPack_C), sizeof(TSC_SuplaChannel_C), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + &srpc_channelpack_get_pack_count_c, &srpc_channelpack_set_pack_count_c, + &srpc_channelpack_get_item_ptr_c, + &srpc_channelpack_get_item_caption_size_c); +} + +void *srpc_channelgroup_pack_get_item_ptr(void *pack, _supla_int_t idx) { + return &((TSC_SuplaChannelGroupPack *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_channelgroup_pack_get_pack_count(void *pack) { + return ((TSC_SuplaChannelGroupPack *)pack)->count; +} + +void srpc_channelgroup_pack_set_pack_count(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaChannelGroupPack *)pack)->count = count; + } else { + ((TSC_SuplaChannelGroupPack *)pack)->count += count; + } +} + +unsigned _supla_int_t srpc_channelgroup_pack_get_item_caption_size(void *item) { + return ((TSC_SuplaChannelGroup *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getchannelgroup_pack(Tsrpc *srpc, + TsrpcReceivedData *rd) { + srpc_getpack(srpc, rd, sizeof(TSC_SuplaChannelGroupPack), + sizeof(TSC_SuplaChannelGroup), SUPLA_CHANNELGROUP_PACK_MAXCOUNT, + SUPLA_CHANNELGROUP_CAPTION_MAXSIZE, + &srpc_channelgroup_pack_get_pack_count, + &srpc_channelgroup_pack_set_pack_count, + &srpc_channelgroup_pack_get_item_ptr, + &srpc_channelgroup_pack_get_item_caption_size); +} + +void *srpc_channelgroup_pack_b_get_item_ptr(void *pack, _supla_int_t idx) { + return &((TSC_SuplaChannelGroupPack_B *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_channelgroup_pack_b_get_pack_count(void *pack) { + return ((TSC_SuplaChannelGroupPack_B *)pack)->count; +} + +void srpc_channelgroup_pack_b_set_pack_count(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaChannelGroupPack_B *)pack)->count = count; + } else { + ((TSC_SuplaChannelGroupPack_B *)pack)->count += count; + } +} + +unsigned _supla_int_t +srpc_channelgroup_pack_b_get_item_caption_size(void *item) { + return ((TSC_SuplaChannelGroup_B *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getchannelgroup_pack_b(Tsrpc *srpc, + TsrpcReceivedData *rd) { + srpc_getpack(srpc, rd, sizeof(TSC_SuplaChannelGroupPack_B), + sizeof(TSC_SuplaChannelGroup_B), + SUPLA_CHANNELGROUP_PACK_MAXCOUNT, + SUPLA_CHANNELGROUP_CAPTION_MAXSIZE, + &srpc_channelgroup_pack_b_get_pack_count, + &srpc_channelgroup_pack_b_set_pack_count, + &srpc_channelgroup_pack_b_get_item_ptr, + &srpc_channelgroup_pack_b_get_item_caption_size); +} + +void *srpc_locationpack_get_item_ptr(void *pack, _supla_int_t idx) { + return &((TSC_SuplaLocationPack *)pack)->items[idx]; // NOLINT +} + +_supla_int_t srpc_locationpack_get_pack_count(void *pack) { + return ((TSC_SuplaLocationPack *)pack)->count; +} + +void srpc_locationpack_set_pack_count(void *pack, _supla_int_t count, + unsigned char increment) { + if (increment == 0) { + ((TSC_SuplaLocationPack *)pack)->count = count; + } else { + ((TSC_SuplaLocationPack *)pack)->count += count; + } +} + +unsigned _supla_int_t srpc_locationpack_get_item_caption_size(void *item) { + return ((TSC_SuplaLocation *)item)->CaptionSize; +} + +void SRPC_ICACHE_FLASH srpc_getlocationpack(Tsrpc *srpc, + TsrpcReceivedData *rd) { + srpc_getpack( + srpc, rd, sizeof(TSC_SuplaLocationPack), sizeof(TSC_SuplaLocation), + SUPLA_LOCATIONPACK_MAXCOUNT, SUPLA_LOCATION_CAPTION_MAXSIZE, + &srpc_locationpack_get_pack_count, &srpc_locationpack_set_pack_count, + &srpc_locationpack_get_item_ptr, + &srpc_locationpack_get_item_caption_size); +} + +char SRPC_ICACHE_FLASH srpc_getdata(void *_srpc, TsrpcReceivedData *rd, + unsigned _supla_int_t rr_id) { + Tsrpc *srpc = (Tsrpc *)_srpc; + char call_with_no_data = 0; + rd->call_type = 0; + + lck_lock(srpc->lck); + + if (SUPLA_RESULT_TRUE == srpc_in_queue_pop(srpc, &srpc->sdp, rr_id)) { + rd->call_type = srpc->sdp.call_type; + rd->rr_id = srpc->sdp.rr_id; + + // first one + rd->data.dcs_ping = NULL; + + switch (srpc->sdp.call_type) { + case SUPLA_DCS_CALL_GETVERSION: + case SUPLA_CS_CALL_GET_NEXT: + case SUPLA_DCS_CALL_GET_REGISTRATION_ENABLED: + call_with_no_data = 1; + break; + + case SUPLA_SDC_CALL_GETVERSION_RESULT: + + if (srpc->sdp.data_size == sizeof(TSDC_SuplaGetVersionResult)) + rd->data.sdc_getversion_result = (TSDC_SuplaGetVersionResult *)malloc( + sizeof(TSDC_SuplaGetVersionResult)); + + break; + + case SUPLA_SDC_CALL_VERSIONERROR: + + if (srpc->sdp.data_size == sizeof(TSDC_SuplaVersionError)) + rd->data.sdc_version_error = + (TSDC_SuplaVersionError *)malloc(sizeof(TSDC_SuplaVersionError)); + + break; + + case SUPLA_DCS_CALL_PING_SERVER: + + if (srpc->sdp.data_size == sizeof(TDCS_SuplaPingServer) || + srpc->sdp.data_size == sizeof(TDCS_SuplaPingServer_COMPAT)) { + rd->data.dcs_ping = + (TDCS_SuplaPingServer *)malloc(sizeof(TDCS_SuplaPingServer)); + +#ifndef __AVR__ + if (srpc->sdp.data_size == sizeof(TDCS_SuplaPingServer_COMPAT)) { + TDCS_SuplaPingServer_COMPAT *compat = + (TDCS_SuplaPingServer_COMPAT *)srpc->sdp.data; + + rd->data.dcs_ping->now.tv_sec = compat->now.tv_sec; + rd->data.dcs_ping->now.tv_usec = compat->now.tv_usec; + call_with_no_data = 1; + } +#endif + } + break; + + case SUPLA_SDC_CALL_PING_SERVER_RESULT: + + if (srpc->sdp.data_size == sizeof(TSDC_SuplaPingServerResult)) + rd->data.sdc_ping_result = (TSDC_SuplaPingServerResult *)malloc( + sizeof(TSDC_SuplaPingServerResult)); + + break; + + case SUPLA_DCS_CALL_SET_ACTIVITY_TIMEOUT: + + if (srpc->sdp.data_size == sizeof(TDCS_SuplaSetActivityTimeout)) + rd->data.dcs_set_activity_timeout = + (TDCS_SuplaSetActivityTimeout *)malloc( + sizeof(TDCS_SuplaSetActivityTimeout)); + + break; + + case SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT: + + if (srpc->sdp.data_size == sizeof(TSDC_SuplaSetActivityTimeoutResult)) + rd->data.sdc_set_activity_timeout_result = + (TSDC_SuplaSetActivityTimeoutResult *)malloc( + sizeof(TSDC_SuplaSetActivityTimeoutResult)); + + break; + + case SUPLA_SDC_CALL_GET_REGISTRATION_ENABLED_RESULT: + + if (srpc->sdp.data_size == sizeof(TSDC_RegistrationEnabled)) + rd->data.sdc_reg_enabled = (TSDC_RegistrationEnabled *)malloc( + sizeof(TSDC_RegistrationEnabled)); + + break; + case SUPLA_DCS_CALL_GET_USER_LOCALTIME: + call_with_no_data = 1; + break; + case SUPLA_DCS_CALL_GET_USER_LOCALTIME_RESULT: + if (srpc->sdp.data_size <= sizeof(TSDC_UserLocalTimeResult) && + srpc->sdp.data_size >= + (sizeof(TSDC_UserLocalTimeResult) - SUPLA_TIMEZONE_MAXSIZE)) { + rd->data.sdc_user_localtime_result = + (TSDC_UserLocalTimeResult *)malloc( + sizeof(TSDC_UserLocalTimeResult)); + } + + break; + + case SUPLA_CSD_CALL_GET_CHANNEL_STATE: + if (srpc->sdp.data_size == sizeof(TCSD_ChannelStateRequest)) + rd->data.csd_channel_state_request = + (TCSD_ChannelStateRequest *)malloc( + sizeof(TCSD_ChannelStateRequest)); + break; + case SUPLA_DSC_CALL_CHANNEL_STATE_RESULT: + if (srpc->sdp.data_size == sizeof(TDSC_ChannelState)) + rd->data.dsc_channel_state = + (TDSC_ChannelState *)malloc(sizeof(TDSC_ChannelState)); + break; + +#ifndef SRPC_EXCLUDE_DEVICE + case SUPLA_DS_CALL_REGISTER_DEVICE: + + if (srpc->sdp.data_size >= + (sizeof(TDS_SuplaRegisterDevice) - + (sizeof(TDS_SuplaDeviceChannel) * SUPLA_CHANNELMAXCOUNT)) && + srpc->sdp.data_size <= sizeof(TDS_SuplaRegisterDevice)) { + rd->data.ds_register_device = (TDS_SuplaRegisterDevice *)malloc( + sizeof(TDS_SuplaRegisterDevice)); + } + + break; + + case SUPLA_DS_CALL_REGISTER_DEVICE_B: // ver. >= 2 + + if (srpc->sdp.data_size >= + (sizeof(TDS_SuplaRegisterDevice_B) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT)) && + srpc->sdp.data_size <= sizeof(TDS_SuplaRegisterDevice_B)) { + rd->data.ds_register_device_b = (TDS_SuplaRegisterDevice_B *)malloc( + sizeof(TDS_SuplaRegisterDevice_B)); + } + + break; + + case SUPLA_DS_CALL_REGISTER_DEVICE_C: // ver. >= 6 + + if (srpc->sdp.data_size >= + (sizeof(TDS_SuplaRegisterDevice_C) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT)) && + srpc->sdp.data_size <= sizeof(TDS_SuplaRegisterDevice_C)) { + rd->data.ds_register_device_c = (TDS_SuplaRegisterDevice_C *)malloc( + sizeof(TDS_SuplaRegisterDevice_C)); + } + + break; + + case SUPLA_DS_CALL_REGISTER_DEVICE_D: // ver. >= 7 + + if (srpc->sdp.data_size >= + (sizeof(TDS_SuplaRegisterDevice_D) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT)) && + srpc->sdp.data_size <= sizeof(TDS_SuplaRegisterDevice_D)) { + rd->data.ds_register_device_d = (TDS_SuplaRegisterDevice_D *)malloc( + sizeof(TDS_SuplaRegisterDevice_D)); + } + + break; + + case SUPLA_DS_CALL_REGISTER_DEVICE_E: // ver. >= 10 + + if (srpc->sdp.data_size >= + (sizeof(TDS_SuplaRegisterDevice_E) - + (sizeof(TDS_SuplaDeviceChannel_C) * SUPLA_CHANNELMAXCOUNT)) && + srpc->sdp.data_size <= sizeof(TDS_SuplaRegisterDevice_E)) { + rd->data.ds_register_device_e = (TDS_SuplaRegisterDevice_E *)malloc( + sizeof(TDS_SuplaRegisterDevice_E)); + } + + break; + + case SUPLA_SD_CALL_REGISTER_DEVICE_RESULT: + + if (srpc->sdp.data_size == sizeof(TSD_SuplaRegisterDeviceResult)) + rd->data.sd_register_device_result = + (TSD_SuplaRegisterDeviceResult *)malloc( + sizeof(TSD_SuplaRegisterDeviceResult)); + break; + + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED: + + if (srpc->sdp.data_size == sizeof(TDS_SuplaDeviceChannelValue)) + rd->data.ds_device_channel_value = + (TDS_SuplaDeviceChannelValue *)malloc( + sizeof(TDS_SuplaDeviceChannelValue)); + + break; + + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_B: + + if (srpc->sdp.data_size == sizeof(TDS_SuplaDeviceChannelValue_B)) + rd->data.ds_device_channel_value_b = + (TDS_SuplaDeviceChannelValue_B *)malloc( + sizeof(TDS_SuplaDeviceChannelValue_B)); + + break; + + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_C: + + if (srpc->sdp.data_size == sizeof(TDS_SuplaDeviceChannelValue_C)) + rd->data.ds_device_channel_value_c = + (TDS_SuplaDeviceChannelValue_C *)malloc( + sizeof(TDS_SuplaDeviceChannelValue_C)); + + break; + + case SUPLA_DS_CALL_DEVICE_CHANNEL_EXTENDEDVALUE_CHANGED: + + if (srpc->sdp.data_size <= + sizeof(TDS_SuplaDeviceChannelExtendedValue) && + srpc->sdp.data_size >= + (sizeof(TDS_SuplaDeviceChannelExtendedValue) - + SUPLA_CHANNELEXTENDEDVALUE_SIZE)) + rd->data.ds_device_channel_extendedvalue = + (TDS_SuplaDeviceChannelExtendedValue *)malloc( + sizeof(TDS_SuplaDeviceChannelExtendedValue)); + + break; + + case SUPLA_SD_CALL_CHANNEL_SET_VALUE: + + if (srpc->sdp.data_size == sizeof(TSD_SuplaChannelNewValue)) + rd->data.sd_channel_new_value = (TSD_SuplaChannelNewValue *)malloc( + sizeof(TSD_SuplaChannelNewValue)); + + break; + + case SUPLA_SD_CALL_CHANNELGROUP_SET_VALUE: + + if (srpc->sdp.data_size == sizeof(TSD_SuplaChannelGroupNewValue)) + rd->data.sd_channelgroup_new_value = + (TSD_SuplaChannelGroupNewValue *)malloc( + sizeof(TSD_SuplaChannelGroupNewValue)); + + break; + + case SUPLA_DS_CALL_CHANNEL_SET_VALUE_RESULT: + + if (srpc->sdp.data_size == sizeof(TDS_SuplaChannelNewValueResult)) + rd->data.ds_channel_new_value_result = + (TDS_SuplaChannelNewValueResult *)malloc( + sizeof(TDS_SuplaChannelNewValueResult)); + + break; + + case SUPLA_DS_CALL_GET_FIRMWARE_UPDATE_URL: + + if (srpc->sdp.data_size == sizeof(TDS_FirmwareUpdateParams)) + rd->data.ds_firmware_update_params = + (TDS_FirmwareUpdateParams *)malloc( + sizeof(TDS_FirmwareUpdateParams)); + + break; + + case SUPLA_SD_CALL_GET_FIRMWARE_UPDATE_URL_RESULT: + + if (srpc->sdp.data_size == sizeof(TSD_FirmwareUpdate_UrlResult) || + srpc->sdp.data_size == sizeof(char)) { + rd->data.sc_firmware_update_url_result = + (TSD_FirmwareUpdate_UrlResult *)malloc( + sizeof(TSD_FirmwareUpdate_UrlResult)); + + if (srpc->sdp.data_size == sizeof(char) && + rd->data.sc_firmware_update_url_result != NULL) + memset(rd->data.sc_firmware_update_url_result, 0, + sizeof(TSD_FirmwareUpdate_UrlResult)); + } + break; + case SUPLA_SD_CALL_DEVICE_CALCFG_REQUEST: + if (srpc->sdp.data_size <= sizeof(TSD_DeviceCalCfgRequest) && + srpc->sdp.data_size >= + (sizeof(TSD_DeviceCalCfgRequest) - SUPLA_CALCFG_DATA_MAXSIZE)) { + rd->data.sd_device_calcfg_request = (TSD_DeviceCalCfgRequest *)malloc( + sizeof(TSD_DeviceCalCfgRequest)); + } + break; + case SUPLA_DS_CALL_DEVICE_CALCFG_RESULT: + if (srpc->sdp.data_size <= sizeof(TDS_DeviceCalCfgResult) && + srpc->sdp.data_size >= + (sizeof(TDS_DeviceCalCfgResult) - SUPLA_CALCFG_DATA_MAXSIZE)) { + rd->data.ds_device_calcfg_result = + (TDS_DeviceCalCfgResult *)malloc(sizeof(TDS_DeviceCalCfgResult)); + } + break; + case SUPLA_DS_CALL_GET_CHANNEL_FUNCTIONS: + call_with_no_data = 1; + break; + case SUPLA_SD_CALL_GET_CHANNEL_FUNCTIONS_RESULT: + if (srpc->sdp.data_size <= sizeof(TSD_ChannelFunctions) && + srpc->sdp.data_size >= + (sizeof(TSD_ChannelFunctions) - + sizeof(_supla_int_t) * SUPLA_CHANNELMAXCOUNT)) { + rd->data.sd_channel_functions = + (TSD_ChannelFunctions *)malloc(sizeof(TSD_ChannelFunctions)); + } + break; +#endif /*#ifndef SRPC_EXCLUDE_DEVICE*/ + +#ifndef SRPC_EXCLUDE_CLIENT + case SUPLA_CS_CALL_REGISTER_CLIENT: + + if (srpc->sdp.data_size == sizeof(TCS_SuplaRegisterClient)) + rd->data.cs_register_client = (TCS_SuplaRegisterClient *)malloc( + sizeof(TCS_SuplaRegisterClient)); + + break; + + case SUPLA_CS_CALL_REGISTER_CLIENT_B: // ver. >= 6 + + if (srpc->sdp.data_size == sizeof(TCS_SuplaRegisterClient_B)) + rd->data.cs_register_client_b = (TCS_SuplaRegisterClient_B *)malloc( + sizeof(TCS_SuplaRegisterClient_B)); + + break; + + case SUPLA_CS_CALL_REGISTER_CLIENT_C: // ver. >= 7 + + if (srpc->sdp.data_size == sizeof(TCS_SuplaRegisterClient_C)) + rd->data.cs_register_client_c = (TCS_SuplaRegisterClient_C *)malloc( + sizeof(TCS_SuplaRegisterClient_C)); + + break; + + case SUPLA_CS_CALL_REGISTER_CLIENT_D: // ver. >= 12 + + if (srpc->sdp.data_size == sizeof(TCS_SuplaRegisterClient_D)) + rd->data.cs_register_client_d = (TCS_SuplaRegisterClient_D *)malloc( + sizeof(TCS_SuplaRegisterClient_D)); + + break; + + case SUPLA_SC_CALL_REGISTER_CLIENT_RESULT: + + if (srpc->sdp.data_size == sizeof(TSC_SuplaRegisterClientResult)) + rd->data.sc_register_client_result = + (TSC_SuplaRegisterClientResult *)malloc( + sizeof(TSC_SuplaRegisterClientResult)); + + break; + + case SUPLA_SC_CALL_REGISTER_CLIENT_RESULT_B: + + if (srpc->sdp.data_size == sizeof(TSC_SuplaRegisterClientResult_B)) + rd->data.sc_register_client_result_b = + (TSC_SuplaRegisterClientResult_B *)malloc( + sizeof(TSC_SuplaRegisterClientResult_B)); + + break; + + case SUPLA_SC_CALL_LOCATION_UPDATE: + + if (srpc->sdp.data_size >= + (sizeof(TSC_SuplaLocation) - SUPLA_LOCATION_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SuplaLocation)) { + rd->data.sc_location = + (TSC_SuplaLocation *)malloc(sizeof(TSC_SuplaLocation)); + } + + break; + + case SUPLA_SC_CALL_LOCATIONPACK_UPDATE: + srpc_getlocationpack(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNEL_UPDATE: + + if (srpc->sdp.data_size >= + (sizeof(TSC_SuplaChannel) - SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SuplaChannel)) { + rd->data.sc_channel = + (TSC_SuplaChannel *)malloc(sizeof(TSC_SuplaChannel)); + } + + break; + + case SUPLA_SC_CALL_CHANNEL_UPDATE_B: + + if (srpc->sdp.data_size >= + (sizeof(TSC_SuplaChannel_B) - SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SuplaChannel_B)) { + rd->data.sc_channel_b = + (TSC_SuplaChannel_B *)malloc(sizeof(TSC_SuplaChannel_B)); + } + + break; + + case SUPLA_SC_CALL_CHANNEL_UPDATE_C: + + if (srpc->sdp.data_size >= + (sizeof(TSC_SuplaChannel_C) - SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SuplaChannel_C)) { + rd->data.sc_channel_c = + (TSC_SuplaChannel_C *)malloc(sizeof(TSC_SuplaChannel_C)); + } + + break; + + case SUPLA_SC_CALL_CHANNELPACK_UPDATE: + srpc_getchannelpack(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNELPACK_UPDATE_B: + srpc_getchannelpack_b(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNELPACK_UPDATE_C: + srpc_getchannelpack_c(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNEL_VALUE_UPDATE: + + if (srpc->sdp.data_size == sizeof(TSC_SuplaChannelValue)) + rd->data.sc_channel_value = + (TSC_SuplaChannelValue *)malloc(sizeof(TSC_SuplaChannelValue)); + + break; + + case SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE: + srpc_getchannelgroup_pack(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE_B: + srpc_getchannelgroup_pack_b(srpc, rd); + break; + + case SUPLA_SC_CALL_CHANNELGROUP_RELATION_PACK_UPDATE: + if (srpc->sdp.data_size <= sizeof(TSC_SuplaChannelGroupRelationPack) && + srpc->sdp.data_size >= + (sizeof(TSC_SuplaChannelGroupRelationPack) - + (sizeof(TSC_SuplaChannelGroupRelation) * + SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT))) { + rd->data.sc_channelgroup_relation_pack = + (TSC_SuplaChannelGroupRelationPack *)malloc( + sizeof(TSC_SuplaChannelGroupRelationPack)); + } + break; + + case SUPLA_SC_CALL_CHANNELVALUE_PACK_UPDATE: + if (srpc->sdp.data_size <= sizeof(TSC_SuplaChannelValuePack) && + srpc->sdp.data_size >= (sizeof(TSC_SuplaChannelValuePack) - + (sizeof(TSC_SuplaChannelValue) * + SUPLA_CHANNELVALUE_PACK_MAXCOUNT))) { + rd->data.sc_channelvalue_pack = (TSC_SuplaChannelValuePack *)malloc( + sizeof(TSC_SuplaChannelValuePack)); + } + break; + + case SUPLA_SC_CALL_CHANNELEXTENDEDVALUE_PACK_UPDATE: + if (srpc->sdp.data_size <= sizeof(TSC_SuplaChannelExtendedValuePack) && + srpc->sdp.data_size >= + (sizeof(TSC_SuplaChannelExtendedValuePack) - + SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXDATASIZE)) { + rd->data.sc_channelextendedvalue_pack = + (TSC_SuplaChannelExtendedValuePack *)malloc( + sizeof(TSC_SuplaChannelExtendedValuePack)); + } + break; + + case SUPLA_CS_CALL_CHANNEL_SET_VALUE: + + if (srpc->sdp.data_size == sizeof(TCS_SuplaChannelNewValue)) + rd->data.cs_channel_new_value = (TCS_SuplaChannelNewValue *)malloc( + sizeof(TCS_SuplaChannelNewValue)); + + break; + + case SUPLA_CS_CALL_SET_VALUE: + + if (srpc->sdp.data_size == sizeof(TCS_SuplaNewValue)) + rd->data.cs_new_value = + (TCS_SuplaNewValue *)malloc(sizeof(TCS_SuplaNewValue)); + + break; + + case SUPLA_CS_CALL_CHANNEL_SET_VALUE_B: + + if (srpc->sdp.data_size == sizeof(TCS_SuplaChannelNewValue_B)) + rd->data.cs_channel_new_value_b = + (TCS_SuplaChannelNewValue_B *)malloc( + sizeof(TCS_SuplaChannelNewValue_B)); + + break; + + case SUPLA_SC_CALL_EVENT: + + if (srpc->sdp.data_size >= + (sizeof(TSC_SuplaEvent) - SUPLA_SENDER_NAME_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SuplaEvent)) { + rd->data.sc_event = (TSC_SuplaEvent *)malloc(sizeof(TSC_SuplaEvent)); + } + + break; + + case SUPLA_CS_CALL_OAUTH_TOKEN_REQUEST: + call_with_no_data = 1; + break; + + case SUPLA_SC_CALL_OAUTH_TOKEN_REQUEST_RESULT: + if (srpc->sdp.data_size >= (sizeof(TSC_OAuthTokenRequestResult) - + SUPLA_OAUTH_TOKEN_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_OAuthTokenRequestResult)) { + rd->data.sc_oauth_tokenrequest_result = + (TSC_OAuthTokenRequestResult *)malloc( + sizeof(TSC_OAuthTokenRequestResult)); + } + break; + case SUPLA_CS_CALL_SUPERUSER_AUTHORIZATION_REQUEST: + if (srpc->sdp.data_size == sizeof(TCS_SuperUserAuthorizationRequest)) + rd->data.cs_superuser_authorization_request = + (TCS_SuperUserAuthorizationRequest *)malloc( + sizeof(TCS_SuperUserAuthorizationRequest)); + break; + case SUPLA_CS_CALL_GET_SUPERUSER_AUTHORIZATION_RESULT: + call_with_no_data = 1; + break; + case SUPLA_SC_CALL_SUPERUSER_AUTHORIZATION_RESULT: + if (srpc->sdp.data_size == sizeof(TSC_SuperUserAuthorizationResult)) + rd->data.sc_superuser_authorization_result = + (TSC_SuperUserAuthorizationResult *)malloc( + sizeof(TSC_SuperUserAuthorizationResult)); + break; + case SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST: + if (srpc->sdp.data_size <= sizeof(TCS_DeviceCalCfgRequest) && + srpc->sdp.data_size >= + (sizeof(TCS_DeviceCalCfgRequest) - SUPLA_CALCFG_DATA_MAXSIZE)) { + rd->data.cs_device_calcfg_request = (TCS_DeviceCalCfgRequest *)malloc( + sizeof(TCS_DeviceCalCfgRequest)); + } + break; + case SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST_B: + if (srpc->sdp.data_size <= sizeof(TCS_DeviceCalCfgRequest_B) && + srpc->sdp.data_size >= (sizeof(TCS_DeviceCalCfgRequest_B) - + SUPLA_CALCFG_DATA_MAXSIZE)) { + rd->data.cs_device_calcfg_request_b = + (TCS_DeviceCalCfgRequest_B *)malloc( + sizeof(TCS_DeviceCalCfgRequest_B)); + } + break; + case SUPLA_SC_CALL_DEVICE_CALCFG_RESULT: + if (srpc->sdp.data_size <= sizeof(TSC_DeviceCalCfgResult) && + srpc->sdp.data_size >= + (sizeof(TSC_DeviceCalCfgResult) - SUPLA_CALCFG_DATA_MAXSIZE)) { + rd->data.sc_device_calcfg_result = + (TSC_DeviceCalCfgResult *)malloc(sizeof(TSC_DeviceCalCfgResult)); + } + break; + + case SUPLA_CS_CALL_GET_CHANNEL_BASIC_CFG: + if (srpc->sdp.data_size == sizeof(TCS_ChannelBasicCfgRequest)) + rd->data.cs_channel_basic_cfg_request = + (TCS_ChannelBasicCfgRequest *)malloc( + sizeof(TCS_ChannelBasicCfgRequest)); + break; + case SUPLA_SC_CALL_CHANNEL_BASIC_CFG_RESULT: + if (srpc->sdp.data_size >= + (sizeof(TSC_ChannelBasicCfg) - SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_ChannelBasicCfg)) + rd->data.sc_channel_basic_cfg = + (TSC_ChannelBasicCfg *)malloc(sizeof(TSC_ChannelBasicCfg)); + break; + + case SUPLA_CS_CALL_SET_CHANNEL_FUNCTION: + if (srpc->sdp.data_size == sizeof(TCS_SetChannelFunction)) + rd->data.cs_set_channel_function = + (TCS_SetChannelFunction *)malloc(sizeof(TCS_SetChannelFunction)); + break; + + case SUPLA_SC_CALL_SET_CHANNEL_FUNCTION_RESULT: + if (srpc->sdp.data_size == sizeof(TSC_SetChannelFunctionResult)) + rd->data.sc_set_channel_function_result = + (TSC_SetChannelFunctionResult *)malloc( + sizeof(TSC_SetChannelFunctionResult)); + break; + + case SUPLA_CS_CALL_SET_CHANNEL_CAPTION: + if (srpc->sdp.data_size >= (sizeof(TCS_SetChannelCaption) - + SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TCS_SetChannelCaption)) + rd->data.cs_set_channel_caption = + (TCS_SetChannelCaption *)malloc(sizeof(TCS_SetChannelCaption)); + break; + + case SUPLA_SC_CALL_SET_CHANNEL_CAPTION_RESULT: + if (srpc->sdp.data_size >= (sizeof(TSC_SetChannelCaptionResult) - + SUPLA_CHANNEL_CAPTION_MAXSIZE) && + srpc->sdp.data_size <= sizeof(TSC_SetChannelCaptionResult)) + rd->data.sc_set_channel_caption_result = + (TSC_SetChannelCaptionResult *)malloc( + sizeof(TSC_SetChannelCaptionResult)); + break; + + case SUPLA_CS_CALL_CLIENTS_RECONNECT_REQUEST: + call_with_no_data = 1; + break; + + case SUPLA_SC_CALL_CLIENTS_RECONNECT_REQUEST_RESULT: + if (srpc->sdp.data_size == sizeof(TSC_ClientsReconnectRequestResult)) + rd->data.sc_clients_reconnect_result = + (TSC_ClientsReconnectRequestResult *)malloc( + sizeof(TSC_ClientsReconnectRequestResult)); + break; + + case SUPLA_CS_CALL_SET_REGISTRATION_ENABLED: + if (srpc->sdp.data_size == sizeof(TCS_SetRegistrationEnabled)) + rd->data.cs_set_registration_enabled = + (TCS_SetRegistrationEnabled *)malloc( + sizeof(TCS_SetRegistrationEnabled)); + break; + + case SUPLA_SC_CALL_SET_REGISTRATION_ENABLED_RESULT: + if (srpc->sdp.data_size == sizeof(TSC_SetRegistrationEnabledResult)) + rd->data.sc_set_registration_enabled_result = + (TSC_SetRegistrationEnabledResult *)malloc( + sizeof(TSC_SetRegistrationEnabledResult)); + break; + + case SUPLA_CS_CALL_DEVICE_RECONNECT_REQUEST: + if (srpc->sdp.data_size == sizeof(TCS_DeviceReconnectRequest)) + rd->data.cs_device_reconnect_request = + (TCS_DeviceReconnectRequest *)malloc( + sizeof(TCS_DeviceReconnectRequest)); + break; + case SUPLA_SC_CALL_DEVICE_RECONNECT_REQUEST_RESULT: + if (srpc->sdp.data_size == sizeof(TSC_DeviceReconnectRequestResult)) + rd->data.sc_device_reconnect_request_result = + (TSC_DeviceReconnectRequestResult *)malloc( + sizeof(TSC_DeviceReconnectRequestResult)); + break; + +#endif /*#ifndef SRPC_EXCLUDE_CLIENT*/ + } + + if (call_with_no_data == 1) { + return lck_unlock_r(srpc->lck, SUPLA_RESULT_TRUE); + } + + if (rd->data.dcs_ping != NULL) { + if (srpc->sdp.data_size > 0) { + memcpy(rd->data.dcs_ping, srpc->sdp.data, srpc->sdp.data_size); + } + + return lck_unlock_r(srpc->lck, SUPLA_RESULT_TRUE); + } + + return lck_unlock_r(srpc->lck, SUPLA_RESULT_DATA_ERROR); + } + + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); +} + +void SRPC_ICACHE_FLASH srpc_rd_free(TsrpcReceivedData *rd) { + if (rd->call_type > 0) { + // first one + + if (rd->data.dcs_ping != NULL) free(rd->data.dcs_ping); + + rd->call_type = 0; + } +} + +unsigned char SRPC_ICACHE_FLASH +srpc_call_min_version_required(void *_srpc, unsigned _supla_int_t call_type) { + switch (call_type) { + case SUPLA_DCS_CALL_GETVERSION: + case SUPLA_SDC_CALL_GETVERSION_RESULT: + case SUPLA_SDC_CALL_VERSIONERROR: + case SUPLA_DCS_CALL_PING_SERVER: + case SUPLA_SDC_CALL_PING_SERVER_RESULT: + case SUPLA_DS_CALL_REGISTER_DEVICE: + case SUPLA_SD_CALL_REGISTER_DEVICE_RESULT: + case SUPLA_CS_CALL_REGISTER_CLIENT: + case SUPLA_SC_CALL_REGISTER_CLIENT_RESULT: + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED: + case SUPLA_SD_CALL_CHANNEL_SET_VALUE: + case SUPLA_DS_CALL_CHANNEL_SET_VALUE_RESULT: + case SUPLA_SC_CALL_LOCATION_UPDATE: + case SUPLA_SC_CALL_LOCATIONPACK_UPDATE: + case SUPLA_SC_CALL_CHANNEL_UPDATE: + case SUPLA_SC_CALL_CHANNELPACK_UPDATE: + case SUPLA_SC_CALL_CHANNEL_VALUE_UPDATE: + case SUPLA_CS_CALL_GET_NEXT: + case SUPLA_SC_CALL_EVENT: + case SUPLA_CS_CALL_CHANNEL_SET_VALUE: + return 1; + + case SUPLA_DS_CALL_REGISTER_DEVICE_B: + case SUPLA_DCS_CALL_SET_ACTIVITY_TIMEOUT: + case SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT: + return 2; + + case SUPLA_CS_CALL_CHANNEL_SET_VALUE_B: + return 3; + + case SUPLA_DS_CALL_GET_FIRMWARE_UPDATE_URL: + case SUPLA_SD_CALL_GET_FIRMWARE_UPDATE_URL_RESULT: + return 5; + + case SUPLA_DS_CALL_REGISTER_DEVICE_C: + case SUPLA_CS_CALL_REGISTER_CLIENT_B: + return 6; + + case SUPLA_CS_CALL_REGISTER_CLIENT_C: + case SUPLA_DS_CALL_REGISTER_DEVICE_D: + case SUPLA_DCS_CALL_GET_REGISTRATION_ENABLED: + case SUPLA_SDC_CALL_GET_REGISTRATION_ENABLED_RESULT: + return 7; + + case SUPLA_SC_CALL_CHANNELPACK_UPDATE_B: + case SUPLA_SC_CALL_CHANNEL_UPDATE_B: + return 8; + + case SUPLA_SC_CALL_REGISTER_CLIENT_RESULT_B: + case SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE: + case SUPLA_SC_CALL_CHANNELGROUP_RELATION_PACK_UPDATE: + case SUPLA_SC_CALL_CHANNELVALUE_PACK_UPDATE: + case SUPLA_CS_CALL_SET_VALUE: + return 9; + + case SUPLA_DS_CALL_DEVICE_CHANNEL_EXTENDEDVALUE_CHANGED: + case SUPLA_SC_CALL_CHANNELEXTENDEDVALUE_PACK_UPDATE: + case SUPLA_CS_CALL_OAUTH_TOKEN_REQUEST: + case SUPLA_SC_CALL_OAUTH_TOKEN_REQUEST_RESULT: + case SUPLA_DS_CALL_REGISTER_DEVICE_E: + case SUPLA_CS_CALL_SUPERUSER_AUTHORIZATION_REQUEST: + case SUPLA_SC_CALL_SUPERUSER_AUTHORIZATION_RESULT: + case SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST: + case SUPLA_SC_CALL_DEVICE_CALCFG_RESULT: + case SUPLA_SD_CALL_DEVICE_CALCFG_REQUEST: + case SUPLA_DS_CALL_DEVICE_CALCFG_RESULT: + case SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE_B: + case SUPLA_SC_CALL_CHANNEL_UPDATE_C: + case SUPLA_SC_CALL_CHANNELPACK_UPDATE_C: + return 10; + case SUPLA_DCS_CALL_GET_USER_LOCALTIME: + case SUPLA_DCS_CALL_GET_USER_LOCALTIME_RESULT: + case SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST_B: + return 11; + case SUPLA_CS_CALL_REGISTER_CLIENT_D: + case SUPLA_CSD_CALL_GET_CHANNEL_STATE: + case SUPLA_DSC_CALL_CHANNEL_STATE_RESULT: + case SUPLA_CS_CALL_GET_CHANNEL_BASIC_CFG: + case SUPLA_SC_CALL_CHANNEL_BASIC_CFG_RESULT: + case SUPLA_CS_CALL_SET_CHANNEL_FUNCTION: + case SUPLA_SC_CALL_SET_CHANNEL_FUNCTION_RESULT: + case SUPLA_CS_CALL_SET_CHANNEL_CAPTION: + case SUPLA_SC_CALL_SET_CHANNEL_CAPTION_RESULT: + case SUPLA_CS_CALL_CLIENTS_RECONNECT_REQUEST: + case SUPLA_SC_CALL_CLIENTS_RECONNECT_REQUEST_RESULT: + case SUPLA_CS_CALL_SET_REGISTRATION_ENABLED: + case SUPLA_SC_CALL_SET_REGISTRATION_ENABLED_RESULT: + case SUPLA_CS_CALL_DEVICE_RECONNECT_REQUEST: + case SUPLA_SC_CALL_DEVICE_RECONNECT_REQUEST_RESULT: + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_B: + case SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_C: + case SUPLA_DS_CALL_GET_CHANNEL_FUNCTIONS: + case SUPLA_SD_CALL_GET_CHANNEL_FUNCTIONS_RESULT: + case SUPLA_CS_CALL_GET_SUPERUSER_AUTHORIZATION_RESULT: + return 12; + case SUPLA_SD_CALL_CHANNELGROUP_SET_VALUE: + return 13; + } + + return 255; +} + +unsigned char SRPC_ICACHE_FLASH +srpc_call_allowed(void *_srpc, unsigned _supla_int_t call_type) { + unsigned char min_ver = srpc_call_min_version_required(_srpc, call_type); + + if (min_ver == 0 || srpc_get_proto_version(_srpc) >= min_ver) { + return 1; + } + + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_async__call(void *_srpc, + unsigned _supla_int_t call_type, + char *data, + unsigned _supla_int_t data_size, + unsigned char *version) { + Tsrpc *srpc = (Tsrpc *)_srpc; + + if (!srpc_call_allowed(_srpc, call_type)) { + if (srpc->params.on_min_version_required != NULL) { + srpc->params.on_min_version_required( + _srpc, call_type, srpc_call_min_version_required(_srpc, call_type), + srpc->params.user_params); + } + + return SUPLA_RESULT_FALSE; + } + + if (srpc->params.before_async_call != NULL) { + srpc->params.before_async_call(_srpc, call_type, srpc->params.user_params); + } + + lck_lock(srpc->lck); + + sproto_sdp_init(srpc->proto, &srpc->sdp); + + if (version != NULL) srpc->sdp.version = *version; + + if (SUPLA_RESULT_TRUE == + sproto_set_data(&srpc->sdp, data, data_size, call_type) && + srpc_out_queue_push(srpc, &srpc->sdp)) { +#ifndef __EH_DISABLED + if (srpc->params.eh != 0) { + eh_raise_event(srpc->params.eh); + } +#endif + + return lck_unlock_r(srpc->lck, srpc->sdp.rr_id); + } + + return lck_unlock_r(srpc->lck, SUPLA_RESULT_FALSE); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_async_call(void *_srpc, unsigned _supla_int_t call_type, char *data, + unsigned _supla_int_t data_size) { + return srpc_async__call(_srpc, call_type, data, data_size, NULL); +} + +unsigned char SRPC_ICACHE_FLASH srpc_get_proto_version(void *_srpc) { + unsigned char version; + + Tsrpc *srpc = (Tsrpc *)_srpc; + lck_lock(srpc->lck); + version = sproto_get_version(srpc->proto); + lck_unlock(srpc->lck); + + return version; +} + +void SRPC_ICACHE_FLASH srpc_set_proto_version(void *_srpc, + unsigned char version) { + Tsrpc *srpc = (Tsrpc *)_srpc; + + lck_lock(srpc->lck); + sproto_set_version(srpc->proto, version); + lck_unlock(srpc->lck); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_getversion(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_DCS_CALL_GETVERSION, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_getversion_result( + void *_srpc, char SoftVer[SUPLA_SOFTVER_MAXSIZE]) { + TSDC_SuplaGetVersionResult gvr; + + gvr.proto_version = SUPLA_PROTO_VERSION; + gvr.proto_version_min = SUPLA_PROTO_VERSION_MIN; + + memcpy(gvr.SoftVer, SoftVer, SUPLA_SOFTVER_MAXSIZE); + + return srpc_async_call(_srpc, SUPLA_SDC_CALL_GETVERSION_RESULT, (char *)&gvr, + sizeof(TSDC_SuplaGetVersionResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sdc_async_versionerror(void *_srpc, unsigned char remote_version) { + TSDC_SuplaVersionError ve; + ve.server_version = SUPLA_PROTO_VERSION; + ve.server_version_min = SUPLA_PROTO_VERSION_MIN; + + return srpc_async__call(_srpc, SUPLA_SDC_CALL_VERSIONERROR, (char *)&ve, + sizeof(TSDC_SuplaVersionError), &remote_version); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_ping_server(void *_srpc) { + TDCS_SuplaPingServer ps; + +#if defined(ESP8266) + unsigned int time = system_get_time(); + ps.now.tv_sec = time / 1000000; + ps.now.tv_usec = time % 1000000; +#elif defined(__AVR__) + ps.now.tv_sec[0] = 0; + ps.now.tv_sec[1] = 0; + ps.now.tv_usec[0] = 0; + ps.now.tv_usec[1] = 0; +#else + struct timeval now; + gettimeofday(&now, NULL); + ps.now.tv_sec = now.tv_sec; + ps.now.tv_usec = now.tv_usec; +#endif + + return srpc_async_call(_srpc, SUPLA_DCS_CALL_PING_SERVER, (char *)&ps, + sizeof(TDCS_SuplaPingServer)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_ping_server_result(void *_srpc) { +#if !defined(ESP8266) && !defined(__AVR__) && !defined(ESP32) + TSDC_SuplaPingServerResult ps; + + struct timeval now; + gettimeofday(&now, NULL); + ps.now.tv_sec = now.tv_sec; + ps.now.tv_usec = now.tv_usec; + + return srpc_async_call(_srpc, SUPLA_SDC_CALL_PING_SERVER_RESULT, (char *)&ps, + sizeof(TSDC_SuplaPingServerResult)); +#else + return 0; +#endif +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_set_activity_timeout( + void *_srpc, TDCS_SuplaSetActivityTimeout *dcs_set_activity_timeout) { + return srpc_async_call(_srpc, SUPLA_DCS_CALL_SET_ACTIVITY_TIMEOUT, + (char *)dcs_set_activity_timeout, + sizeof(TDCS_SuplaSetActivityTimeout)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_set_activity_timeout_result( + void *_srpc, + TSDC_SuplaSetActivityTimeoutResult *sdc_set_activity_timeout_result) { + return srpc_async_call(_srpc, SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT, + (char *)sdc_set_activity_timeout_result, + sizeof(TSDC_SuplaSetActivityTimeoutResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_dcs_async_get_registration_enabled(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_DCS_CALL_GET_REGISTRATION_ENABLED, NULL, + 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_get_registration_enabled_result( + void *_srpc, TSDC_RegistrationEnabled *reg_enabled) { + return srpc_async_call(_srpc, SUPLA_SDC_CALL_GET_REGISTRATION_ENABLED_RESULT, + (char *)reg_enabled, sizeof(TSDC_RegistrationEnabled)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_get_user_localtime(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_DCS_CALL_GET_USER_LOCALTIME, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_get_user_localtime_result( + void *_srpc, TSDC_UserLocalTimeResult *localtime) { + if (localtime == NULL || localtime->timezoneSize > SUPLA_TIMEZONE_MAXSIZE) { + return 0; + } + + unsigned int size = sizeof(TSDC_UserLocalTimeResult) - + SUPLA_TIMEZONE_MAXSIZE + localtime->timezoneSize; + + return srpc_async_call(_srpc, SUPLA_DCS_CALL_GET_USER_LOCALTIME_RESULT, + (char *)localtime, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_csd_async_get_channel_state( + void *_srpc, TCSD_ChannelStateRequest *request) { + return srpc_async_call(_srpc, SUPLA_CSD_CALL_GET_CHANNEL_STATE, + (char *)request, sizeof(TCSD_ChannelStateRequest)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_csd_async_channel_state_result(void *_srpc, TDSC_ChannelState *state) { + return srpc_async_call(_srpc, SUPLA_DSC_CALL_CHANNEL_STATE_RESULT, + (char *)state, sizeof(TDSC_ChannelState)); +} + +#ifndef SRPC_EXCLUDE_DEVICE +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_firmware_update_url( + void *_srpc, TDS_FirmwareUpdateParams *params) { + return srpc_async_call(_srpc, SUPLA_DS_CALL_GET_FIRMWARE_UPDATE_URL, + (char *)params, sizeof(TDS_FirmwareUpdateParams)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice( + void *_srpc, TDS_SuplaRegisterDevice *registerdevice) { + _supla_int_t size = + sizeof(TDS_SuplaRegisterDevice) - + (sizeof(TDS_SuplaDeviceChannel) * SUPLA_CHANNELMAXCOUNT) + + (sizeof(TDS_SuplaDeviceChannel) * registerdevice->channel_count); + + if (size > sizeof(TDS_SuplaRegisterDevice)) return 0; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_REGISTER_DEVICE, + (char *)registerdevice, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_b( + void *_srpc, TDS_SuplaRegisterDevice_B *registerdevice) { + _supla_int_t size = + sizeof(TDS_SuplaRegisterDevice_B) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT) + + (sizeof(TDS_SuplaDeviceChannel_B) * registerdevice->channel_count); + + if (size > sizeof(TDS_SuplaRegisterDevice_B)) return 0; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_REGISTER_DEVICE_B, + (char *)registerdevice, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_c( + void *_srpc, TDS_SuplaRegisterDevice_C *registerdevice) { + _supla_int_t size = + sizeof(TDS_SuplaRegisterDevice_C) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT) + + (sizeof(TDS_SuplaDeviceChannel_B) * registerdevice->channel_count); + + if (size > sizeof(TDS_SuplaRegisterDevice_C)) return 0; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_REGISTER_DEVICE_C, + (char *)registerdevice, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_d( + void *_srpc, TDS_SuplaRegisterDevice_D *registerdevice) { + _supla_int_t size = + sizeof(TDS_SuplaRegisterDevice_D) - + (sizeof(TDS_SuplaDeviceChannel_B) * SUPLA_CHANNELMAXCOUNT) + + (sizeof(TDS_SuplaDeviceChannel_B) * registerdevice->channel_count); + + if (size > sizeof(TDS_SuplaRegisterDevice_D)) return 0; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_REGISTER_DEVICE_D, + (char *)registerdevice, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_e( + void *_srpc, TDS_SuplaRegisterDevice_E *registerdevice) { + _supla_int_t size = + sizeof(TDS_SuplaRegisterDevice_E) - + (sizeof(TDS_SuplaDeviceChannel_C) * SUPLA_CHANNELMAXCOUNT) + + (sizeof(TDS_SuplaDeviceChannel_C) * registerdevice->channel_count); + + if (size > sizeof(TDS_SuplaRegisterDevice_E)) return 0; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_REGISTER_DEVICE_E, + (char *)registerdevice, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_registerdevice_result( + void *_srpc, TSD_SuplaRegisterDeviceResult *registerdevice_result) { + return srpc_async_call(_srpc, SUPLA_SD_CALL_REGISTER_DEVICE_RESULT, + (char *)registerdevice_result, + sizeof(TSD_SuplaRegisterDeviceResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sd_async_set_channel_value(void *_srpc, TSD_SuplaChannelNewValue *value) { + return srpc_async_call(_srpc, SUPLA_SD_CALL_CHANNEL_SET_VALUE, (char *)value, + sizeof(TSD_SuplaChannelNewValue)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_set_channelgroup_value( + void *_srpc, TSD_SuplaChannelGroupNewValue *value) { + return srpc_async_call(_srpc, SUPLA_SD_CALL_CHANNELGROUP_SET_VALUE, + (char *)value, sizeof(TSD_SuplaChannelGroupNewValue)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_set_channel_result(void *_srpc, unsigned char ChannelNumber, + _supla_int_t SenderID, char Success) { + TDS_SuplaChannelNewValueResult result; + result.ChannelNumber = ChannelNumber; + result.SenderID = SenderID; + result.Success = Success; + + return srpc_async_call(_srpc, SUPLA_DS_CALL_CHANNEL_SET_VALUE_RESULT, + (char *)&result, + sizeof(TDS_SuplaChannelNewValueResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_firmware_update_url_result( + void *_srpc, TSD_FirmwareUpdate_UrlResult *result) { + return srpc_async_call( + _srpc, SUPLA_SD_CALL_GET_FIRMWARE_UPDATE_URL_RESULT, (char *)result, + result->exists == 1 ? sizeof(TSD_FirmwareUpdate_UrlResult) + : sizeof(char)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value) { + TDS_SuplaDeviceChannelValue ncsc; + ncsc.ChannelNumber = channel_number; + memcpy(ncsc.value, value, SUPLA_CHANNELVALUE_SIZE); + + return srpc_async_call(_srpc, SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED, + (char *)&ncsc, sizeof(TDS_SuplaDeviceChannelValue)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_channel_value_changed_b(void *_srpc, unsigned char channel_number, + char *value, unsigned char offline) { + TDS_SuplaDeviceChannelValue_B ncsc; + ncsc.ChannelNumber = channel_number; + ncsc.Offline = !!offline; + memcpy(ncsc.value, value, SUPLA_CHANNELVALUE_SIZE); + + return srpc_async_call(_srpc, SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_B, + (char *)&ncsc, sizeof(TDS_SuplaDeviceChannelValue_B)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed_c( + void *_srpc, unsigned char channel_number, char *value, + unsigned char offline, unsigned _supla_int_t validity_time_sec) { + TDS_SuplaDeviceChannelValue_C ncsc; + ncsc.ChannelNumber = channel_number; + ncsc.Offline = !!offline; + ncsc.ValidityTimeSec = validity_time_sec; + memcpy(ncsc.value, value, SUPLA_CHANNELVALUE_SIZE); + + return srpc_async_call(_srpc, SUPLA_DS_CALL_DEVICE_CHANNEL_VALUE_CHANGED_C, + (char *)&ncsc, sizeof(TDS_SuplaDeviceChannelValue_C)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value) { + if (value == NULL || value->size > SUPLA_CHANNELEXTENDEDVALUE_SIZE || + value->size == 0) { + return 0; + } + + TDS_SuplaDeviceChannelExtendedValue ncsc; + ncsc.ChannelNumber = channel_number; + memcpy(&ncsc.value, value, sizeof(TSuplaChannelExtendedValue)); + + return srpc_async_call( + _srpc, SUPLA_DS_CALL_DEVICE_CHANNEL_EXTENDEDVALUE_CHANGED, (char *)&ncsc, + sizeof(TDS_SuplaDeviceChannelExtendedValue) - + (SUPLA_CHANNELEXTENDEDVALUE_SIZE - ncsc.value.size)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_device_calcfg_request( + void *_srpc, TSD_DeviceCalCfgRequest *request) { + if (request == NULL || request->DataSize > SUPLA_CALCFG_DATA_MAXSIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_SD_CALL_DEVICE_CALCFG_REQUEST, + (char *)request, + sizeof(TSD_DeviceCalCfgRequest) - + SUPLA_CALCFG_DATA_MAXSIZE + request->DataSize); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_device_calcfg_result( + void *_srpc, TDS_DeviceCalCfgResult *result) { + if (result == NULL || result->DataSize > SUPLA_CALCFG_DATA_MAXSIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_DS_CALL_DEVICE_CALCFG_RESULT, + (char *)result, + sizeof(TDS_DeviceCalCfgResult) - + SUPLA_CALCFG_DATA_MAXSIZE + result->DataSize); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_get_channel_functions(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_DS_CALL_GET_CHANNEL_FUNCTIONS, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_channel_functions_result( + void *_srpc, TSD_ChannelFunctions *result) { + if (result == NULL || result->ChannelCount > SUPLA_CHANNELMAXCOUNT) { + return 0; + } + + _supla_int_t size = sizeof(TSD_ChannelFunctions) - + sizeof(_supla_int_t) * SUPLA_CHANNELMAXCOUNT + + sizeof(_supla_int_t) * result->ChannelCount; + + if (size > sizeof(TSD_ChannelFunctions)) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_SD_CALL_GET_CHANNEL_FUNCTIONS_RESULT, + (char *)result, size); +} + +#endif /*SRPC_EXCLUDE_DEVICE*/ + +#ifndef SRPC_EXCLUDE_CLIENT +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient( + void *_srpc, TCS_SuplaRegisterClient *registerclient) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_REGISTER_CLIENT, + (char *)registerclient, + sizeof(TCS_SuplaRegisterClient)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_b( + void *_srpc, TCS_SuplaRegisterClient_B *registerclient) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_REGISTER_CLIENT_B, + (char *)registerclient, + sizeof(TCS_SuplaRegisterClient_B)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_c( + void *_srpc, TCS_SuplaRegisterClient_C *registerclient) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_REGISTER_CLIENT_C, + (char *)registerclient, + sizeof(TCS_SuplaRegisterClient_C)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_d( + void *_srpc, TCS_SuplaRegisterClient_D *registerclient) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_REGISTER_CLIENT_D, + (char *)registerclient, + sizeof(TCS_SuplaRegisterClient_D)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_registerclient_result( + void *_srpc, TSC_SuplaRegisterClientResult *registerclient_result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_REGISTER_CLIENT_RESULT, + (char *)registerclient_result, + sizeof(TSC_SuplaRegisterClientResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_registerclient_result_b( + void *_srpc, TSC_SuplaRegisterClientResult_B *registerclient_result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_REGISTER_CLIENT_RESULT_B, + (char *)registerclient_result, + sizeof(TSC_SuplaRegisterClientResult_B)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_location_update(void *_srpc, TSC_SuplaLocation *location) { + _supla_int_t size = sizeof(TSC_SuplaLocation) - + SUPLA_LOCATION_CAPTION_MAXSIZE + location->CaptionSize; + + if (size > sizeof(TSC_SuplaLocation)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_LOCATION_UPDATE, (char *)location, + size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_set_pack( + void *_srpc, void *pack, _supla_int_t count, + _func_srpc_pack_get_caption_size get_caption_size, + _func_srpc_pack_get_item_ptr get_item_ptr, + _func_srpc_pack_set_pack_count set_pack_count, + unsigned _supla_int_t pack_sizeof, unsigned _supla_int_t pack_max_count, + unsigned _supla_int_t caption_max_size, unsigned _supla_int_t item_sizeof, + unsigned _supla_int_t call_type) { + _supla_int_t result = 0; + _supla_int_t a; + _supla_int_t n = 0; + _supla_int_t size = 0; + _supla_int_t offset = 0; + + if (count < 1 || count > pack_max_count) return 0; + + size = pack_sizeof - (item_sizeof * pack_max_count); + offset = size; + + char *buffer = malloc(size); + + if (buffer == NULL) return 0; + + memcpy(buffer, pack, size); + + for (a = 0; a < count; a++) { + if (get_caption_size(pack, a) <= caption_max_size) { + size += item_sizeof - caption_max_size + get_caption_size(pack, a); + + char *new_buffer = (char *)realloc(buffer, size); + + if (new_buffer == NULL) { + free(buffer); + return 0; + } + + buffer = new_buffer; + memcpy(&buffer[offset], get_item_ptr(pack, a), size - offset); + offset += size - offset; + n++; + } + } + + set_pack_count(buffer, n, 0); + + result = srpc_async_call(_srpc, call_type, buffer, size); + + free(buffer); + return result; +} + +unsigned _supla_int_t srpc_locationpack_get_caption_size(void *pack, + _supla_int_t idx) { + return ((TSC_SuplaLocationPack *)pack)->items[idx].CaptionSize; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_locationpack_update( + void *_srpc, TSC_SuplaLocationPack *location_pack) { + return srpc_set_pack( + _srpc, location_pack, location_pack->count, + &srpc_locationpack_get_caption_size, &srpc_locationpack_get_item_ptr, + &srpc_locationpack_set_pack_count, sizeof(TSC_SuplaLocationPack), + SUPLA_LOCATIONPACK_MAXCOUNT, SUPLA_LOCATION_CAPTION_MAXSIZE, + sizeof(TSC_SuplaLocation), SUPLA_SC_CALL_LOCATIONPACK_UPDATE); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_channel_update(void *_srpc, TSC_SuplaChannel *channel) { + _supla_int_t size = sizeof(TSC_SuplaChannel) - SUPLA_CHANNEL_CAPTION_MAXSIZE + + channel->CaptionSize; + + if (size > sizeof(TSC_SuplaChannel)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNEL_UPDATE, (char *)channel, + size); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_channel_update_b(void *_srpc, TSC_SuplaChannel_B *channel_b) { + _supla_int_t size = sizeof(TSC_SuplaChannel_B) - + SUPLA_CHANNEL_CAPTION_MAXSIZE + channel_b->CaptionSize; + + if (size > sizeof(TSC_SuplaChannel_B)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNEL_UPDATE_B, + (char *)channel_b, size); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_channel_update_c(void *_srpc, TSC_SuplaChannel_C *channel_c) { + _supla_int_t size = sizeof(TSC_SuplaChannel_C) - + SUPLA_CHANNEL_CAPTION_MAXSIZE + channel_c->CaptionSize; + + if (size > sizeof(TSC_SuplaChannel_C)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNEL_UPDATE_C, + (char *)channel_c, size); +} + +unsigned _supla_int_t srpc_channelpack_get_caption_size(void *pack, + _supla_int_t idx) { + return ((TSC_SuplaChannelPack *)pack)->items[idx].CaptionSize; +} + +unsigned _supla_int_t srpc_channelpack_get_caption_size_b(void *pack, + _supla_int_t idx) { + return ((TSC_SuplaChannelPack_B *)pack)->items[idx].CaptionSize; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update( + void *_srpc, TSC_SuplaChannelPack *channel_pack) { + return srpc_set_pack( + _srpc, channel_pack, channel_pack->count, + &srpc_channelpack_get_caption_size, &srpc_channelpack_get_item_ptr, + &srpc_channelpack_set_pack_count, sizeof(TSC_SuplaChannelPack), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + sizeof(TSC_SuplaChannel), SUPLA_SC_CALL_CHANNELPACK_UPDATE); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update_b( + void *_srpc, TSC_SuplaChannelPack_B *channel_pack) { + return srpc_set_pack( + _srpc, channel_pack, channel_pack->count, + &srpc_channelpack_get_caption_size_b, &srpc_channelpack_get_item_ptr_b, + &srpc_channelpack_set_pack_count_b, sizeof(TSC_SuplaChannelPack_B), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + sizeof(TSC_SuplaChannel_B), SUPLA_SC_CALL_CHANNELPACK_UPDATE_B); +} + +unsigned _supla_int_t srpc_channelpack_get_caption_size_c(void *pack, + _supla_int_t idx) { + return ((TSC_SuplaChannelPack_C *)pack)->items[idx].CaptionSize; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update_c( + void *_srpc, TSC_SuplaChannelPack_C *channel_pack) { + return srpc_set_pack( + _srpc, channel_pack, channel_pack->count, + &srpc_channelpack_get_caption_size_c, &srpc_channelpack_get_item_ptr_c, + &srpc_channelpack_set_pack_count_c, sizeof(TSC_SuplaChannelPack_C), + SUPLA_CHANNELPACK_MAXCOUNT, SUPLA_CHANNEL_CAPTION_MAXSIZE, + sizeof(TSC_SuplaChannel_C), SUPLA_SC_CALL_CHANNELPACK_UPDATE_C); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_value_update( + void *_srpc, TSC_SuplaChannelValue *channel_value) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNEL_VALUE_UPDATE, + (char *)channel_value, sizeof(TSC_SuplaChannelValue)); +} + +unsigned _supla_int_t +srpc_channelgroup_pack_get_caption_size(void *pack, _supla_int_t idx) { + return ((TSC_SuplaChannelGroupPack *)pack)->items[idx].CaptionSize; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_pack_update( + void *_srpc, TSC_SuplaChannelGroupPack *channelgroup_pack) { + return srpc_set_pack( + _srpc, channelgroup_pack, channelgroup_pack->count, + &srpc_channelgroup_pack_get_caption_size, + &srpc_channelgroup_pack_get_item_ptr, + &srpc_channelgroup_pack_set_pack_count, sizeof(TSC_SuplaChannelGroupPack), + SUPLA_CHANNELGROUP_PACK_MAXCOUNT, SUPLA_CHANNELGROUP_CAPTION_MAXSIZE, + sizeof(TSC_SuplaChannelGroup), SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE); +} + +unsigned _supla_int_t +srpc_channelgroup_pack_b_get_caption_size(void *pack, _supla_int_t idx) { + return ((TSC_SuplaChannelGroupPack_B *)pack)->items[idx].CaptionSize; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_pack_update_b( + void *_srpc, TSC_SuplaChannelGroupPack_B *channelgroup_pack) { + return srpc_set_pack( + _srpc, channelgroup_pack, channelgroup_pack->count, + &srpc_channelgroup_pack_b_get_caption_size, + &srpc_channelgroup_pack_b_get_item_ptr, + &srpc_channelgroup_pack_b_set_pack_count, + sizeof(TSC_SuplaChannelGroupPack_B), SUPLA_CHANNELGROUP_PACK_MAXCOUNT, + SUPLA_CHANNELGROUP_CAPTION_MAXSIZE, sizeof(TSC_SuplaChannelGroup_B), + SUPLA_SC_CALL_CHANNELGROUP_PACK_UPDATE_B); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_relation_pack_update( + void *_srpc, + TSC_SuplaChannelGroupRelationPack *channelgroup_relation_pack) { + if (channelgroup_relation_pack->count < 1 || + channelgroup_relation_pack->count > + SUPLA_CHANNELGROUP_RELATION_PACK_MAXCOUNT) { + return 0; + } + + unsigned _supla_int_t size = sizeof(TSC_SuplaChannelGroupRelationPack) - + sizeof(channelgroup_relation_pack->items) + + (sizeof(TSC_SuplaChannelGroupRelation) * + channelgroup_relation_pack->count); + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNELGROUP_RELATION_PACK_UPDATE, + (char *)channelgroup_relation_pack, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelvalue_pack_update( + void *_srpc, TSC_SuplaChannelValuePack *channelvalue_pack) { + if (channelvalue_pack->count < 1 || + channelvalue_pack->count > SUPLA_CHANNELVALUE_PACK_MAXCOUNT) { + return 0; + } + + unsigned _supla_int_t size = + sizeof(TSC_SuplaChannelValuePack) - sizeof(channelvalue_pack->items) + + (sizeof(TSC_SuplaChannelValue) * channelvalue_pack->count); + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNELVALUE_PACK_UPDATE, + (char *)channelvalue_pack, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelextendedvalue_pack_update( + void *_srpc, TSC_SuplaChannelExtendedValuePack *extendedvalue_pack) { + if (extendedvalue_pack == NULL || extendedvalue_pack->count < 1 || + extendedvalue_pack->count > SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXCOUNT || + extendedvalue_pack->pack_size < 1 || + extendedvalue_pack->pack_size > + SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXDATASIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNELEXTENDEDVALUE_PACK_UPDATE, + (char *)extendedvalue_pack, + sizeof(TSC_SuplaChannelExtendedValuePack) - + SUPLA_CHANNELEXTENDEDVALUE_PACK_MAXDATASIZE + + extendedvalue_pack->pack_size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_get_next(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_GET_NEXT, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_event(void *_srpc, + TSC_SuplaEvent *event) { + _supla_int_t size = sizeof(TSC_SuplaEvent) - SUPLA_SENDER_NAME_MAXSIZE + + event->SenderNameSize; + + if (size > sizeof(TSC_SuplaEvent)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_EVENT, (char *)event, size); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_value(void *_srpc, TCS_SuplaChannelNewValue *value) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_CHANNEL_SET_VALUE, (char *)value, + sizeof(TCS_SuplaChannelNewValue)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_set_channel_value_b( + void *_srpc, TCS_SuplaChannelNewValue_B *value) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_CHANNEL_SET_VALUE_B, + (char *)value, sizeof(TCS_SuplaChannelNewValue_B)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_value(void *_srpc, TCS_SuplaNewValue *value) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_SET_VALUE, (char *)value, + sizeof(TCS_SuplaNewValue)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_oauth_token_request(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_OAUTH_TOKEN_REQUEST, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_oauth_token_request_result( + void *_srpc, TSC_OAuthTokenRequestResult *result) { + if (result == NULL || result->Token.TokenSize > SUPLA_OAUTH_TOKEN_MAXSIZE) { + return 0; + } + + return srpc_async_call( + _srpc, SUPLA_SC_CALL_OAUTH_TOKEN_REQUEST_RESULT, (char *)result, + sizeof(TSC_OAuthTokenRequestResult) - + (SUPLA_OAUTH_TOKEN_MAXSIZE - result->Token.TokenSize)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_superuser_authorization_request( + void *_srpc, TCS_SuperUserAuthorizationRequest *request) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_SUPERUSER_AUTHORIZATION_REQUEST, + (char *)request, + sizeof(TCS_SuperUserAuthorizationRequest)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_get_superuser_authorization_result(void *_srpc) { + return srpc_async_call( + _srpc, SUPLA_CS_CALL_GET_SUPERUSER_AUTHORIZATION_RESULT, NULL, 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_superuser_authorization_result( + void *_srpc, TSC_SuperUserAuthorizationResult *result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_SUPERUSER_AUTHORIZATION_RESULT, + (char *)result, + sizeof(TSC_SuperUserAuthorizationResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_calcfg_request( + void *_srpc, TCS_DeviceCalCfgRequest *request) { + if (request == NULL || request->DataSize > SUPLA_CALCFG_DATA_MAXSIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST, + (char *)request, + sizeof(TCS_DeviceCalCfgRequest) - + SUPLA_CALCFG_DATA_MAXSIZE + request->DataSize); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_calcfg_request_b( + void *_srpc, TCS_DeviceCalCfgRequest_B *request) { + if (request == NULL || request->DataSize > SUPLA_CALCFG_DATA_MAXSIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_CS_CALL_DEVICE_CALCFG_REQUEST_B, + (char *)request, + sizeof(TCS_DeviceCalCfgRequest_B) - + SUPLA_CALCFG_DATA_MAXSIZE + request->DataSize); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_device_calcfg_result( + void *_srpc, TSC_DeviceCalCfgResult *result) { + if (result == NULL || result->DataSize > SUPLA_CALCFG_DATA_MAXSIZE) { + return 0; + } + + return srpc_async_call(_srpc, SUPLA_SC_CALL_DEVICE_CALCFG_RESULT, + (char *)result, + sizeof(TSC_DeviceCalCfgResult) - + SUPLA_CALCFG_DATA_MAXSIZE + result->DataSize); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_get_channel_basic_cfg(void *_srpc, _supla_int_t ChannelID) { + TCS_ChannelBasicCfgRequest request; + memset(&request, 0, sizeof(TCS_ChannelBasicCfgRequest)); + request.ChannelID = ChannelID; + + return srpc_async_call(_srpc, SUPLA_CS_CALL_GET_CHANNEL_BASIC_CFG, + (char *)&request, sizeof(TCS_ChannelBasicCfgRequest)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_basic_cfg_result( + void *_srpc, TSC_ChannelBasicCfg *basic_cfg) { + _supla_int_t size = sizeof(TSC_ChannelBasicCfg) - + SUPLA_CHANNEL_CAPTION_MAXSIZE + basic_cfg->CaptionSize; + + if (size > sizeof(TSC_ChannelBasicCfg)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_CHANNEL_BASIC_CFG_RESULT, + (char *)basic_cfg, size); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_function(void *_srpc, TCS_SetChannelFunction *func) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_SET_CHANNEL_FUNCTION, + (char *)func, sizeof(TCS_SetChannelFunction)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_channel_function_result( + void *_srpc, TSC_SetChannelFunctionResult *result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_SET_CHANNEL_FUNCTION_RESULT, + (char *)result, sizeof(TSC_SetChannelFunctionResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_caption(void *_srpc, TCS_SetChannelCaption *caption) { + _supla_int_t size = sizeof(TCS_SetChannelCaption) - + SUPLA_CHANNEL_CAPTION_MAXSIZE + caption->CaptionSize; + + if (size > sizeof(TCS_SetChannelCaption)) return 0; + + return srpc_async_call(_srpc, SUPLA_CS_CALL_SET_CHANNEL_CAPTION, + (char *)caption, size); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_channel_caption_result( + void *_srpc, TSC_SetChannelCaptionResult *result) { + _supla_int_t size = sizeof(TSC_SetChannelCaptionResult) - + SUPLA_CHANNEL_CAPTION_MAXSIZE + result->CaptionSize; + + if (size > sizeof(TSC_SetChannelCaptionResult)) return 0; + + return srpc_async_call(_srpc, SUPLA_SC_CALL_SET_CHANNEL_CAPTION_RESULT, + (char *)result, size); +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_clients_reconnect_request(void *_srpc) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_CLIENTS_RECONNECT_REQUEST, NULL, + 0); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_clients_reconnect_request_result( + void *_srpc, TSC_ClientsReconnectRequestResult *result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_CLIENTS_RECONNECT_REQUEST_RESULT, + (char *)result, + sizeof(TSC_ClientsReconnectRequestResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_set_registration_enabled( + void *_srpc, TCS_SetRegistrationEnabled *reg_enabled) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_SET_REGISTRATION_ENABLED, + (char *)reg_enabled, + sizeof(TCS_SetRegistrationEnabled)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_registration_enabled_result( + void *_srpc, TSC_SetRegistrationEnabledResult *result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_SET_REGISTRATION_ENABLED_RESULT, + (char *)result, + sizeof(TSC_SetRegistrationEnabledResult)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_reconnect_request( + void *_srpc, TCS_DeviceReconnectRequest *request) { + return srpc_async_call(_srpc, SUPLA_CS_CALL_DEVICE_RECONNECT_REQUEST, + (char *)request, sizeof(TCS_DeviceReconnectRequest)); +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_device_reconnect_request_result( + void *_srpc, TSC_DeviceReconnectRequestResult *result) { + return srpc_async_call(_srpc, SUPLA_SC_CALL_DEVICE_RECONNECT_REQUEST_RESULT, + (char *)result, + sizeof(TSC_DeviceReconnectRequestResult)); +} + +#endif /*SRPC_EXCLUDE_CLIENT*/ + +#ifndef SRPC_EXCLUDE_EXTENDEDVALUE_TOOLS + +#ifdef USE_DEPRECATED_EMEV_V1 +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_emextended2extended( + TElectricityMeter_ExtendedValue *em_ev, TSuplaChannelExtendedValue *ev) { + if (em_ev == NULL || ev == NULL || em_ev->m_count > EM_MEASUREMENT_COUNT || + em_ev->m_count < 0) { + return 0; + } + + memset(ev, 0, sizeof(TSuplaChannelExtendedValue)); + ev->type = EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V1; + + ev->size = sizeof(TElectricityMeter_ExtendedValue) - + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT + + sizeof(TElectricityMeter_Measurement) * em_ev->m_count; + + if (ev->size > 0 && ev->size <= SUPLA_CHANNELEXTENDEDVALUE_SIZE) { + memcpy(ev->value, em_ev, ev->size); + return 1; + } + + ev->size = 0; + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2emextended( + TSuplaChannelExtendedValue *ev, TElectricityMeter_ExtendedValue *em_ev) { + if (em_ev == NULL || ev == NULL || + ev->type != EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V1 || ev->size == 0 || + ev->size > sizeof(TElectricityMeter_ExtendedValue)) { + return 0; + } + + memset(em_ev, 0, sizeof(TElectricityMeter_ExtendedValue)); + memcpy(em_ev, ev->value, ev->size); + + _supla_int_t expected_size = 0; + + if (em_ev->m_count <= EM_MEASUREMENT_COUNT) { + expected_size = + sizeof(TElectricityMeter_ExtendedValue) - + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT + + sizeof(TElectricityMeter_Measurement) * em_ev->m_count; + } + + if (ev->size != expected_size) { + memset(em_ev, 0, sizeof(TElectricityMeter_ExtendedValue)); + return 0; + } + + return 1; +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_evtool_emev_v1to2(TElectricityMeter_ExtendedValue *v1, + TElectricityMeter_ExtendedValue_V2 *v2) { + if (v1 == NULL || v2 == NULL) { + return 0; + } + memset(v2, 0, sizeof(TElectricityMeter_ExtendedValue_V2)); + + for (int a = 0; a < 3; a++) { + v2->total_forward_active_energy[a] = v1->total_forward_active_energy[a]; + v2->total_reverse_active_energy[a] = v1->total_reverse_active_energy[a]; + v2->total_forward_reactive_energy[a] = v1->total_forward_reactive_energy[a]; + v2->total_reverse_reactive_energy[a] = v1->total_reverse_reactive_energy[a]; + } + + v2->total_cost = v1->total_cost; + v2->price_per_unit = v1->price_per_unit; + memcpy(v2->currency, v1->currency, sizeof(v2->currency)); + v2->measured_values = v1->measured_values; + v2->period = v1->period; + v2->m_count = v1->m_count; + + memcpy(v2->m, v1->m, + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT); + + v2->measured_values ^= + v1->measured_values & EM_VAR_FORWARD_ACTIVE_ENERGY_BALANCED; + v2->measured_values ^= + v1->measured_values & EM_VAR_REVERSE_ACTIVE_ENERGY_BALANCED; + + return 1; +} + +_supla_int_t SRPC_ICACHE_FLASH +srpc_evtool_emev_v2to1(TElectricityMeter_ExtendedValue_V2 *v2, + TElectricityMeter_ExtendedValue *v1) { + if (v1 == NULL || v2 == NULL) { + return 0; + } + memset(v1, 0, sizeof(TElectricityMeter_ExtendedValue)); + + for (int a = 0; a < 3; a++) { + v1->total_forward_active_energy[a] = v2->total_forward_active_energy[a]; + v1->total_reverse_active_energy[a] = v2->total_reverse_active_energy[a]; + v1->total_forward_reactive_energy[a] = v2->total_forward_reactive_energy[a]; + v1->total_reverse_reactive_energy[a] = v2->total_reverse_reactive_energy[a]; + } + + v1->total_cost = v2->total_cost; + v1->price_per_unit = v2->price_per_unit; + memcpy(v1->currency, v2->currency, sizeof(v2->currency)); + v1->measured_values = v2->measured_values; + v1->period = v2->period; + v1->m_count = v2->m_count; + + memcpy(v1->m, v2->m, + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT); + + v1->measured_values ^= + v1->measured_values & EM_VAR_FORWARD_ACTIVE_ENERGY_BALANCED; + v1->measured_values ^= + v1->measured_values & EM_VAR_REVERSE_ACTIVE_ENERGY_BALANCED; + + return 1; +} + +#endif /*USE_DEPRECATED_EMEV_V1*/ + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v2_emextended2extended( + TElectricityMeter_ExtendedValue_V2 *em_ev, TSuplaChannelExtendedValue *ev) { + if (em_ev == NULL || ev == NULL || em_ev->m_count > EM_MEASUREMENT_COUNT || + em_ev->m_count < 0) { + return 0; + } + + memset(ev, 0, sizeof(TSuplaChannelExtendedValue)); + ev->type = EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V2; + + ev->size = sizeof(TElectricityMeter_ExtendedValue_V2) - + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT + + sizeof(TElectricityMeter_Measurement) * em_ev->m_count; + + if (ev->size > 0 && ev->size <= SUPLA_CHANNELEXTENDEDVALUE_SIZE) { + memcpy(ev->value, em_ev, ev->size); + return 1; + } + + ev->size = 0; + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v2_extended2emextended( + TSuplaChannelExtendedValue *ev, TElectricityMeter_ExtendedValue_V2 *em_ev) { + if (em_ev == NULL || ev == NULL || + ev->type != EV_TYPE_ELECTRICITY_METER_MEASUREMENT_V2 || ev->size == 0 || + ev->size > sizeof(TElectricityMeter_ExtendedValue_V2)) { + return 0; + } + + memset(em_ev, 0, sizeof(TElectricityMeter_ExtendedValue_V2)); + memcpy(em_ev, ev->value, ev->size); + + _supla_int_t expected_size = 0; + + if (em_ev->m_count <= EM_MEASUREMENT_COUNT) { + expected_size = + sizeof(TElectricityMeter_ExtendedValue_V2) - + sizeof(TElectricityMeter_Measurement) * EM_MEASUREMENT_COUNT + + sizeof(TElectricityMeter_Measurement) * em_ev->m_count; + } + + if (ev->size != expected_size) { + memset(em_ev, 0, sizeof(TElectricityMeter_ExtendedValue_V2)); + return 0; + } + + return 1; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2thermostatextended( + TSuplaChannelExtendedValue *ev, TThermostat_ExtendedValue *th_ev) { + if (ev == NULL || th_ev == NULL || + ev->type != EV_TYPE_THERMOSTAT_DETAILS_V1 || ev->size == 0 || + ev->size > sizeof(TThermostat_ExtendedValue)) { + return 0; + } + + memset(th_ev, 0, sizeof(TThermostat_ExtendedValue)); + memcpy(th_ev, ev->value, ev->size); + + return 1; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_thermostatextended2extended( + TThermostat_ExtendedValue *th_ev, TSuplaChannelExtendedValue *ev) { + if (th_ev == NULL || ev == NULL) { + return 0; + } + + memset(ev, 0, sizeof(TSuplaChannelExtendedValue)); + ev->type = EV_TYPE_THERMOSTAT_DETAILS_V1; + ev->size = 0; + + unsigned _supla_int_t size = sizeof(TThermostat_ExtendedValue); + + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_Schedule)) { + size -= sizeof(th_ev->Schedule); + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_Time)) { + size -= sizeof(th_ev->Time); + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_Values)) { + size -= sizeof(th_ev->Values); + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_Flags)) { + size -= sizeof(th_ev->Flags); + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_PresetTemperatures)) { + size -= sizeof(th_ev->PresetTemperature); + if (0 == (th_ev->Fields & THERMOSTAT_FIELD_MeasuredTemperatures)) { + size -= sizeof(th_ev->MeasuredTemperature); + } + } + } + } + } + } + + if (size > 0) { + ev->size = size; + memcpy(ev->value, th_ev, size); + return 1; + } + + return 0; +} + +#ifndef SRPC_EXCLUDE_CLIENT +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_icextended2extended( + TSC_ImpulseCounter_ExtendedValue *ic_ev, TSuplaChannelExtendedValue *ev) { + if (ic_ev == NULL || ev == NULL) { + return 0; + } + + memset(ev, 0, sizeof(TSuplaChannelExtendedValue)); + ev->type = EV_TYPE_IMPULSE_COUNTER_DETAILS_V1; + ev->size = sizeof(TSC_ImpulseCounter_ExtendedValue); + + memcpy(ev->value, ic_ev, ev->size); + return 1; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2icextended( + TSuplaChannelExtendedValue *ev, TSC_ImpulseCounter_ExtendedValue *ic_ev) { + if (ic_ev == NULL || ev == NULL || + ev->type != EV_TYPE_IMPULSE_COUNTER_DETAILS_V1 || ev->size == 0 || + ev->size != sizeof(TSC_ImpulseCounter_ExtendedValue)) { + return 0; + } + + memset(ic_ev, 0, sizeof(TSC_ImpulseCounter_ExtendedValue)); + memcpy(ic_ev, ev->value, ev->size); + + return 1; +} +#endif /*SRPC_EXCLUDE_CLIENT*/ + +#endif /*SRPC_EXCLUDE_EXTENDEDVALUE_TOOLS*/ diff --git a/lib/SuplaDevice/src/supla-common/srpc.h b/lib/SuplaDevice/src/supla-common/srpc.h new file mode 100644 index 00000000..7559298e --- /dev/null +++ b/lib/SuplaDevice/src/supla-common/srpc.h @@ -0,0 +1,398 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef supladex_H_ +#define supladex_H_ + +#include +#include +#include "eh.h" +#include "proto.h" +#if defined(ESP32) +#include +#endif + +#ifdef __ANDROID__ +#define SRPC_EXCLUDE_DEVICE +#endif /*__ANDROID__*/ + +#if defined(ESP8266) || defined(ESP32) + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#define SRPC_WITHOUT_OUT_QUEUE +#define SRPC_WITHOUT_IN_QUEUE +#endif /* defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) */ + +#define SRPC_EXCLUDE_CLIENT +#define SRPC_ICACHE_FLASH ICACHE_FLASH_ATTR + +#include +#if !defined(ESP32) +#include +#endif +#else +#define SRPC_ICACHE_FLASH +#endif + +#if defined(__AVR__) +#define SRPC_EXCLUDE_CLIENT +#define SRPC_WITHOUT_OUT_QUEUE +#define SRPC_WITHOUT_IN_QUEUE +#endif /*__AVR__*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef _supla_int_t (*_func_srpc_DataRW)(void *buf, _supla_int_t count, + void *user_params); +typedef void (*_func_srpc_event_OnRemoteCallReceived)( + void *_srpc, unsigned _supla_int_t rr_id, unsigned _supla_int_t call_type, + void *user_params, unsigned char proto_version); +typedef void (*_func_srpc_event_BeforeCall)(void *_srpc, + unsigned _supla_int_t call_type, + void *user_params); +typedef void (*_func_srpc_event_OnVersionError)(void *_srpc, + unsigned char remote_version, + void *user_params); +typedef void (*_func_srpc_event_OnMinVersionRequired)( + void *_srpc, unsigned _supla_int_t call_type, unsigned char min_version, + void *user_params); + +typedef struct { + _func_srpc_DataRW data_read; + _func_srpc_DataRW data_write; + _func_srpc_event_OnRemoteCallReceived on_remote_call_received; + _func_srpc_event_OnVersionError on_version_error; + _func_srpc_event_BeforeCall before_async_call; + _func_srpc_event_OnMinVersionRequired on_min_version_required; + + TEventHandler *eh; + + void *user_params; +} TsrpcParams; + +union TsrpcDataPacketData { + TDCS_SuplaPingServer *dcs_ping; + TSDC_SuplaPingServerResult *sdc_ping_result; + TSDC_SuplaGetVersionResult *sdc_getversion_result; + TSDC_SuplaVersionError *sdc_version_error; + TDCS_SuplaSetActivityTimeout *dcs_set_activity_timeout; + TSDC_SuplaSetActivityTimeoutResult *sdc_set_activity_timeout_result; + TDS_SuplaRegisterDevice *ds_register_device; + TDS_SuplaRegisterDevice_B *ds_register_device_b; + TDS_SuplaRegisterDevice_C *ds_register_device_c; + TDS_SuplaRegisterDevice_D *ds_register_device_d; + TDS_SuplaRegisterDevice_E *ds_register_device_e; + TSD_SuplaRegisterDeviceResult *sd_register_device_result; + TCS_SuplaRegisterClient *cs_register_client; + TCS_SuplaRegisterClient_B *cs_register_client_b; + TCS_SuplaRegisterClient_C *cs_register_client_c; + TCS_SuplaRegisterClient_D *cs_register_client_d; + TSC_SuplaRegisterClientResult *sc_register_client_result; + TSC_SuplaRegisterClientResult_B *sc_register_client_result_b; + TDS_SuplaDeviceChannelValue *ds_device_channel_value; + TDS_SuplaDeviceChannelValue_B *ds_device_channel_value_b; + TDS_SuplaDeviceChannelValue_C *ds_device_channel_value_c; + TDS_SuplaDeviceChannelExtendedValue *ds_device_channel_extendedvalue; + TSC_SuplaLocation *sc_location; + TSC_SuplaLocationPack *sc_location_pack; + TSC_SuplaChannel *sc_channel; + TSC_SuplaChannel_B *sc_channel_b; + TSC_SuplaChannel_C *sc_channel_c; + TSC_SuplaChannelPack *sc_channel_pack; + TSC_SuplaChannelPack_B *sc_channel_pack_b; + TSC_SuplaChannelPack_C *sc_channel_pack_c; + TSC_SuplaChannelValue *sc_channel_value; + TSC_SuplaEvent *sc_event; + TSD_SuplaChannelNewValue *sd_channel_new_value; + TSD_SuplaChannelGroupNewValue *sd_channelgroup_new_value; + TDS_SuplaChannelNewValueResult *ds_channel_new_value_result; + TCS_SuplaChannelNewValue *cs_channel_new_value; + TCS_SuplaChannelNewValue_B *cs_channel_new_value_b; + TDS_FirmwareUpdateParams *ds_firmware_update_params; + TSD_FirmwareUpdate_UrlResult *sc_firmware_update_url_result; + TSDC_RegistrationEnabled *sdc_reg_enabled; + TSC_SuplaChannelGroupPack *sc_channelgroup_pack; + TSC_SuplaChannelGroupPack_B *sc_channelgroup_pack_b; + TSC_SuplaChannelGroupRelationPack *sc_channelgroup_relation_pack; + TSC_SuplaChannelValuePack *sc_channelvalue_pack; + TSC_SuplaChannelExtendedValuePack *sc_channelextendedvalue_pack; + TCS_SuplaNewValue *cs_new_value; + TSC_OAuthTokenRequestResult *sc_oauth_tokenrequest_result; + TCS_SuperUserAuthorizationRequest *cs_superuser_authorization_request; + TSC_SuperUserAuthorizationResult *sc_superuser_authorization_result; + TCS_DeviceCalCfgRequest *cs_device_calcfg_request; + TCS_DeviceCalCfgRequest_B *cs_device_calcfg_request_b; + TSC_DeviceCalCfgResult *sc_device_calcfg_result; + TSD_DeviceCalCfgRequest *sd_device_calcfg_request; + TDS_DeviceCalCfgResult *ds_device_calcfg_result; + TSDC_UserLocalTimeResult *sdc_user_localtime_result; + TCSD_ChannelStateRequest *csd_channel_state_request; + TDSC_ChannelState *dsc_channel_state; + TCS_ChannelBasicCfgRequest *cs_channel_basic_cfg_request; + TSC_ChannelBasicCfg *sc_channel_basic_cfg; + TCS_SetChannelFunction *cs_set_channel_function; + TSC_SetChannelFunctionResult *sc_set_channel_function_result; + TCS_SetChannelCaption *cs_set_channel_caption; + TSC_SetChannelCaptionResult *sc_set_channel_caption_result; + TSC_ClientsReconnectRequestResult *sc_clients_reconnect_result; + TCS_SetRegistrationEnabled *cs_set_registration_enabled; + TSC_SetRegistrationEnabledResult *sc_set_registration_enabled_result; + TCS_DeviceReconnectRequest *cs_device_reconnect_request; + TSC_DeviceReconnectRequestResult *sc_device_reconnect_request_result; + TSD_ChannelFunctions *sd_channel_functions; +}; + +typedef struct { + unsigned _supla_int_t call_type; + unsigned _supla_int_t rr_id; + + union TsrpcDataPacketData data; +} TsrpcReceivedData; + +void SRPC_ICACHE_FLASH srpc_params_init(TsrpcParams *params); + +void *SRPC_ICACHE_FLASH srpc_init(TsrpcParams *params); +void SRPC_ICACHE_FLASH srpc_free(void *_srpc); + +char SRPC_ICACHE_FLASH srpc_input_dataexists(void *_srpc); +char SRPC_ICACHE_FLASH srpc_output_dataexists(void *_srpc); +unsigned char SRPC_ICACHE_FLASH srpc_out_queue_item_count(void *srpc); + +char SRPC_ICACHE_FLASH srpc_iterate(void *_srpc); + +char SRPC_ICACHE_FLASH srpc_getdata(void *_srpc, TsrpcReceivedData *rd, + unsigned _supla_int_t rr_id); + +void SRPC_ICACHE_FLASH srpc_rd_free(TsrpcReceivedData *rd); + +unsigned char SRPC_ICACHE_FLASH srpc_get_proto_version(void *_srpc); +void SRPC_ICACHE_FLASH srpc_set_proto_version(void *_srpc, + unsigned char version); + +unsigned char SRPC_ICACHE_FLASH +srpc_call_min_version_required(void *_srpc, unsigned _supla_int_t call_type); +unsigned char SRPC_ICACHE_FLASH +srpc_call_allowed(void *_srpc, unsigned _supla_int_t call_type); + +// device/client <-> server +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_getversion(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_getversion_result( + void *_srpc, char SoftVer[SUPLA_SOFTVER_MAXSIZE]); +_supla_int_t SRPC_ICACHE_FLASH +srpc_sdc_async_versionerror(void *_srpc, unsigned char remote_version); +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_ping_server(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_ping_server_result(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_set_activity_timeout( + void *_srpc, TDCS_SuplaSetActivityTimeout *dcs_set_activity_timeout); +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_set_activity_timeout_result( + void *_srpc, + TSDC_SuplaSetActivityTimeoutResult *sdc_set_activity_timeout_result); +_supla_int_t SRPC_ICACHE_FLASH +srpc_dcs_async_get_registration_enabled(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_get_registration_enabled_result( + void *_srpc, TSDC_RegistrationEnabled *reg_enabled); +_supla_int_t SRPC_ICACHE_FLASH srpc_dcs_async_get_user_localtime(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sdc_async_get_user_localtime_result( + void *_srpc, TSDC_UserLocalTimeResult *localtime); +_supla_int_t SRPC_ICACHE_FLASH srpc_csd_async_get_channel_state( + void *_srpc, TCSD_ChannelStateRequest *request); +_supla_int_t SRPC_ICACHE_FLASH +srpc_csd_async_channel_state_result(void *_srpc, TDSC_ChannelState *state); + +#ifndef SRPC_EXCLUDE_DEVICE +// device <-> server +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice( + void *_srpc, TDS_SuplaRegisterDevice *registerdevice); +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_b( + void *_srpc, TDS_SuplaRegisterDevice_B *registerdevice); // ver. >= 2 +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_c( + void *_srpc, TDS_SuplaRegisterDevice_C *registerdevice); // ver. >= 6 +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_d( + void *_srpc, TDS_SuplaRegisterDevice_D *registerdevice); // ver. >= 7 +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_registerdevice_e( + void *_srpc, TDS_SuplaRegisterDevice_E *registerdevice); // ver. >= 10 +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_registerdevice_result( + void *_srpc, TSD_SuplaRegisterDeviceResult *registerdevice_result); +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value); +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_channel_value_changed_b(void *_srpc, unsigned char channel_number, + char *value, unsigned char offline); +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed_c( + void *_srpc, unsigned char channel_number, char *value, + unsigned char offline, unsigned _supla_int_t validity_time_sec); +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value); +_supla_int_t SRPC_ICACHE_FLASH +srpc_sd_async_set_channel_value(void *_srpc, TSD_SuplaChannelNewValue *value); +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_set_channelgroup_value( + void *_srpc, TSD_SuplaChannelGroupNewValue *value); // ver. >= 13 +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_set_channel_result(void *_srpc, unsigned char ChannelNumber, + _supla_int_t SenderID, char Success); +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_firmware_update_url( + void *_srpc, TDS_FirmwareUpdateParams *params); +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_firmware_update_url_result( + void *_srpc, TSD_FirmwareUpdate_UrlResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_device_calcfg_request( + void *_srpc, TSD_DeviceCalCfgRequest *request); +_supla_int_t SRPC_ICACHE_FLASH +srpc_ds_async_device_calcfg_result(void *_srpc, TDS_DeviceCalCfgResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_get_channel_functions(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sd_async_get_channel_functions_result( + void *_srpc, TSD_ChannelFunctions *result); + +#endif /*SRPC_EXCLUDE_DEVICE*/ + +#ifndef SRPC_EXCLUDE_CLIENT +// client <-> server +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient( + void *_srpc, TCS_SuplaRegisterClient *registerclient); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_b( + void *_srpc, TCS_SuplaRegisterClient_B *registerclient); // ver. >= 6 +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_c( + void *_srpc, TCS_SuplaRegisterClient_C *registerclient); // ver. >= 7 +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_registerclient_d( + void *_srpc, TCS_SuplaRegisterClient_D *registerclient); // ver. >= 11 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_registerclient_result( + void *_srpc, TSC_SuplaRegisterClientResult *registerclient_result); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_registerclient_result_b( + void *_srpc, + TSC_SuplaRegisterClientResult_B *registerclient_result); // ver. >= 9 +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_location_update(void *_srpc, TSC_SuplaLocation *location); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_locationpack_update( + void *_srpc, TSC_SuplaLocationPack *location_pack); +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_channel_update(void *_srpc, TSC_SuplaChannel *channel); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_update_b( + void *_srpc, TSC_SuplaChannel_B *channel); // ver. >= 8 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_update_c( + void *_srpc, TSC_SuplaChannel_C *channel); // ver. >= 10 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update( + void *_srpc, TSC_SuplaChannelPack *channel_pack); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update_b( + void *_srpc, TSC_SuplaChannelPack_B *channel_pack); // ver. >= 8 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelpack_update_c( + void *_srpc, TSC_SuplaChannelPack_C *channel_pack); // ver. >= 10 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_value_update( + void *_srpc, TSC_SuplaChannelValue *channel_item_value); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_pack_update( + void *_srpc, TSC_SuplaChannelGroupPack *channelgroup_pack); // ver. >= 9 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_pack_update_b( + void *_srpc, TSC_SuplaChannelGroupPack_B *channelgroup_pack); // ver. >= 10 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelgroup_relation_pack_update( + void *_srpc, TSC_SuplaChannelGroupRelationPack + *channelgroup_relation_pack); // ver. >= 9 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelvalue_pack_update( + void *_srpc, TSC_SuplaChannelValuePack *channelvalue_pack); // ver. >= 9 +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channelextendedvalue_pack_update( + void *_srpc, + TSC_SuplaChannelExtendedValuePack *extendedvalue_pack); // ver. >= 10 +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_get_next(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_event(void *_srpc, + TSC_SuplaEvent *event); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_value(void *_srpc, TCS_SuplaChannelNewValue *value); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_value(void *_srpc, TCS_SuplaNewValue *value); // ver. >= 9 +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_set_channel_value_b( + void *_srpc, TCS_SuplaChannelNewValue_B *value); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_oauth_token_request(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_oauth_token_request_result( + void *_srpc, TSC_OAuthTokenRequestResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_superuser_authorization_request( + void *_srpc, TCS_SuperUserAuthorizationRequest *request); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_get_superuser_authorization_result(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_superuser_authorization_result( + void *_srpc, TSC_SuperUserAuthorizationResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_calcfg_request( + void *_srpc, TCS_DeviceCalCfgRequest *request); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_calcfg_request_b( + void *_srpc, TCS_DeviceCalCfgRequest_B *request); +_supla_int_t SRPC_ICACHE_FLASH +srpc_sc_async_device_calcfg_result(void *_srpc, TSC_DeviceCalCfgResult *result); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_get_channel_basic_cfg(void *_srpc, _supla_int_t ChannelID); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_channel_basic_cfg_result( + void *_srpc, TSC_ChannelBasicCfg *basic_cfg); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_function(void *_srpc, TCS_SetChannelFunction *func); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_channel_function_result( + void *_srpc, TSC_SetChannelFunctionResult *result); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_set_channel_caption(void *_srpc, TCS_SetChannelCaption *caption); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_channel_caption_result( + void *_srpc, TSC_SetChannelCaptionResult *caption); +_supla_int_t SRPC_ICACHE_FLASH +srpc_cs_async_clients_reconnect_request(void *_srpc); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_clients_reconnect_request_result( + void *_srpc, TSC_ClientsReconnectRequestResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_set_registration_enabled( + void *_srpc, TCS_SetRegistrationEnabled *reg_enabled); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_set_registration_enabled_result( + void *_srpc, TSC_SetRegistrationEnabledResult *result); +_supla_int_t SRPC_ICACHE_FLASH srpc_cs_async_device_reconnect_request( + void *_srpc, TCS_DeviceReconnectRequest *request); +_supla_int_t SRPC_ICACHE_FLASH srpc_sc_async_device_reconnect_request_result( + void *_srpc, TSC_DeviceReconnectRequestResult *result); +#endif /*SRPC_EXCLUDE_CLIENT*/ + +#ifndef SRPC_EXCLUDE_EXTENDEDVALUE_TOOLS +#ifdef USE_DEPRECATED_EMEV_V1 +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_emextended2extended( + TElectricityMeter_ExtendedValue *em_ev, TSuplaChannelExtendedValue *ev); +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2emextended( + TSuplaChannelExtendedValue *ev, TElectricityMeter_ExtendedValue *em_ev); + +_supla_int_t SRPC_ICACHE_FLASH +srpc_evtool_emev_v1to2(TElectricityMeter_ExtendedValue *v1, + TElectricityMeter_ExtendedValue_V2 *v2); +_supla_int_t SRPC_ICACHE_FLASH +srpc_evtool_emev_v2to1(TElectricityMeter_ExtendedValue_V2 *v2, + TElectricityMeter_ExtendedValue *v1); + +#endif /*USE_DEPRECATED_EMEV_V1*/ + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v2_emextended2extended( + TElectricityMeter_ExtendedValue_V2 *em_ev, TSuplaChannelExtendedValue *ev); +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v2_extended2emextended( + TSuplaChannelExtendedValue *ev, TElectricityMeter_ExtendedValue_V2 *em_ev); + +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2thermostatextended( + TSuplaChannelExtendedValue *ev, TThermostat_ExtendedValue *th_ev); +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_thermostatextended2extended( + TThermostat_ExtendedValue *th_ev, TSuplaChannelExtendedValue *ev); + +#ifndef SRPC_EXCLUDE_CLIENT +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_icextended2extended( + TSC_ImpulseCounter_ExtendedValue *ic_ev, TSuplaChannelExtendedValue *ev); +_supla_int_t SRPC_ICACHE_FLASH srpc_evtool_v1_extended2icextended( + TSuplaChannelExtendedValue *ev, TSC_ImpulseCounter_ExtendedValue *ic_ev); +#endif /*SRPC_EXCLUDE_CLIENT*/ + +#endif /*SRPC_EXCLUDE_EXTENDEDVALUE_TOOLS*/ + +#ifdef __cplusplus +} +#endif + +#endif /* supladex_H_ */ diff --git a/lib/SuplaDevice/src/supla/action_handler.h b/lib/SuplaDevice/src/supla/action_handler.h new file mode 100644 index 00000000..b0e38d63 --- /dev/null +++ b/lib/SuplaDevice/src/supla/action_handler.h @@ -0,0 +1,28 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _action_handler_h +#define _action_handler_h + +namespace Supla { +class ActionHandler { + public: + virtual void handleAction(int event, int action) = 0; +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/actions.h b/lib/SuplaDevice/src/supla/actions.h new file mode 100644 index 00000000..eac71d00 --- /dev/null +++ b/lib/SuplaDevice/src/supla/actions.h @@ -0,0 +1,82 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _actions_h +#define _actions_h + +// Actions are used in ActionHandler elements. They are grouped by most common +// usage, but you should not rely on it. Please check exact supported actions +// in ActionHandler's element documentation + +namespace Supla { +enum Action { + // Relays + TURN_ON, + TURN_OFF, + TOGGLE, + + // Settable binary sensors + SET, + CLEAR, + + // Roller shutters + OPEN, + CLOSE, + STOP, + OPEN_OR_STOP, + CLOSE_OR_STOP, + COMFORT_UP_POSITION, + COMFORT_DOWN_POSITION, + STEP_BY_STEP, + MOVE_UP, + MOVE_DOWN, + MOVE_UP_OR_STOP, + MOVE_DOWN_OR_STOP, + + // Dimmable light, RGB(W) LEDs + BRIGHTEN_ALL, + DIM_ALL, + BRIGHTEN_R, + BRIGHTEN_G, + BRIGHTEN_B, + BRIGHTEN_W, + BRIGHTEN_RGB, + DIM_R, + DIM_G, + DIM_B, + DIM_W, + DIM_RGB, + TURN_ON_RGB, + TURN_OFF_RGB, + TOGGLE_RGB, + TURN_ON_W, + TURN_OFF_W, + TOGGLE_W, + TURN_ON_RGB_DIMMED, + TURN_ON_W_DIMMED, + TURN_ON_ALL_DIMMED, + ITERATE_DIM_RGB, + ITERATE_DIM_W, + ITERATE_DIM_ALL, + + // Impulse counter + RESET + +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/channel.cpp b/lib/SuplaDevice/src/supla/channel.cpp new file mode 100644 index 00000000..005d8dfa --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel.cpp @@ -0,0 +1,326 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "supla/channel.h" +#include "supla-common/log.h" +#include "supla-common/srpc.h" +#include "tools.h" +#include "events.h" + +namespace Supla { + +unsigned long Channel::lastCommunicationTimeMs = 0; +TDS_SuplaRegisterDevice_E Channel::reg_dev; + +Channel::Channel() { + valueChanged = false; + channelNumber = -1; + if (reg_dev.channel_count < SUPLA_CHANNELMAXCOUNT) { + channelNumber = reg_dev.channel_count; + + memset(®_dev.channels[channelNumber], 0, sizeof(reg_dev.channels[channelNumber])); + reg_dev.channels[channelNumber].Number = channelNumber; + + reg_dev.channel_count++; + } else { +// TODO: add status CHANNEL_LIMIT_EXCEEDED + } + + setFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); +} + +Channel::~Channel() { + reg_dev.channel_count--; +} + +void Channel::setNewValue(double dbl) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + if (sizeof(double) == 8) { + memcpy(newValue, &dbl, 8); + } else if (sizeof(double) == 4) { + float2DoublePacked(dbl, (uint8_t *)(newValue)); + } + if (setNewValue(newValue)) { + runAction(ON_CHANGE); + supla_log(LOG_DEBUG, "Channel(%d) value changed to %f", channelNumber, dbl); + } +} + +void Channel::setNewValue(double temp, double humi) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + _supla_int_t t = temp * 1000.00; + _supla_int_t h = humi * 1000.00; + + memcpy(newValue, &t, 4); + memcpy(&(newValue[4]), &h, 4); + + if (setNewValue(newValue)) { + runAction(ON_CHANGE); + supla_log(LOG_DEBUG, + "Channel(%d) value changed to temp(%f), humi(%f)", + channelNumber, + temp, + humi); + } +} + +void Channel::setNewValue(_supla_int64_t value) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + + memcpy(newValue, &value, sizeof(_supla_int64_t)); + if (setNewValue(newValue)) { + runAction(ON_CHANGE); + supla_log( + LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, static_cast(value)); + } +} + +void Channel::setNewValue(_supla_int_t value) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + + memcpy(newValue, &value, sizeof(value)); + if (setNewValue(newValue)) { + runAction(ON_CHANGE); + supla_log( + LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, value); + } +} + +void Channel::setNewValue(bool value) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + + newValue[0] = value; + if (setNewValue(newValue)) { + if (value) { + runAction(Supla::ON_TURN_ON); + } else { + runAction(Supla::ON_TURN_OFF); + } + runAction(Supla::ON_CHANGE); + + supla_log( + LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, value); + } +} + +void Channel::setNewValue(TElectricityMeter_ExtendedValue_V2 &emValue) { + // Prepare standard channel value + if (sizeof(TElectricityMeter_Value) <= SUPLA_CHANNELVALUE_SIZE) { + TElectricityMeter_Measurement *m = nullptr; + TElectricityMeter_Value v; + memset(&v, 0, sizeof(TElectricityMeter_Value)); + + unsigned _supla_int64_t fae_sum = emValue.total_forward_active_energy[0] + + emValue.total_forward_active_energy[1] + + emValue.total_forward_active_energy[2]; + + v.total_forward_active_energy = fae_sum / 1000; + + if (emValue.m_count && emValue.measured_values & EM_VAR_VOLTAGE) { + m = &emValue.m[emValue.m_count - 1]; + + if (m->voltage[0] > 0) { + v.flags |= EM_VALUE_FLAG_PHASE1_ON; + } + + if (m->voltage[1] > 0) { + v.flags |= EM_VALUE_FLAG_PHASE2_ON; + } + + if (m->voltage[2] > 0) { + v.flags |= EM_VALUE_FLAG_PHASE3_ON; + } + } + + memcpy(reg_dev.channels[channelNumber].value, + &v, + sizeof(TElectricityMeter_Value)); + setUpdateReady(); + } +} + +bool Channel::setNewValue(char *newValue) { + if (memcmp(newValue, reg_dev.channels[channelNumber].value, 8) != 0) { + memcpy(reg_dev.channels[channelNumber].value, newValue, 8); + setUpdateReady(); + return true; + } + return false; +} + +void Channel::setType(_supla_int_t type) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].Type = type; + } +} + +void Channel::setDefault(_supla_int_t value) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].Default = value; + } +} + +void Channel::setFlag(_supla_int_t flag) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].Flags |= flag; + } +} + +void Channel::unsetFlag(_supla_int_t flag) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].Flags &= ~flag; + } +} + +void Channel::setFuncList(_supla_int_t functions) { + if (channelNumber >= 0) { + reg_dev.channels[channelNumber].FuncList = functions; + } +} + +int Channel::getChannelNumber() { + return channelNumber; +} + +void Channel::clearUpdateReady() { + valueChanged = false; +}; + +void Channel::sendUpdate(void *srpc) { + clearUpdateReady(); + srpc_ds_async_channel_value_changed( + srpc, channelNumber, reg_dev.channels[channelNumber].value); + + // returns null for non-extended channels + TSuplaChannelExtendedValue *extValue = getExtValue(); + if (extValue) { + srpc_ds_async_channel_extendedvalue_changed(srpc, channelNumber, extValue); + } + +} + +TSuplaChannelExtendedValue *Channel::getExtValue() { + return nullptr; +} + +void Channel::setUpdateReady() { + valueChanged = true; +}; + +bool Channel::isUpdateReady() { + return valueChanged; +}; + +bool Channel::isExtended() { + return false; +} + +void Channel::setNewValue(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) { + char newValue[SUPLA_CHANNELVALUE_SIZE]; + memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); + newValue[0] = brightness; + newValue[1] = colorBrightness; + newValue[2] = blue; + newValue[3] = green; + newValue[4] = red; + if (setNewValue(newValue)) { + runAction(ON_CHANGE); + supla_log(LOG_DEBUG, "Channel(%d) value changed to RGB(%d, %d, %d), colBr(%d), bright(%d)", channelNumber, red, green, blue, colorBrightness, brightness); + } +} + +_supla_int_t Channel::getChannelType() { + if (channelNumber >= 0) { + return reg_dev.channels[channelNumber].Type; + } + return -1; +} + +double Channel::getValueDouble() { + double value; + if (sizeof(double) == 8) { + memcpy(&value, reg_dev.channels[channelNumber].value, 8); + } else if (sizeof(double) == 4) { + value = doublePacked2float((uint8_t *)(reg_dev.channels[channelNumber].value)); + } + + return value; +} + +double Channel::getValueDoubleFirst() { + _supla_int_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, 4); + + return value / 1000.0; +} + +double Channel::getValueDoubleSecond() { + _supla_int_t value; + memcpy(&value, &(reg_dev.channels[channelNumber].value[4]), 4); + + return value / 1000.0; +} + +_supla_int_t Channel::getValueInt32() { + _supla_int_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, sizeof(value)); + return value; +} + +_supla_int64_t Channel::getValueInt64() { + _supla_int64_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, sizeof(value)); + return value; +} + +bool Channel::getValueBool() { + return reg_dev.channels[channelNumber].value[0]; +} + +uint8_t Channel::getValueRed() { + return reg_dev.channels[channelNumber].value[4]; +} + +uint8_t Channel::getValueGreen() { + return reg_dev.channels[channelNumber].value[3]; +} + +uint8_t Channel::getValueBlue() { + return reg_dev.channels[channelNumber].value[2]; +} + +uint8_t Channel::getValueColorBrightness() { + return reg_dev.channels[channelNumber].value[1]; +} + +uint8_t Channel::getValueBrightness() { + return reg_dev.channels[channelNumber].value[0]; +} + + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/channel.h b/lib/SuplaDevice/src/supla/channel.h new file mode 100644 index 00000000..453d6f88 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel.h @@ -0,0 +1,84 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _channel_h +#define _channel_h + +#include + +#include "supla-common/proto.h" +#include "local_action.h" + +namespace Supla { + +class Channel : public LocalAction { + public: + Channel(); + ~Channel(); + + void setNewValue(double dbl); + void setNewValue(double temp, double humi); + void setNewValue(_supla_int_t value); + void setNewValue(bool value); + void setNewValue(TElectricityMeter_ExtendedValue_V2 &emValue); + void setNewValue(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness); + void setNewValue(_supla_int64_t value); + bool setNewValue(char *newValue); + + double getValueDouble(); + double getValueDoubleFirst(); + double getValueDoubleSecond(); + _supla_int_t getValueInt32(); + _supla_int64_t getValueInt64(); + bool getValueBool(); + uint8_t getValueRed(); + uint8_t getValueGreen(); + uint8_t getValueBlue(); + uint8_t getValueColorBrightness(); + uint8_t getValueBrightness(); + + virtual bool isExtended(); + bool isUpdateReady(); + int getChannelNumber(); + _supla_int_t getChannelType(); + + void setType(_supla_int_t type); + void setDefault(_supla_int_t value); + void setFlag(_supla_int_t flag); + void unsetFlag(_supla_int_t flag); + void setFuncList(_supla_int_t functions); + void clearUpdateReady(); + void sendUpdate(void *srpc); + virtual TSuplaChannelExtendedValue *getExtValue(); + + static unsigned long lastCommunicationTimeMs; + static TDS_SuplaRegisterDevice_E reg_dev; + + protected: + void setUpdateReady(); + + bool valueChanged; + int channelNumber; + +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/channel_element.cpp b/lib/SuplaDevice/src/supla/channel_element.cpp new file mode 100644 index 00000000..2a5da7b8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_element.cpp @@ -0,0 +1,31 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "channel_element.h" + +Supla::Channel *Supla::ChannelElement::getChannel() { + return &channel; +} + +void Supla::ChannelElement::addAction(int action, ActionHandler &client, int event) { + channel.addAction(action, client, event); +} + +void Supla::ChannelElement::addAction(int action, ActionHandler *client, int event) { + addAction(action, *client, event); +} + + diff --git a/lib/SuplaDevice/src/supla/channel_element.h b/lib/SuplaDevice/src/supla/channel_element.h new file mode 100644 index 00000000..372a72f0 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_element.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _channel_element_h +#define _channel_element_h + +#include "element.h" +#include "channel.h" +#include "local_action.h" +#include "action_handler.h" + +namespace Supla { + +class ChannelElement : public Element, public LocalAction { + public: + + Channel *getChannel(); + + // Override local action methods in order to delegate execution to Channel + void addAction(int action, ActionHandler &client, int event); + void addAction(int action, ActionHandler *client, int event); + + protected: + Channel channel; +}; + +}; + +#endif + diff --git a/lib/SuplaDevice/src/supla/channel_extended.cpp b/lib/SuplaDevice/src/supla/channel_extended.cpp new file mode 100644 index 00000000..623f3654 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_extended.cpp @@ -0,0 +1,28 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "supla/channel_extended.h" + +namespace Supla { +bool ChannelExtended::isExtended() { + return true; +} + +TSuplaChannelExtendedValue *ChannelExtended::getExtValue() { + return &extValue; +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/channel_extended.h b/lib/SuplaDevice/src/supla/channel_extended.h new file mode 100644 index 00000000..d463e141 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_extended.h @@ -0,0 +1,34 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _channel_extended_h +#define _channel_extended_h + +#include "channel.h" + +namespace Supla { +class ChannelExtended : public Channel { + public: + bool isExtended(); + TSuplaChannelExtendedValue *getExtValue(); + + protected: + TSuplaChannelExtendedValue extValue; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/clock/clock.cpp b/lib/SuplaDevice/src/supla/clock/clock.cpp new file mode 100644 index 00000000..ffcd3f4b --- /dev/null +++ b/lib/SuplaDevice/src/supla/clock/clock.cpp @@ -0,0 +1,157 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "clock.h" +#include +#include "supla-common/srpc.h" + +using namespace Supla; + +Clock::Clock() : localtime(0), lastServerUpdate(0), lastMillis(0) {}; + +bool Clock::isReady() { + return true; +} + +int Clock::getYear() { + struct tm timeinfo; + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_year + 1900; +} + +int Clock::getMonth() { + struct tm timeinfo; +// timeinfo = gmtime(time(0)); + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_mon + 1; +} + +int Clock::getDay() { + struct tm timeinfo; +// timeinfo = gmtime(time(0)); + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_mday; +} + +int Clock::getDayOfWeek() { + struct tm timeinfo; +// timeinfo = gmtime(time(0)); + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_wday + 1; // 1 - Sunday, 2 - Monday ... +} + +int Clock::getHour() { + struct tm timeinfo; + //timeinfo = gmtime(time(0)); + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_hour; +} + +int Clock::getMin() { + struct tm timeinfo; + //timeinfo = gmtime(time(0)); + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_min; +} + +int Clock::getSec() { + struct tm timeinfo; + time_t currentTime = time(0); + gmtime_r(¤tTime, &timeinfo); + return timeinfo.tm_sec; +} + + +void Clock::parseLocaltimeFromServer(TSDC_UserLocalTimeResult *result) { + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(timeinfo)); + + Serial.print(F("Current local time: ")); + Serial.print(getYear()); + Serial.print(F("-")); + Serial.print(getMonth()); + Serial.print(F("-")); + Serial.print(getDay()); + Serial.print(F(" ")); + Serial.print(getHour()); + Serial.print(F(":")); + Serial.print(getMin()); + Serial.print(F(":")); + Serial.println(getSec()); + + + + Serial.print(F("Received local time from server: ")); + Serial.print(result->year); + Serial.print(F("-")); + Serial.print(static_cast(result->month)); + Serial.print(F("-")); + Serial.print(static_cast(result->day)); + Serial.print(F(" ")); + Serial.print(static_cast(result->hour)); + Serial.print(F(":")); + Serial.print(static_cast(result->min)); + Serial.print(F(":")); + Serial.println(static_cast(result->sec)); + + timeinfo.tm_year = result->year - 1900; + timeinfo.tm_mon = result->month - 1; + timeinfo.tm_mday = result->day; + timeinfo.tm_hour = result->hour; + timeinfo.tm_min = result->min; + timeinfo.tm_sec = result->sec; + + localtime = mktime(&timeinfo); + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + timeval tv = { localtime, 0 }; + settimeofday(&tv, nullptr); +#elif defined(ARDUINO_ARCH_AVR) + set_system_time(mktime(&timeinfo)); +#endif + + +} + +void Clock::onTimer() { + unsigned long curMillis = millis(); + int seconds = (curMillis - lastMillis) / 1000; + if (seconds > 0) { + lastMillis = curMillis - ((curMillis - lastMillis) % 1000); + for (int i = 0; i < seconds; i++) { +#if defined(ARDUINO_ARCH_AVR) + system_tick(); +#endif + localtime++; + } + } + +} + +bool Clock::iterateConnected(void *srpc) { + if (lastServerUpdate == 0 || millis() - lastServerUpdate > 5*60000) { // update every 5 min + srpc_dcs_async_get_user_localtime(srpc); + lastServerUpdate = millis(); + return false; + } + return true; +} diff --git a/lib/SuplaDevice/src/supla/clock/clock.h b/lib/SuplaDevice/src/supla/clock/clock.h new file mode 100644 index 00000000..f282e8da --- /dev/null +++ b/lib/SuplaDevice/src/supla/clock/clock.h @@ -0,0 +1,52 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_clock_h +#define _supla_clock_h + +#include +#include +#include + +namespace Supla { + +class Clock : public Element { + public: + Clock(); + virtual bool isReady(); + virtual int getYear(); + virtual int getMonth(); + virtual int getDay(); + virtual int getDayOfWeek(); // 1 - Sunday, 2 - Monday + virtual int getHour(); + virtual int getMin(); + virtual int getSec(); + + void onTimer(); + bool iterateConnected(void *srpc); + + virtual void parseLocaltimeFromServer(TSDC_UserLocalTimeResult *result); + + protected: + time_t localtime; + unsigned long lastServerUpdate; + unsigned long lastMillis; + +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp new file mode 100644 index 00000000..af9a21c3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp @@ -0,0 +1,205 @@ +#include +#include +#include "S_MCP23017.h" + +static const uint8_t MCP23017_BASEADDRESS = 0x20; + +static const uint8_t MCP23017_IODIRA = 0x00; +static const uint8_t MCP23017_IODIRB = 0x01; +static const uint8_t MCP23017_IPOLA = 0x02; +static const uint8_t MCP23017_IPOLB = 0x03; +static const uint8_t MCP23017_GPINTENA = 0x04; +static const uint8_t MCP23017_GPINTENB = 0x05; +static const uint8_t MCP23017_DEFVALA = 0x06; +static const uint8_t MCP23017_DEFVALB = 0x07; +static const uint8_t MCP23017_INTCONA = 0x08; +static const uint8_t MCP23017_INTCONB = 0x09; +static const uint8_t MCP23017_IOCONA = 0x0A; +static const uint8_t MCP23017_IOCONB = 0x0B; +static const uint8_t MCP23017_GPPUA = 0x0C; +static const uint8_t MCP23017_GPPUB = 0x0D; +static const uint8_t MCP23017_INTFA = 0x0E; +static const uint8_t MCP23017_INTFB = 0x0F; +static const uint8_t MCP23017_INTCAPA = 0x10; +static const uint8_t MCP23017_INTCAPB = 0x11; +static const uint8_t MCP23017_GPIOA = 0x12; +static const uint8_t MCP23017_GPIOB = 0x13; +static const uint8_t MCP23017_OLATA = 0x14; +static const uint8_t MCP23017_OLATB = 0x15; + +#ifdef ESP8266 +void MCP23017::init(uint8_t sda, uint8_t scl, bool fast) { + Wire.begin(sda, scl); + if (fast) + Wire.setClock(400000); +} +#else +void MCP23017::init(bool fast) { + Wire.begin(); + if (fast) + Wire.setClock(400000); +} +#endif + +bool MCP23017::begin(uint8_t address) { + _address = MCP23017_BASEADDRESS | (address & 0x07); + return (writeReg16(MCP23017_IOCONA, 0x4242) && writeReg16(MCP23017_IODIRA, 0xFFFF)); // INT MIRROR & INT POL HIGH, ALL INPUTS +} + +void MCP23017::pinMode(uint8_t pin, uint8_t mode) { + if (pin < 16) { + if (mode == OUTPUT) { + updateReg(pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } else if ((mode == INPUT) || (mode == INPUT_PULLUP)) { + updateReg(pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB, 0xFF, 1 << (pin % 8)); + if (mode == INPUT_PULLUP) { + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, 0xFF, 1 << (pin % 8)); + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, 0xFF, 1 << (pin % 8)); + } else { + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, ~(uint8_t)(1 << (pin % 8)), 0x00); + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } + } + } +} + +void MCP23017::setPullup(uint8_t pin, bool pullup, bool inverse) { + if (pin < 16) { + if (pullup) + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, ~(uint8_t)(1 << (pin % 8)), 0x00); + if (inverse) + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +bool MCP23017::digitalRead(uint8_t pin) { + unsigned long now = millis(); + if (now > get_ba){ + ba = readReg16(MCP23017_GPIOA); // --------- reads "readReg16" only once every 20 ms ---- + get_ba = now + 20; + } + if (pin < 16) { + return ((ba >> (pin % 16)) & 0x01); + } +} + +void MCP23017::digitalWrite(uint8_t pin, bool value) { + if (pin < 16) { + if (value) + updateReg(pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +uint16_t MCP23017::digitalReads() { + return readReg16(MCP23017_GPIOA); +} + +void MCP23017::digitalWrites(uint16_t values) { + writeReg16(MCP23017_GPIOA, values); +} + +void MCP23017::attachInterrupt(uint8_t pin, callback_t callback) { + _callback = callback; + ::pinMode(pin, INPUT); + ::attachInterrupt(digitalPinToInterrupt(pin), std::bind(&MCP23017::_interrupt, this), RISING); +} + +void MCP23017::detachInterrupt(uint8_t pin) { + ::detachInterrupt(digitalPinToInterrupt(pin)); + _callback = NULL; +} + +void MCP23017::setupInterrupt(uint8_t pin, bool enable) { + if (pin < 16) { + if (enable) + updateReg(pin < 8 ? MCP23017_GPINTENA : MCP23017_GPINTENB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPINTENA : MCP23017_GPINTENB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +void MCP23017::setupInterrupts(uint16_t pins, bool enable) { + if (enable) + updateReg16(MCP23017_GPINTENA, 0xFFFF, pins); + else + updateReg16(MCP23017_GPINTENA, ~pins, 0x00); +} + +bool MCP23017::writeReg(uint8_t reg, uint8_t value) { + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(value); + return (Wire.endTransmission() == 0); +} + +bool MCP23017::writeReg16(uint8_t reg, uint16_t value) { + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(value & 0xFF); + Wire.write(value >> 8); + return (Wire.endTransmission() == 0); +} + +uint8_t MCP23017::readReg(uint8_t reg) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return 0; // Error! + Wire.requestFrom(_address, (uint8_t)1); + return Wire.read(); +} + +uint16_t MCP23017::readReg16(uint8_t reg) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return 0; // Error! + Wire.requestFrom(_address, (uint8_t)2); + uint8_t a = Wire.read(); + return ((Wire.read() << 8) | a); +} + +bool MCP23017::updateReg(uint8_t reg, uint8_t andMask, uint8_t orMask) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return false; // Error! + Wire.requestFrom(_address, (uint8_t)1); + uint8_t a = (Wire.read() & andMask) | orMask; + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(a); + return (Wire.endTransmission() == 0); +} + +bool MCP23017::updateReg16(uint8_t reg, uint16_t andMask, uint16_t orMask) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return false; // Error! + Wire.requestFrom(_address, (uint8_t)2); + uint16_t ab = Wire.read(); + ab |= (Wire.read() << 8); + ab &= andMask; + ab |= orMask; + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(ab & 0xFF); + Wire.write(ab >> 8); + return (Wire.endTransmission() == 0); +} + +void IRAM_ATTR MCP23017::_interrupt() { + uint16_t pins, values; + + pins = readReg16(MCP23017_INTFA); + values = readReg16(MCP23017_INTCAPA); + if (_callback) + _callback(pins, values); +} diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h new file mode 100644 index 00000000..ef08791e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h @@ -0,0 +1,52 @@ +#ifndef _S_MCP23017_H +#define _S_MCP23017_H + +#include +#include + +class MCP23017 { +public: + typedef std::function callback_t; + + MCP23017() : _callback(NULL) {} + +#ifdef ESP8266 + static void init(uint8_t sda, uint8_t scl, bool fast = true); + static void init(bool fast = true) { + init(SDA, SCL, fast); + } +#else + static void init(bool fast = true); +#endif + + bool begin(uint8_t address = 0); + + void pinMode(uint8_t pin, uint8_t mode); + void setPullup(uint8_t pin, bool pullup, bool inverse = false); + bool digitalRead(uint8_t pin); + void digitalWrite(uint8_t pin, bool value); + uint16_t digitalReads(); + void digitalWrites(uint16_t values); + + void attachInterrupt(uint8_t pin, callback_t callback); + void detachInterrupt(uint8_t pin); + void setupInterrupt(uint8_t pin, bool enable = true); + void setupInterrupts(uint16_t pins, bool enable = true); + +protected: + bool writeReg(uint8_t reg, uint8_t value); + bool writeReg16(uint8_t reg, uint16_t value); + uint8_t readReg(uint8_t reg); + uint16_t readReg16(uint8_t reg); + bool updateReg(uint8_t reg, uint8_t andMask, uint8_t orMask); + bool updateReg16(uint8_t reg, uint16_t andMask, uint16_t orMask); + + void _interrupt(); + + uint8_t _address; + uint16_t ba = 0; + unsigned long get_ba = 0; + callback_t _callback; +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino b/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino new file mode 100644 index 00000000..dd4ee2be --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino @@ -0,0 +1,189 @@ +#define supla_lib_config_h_ // silences unnecessary debug messages "should be disabled by default" +#include +#include +#include +#include + +// +// ESP8266 based board: +#include +Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +// + +void setup() { + + Serial.begin(115200); + + mcp1.init(4, 5); // init(uint8_t sda, uint8_t scl, bool fast) = Wire.begin + + if (! mcp1.begin(0))Serial.println("MCP23017 1 not found!"); // begin(uint8_t address) "Pin 100 - 115" + if (! mcp2.begin(1))Serial.println("MCP23017 2 not found!"); // begin(uint8_t address) "Pin 116 - 131" + if (! mcp3.begin(2))Serial.println("MCP23017 3 not found!"); // begin(uint8_t address) "Pin 132 - 147" + if (! mcp4.begin(3))Serial.println("MCP23017 4 not found!"); // begin(uint8_t address) "Pin 148 - 163" + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + auto relay_1 = new Supla::Control::Relay(100, true); + auto relay_2 = new Supla::Control::Relay(101, true); + auto relay_3 = new Supla::Control::Relay(102, true); + auto relay_4 = new Supla::Control::Relay(103, true); + auto relay_5 = new Supla::Control::Relay(104, true); + auto relay_6 = new Supla::Control::Relay(105, true); + auto relay_7 = new Supla::Control::Relay(106, true); + auto relay_8 = new Supla::Control::Relay(107, true); + auto relay_9 = new Supla::Control::Relay(108, true); + auto relay_10 = new Supla::Control::Relay(109, true); + auto relay_11 = new Supla::Control::Relay(110, true); + auto relay_12 = new Supla::Control::Relay(111, true); + auto relay_13 = new Supla::Control::Relay(112, true); + auto relay_14 = new Supla::Control::Relay(113, true); + auto relay_15 = new Supla::Control::Relay(114, true); + auto relay_16 = new Supla::Control::Relay(115, true); + + auto relay_17 = new Supla::Control::Relay(148, true); + auto relay_18 = new Supla::Control::Relay(149, true); + auto relay_19 = new Supla::Control::Relay(150, true); + auto relay_20 = new Supla::Control::Relay(151, true); + auto relay_21 = new Supla::Control::Relay(152, true); + auto relay_22 = new Supla::Control::Relay(153, true); + auto relay_23 = new Supla::Control::Relay(154, true); + auto relay_24 = new Supla::Control::Relay(155, true); + auto relay_25 = new Supla::Control::Relay(156, true); + auto relay_26 = new Supla::Control::Relay(157, true); + auto relay_27 = new Supla::Control::Relay(158, true); + auto relay_28 = new Supla::Control::Relay(159, true); + auto relay_29 = new Supla::Control::Relay(160, true); + auto relay_30 = new Supla::Control::Relay(161, true); + auto relay_31 = new Supla::Control::Relay(162, true); + auto relay_32 = new Supla::Control::Relay(163, true); + + auto button_1 = new Supla::Control::Button(116, true, true); + auto button_2 = new Supla::Control::Button(117, true, true); + auto button_3 = new Supla::Control::Button(118, true, true); + auto button_4 = new Supla::Control::Button(119, true, true); + auto button_5 = new Supla::Control::Button(120, true, true); + auto button_6 = new Supla::Control::Button(121, true, true); + auto button_7 = new Supla::Control::Button(122, true, true); + auto button_8 = new Supla::Control::Button(123, true, true); + auto button_9 = new Supla::Control::Button(124, true, true); + auto button_10 = new Supla::Control::Button(125, true, true); + auto button_11 = new Supla::Control::Button(126, true, true); + auto button_12 = new Supla::Control::Button(127, true, true); + auto button_13 = new Supla::Control::Button(128, true, true); + auto button_14 = new Supla::Control::Button(129, true, true); + auto button_15 = new Supla::Control::Button(130, true, true); + auto button_16 = new Supla::Control::Button(131, true, true); + + auto button_17 = new Supla::Control::Button(132, true, true); + auto button_18 = new Supla::Control::Button(133, true, true); + auto button_19 = new Supla::Control::Button(134, true, true); + auto button_20 = new Supla::Control::Button(135, true, true); + auto button_21 = new Supla::Control::Button(136, true, true); + auto button_22 = new Supla::Control::Button(137, true, true); + auto button_23 = new Supla::Control::Button(138, true, true); + auto button_24 = new Supla::Control::Button(139, true, true); + auto button_25 = new Supla::Control::Button(140, true, true); + auto button_26 = new Supla::Control::Button(141, true, true); + auto button_27 = new Supla::Control::Button(142, true, true); + auto button_28 = new Supla::Control::Button(143, true, true); + auto button_29 = new Supla::Control::Button(144, true, true); + auto button_30 = new Supla::Control::Button(145, true, true); + auto button_31 = new Supla::Control::Button(146, true, true); + auto button_32 = new Supla::Control::Button(147, true, true); + + button_1->addAction(Supla::TOGGLE, relay_1, Supla::ON_PRESS); + button_1->setSwNoiseFilterDelay(50); + button_2->addAction(Supla::TOGGLE, relay_2, Supla::ON_PRESS); + button_2->setSwNoiseFilterDelay(50); + button_3->addAction(Supla::TOGGLE, relay_3, Supla::ON_PRESS); + button_3->setSwNoiseFilterDelay(50); + button_4->addAction(Supla::TOGGLE, relay_4, Supla::ON_PRESS); + button_4->setSwNoiseFilterDelay(50); + button_5->addAction(Supla::TOGGLE, relay_5, Supla::ON_PRESS); + button_5->setSwNoiseFilterDelay(50); + button_6->addAction(Supla::TOGGLE, relay_6, Supla::ON_PRESS); + button_6->setSwNoiseFilterDelay(50); + button_7->addAction(Supla::TOGGLE, relay_7, Supla::ON_PRESS); + button_7->setSwNoiseFilterDelay(50); + button_8->addAction(Supla::TOGGLE, relay_8, Supla::ON_PRESS); + button_8->setSwNoiseFilterDelay(50); + button_9->addAction(Supla::TOGGLE, relay_9, Supla::ON_PRESS); + button_9->setSwNoiseFilterDelay(50); + button_10->addAction(Supla::TOGGLE, relay_10, Supla::ON_PRESS); + button_10->setSwNoiseFilterDelay(50); + button_11->addAction(Supla::TOGGLE, relay_11, Supla::ON_PRESS); + button_11->setSwNoiseFilterDelay(50); + button_12->addAction(Supla::TOGGLE, relay_12, Supla::ON_PRESS); + button_12->setSwNoiseFilterDelay(50); + button_13->addAction(Supla::TOGGLE, relay_13, Supla::ON_PRESS); + button_13->setSwNoiseFilterDelay(50); + button_14->addAction(Supla::TOGGLE, relay_14, Supla::ON_PRESS); + button_14->setSwNoiseFilterDelay(50); + button_15->addAction(Supla::TOGGLE, relay_15, Supla::ON_PRESS); + button_15->setSwNoiseFilterDelay(50); + button_16->addAction(Supla::TOGGLE, relay_16, Supla::ON_PRESS); + button_16->setSwNoiseFilterDelay(50); + + button_17->addAction(Supla::TOGGLE, relay_17, Supla::ON_PRESS); + button_17->setSwNoiseFilterDelay(50); + button_18->addAction(Supla::TOGGLE, relay_18, Supla::ON_PRESS); + button_18->setSwNoiseFilterDelay(50); + button_19->addAction(Supla::TOGGLE, relay_19, Supla::ON_PRESS); + button_19->setSwNoiseFilterDelay(50); + button_20->addAction(Supla::TOGGLE, relay_20, Supla::ON_PRESS); + button_20->setSwNoiseFilterDelay(50); + button_21->addAction(Supla::TOGGLE, relay_21, Supla::ON_PRESS); + button_21->setSwNoiseFilterDelay(50); + button_22->addAction(Supla::TOGGLE, relay_22, Supla::ON_PRESS); + button_22->setSwNoiseFilterDelay(50); + button_23->addAction(Supla::TOGGLE, relay_23, Supla::ON_PRESS); + button_23->setSwNoiseFilterDelay(50); + button_24->addAction(Supla::TOGGLE, relay_24, Supla::ON_PRESS); + button_24->setSwNoiseFilterDelay(50); + button_25->addAction(Supla::TOGGLE, relay_25, Supla::ON_PRESS); + button_25->setSwNoiseFilterDelay(50); + button_26->addAction(Supla::TOGGLE, relay_26, Supla::ON_PRESS); + button_26->setSwNoiseFilterDelay(50); + button_27->addAction(Supla::TOGGLE, relay_27, Supla::ON_PRESS); + button_27->setSwNoiseFilterDelay(50); + button_28->addAction(Supla::TOGGLE, relay_28, Supla::ON_PRESS); + button_28->setSwNoiseFilterDelay(50); + button_29->addAction(Supla::TOGGLE, relay_29, Supla::ON_PRESS); + button_29->setSwNoiseFilterDelay(50); + button_30->addAction(Supla::TOGGLE, relay_30, Supla::ON_PRESS); + button_30->setSwNoiseFilterDelay(50); + button_31->addAction(Supla::TOGGLE, relay_31, Supla::ON_PRESS); + button_31->setSwNoiseFilterDelay(50); + button_32->addAction(Supla::TOGGLE, relay_32, Supla::ON_PRESS); + button_32->setSwNoiseFilterDelay(50); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); + delay(25); +} diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt b/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt new file mode 100644 index 00000000..9cb235ee --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt @@ -0,0 +1,15 @@ +Support for up to 4 x "MCP23017" additional 64 Gpios. + + +BASIC USE: + +INSTANTIATE +#include + +SETUP + mcp1.init(uint8_t sda, uint8_t scl); // init(uint8_t sda, uint8_t scl) = Wire.begin(); + + mcp1.begin(uint8_t mcp23017_address); // Pin 100 - 115 + mcp2.begin(uint8_t mcp23017_address); // Pin 116 - 131 + mcp3.begin(uint8_t mcp23017_address); // Pin 132 - 147 + mcp4.begin(uint8_t mcp23017_address); // Pin 148 - 163 \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h b/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h new file mode 100644 index 00000000..8dfa9598 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h @@ -0,0 +1,78 @@ + +#ifndef S_mcp_23017_h +#define S_mcp_23017_h + +#include +#include +#include + + + MCP23017 mcp1; + MCP23017 mcp2; + MCP23017 mcp3; + MCP23017 mcp4; + + +class CustomControl : public Supla::Io { + public: + void customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val) { + if (pin < 100) { + return ::digitalWrite(pin,val); + } + if ((pin > 99) && (pin < 116)){ + mcp1.digitalWrite(pin - 100, val); + return; + } + if ((pin > 115) && (pin < 132)){ + mcp2.digitalWrite(pin - 116, val); + } + if ((pin > 131) && (pin < 148)){ + mcp3.digitalWrite(pin - 132, val); + return; + } + if ((pin > 147) && (pin < 164)){ + mcp4.digitalWrite(pin - 148, val); + return; + } + } + int customDigitalRead(int channelNumber, uint8_t pin) { + if (pin < 100){ + return ::digitalRead(pin); + } + if ((pin > 99)&& (pin < 116)){ + return mcp1.digitalRead(pin - 100); + } + if ((pin > 115)&& (pin < 132)){ + return mcp2.digitalRead(pin - 116); + } + if ((pin > 131)&& (pin < 148)){ + return mcp3.digitalRead(pin - 132); + } + if ((pin > 147)&& (pin < 164)){ + return mcp4.digitalRead(pin - 148); + } + } + void customPinMode(int channelNumber, uint8_t pin, uint8_t mode) { + (void)(channelNumber); + if (pin < 100){ + return ::pinMode(pin, mode); + } + if ((pin > 99)&& (pin < 116)){ + mcp1.pinMode(pin - 100, mode); + } + if ((pin > 115)&& (pin < 132)){ + mcp2.pinMode(pin - 116, mode); + } + if ((pin > 131)&& (pin < 148)){ + mcp3.pinMode(pin - 132, mode); + } + if ((pin > 147)&& (pin < 164)){ + mcp4.pinMode(pin - 148, mode); + } + } + +}CustomControl; + + +#endif + diff --git a/lib/SuplaDevice/src/supla/control/bistable_relay.cpp b/lib/SuplaDevice/src/supla/control/bistable_relay.cpp new file mode 100644 index 00000000..d03f2aa4 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/bistable_relay.cpp @@ -0,0 +1,152 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "bistable_relay.h" + +using namespace Supla; +using namespace Control; + +#define STATE_ON_INIT_KEEP 2 + +BistableRelay::BistableRelay(int pin, + int statusPin, + bool statusPullUp, + bool statusHighIsOn, + bool highIsOn, + _supla_int_t functions) + : Relay(pin, highIsOn, functions), + statusPin(statusPin), + statusPullUp(statusPullUp), + statusHighIsOn(statusHighIsOn), + disarmTimeMs(0), + lastReadTime(0), + busy(false) { + stateOnInit = STATE_ON_INIT_KEEP; +} + +void BistableRelay::onInit() { + + if (statusPin >= 0) { + Supla::Io::pinMode(channel.getChannelNumber(), statusPin, statusPullUp ? INPUT_PULLUP : INPUT); + channel.setNewValue(isOn()); + } else { + channel.setNewValue(false); + } + + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + + if (stateOnInit == STATE_ON_INIT_ON || + stateOnInit == STATE_ON_INIT_RESTORED_ON) { + turnOn(); + } else if (stateOnInit == STATE_ON_INIT_OFF || + stateOnInit == STATE_ON_INIT_RESTORED_OFF) { + turnOff(); + } + + Supla::Io::pinMode(channel.getChannelNumber(), pin, OUTPUT); +} + +void BistableRelay::iterateAlways() { + Relay::iterateAlways(); + + if (statusPin >= 0 && (millis() - lastReadTime > 100)) { + lastReadTime = millis(); + channel.setNewValue(isOn()); + } + + if (busy && millis() - disarmTimeMs > 200) { + busy = false; + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + } +} + +int BistableRelay::handleNewValueFromServer( + TSD_SuplaChannelNewValue *newValue) { + // ignore new requests if we are in the middle of state change + if (busy) { + return 0; + } else { + return Relay::handleNewValueFromServer(newValue); + } +} + +void BistableRelay::turnOn(_supla_int_t duration) { + if (busy) { + return; + } + + durationMs = 0; + + if (keepTurnOnDurationMs) { + duration = storedTurnOnDurationMs; + } + // Change turn on requests duration to be at least 1 s + if (duration > 0 && duration < 1000) { + duration = 1000; + } + if (duration > 0) { + durationMs = duration; + durationTimestamp = millis(); + } + + if (isStatusUnknown() || !isOn()) { + internalToggle(); + } +} + +void BistableRelay::turnOff(_supla_int_t duration) { + (void)(duration); + if (busy) { + return; + } + + durationMs = 0; + + if (isStatusUnknown()) { + internalToggle(); + } else if (isOn()) { + internalToggle(); + } +} + +bool BistableRelay::isOn() { + if (isStatusUnknown()) { + return false; + } + return Supla::Io::digitalRead(channel.getChannelNumber(), statusPin) == + (statusHighIsOn ? HIGH : LOW); +} + +bool BistableRelay::isStatusUnknown() { + return (statusPin < 0); +} + +void BistableRelay::internalToggle() { + busy = true; + disarmTimeMs = millis(); + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +void BistableRelay::toggle(_supla_int_t duration) { + if (isOn() || isStatusUnknown()) { + turnOff(duration); + } else { + turnOn(duration); + } +} diff --git a/lib/SuplaDevice/src/supla/control/bistable_relay.h b/lib/SuplaDevice/src/supla/control/bistable_relay.h new file mode 100644 index 00000000..e75578fb --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/bistable_relay.h @@ -0,0 +1,67 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* BistableRelay + * This class can be used to controll bistable relay. + * Supla device will send short impulses (<0.5 s) on GPIO to toggle bistable + * relay state. + * Device does not have knowledge about the status of bistable relay, so it + * has to be read on a different GPIO (statusPin) + * This class can work without statusPin information, but Supla will lose + * information about status of bistable relay. + */ + +#ifndef _bistable_relay_h +#define _bistable_relay_h + +#include "relay.h" + +namespace Supla { +namespace Control { +class BistableRelay : public Relay { + public: + BistableRelay(int pin, + int statusPin = -1, + bool statusPullUp = true, + bool statusHighIsOn = true, + bool highIsOn = true, + _supla_int_t functions = + (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)); + void onInit(); + void iterateAlways(); + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + void turnOn(_supla_int_t duration = 0); + void turnOff(_supla_int_t duration = 0); + void toggle(_supla_int_t duration = 0); + + virtual bool isOn(); + bool isStatusUnknown(); + + protected: + void internalToggle(); + + int statusPin; + bool statusPullUp; + bool statusHighIsOn; + unsigned long disarmTimeMs; + unsigned long lastReadTime; + bool busy; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp new file mode 100644 index 00000000..9401633f --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp @@ -0,0 +1,76 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "bistable_roller_shutter.h" +#include + +namespace Supla { +namespace Control { + +BistableRollerShutter::BistableRollerShutter(int pinUp, int pinDown, bool highIsOn) + : RollerShutter(pinUp, pinDown, highIsOn), + activeBiRelay(false), + toggleTime(0) { +} + +void BistableRollerShutter::stopMovement() { + if (currentDirection == UP_DIR) { + relayUpOn(); + } else if (currentDirection == DOWN_DIR) { + relayDownOn(); + } + currentDirection = STOP_DIR; + doNothingTime = millis(); + // Schedule save in 5 s after stop movement of roller shutter + Supla::Storage::ScheduleSave(5000); +} + +void BistableRollerShutter::relayDownOn() { + activeBiRelay = true; + toggleTime = millis(); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinDown, highIsOn ? HIGH : LOW); +} + +void BistableRollerShutter::relayUpOn() { + activeBiRelay = true; + toggleTime = millis(); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinUp, highIsOn ? HIGH : LOW); +} + +void BistableRollerShutter::relayDownOff() { + activeBiRelay = false; + Supla::Io::digitalWrite(channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); +} + +void BistableRollerShutter::relayUpOff() { + activeBiRelay = false; + Supla::Io::digitalWrite(channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); +} + +void BistableRollerShutter::onTimer() { + if (activeBiRelay && millis() - toggleTime > 200) { + switchOffRelays(); + doNothingTime = millis(); + } + if (activeBiRelay) { + return; + } + + RollerShutter::onTimer(); +} + +}; // namespace Control +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.h b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.h new file mode 100644 index 00000000..4d2fb735 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.h @@ -0,0 +1,46 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _bistable_roller_shutter_h +#define _bistable_roller_shutter_h + +#include "roller_shutter.h" + +namespace Supla { +namespace Control { +class BistableRollerShutter : public RollerShutter { + public: + BistableRollerShutter(int pinUp, int pinDown, bool highIsOn = true); + + void onTimer(); + + protected: + void stopMovement(); + void relayDownOn(); + void relayUpOn(); + void relayUpOff(); + void relayDownOff(); + + bool activeBiRelay; + unsigned long toggleTime; +}; + +}; // namespace Control +}; // namespace Supla + +#endif + + diff --git a/lib/SuplaDevice/src/supla/control/button.cpp b/lib/SuplaDevice/src/supla/control/button.cpp new file mode 100644 index 00000000..8fcb294e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/button.cpp @@ -0,0 +1,121 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "button.h" + + +Supla::Control::Button::Button(int pin, bool pullUp, bool invertLogic) + : SimpleButton(pin, pullUp, invertLogic), + holdTimeMs(0), + multiclickTimeMs(0), + lastStateChangeMs(0), + clickCounter(0), + holdSend(false), + bistable(false) { +} + +void Supla::Control::Button::onTimer() { + unsigned int timeDelta = millis() - lastStateChangeMs; + bool stateChanged = false; + int stateResult = state.update(); + if (stateResult == TO_PRESSED) { + stateChanged = true; + runAction(ON_PRESS); + runAction(ON_CHANGE); + } else if (stateResult == TO_RELEASED) { + stateChanged = true; + runAction(ON_RELEASE); + runAction(ON_CHANGE); + } + + if (stateChanged) { + lastStateChangeMs = millis(); + if (stateResult == TO_PRESSED || bistable) { + clickCounter++; + } + } + + if (!stateChanged) { + if (!bistable && stateResult == PRESSED) { + if (clickCounter <= 1 && holdTimeMs > 0 && timeDelta > holdTimeMs && !holdSend) { + runAction(ON_HOLD); + holdSend = true; + } + } else if (clickCounter > 0 && (bistable || stateResult == RELEASED)) { + if (multiclickTimeMs == 0) { + holdSend = false; + clickCounter = 0; + } + if (multiclickTimeMs > 0 && timeDelta > multiclickTimeMs) { + if (!holdSend) { + switch (clickCounter) { + case 1: + runAction(ON_CLICK_1); + break; + case 2: + runAction(ON_CLICK_2); + break; + case 3: + runAction(ON_CLICK_3); + break; + case 4: + runAction(ON_CLICK_4); + break; + case 5: + runAction(ON_CLICK_5); + break; + case 6: + runAction(ON_CLICK_6); + break; + case 7: + runAction(ON_CLICK_7); + break; + case 8: + runAction(ON_CLICK_8); + break; + case 9: + runAction(ON_CLICK_9); + break; + case 10: + runAction(ON_CLICK_10); + break; + } + if (clickCounter >= 10) { + runAction(ON_CRAZY_CLICKER); + } + } + holdSend = false; + clickCounter = 0; + } + } + } +} + +void Supla::Control::Button::setHoldTime(unsigned int timeMs) { + holdTimeMs = timeMs; + if (bistable) { + holdTimeMs = 0; + } +} + +void Supla::Control::Button::setMulticlickTime(unsigned int timeMs, bool bistableButton) { + multiclickTimeMs = timeMs; + bistable = bistableButton; + if (bistable) { + holdTimeMs = 0; + } +} + diff --git a/lib/SuplaDevice/src/supla/control/button.h b/lib/SuplaDevice/src/supla/control/button.h new file mode 100644 index 00000000..a48328d5 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/button.h @@ -0,0 +1,46 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _button_h +#define _button_h + +#include +#include "simple_button.h" + +namespace Supla { +namespace Control { + +class Button : public SimpleButton { + public: + Button(int pin, bool pullUp = false, bool invertLogic = false); + + void onTimer(); + void setHoldTime(unsigned int timeMs); + void setMulticlickTime(unsigned int timeMs, bool bistableButton = false); + + protected: + unsigned int holdTimeMs; + unsigned int multiclickTimeMs; + unsigned long lastStateChangeMs; + uint8_t clickCounter; + bool holdSend; + bool bistable; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/dimmer_base.h b/lib/SuplaDevice/src/supla/control/dimmer_base.h new file mode 100644 index 00000000..cd88b026 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/dimmer_base.h @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _dimmer_base_h +#define _dimmer_base_h + + +#include "rgbw_base.h" + +namespace Supla { +namespace Control { +class DimmerBase : public RGBWBase { + public: + DimmerBase() { + channel.setType(SUPLA_CHANNELTYPE_DIMMER); + channel.setDefault(SUPLA_CHANNELFNC_DIMMER); + } + +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp b/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp new file mode 100644 index 00000000..c889cb40 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "internal_pin_output.h" +#include "../events.h" + +Supla::Control::InternalPinOutput::InternalPinOutput(int pin, bool highIsOn) + : pin(pin), + highIsOn(highIsOn), + stateOnInit(STATE_ON_INIT_OFF), + storedTurnOnDurationMs(0), + durationTimestamp(0), + durationMs(0) { +} + +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDefaultStateOn() { + stateOnInit = STATE_ON_INIT_ON; + return *this; +} +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDefaultStateOff() { + stateOnInit = STATE_ON_INIT_OFF; + return *this; +} + +uint8_t Supla::Control::InternalPinOutput::pinOnValue() { + return highIsOn ? HIGH : LOW; +} + +uint8_t Supla::Control::InternalPinOutput::pinOffValue() { + return highIsOn ? LOW : HIGH; +} + +void Supla::Control::InternalPinOutput::turnOn(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + if (storedTurnOnDurationMs) { + durationMs = storedTurnOnDurationMs; + } + + runAction(Supla::ON_TURN_ON); + runAction(Supla::ON_CHANGE); + + Supla::Io::digitalWrite(pin, pinOnValue()); +} + +void Supla::Control::InternalPinOutput::turnOff(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + + runAction(Supla::ON_TURN_OFF); + runAction(Supla::ON_CHANGE); + + Supla::Io::digitalWrite(pin, pinOffValue()); +} + +bool Supla::Control::InternalPinOutput::isOn() { + return Supla::Io::digitalRead(pin) == pinOnValue(); +} + +void Supla::Control::InternalPinOutput::toggle(_supla_int_t duration) { + if (isOn()) { + turnOff(duration); + } else { + turnOn(duration); + } +} + +void Supla::Control::InternalPinOutput::handleAction(int event, int action) { + (void)(event); + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + } +} + +void Supla::Control::InternalPinOutput::onInit() { + if (stateOnInit == STATE_ON_INIT_ON) { + turnOn(); + } else { + turnOff(); + } + + Supla::Io::pinMode( + pin, OUTPUT); // pin mode is set after setting pin value in order to + // avoid problems with LOW trigger relays +} +void Supla::Control::InternalPinOutput::iterateAlways() { + if (durationMs && millis() - durationTimestamp > durationMs) { + toggle(); + } +} + +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDurationMs(_supla_int_t duration) { + storedTurnOnDurationMs = duration; + return *this; +} diff --git a/lib/SuplaDevice/src/supla/control/internal_pin_output.h b/lib/SuplaDevice/src/supla/control/internal_pin_output.h new file mode 100644 index 00000000..e1efe8bd --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/internal_pin_output.h @@ -0,0 +1,67 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _internal_pin_output_h +#define _internal_pin_output_h + +#include "../action_handler.h" +#include "../actions.h" +#include "../element.h" +#include "../io.h" +#include "../local_action.h" + +#define STATE_ON_INIT_OFF 0 +#define STATE_ON_INIT_ON 1 + +namespace Supla { +namespace Control { +class InternalPinOutput : public Element, + public ActionHandler, + public LocalAction { + public: + InternalPinOutput(int pin, bool highIsOn = true); + + virtual InternalPinOutput &setDefaultStateOn(); + virtual InternalPinOutput &setDefaultStateOff(); + virtual InternalPinOutput &setDurationMs(_supla_int_t duration); + + virtual uint8_t pinOnValue(); + virtual uint8_t pinOffValue(); + virtual void turnOn(_supla_int_t duration = 0); + virtual void turnOff(_supla_int_t duration = 0); + virtual bool isOn(); + virtual void toggle(_supla_int_t duration = 0); + + void handleAction(int event, int action); + + void onInit(); + void iterateAlways(); + + protected: + int pin; + bool highIsOn; + + int8_t stateOnInit; + + unsigned _supla_int_t durationMs; + unsigned _supla_int_t storedTurnOnDurationMs; + unsigned long durationTimestamp; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/light_relay.cpp b/lib/SuplaDevice/src/supla/control/light_relay.cpp new file mode 100644 index 00000000..747a3f69 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/light_relay.cpp @@ -0,0 +1,109 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "light_relay.h" + +using namespace Supla; +using namespace Control; + +#pragma pack(push, 1) +struct LightRelayStateData { + unsigned short lifespan; + _supla_int_t turnOnSecondsCumulative; +}; +#pragma pack(pop) + +LightRelay::LightRelay(int pin, bool highIsOn) + : Relay(pin, + highIsOn, + SUPLA_BIT_FUNC_LIGHTSWITCH | SUPLA_BIT_FUNC_STAIRCASETIMER), + lifespan(10000), + turnOnSecondsCumulative(0) { + channel.setFlag(SUPLA_CHANNEL_FLAG_LIGHTSOURCELIFESPAN_SETTABLE); +} + +void LightRelay::handleGetChannelState(TDSC_ChannelState &channelState) { + channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_LIGHTSOURCELIFESPAN | + SUPLA_CHANNELSTATE_FIELD_LIGHTSOURCEOPERATINGTIME; + channelState.LightSourceLifespan = lifespan; + channelState.LightSourceOperatingTime = turnOnSecondsCumulative; +} + +int LightRelay::handleCalcfgFromServer(TSD_DeviceCalCfgRequest *request) { + if (request && + request->Command == SUPLA_CALCFG_CMD_SET_LIGHTSOURCE_LIFESPAN) { + if (request->DataSize == sizeof(TCalCfg_LightSourceLifespan)) { + TCalCfg_LightSourceLifespan *config = + reinterpret_cast(request->Data); + + if (config->SetTime) { + lifespan = config->LightSourceLifespan; + } + if (config->ResetCounter) { + turnOnSecondsCumulative = 0; + } + + return SUPLA_CALCFG_RESULT_DONE; + } + } + + return SUPLA_CALCFG_RESULT_NOT_SUPPORTED; +} + +void LightRelay::onLoadState() { + LightRelayStateData data; + if (Supla::Storage::ReadState((unsigned char *)&data, sizeof(data))) { + lifespan = data.lifespan; + turnOnSecondsCumulative = data.turnOnSecondsCumulative; + + Serial.print(F("LightRelay[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("] settings restored from storage. Total lifespan: ")); + Serial.print(lifespan); + Serial.print(F(" h; current operating time: ")); + Serial.print(turnOnSecondsCumulative); + Serial.println(F(" s")); + } + Relay::onLoadState(); +} + +void LightRelay::onSaveState() { + LightRelayStateData data; + + data.lifespan = lifespan; + data.turnOnSecondsCumulative = turnOnSecondsCumulative; + Supla::Storage::WriteState((unsigned char *)&data, sizeof(data)); + Relay::onSaveState(); +} + +void LightRelay::turnOn(_supla_int_t duration) { + turnOnTimestamp = millis(); + Relay::turnOn(duration); +} + +void LightRelay::iterateAlways() { + if (isOn()) { + unsigned long currentMillis = millis(); + int seconds = (currentMillis - turnOnTimestamp) / 1000; + if (seconds > 0) { + turnOnTimestamp = + currentMillis - ((currentMillis - turnOnTimestamp) % 1000); + turnOnSecondsCumulative += seconds; + } + } + + Relay::iterateAlways(); +} diff --git a/lib/SuplaDevice/src/supla/control/light_relay.h b/lib/SuplaDevice/src/supla/control/light_relay.h new file mode 100644 index 00000000..7c93598a --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/light_relay.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _light_relay_h +#define _light_relay_h + +#include "relay.h" + +namespace Supla { +namespace Control { +class LightRelay : public Relay { + public: + LightRelay(int pin, bool highIsOn = true); + void handleGetChannelState(TDSC_ChannelState &channelState); + int handleCalcfgFromServer(TSD_DeviceCalCfgRequest *request); + void onLoadState(); + void onSaveState(); + void turnOn(_supla_int_t duration = 0); + void iterateAlways(); + + protected: + unsigned short lifespan; + _supla_int_t turnOnSecondsCumulative; + unsigned long turnOnTimestamp; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/pin_status_led.cpp b/lib/SuplaDevice/src/supla/control/pin_status_led.cpp new file mode 100644 index 00000000..dd4d89e3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pin_status_led.cpp @@ -0,0 +1,48 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "../io.h" +#include "pin_status_led.h" + +Supla::Control::PinStatusLed::PinStatusLed(uint8_t srcPin, + uint8_t outPin, + bool invert) + : srcPin(srcPin), outPin(outPin), invert(invert) { +} + +void Supla::Control::PinStatusLed::onInit() { + updatePin(); + Supla::Io::pinMode(outPin, OUTPUT); +} + +void Supla::Control::PinStatusLed::iterateAlways() { + updatePin(); +} + +void Supla::Control::PinStatusLed::setInvertedLogic(bool invertedLogic) { + invert = invertedLogic; + updatePin(); +} + +void Supla::Control::PinStatusLed::updatePin() { + int value = Supla::Io::digitalRead(srcPin); + value = invert ? !value : value; + if (value != Supla::Io::digitalRead(outPin)) { + Supla::Io::digitalWrite(outPin, value); + } +} diff --git a/lib/SuplaDevice/src/supla/control/pin_status_led.h b/lib/SuplaDevice/src/supla/control/pin_status_led.h new file mode 100644 index 00000000..39d2fc0a --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pin_status_led.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _pin_status_led_h +#define _pin_status_led_h + +#include "../element.h" + +namespace Supla { +namespace Control { +class PinStatusLed : public Element { + public: + PinStatusLed(uint8_t srcPin, uint8_t outPin, bool invert = false); + + void onInit(); + void iterateAlways(); + void setInvertedLogic(bool invertedLogic); + + protected: + void updatePin(); + + uint8_t srcPin; + uint8_t outPin; + bool invert; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/relay.cpp b/lib/SuplaDevice/src/supla/control/relay.cpp new file mode 100644 index 00000000..488e9b3c --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/relay.cpp @@ -0,0 +1,204 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Relay class + * This class is used to control any type of relay that can be controlled + * by setting LOW or HIGH output on selected GPIO. + */ + +#include + +#include "../actions.h" +#include "../io.h" +#include "../storage/storage.h" +#include "relay.h" + +using namespace Supla; +using namespace Control; + +Relay::Relay(int pin, bool highIsOn, _supla_int_t functions) + : pin(pin), + highIsOn(highIsOn), + stateOnInit(STATE_ON_INIT_OFF), + durationMs(0), + storedTurnOnDurationMs(0), + durationTimestamp(0), + keepTurnOnDurationMs(false) { + channel.setType(SUPLA_CHANNELTYPE_RELAY); + channel.setFuncList(functions); +} + +uint8_t Relay::pinOnValue() { + return highIsOn ? HIGH : LOW; +} + +uint8_t Relay::pinOffValue() { + return highIsOn ? LOW : HIGH; +} + +void Relay::onInit() { + if (stateOnInit == STATE_ON_INIT_ON || + stateOnInit == STATE_ON_INIT_RESTORED_ON) { + turnOn(); + } else { + turnOff(); + } + + Supla::Io::pinMode(channel.getChannelNumber(), pin, OUTPUT); // pin mode is set after setting pin value in order to + // avoid problems with LOW trigger relays +} + +void Relay::iterateAlways() { + if (durationMs && millis() - durationTimestamp > durationMs) { + toggle(); + } +} + +int Relay::handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + int result = -1; + if (newValue->value[0] == 1) { + if (keepTurnOnDurationMs) { + storedTurnOnDurationMs = newValue->DurationMS; + } + turnOn(newValue->DurationMS); + result = 1; + } else if (newValue->value[0] == 0) { + turnOff(0); // newValue->DurationMS may contain "turn on duration" which + // result in unexpected "turn on after duration ms received in + // turnOff message" + result = 1; + } + + return result; +} + +void Relay::turnOn(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + if (keepTurnOnDurationMs) { + durationMs = storedTurnOnDurationMs; + } + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); + + channel.setNewValue(true); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +void Relay::turnOff(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); + + channel.setNewValue(false); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +bool Relay::isOn() { + return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == + pinOnValue(); +} + +void Relay::toggle(_supla_int_t duration) { + if (isOn()) { + turnOff(duration); + } else { + turnOn(duration); + } +} + +void Relay::handleAction(int event, int action) { + (void)(event); + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + } +} + +void Relay::onSaveState() { + Supla::Storage::WriteState((unsigned char *)&storedTurnOnDurationMs, + sizeof(storedTurnOnDurationMs)); + bool enabled = false; + if (stateOnInit < 0) { + enabled = isOn(); + } + Supla::Storage::WriteState((unsigned char *)&enabled, sizeof(enabled)); +} + +void Relay::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&storedTurnOnDurationMs, + sizeof(storedTurnOnDurationMs)); + if (keepTurnOnDurationMs) { + Serial.print(F("Relay[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("]: restored durationMs: ")); + Serial.println(storedTurnOnDurationMs); + } else { + storedTurnOnDurationMs = 0; + } + + bool enabled = false; + Supla::Storage::ReadState((unsigned char *)&enabled, sizeof(enabled)); + if (stateOnInit < 0) { + Serial.print(F("Relay[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("]: restored relay state: ")); + if (enabled) { + Serial.println(F("ON")); + stateOnInit = STATE_ON_INIT_RESTORED_ON; + } else { + Serial.println(F("OFF")); + stateOnInit = STATE_ON_INIT_RESTORED_OFF; + } + } +} + +Relay &Relay::setDefaultStateOn() { + stateOnInit = STATE_ON_INIT_ON; + return *this; +} + +Relay &Relay::setDefaultStateOff() { + stateOnInit = STATE_ON_INIT_OFF; + return *this; +} + +Relay &Relay::setDefaultStateRestore() { + stateOnInit = STATE_ON_INIT_RESTORE; + return *this; +} + +Relay &Relay::keepTurnOnDuration(bool keep) { + keepTurnOnDurationMs = keep; + return *this; +} + +unsigned _supla_int_t Relay::getStoredTurnOnDurationMs() { + return storedTurnOnDurationMs; +} diff --git a/lib/SuplaDevice/src/supla/control/relay.h b/lib/SuplaDevice/src/supla/control/relay.h new file mode 100644 index 00000000..dcbf6f6e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/relay.h @@ -0,0 +1,86 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Relay class + * This class is used to control any type of relay that can be controlled + * by setting LOW or HIGH output on selected GPIO. + */ + +#ifndef _relay_h +#define _relay_h + +#include + +#include "../actions.h" +#include "../channel_element.h" +#include "../io.h" +#include "../storage/storage.h" +#include "../action_handler.h" +#include "../local_action.h" + +#define STATE_ON_INIT_RESTORED_OFF -3 +#define STATE_ON_INIT_RESTORED_ON -2 +#define STATE_ON_INIT_RESTORE -1 +#define STATE_ON_INIT_OFF 0 +#define STATE_ON_INIT_ON 1 + +namespace Supla { +namespace Control { +class Relay : public ChannelElement, public ActionHandler { + public: + Relay(int pin, + bool highIsOn = true, + _supla_int_t functions = (0xFF ^ + SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)); + + virtual Relay &setDefaultStateOn(); + virtual Relay &setDefaultStateOff(); + virtual Relay &setDefaultStateRestore(); + virtual Relay &keepTurnOnDuration(bool keep = true); + + virtual uint8_t pinOnValue(); + virtual uint8_t pinOffValue(); + virtual void turnOn(_supla_int_t duration = 0); + virtual void turnOff(_supla_int_t duration = 0); + virtual bool isOn(); + virtual void toggle(_supla_int_t duration = 0); + + void handleAction(int event, int action); + + void onInit(); + void onLoadState(); + void onSaveState(); + void iterateAlways(); + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + unsigned _supla_int_t getStoredTurnOnDurationMs(); + + protected: + int pin; + bool highIsOn; + + int8_t stateOnInit; + + unsigned _supla_int_t durationMs; + unsigned _supla_int_t storedTurnOnDurationMs; + unsigned long durationTimestamp; + bool keepTurnOnDurationMs; + +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/rgb_base.h b/lib/SuplaDevice/src/supla/control/rgb_base.h new file mode 100644 index 00000000..7bcc3936 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgb_base.h @@ -0,0 +1,36 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _rgb_base_h +#define _rgb_base_h + +#include "rgbw_base.h" + +namespace Supla { +namespace Control { +class RGBBase : public RGBWBase { + public: + RGBBase() { + channel.setType(SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); + channel.setDefault(SUPLA_CHANNELFNC_RGBLIGHTING); + } + +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/rgbw_base.cpp b/lib/SuplaDevice/src/supla/control/rgbw_base.cpp new file mode 100644 index 00000000..e43cb80a --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgbw_base.cpp @@ -0,0 +1,424 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include "rgbw_base.h" + +namespace Supla { +namespace Control { + +RGBWBase::RGBWBase() + : buttonStep(10), + curRed(0), + curGreen(255), + curBlue(0), + curColorBrightness(0), + curBrightness(0), + lastColorBrightness(100), + lastBrightness(100), + defaultDimmedBrightness(20), + dimIterationDirection(false), + iterationDelayCounter(0), + fadeEffect(1000), + hwRed(0), + hwGreen(255), + hwBlue(0), + hwColorBrightness(0), + hwBrightness(0), + lastTick(0) { + channel.setType(SUPLA_CHANNELTYPE_DIMMERANDRGBLED); + channel.setDefault(SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING); +} + + +void RGBWBase::setRGBW( + int red, int green, int blue, int colorBrightness, int brightness) { + // Store last non 0 brightness for turn on/toggle operations + if (colorBrightness > 0) { + lastColorBrightness = colorBrightness; + } + if (brightness > 0) { + lastBrightness = brightness; + } + + // Store current values + if (red >= 0) { + curRed = red; + } + if (green >= 0) { + curGreen = green; + } + if (blue >= 0) { + curBlue = blue; + } + if (colorBrightness >= 0) { + curColorBrightness = colorBrightness; + } + if (brightness >= 0) { + curBrightness = brightness; + } + + // If fade effect is disabled, then set new values to device directly + if (fadeEffect <= 0) { + setRGBWValueOnDevice( + curRed, curGreen, curBlue, curColorBrightness, curBrightness); + } + + // Send to Supla server new values + channel.setNewValue( + curRed, curGreen, curBlue, curColorBrightness, curBrightness); +} + +int RGBWBase::handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + uint8_t red = static_cast(newValue->value[4]); + uint8_t green = static_cast(newValue->value[3]); + uint8_t blue = static_cast(newValue->value[2]); + uint8_t colorBrightness = static_cast(newValue->value[1]); + uint8_t brightness = static_cast(newValue->value[0]); + + setRGBW(red, green, blue, colorBrightness, brightness); + + return -1; +} + +void RGBWBase::turnOn() { + setRGBW(-1, -1, -1, lastColorBrightness, lastBrightness); +} +void RGBWBase::turnOff() { + setRGBW(-1, -1, -1, 0, 0); +} + +void RGBWBase::toggle() { + setRGBW(-1, + -1, + -1, + curColorBrightness > 0 ? 0 : lastColorBrightness, + curBrightness > 0 ? 0 : lastBrightness); +} + +uint8_t RGBWBase::addWithLimit(int value, int addition, int limit) { + if (addition > 0 && value + addition > limit) { + return limit; + } + if (addition < 0 && value + addition < 0) { + return 0; + } + return value + addition; +} + +void RGBWBase::handleAction(int event, int action) { + (void)(event); + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + case BRIGHTEN_ALL: { + setRGBW(-1, + -1, + -1, + addWithLimit(curColorBrightness, buttonStep, 100), + addWithLimit(curBrightness, buttonStep, 100)); + break; + } + case DIM_ALL: { + setRGBW(-1, + -1, + -1, + addWithLimit(curColorBrightness, -buttonStep, 100), + addWithLimit(curBrightness, -buttonStep, 100)); + break; + } + case BRIGHTEN_R: { + setRGBW(addWithLimit(curRed, buttonStep), -1, -1, -1, -1); + break; + } + case DIM_R: { + setRGBW(addWithLimit(curRed, -buttonStep), -1, -1, -1, -1); + break; + } + case BRIGHTEN_G: { + setRGBW(-1, addWithLimit(curGreen, buttonStep), -1, -1, -1); + break; + } + case DIM_G: { + setRGBW(-1, addWithLimit(curGreen, -buttonStep), -1, -1, -1); + break; + } + case BRIGHTEN_B: { + setRGBW(-1, -1, addWithLimit(curBlue, buttonStep), -1, -1); + break; + } + case DIM_B: { + setRGBW(-1, -1, addWithLimit(curBlue, -buttonStep), -1, -1); + break; + } + case BRIGHTEN_W: { + setRGBW(-1, -1, -1, -1, addWithLimit(curBrightness, buttonStep, 100)); + break; + } + case DIM_W: { + setRGBW(-1, -1, -1, -1, addWithLimit(curBrightness, -buttonStep, 100)); + break; + } + case BRIGHTEN_RGB: { + setRGBW( + -1, -1, -1, addWithLimit(curColorBrightness, buttonStep, 100), -1); + break; + } + case DIM_RGB: { + setRGBW( + -1, -1, -1, addWithLimit(curColorBrightness, -buttonStep, 100), -1); + break; + } + case TURN_ON_RGB: { + setRGBW(-1, -1, -1, lastColorBrightness, -1); + break; + } + case TURN_OFF_RGB: { + setRGBW(-1, -1, -1, 0, -1); + break; + } + case TOGGLE_RGB: { + setRGBW(-1, -1, -1, curColorBrightness > 0 ? 0 : lastColorBrightness, -1); + break; + } + case TURN_ON_W: { + setRGBW(-1, -1, -1, -1, lastBrightness); + break; + } + case TURN_OFF_W: { + setRGBW(-1, -1, -1, -1, 0); + break; + } + case TOGGLE_W: { + setRGBW(-1, -1, -1, -1, curBrightness > 0 ? 0 : lastBrightness); + break; + } + case TURN_ON_RGB_DIMMED: { + if (curColorBrightness == 0) { + setRGBW(-1, -1, -1, defaultDimmedBrightness, -1); + } + break; + } + case TURN_ON_W_DIMMED: { + if (curBrightness == 0) { + setRGBW(-1, -1, -1, -1, defaultDimmedBrightness); + } + break; + } + case TURN_ON_ALL_DIMMED: { + if (curBrightness == 0 && curColorBrightness == 0) { + setRGBW(-1, -1, -1, defaultDimmedBrightness, defaultDimmedBrightness); + } + break; + } + case ITERATE_DIM_RGB: { + iterateDimmerRGBW(buttonStep, 0); + break; + } + case ITERATE_DIM_W: { + iterateDimmerRGBW(0, buttonStep); + break; + } + case ITERATE_DIM_ALL: { + iterateDimmerRGBW(buttonStep, buttonStep); + break; + } + } +} + +void RGBWBase::iterateDimmerRGBW(int rgbStep, int wStep) { + // if we iterate both RGB and W, then we should sync brightness + if (rgbStep > 0 && wStep > 0) { + curBrightness = curColorBrightness; + } + if (rgbStep > 0) { + if (curColorBrightness <= 10 && dimIterationDirection == true) { + iterationDelayCounter++; + if (iterationDelayCounter == 5) { + dimIterationDirection = false; + iterationDelayCounter = 0; + } else { + return; + } + } else if (curColorBrightness == 100 && dimIterationDirection == false) { + iterationDelayCounter++; + if (iterationDelayCounter == 5) { + dimIterationDirection = true; + iterationDelayCounter = 0; + } else { + return; + } + } + } else if (wStep > 0) { + if (curBrightness <= 10 && dimIterationDirection == true) { + iterationDelayCounter++; + if (iterationDelayCounter == 5) { + dimIterationDirection = false; + iterationDelayCounter = 0; + } else { + return; + } + } else if (curBrightness == 100 && dimIterationDirection == false) { + iterationDelayCounter++; + if (iterationDelayCounter == 5) { + dimIterationDirection = true; + iterationDelayCounter = 0; + } else { + return; + } + } + } + iterationDelayCounter = 0; + + // If direction is dim, then brightness step is set to negative + if (dimIterationDirection) { + rgbStep = -rgbStep; + wStep = -wStep; + } + + setRGBW(-1, + -1, + -1, + addWithLimit(curColorBrightness, rgbStep, 100), + addWithLimit(curBrightness, wStep, 100)); +} + +void RGBWBase::setStep(int step) { + buttonStep = step; +} + +void RGBWBase::setDefaultDimmedBrightness(int dimmedBrightness) { + defaultDimmedBrightness = dimmedBrightness; +} + +void RGBWBase::setFadeEffectTime(int timeMs) { + fadeEffect = timeMs; +} + +void RGBWBase::onTimer() { + // exit it fade effect is disabled + if (fadeEffect <= 0) { + return; + } + unsigned long timeDiff = millis() - lastTick; + lastTick = millis(); + + if (timeDiff > 0) { + int divider = fadeEffect / timeDiff; + if (divider <= 0) { + divider = 1; + } + + uint8_t rgbStep = 255 / divider; + uint8_t brightnessStep = 100 / divider; + bool valueChanged = false; + if (rgbStep < 1) { + rgbStep = 1; + } + if (brightnessStep < 1) { + brightnessStep = 1; + } + + if (curRed > hwRed) { + valueChanged = true; + hwRed += rgbStep; + if (hwRed > curRed) { + hwRed = curRed; + } + } else if (curRed < hwRed) { + valueChanged = true; + hwRed -= rgbStep; + if (hwRed < curRed) { + hwRed = curRed; + } + } + + if (curGreen > hwGreen) { + valueChanged = true; + hwGreen += rgbStep; + if (hwGreen > curGreen) { + hwGreen = curGreen; + } + } else if (curGreen < hwGreen) { + valueChanged = true; + hwGreen -= rgbStep; + if (hwGreen < curGreen) { + hwGreen = curGreen; + } + } + + if (curBlue > hwBlue) { + valueChanged = true; + hwBlue += rgbStep; + if (hwBlue > curBlue) { + hwBlue = curBlue; + } + } else if (curBlue < hwBlue) { + valueChanged = true; + hwBlue -= rgbStep; + if (hwBlue < curBlue) { + hwBlue = curBlue; + } + } + + if (curColorBrightness > hwColorBrightness) { + valueChanged = true; + hwColorBrightness += brightnessStep; + if (hwColorBrightness > curColorBrightness) { + hwColorBrightness = curColorBrightness; + } + } else if (curColorBrightness < hwColorBrightness) { + valueChanged = true; + hwColorBrightness -= brightnessStep; + if (hwColorBrightness < curColorBrightness) { + hwColorBrightness = curColorBrightness; + } + } + + if (curBrightness > hwBrightness) { + valueChanged = true; + hwBrightness += brightnessStep; + if (hwBrightness > curBrightness) { + hwBrightness = curBrightness; + } + } else if (curBrightness < hwBrightness) { + valueChanged = true; + hwBrightness -= brightnessStep; + if (hwBrightness < curBrightness) { + hwBrightness = curBrightness; + } + } + + if (valueChanged) { + setRGBWValueOnDevice(hwRed, hwGreen, hwBlue, hwColorBrightness, hwBrightness); + } + } +} + +}; // namespace Control +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/rgbw_base.h b/lib/SuplaDevice/src/supla/control/rgbw_base.h new file mode 100644 index 00000000..58b9d487 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgbw_base.h @@ -0,0 +1,85 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _rgbw_base_h +#define _rgbw_base_h + +#include +#include + +#include "../actions.h" +#include "../channel_element.h" +#include "../action_handler.h" + +namespace Supla { +namespace Control { +class RGBWBase : public ChannelElement, public ActionHandler { + public: + RGBWBase(); + + virtual void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) = 0; + + virtual void setRGBW( + int red, int green, int blue, int colorBrightness, int brightness); + + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + virtual void turnOn(); + virtual void turnOff(); + virtual void toggle(); + void handleAction(int event, int action); + void setStep(int step); + void setDefaultDimmedBrightness(int dimmedBrightness); + void setFadeEffectTime(int timeMs); + void onTimer(); + + void onInit() { + // Send to Supla server new values + channel.setNewValue( + curRed, curGreen, curBlue, curColorBrightness, curBrightness); + } + + protected: + uint8_t addWithLimit(int value, int addition, int limit = 255); + void iterateDimmerRGBW(int rgbStep, int wStep); + + uint8_t buttonStep; // 10 + uint8_t curRed; // 0 - 255 + uint8_t curGreen; // 0 - 255 + uint8_t curBlue; // 0 - 255 + uint8_t curColorBrightness; // 0 - 100 + uint8_t curBrightness; // 0 - 100 + uint8_t lastColorBrightness; // 0 - 100 + uint8_t lastBrightness; // 0 - 100 + uint8_t defaultDimmedBrightness; // 20 + bool dimIterationDirection; + int iterationDelayCounter; + int fadeEffect; + int hwRed; // 0 - 255 + int hwGreen; // 0 - 255 + int hwBlue; // 0 - 255 + int hwColorBrightness; // 0 - 100 + int hwBrightness; // 0 - 100 + unsigned long lastTick; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/roller_shutter.cpp b/lib/SuplaDevice/src/supla/control/roller_shutter.cpp new file mode 100644 index 00000000..237b54aa --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/roller_shutter.cpp @@ -0,0 +1,519 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "roller_shutter.h" + +namespace Supla { +namespace Control { + +#pragma pack(push, 1) +struct RollerShutterStateData { + uint32_t closingTimeMs; + uint32_t openingTimeMs; + int8_t currentPosition; // 0 - closed; 100 - opened +}; +#pragma pack(pop) + +RollerShutter::RollerShutter(int pinUp, int pinDown, bool highIsOn) + : closingTimeMs(0), + openingTimeMs(0), + calibrate(true), + comfortDownValue(20), + comfortUpValue(80), + newTargetPositionAvailable(false), + highIsOn(highIsOn), + currentDirection(STOP_DIR), + lastDirection(STOP_DIR), + currentPosition(UNKNOWN_POSITION), + targetPosition(STOP_POSITION), + pinUp(pinUp), + pinDown(pinDown), + lastMovementStartTime(0), + doNothingTime(0), + calibrationTime(0), + operationTimeout(0) { + lastPositionBeforeMovement = currentPosition; + channel.setType(SUPLA_CHANNELTYPE_RELAY); + channel.setDefault(SUPLA_CHANNELFNC_CONTROLLINGTHEROLLERSHUTTER); + channel.setFuncList(SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER); +} + +void RollerShutter::onInit() { + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); + Supla::Io::pinMode(channel.getChannelNumber(), pinUp, OUTPUT); + Supla::Io::pinMode(channel.getChannelNumber(), pinDown, OUTPUT); +} + +/* + * Protocol: + * value[0]: + * 0 - stop + * 1 - down + * 2 - up + * 10 - 110 - 0% - 100% + * + * time is send in 0.1 s. i.e. 105 -> 10.5 s + * time * 100 = gives time in ms + * + */ + +int RollerShutter::handleNewValueFromServer( + TSD_SuplaChannelNewValue *newValue) { + uint32_t newClosingTime = (newValue->DurationMS & 0xFFFF) * 100; + uint32_t newOpeningTime = ((newValue->DurationMS >> 16) & 0xFFFF) * 100; + + setOpenCloseTime(newClosingTime, newOpeningTime); + + char task = newValue->value[0]; + Serial.print(F("RollerShutter[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("] new value from server: ")); + if (task == 0) { + Serial.println(F("STOP")); + stop(); + } else if (task == 1) { + Serial.println(F("MOVE_DOWN")); + moveDown(); + } else if (task == 2) { + Serial.println(F("MOVE_UP")); + moveUp(); + } else if (task >= 10 && task <= 110) { + setTargetPosition(task - 10); + Serial.println(static_cast(task - 10)); + } + + return -1; +} + +void RollerShutter::setOpenCloseTime(uint32_t newClosingTimeMs, + uint32_t newOpeningTimeMs) { + if (newClosingTimeMs == 0) { + newClosingTimeMs = closingTimeMs; + } + if (newOpeningTimeMs == 0) { + newOpeningTimeMs = openingTimeMs; + } + + if (newClosingTimeMs != closingTimeMs || newOpeningTimeMs != openingTimeMs) { + closingTimeMs = newClosingTimeMs; + openingTimeMs = newOpeningTimeMs; + calibrate = true; + currentPosition = UNKNOWN_POSITION; + Serial.print(F("RollerShutter[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("] new time settings received. Opening time: ")); + Serial.print(openingTimeMs); + Serial.print(F(" ms; closing time: ")); + Serial.print(closingTimeMs); + Serial.println(F(" ms. Starting calibration...")); + } +} + +void RollerShutter::handleAction(int event, int action) { + (void)(event); + switch (action) { + case CLOSE_OR_STOP: { + if (inMove()) { + stop(); + } else { + close(); + } + break; + } + + case CLOSE: { + close(); + break; + } + case OPEN_OR_STOP: { + if (inMove()) { + stop(); + } else { + open(); + } + break; + } + + case OPEN: { + open(); + break; + } + + case COMFORT_DOWN_POSITION: { + setTargetPosition(comfortDownValue); + break; + } + + case COMFORT_UP_POSITION: { + setTargetPosition(comfortUpValue); + break; + } + + case STOP: { + stop(); + break; + } + + case STEP_BY_STEP: { + if (inMove()) { + stop(); + } else if (lastDirectionWasOpen()) { + close(); + } else if (lastDirectionWasClose()) { + open(); + } else if (currentPosition < 50) { + close(); + } else { + open(); + } + break; + } + + case MOVE_UP: { + moveUp(); + break; + } + + case MOVE_DOWN: { + moveDown(); + break; + } + + case MOVE_UP_OR_STOP: { + if (inMove()) { + stop(); + } else { + moveUp(); + } + break; + } + case MOVE_DOWN_OR_STOP: { + if (inMove()) { + stop(); + } else { + moveDown(); + } + break; + } + } +} + +void RollerShutter::close() { + setTargetPosition(100); +} + +void RollerShutter::open() { + setTargetPosition(0); +} + +void RollerShutter::moveDown() { + setTargetPosition(MOVE_DOWN_POSITION); +} + +void RollerShutter::moveUp() { + setTargetPosition(MOVE_UP_POSITION); +} + +void RollerShutter::stop() { + setTargetPosition(STOP_POSITION); +} + +void RollerShutter::setTargetPosition(int newPosition) { + targetPosition = newPosition; + newTargetPositionAvailable = true; + + // Negative targetPosition is either unknown or stop command, so we + // ignore it + if (targetPosition == MOVE_UP_POSITION) { + lastDirection = UP_DIR; + } else if (targetPosition == MOVE_DOWN_POSITION) { + lastDirection = DOWN_DIR; + } else if (targetPosition >= 0) { + if (targetPosition < currentPosition) { + lastDirection = UP_DIR; + } else if (targetPosition > currentPosition) { + lastDirection = DOWN_DIR; + } + } +} + +bool RollerShutter::lastDirectionWasOpen() { + return lastDirection == UP_DIR; +} + +bool RollerShutter::lastDirectionWasClose() { + return lastDirection == DOWN_DIR; +} + +bool RollerShutter::inMove() { + return currentDirection != STOP_DIR; +} + +void RollerShutter::stopMovement() { + switchOffRelays(); + currentDirection = STOP_DIR; + doNothingTime = millis(); + // Schedule save in 5 s after stop movement of roller shutter + Supla::Storage::ScheduleSave(5000); +} + +void RollerShutter::relayDownOn() { + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? HIGH : LOW); +} + +void RollerShutter::relayUpOn() { + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? HIGH : LOW); +} + +void RollerShutter::relayDownOff() { + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); +} + +void RollerShutter::relayUpOff() { + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); +} + +void RollerShutter::startClosing() { + currentDirection = DOWN_DIR; + relayUpOff(); // just to make sure + relayDownOn(); +} + +void RollerShutter::startOpening() { + currentDirection = UP_DIR; + relayDownOff(); // just to make sure + relayUpOn(); +} + +void RollerShutter::switchOffRelays() { + relayUpOff(); + relayDownOff(); +} + +void RollerShutter::onTimer() { + if (millis() - doNothingTime < + 300) { // doNothingTime time is used when we change + // direction of roller - to stop for a moment + // before enabling opposite direction + return; + } + + if (operationTimeout != 0 && + millis() - lastMovementStartTime > operationTimeout) { + setTargetPosition(STOP_POSITION); + operationTimeout = 0; + } + + if (targetPosition == STOP_POSITION && inMove()) { + stopMovement(); + calibrationTime = 0; + } + + if (calibrate && targetPosition == STOP_POSITION) { + return; + } else if (calibrate) { + // If calibrationTime is not set, then it means we should start calibration + if (calibrationTime == 0) { + // If roller shutter wasn't in move when calibration is requested, we + // select direction based on requested targetPosition + operationTimeout = 0; + if (targetPosition > 50 || targetPosition == MOVE_DOWN_POSITION) { + if (currentDirection == UP_DIR) { + stopMovement(); + } else if (currentDirection == STOP_DIR) { + Serial.println(F("Calibration: closing")); + calibrationTime = closingTimeMs; + lastMovementStartTime = millis(); + if (calibrationTime == 0) { + operationTimeout = 60000; + } + startClosing(); + } + } else { + if (currentDirection == DOWN_DIR) { + stopMovement(); + } else if (currentDirection == STOP_DIR) { + Serial.println(F("Calibration: opening")); + calibrationTime = openingTimeMs; + lastMovementStartTime = millis(); + if (calibrationTime == 0) { + operationTimeout = 60000; + } + startOpening(); + } + } + // + // Time used for calibaration is 10% higher then requested by user + calibrationTime *= 1.1; + if (calibrationTime > 0) { + Serial.print(F("Calibration time: ")); + Serial.println(calibrationTime); + } + } + + if (calibrationTime != 0 && + millis() - lastMovementStartTime > calibrationTime) { + Serial.println(F("Calibration done")); + calibrationTime = 0; + calibrate = false; + if (currentDirection == UP_DIR) { + currentPosition = 0; + } else { + currentPosition = 100; + } + stopMovement(); + } + + } else if (!newTargetPositionAvailable && + currentDirection != + STOP_DIR) { // no new command available and it is moving, + // just handle roller movement/status + if (currentDirection == UP_DIR && currentPosition > 0) { + int movementDistance = lastPositionBeforeMovement; + int timeRequired = (1.0 * openingTimeMs * movementDistance / 100.0); + float fractionOfMovemendDone = + (1.0 * (millis() - lastMovementStartTime) / timeRequired); + if (fractionOfMovemendDone > 1) { + fractionOfMovemendDone = 1; + } + currentPosition = lastPositionBeforeMovement - + movementDistance * fractionOfMovemendDone; + if (targetPosition >= 0 && currentPosition <= targetPosition) { + stopMovement(); + } + } else if (currentDirection == DOWN_DIR && currentPosition < 100) { + int movementDistance = 100 - lastPositionBeforeMovement; + int timeRequired = (1.0 * closingTimeMs * movementDistance / 100.0); + float fractionOfMovemendDone = + (1.0 * (millis() - lastMovementStartTime) / timeRequired); + if (fractionOfMovemendDone > 1) { + fractionOfMovemendDone = 1; + } + currentPosition = lastPositionBeforeMovement + + movementDistance * fractionOfMovemendDone; + if (targetPosition >= 0 && currentPosition >= targetPosition) { + stopMovement(); + } + } + + if (currentPosition > 100) { + currentPosition = 100; + } else if (currentPosition < 0) { + currentPosition = 0; + } + + } else if (newTargetPositionAvailable && targetPosition != STOP_POSITION) { + // new target state was set, let's handle it + int newDirection = STOP_DIR; + if (targetPosition == MOVE_UP_POSITION) { + newDirection = UP_DIR; + operationTimeout = 60000; + } else if (targetPosition == MOVE_DOWN_POSITION) { + newDirection = DOWN_DIR; + operationTimeout = 60000; + } else { + operationTimeout = 0; + int newMovementValue = targetPosition - currentPosition; + // 0 - 100 = -100 (move down); 50 - + // 20 = 30 (move up 30%), etc + if (newMovementValue > 0) { + newDirection = DOWN_DIR; // move down + } else if (newMovementValue < 0) { + newDirection = UP_DIR; // move up + } + } + // If new direction is the same as current move, then keep movin` + if (newDirection == currentDirection) { + newTargetPositionAvailable = false; + } else if (currentDirection == STOP_DIR) { // else start moving + newTargetPositionAvailable = false; + lastPositionBeforeMovement = currentPosition; + lastMovementStartTime = millis(); + if (newDirection == DOWN_DIR) { + startClosing(); + } else { + startOpening(); + } + } else { // else stop before changing direction + stopMovement(); + } + } + // if (newCurrentPosition != currentPosition) { + // currentPosition = newCurrentPosition; + channel.setNewValue(static_cast<_supla_int_t>( + currentPosition)); // value set on channel will be send to server + // during iterateConnected() execution + // } +} + +void RollerShutter::configComfortUpValue(uint8_t position) { + comfortUpValue = position; + if (comfortUpValue > 100) { + comfortUpValue = 100; + } +} + +void RollerShutter::configComfortDownValue(uint8_t position) { + comfortDownValue = position; + if (comfortDownValue > 100) { + comfortDownValue = 100; + } +} + +void RollerShutter::onLoadState() { + RollerShutterStateData data = RollerShutterStateData(); + if (Supla::Storage::ReadState((unsigned char *)&data, sizeof(data))) { + closingTimeMs = data.closingTimeMs; + openingTimeMs = data.openingTimeMs; + currentPosition = data.currentPosition; + if (currentPosition >= 0) { + calibrate = false; + } + Serial.print(F("RollerShutter[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("] settings restored from storage. Opening time: ")); + Serial.print(openingTimeMs); + Serial.print(F(" ms; closing time: ")); + Serial.print(closingTimeMs); + Serial.print(F(" ms. Position: ")); + Serial.println(currentPosition); + } +} + +void RollerShutter::onSaveState() { + RollerShutterStateData data; + data.closingTimeMs = closingTimeMs; + data.openingTimeMs = openingTimeMs; + data.currentPosition = currentPosition; + + Supla::Storage::WriteState((unsigned char *)&data, sizeof(data)); +} + +int RollerShutter::getCurrentPosition() { + return currentPosition; +} + +}; // namespace Control +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/roller_shutter.h b/lib/SuplaDevice/src/supla/control/roller_shutter.h new file mode 100644 index 00000000..d9a55233 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/roller_shutter.h @@ -0,0 +1,108 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _roller_shutter_h +#define _roller_shutter_h + +#include + +#include "../io.h" +#include "../channel_element.h" +#include "../action_handler.h" +#include "../actions.h" + +#define UNKNOWN_POSITION -1 +#define STOP_POSITION -2 +#define MOVE_UP_POSITION -3 +#define MOVE_DOWN_POSITION -4 + +namespace Supla { +namespace Control { + +enum Directions { STOP_DIR, DOWN_DIR, UP_DIR }; + +class RollerShutter : public ChannelElement, public ActionHandler { + public: + RollerShutter(int pinUp, int pinDown, bool highIsOn = true); + + int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + void handleAction(int event, int action); + + void close(); // Sets target position to 100% + void open(); // Sets target position to 0% + void stop(); // Stop motor + void moveUp(); // start opening roller shutter regardless of its position (keep motor going up) + void moveDown(); // starts closing roller shutter regardless of its position (keep motor going down) + void setTargetPosition(int newPosition); + int getCurrentPosition(); + + void configComfortUpValue(uint8_t position); + void configComfortDownValue(uint8_t position); + + + void onInit(); + void onTimer(); + void onLoadState(); + void onSaveState(); + + protected: + virtual void stopMovement(); + virtual void relayDownOn(); + virtual void relayUpOn(); + virtual void relayDownOff(); + virtual void relayUpOff(); + virtual void startClosing(); + virtual void startOpening(); + virtual void switchOffRelays(); + void setOpenCloseTime(uint32_t newClosingTimeMs, uint32_t newOpeningTimeMs); + + bool lastDirectionWasOpen(); + bool lastDirectionWasClose(); + bool inMove(); + + uint32_t closingTimeMs; + uint32_t openingTimeMs; + bool calibrate; // set to true when new closing/opening time is given - calibration is done to sync roller shutter position + + uint8_t comfortDownValue; + uint8_t comfortUpValue; + + bool newTargetPositionAvailable; + + bool highIsOn; + + uint8_t currentDirection; // stop, up, down + uint8_t lastDirection; + + int8_t currentPosition; // 0 - closed; 100 - opened + int8_t targetPosition; // 0-100 + int8_t lastPositionBeforeMovement; // 0-100 + + int pinUp; + int pinDown; + + unsigned long lastMovementStartTime; + unsigned long doNothingTime; + unsigned long calibrationTime; + unsigned long operationTimeout; +}; + +}; // namespace Control +}; // namespace Supla + +#endif + + diff --git a/lib/SuplaDevice/src/supla/control/sequence_button.cpp b/lib/SuplaDevice/src/supla/control/sequence_button.cpp new file mode 100644 index 00000000..f86dd4c8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/sequence_button.cpp @@ -0,0 +1,137 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sequence_button.h" +#include + +Supla::Control::SequenceButton::SequenceButton(int pin, bool pullUp, bool invertLogic) + : SimpleButton(pin, pullUp, invertLogic), + lastStateChangeMs(0), + longestSequenceTimeDeltaWithMargin(800), + clickCounter(0), + sequenceDetectecion(true), + currentSequence(), + matchSequence(), + margin(0.3) { +} + +void Supla::Control::SequenceButton::onTimer() { + unsigned int timeDelta = millis() - lastStateChangeMs; + bool stateChanged = false; + int stateResult = state.update(); + if (stateResult == TO_PRESSED) { + stateChanged = true; + runAction(ON_PRESS); + runAction(ON_CHANGE); + } else if (stateResult == TO_RELEASED) { + stateChanged = true; + runAction(ON_RELEASE); + runAction(ON_CHANGE); + } + + if (stateChanged) { + lastStateChangeMs = millis(); + if (clickCounter > 0 && clickCounter < SEQUENCE_MAX_SIZE + 1) { + currentSequence.data[clickCounter - 1] = timeDelta; + } + if (clickCounter == 0) { + memset(currentSequence.data, 0, sizeof(uint16_t [SEQUENCE_MAX_SIZE])); + } + clickCounter++; + } + + if (!stateChanged) { + if (clickCounter > 0 && stateResult == RELEASED) { + if (timeDelta > longestSequenceTimeDeltaWithMargin) { + Serial.print(F("Recorded sequence: ")); + if (clickCounter > 31) { + clickCounter = 31; + } + for (int i = 0; i < clickCounter - 1; i++) { + Serial.print(currentSequence.data[i]); + Serial.print(F(", ")); + } + Serial.println(); + + int matchSequenceSize = 0; + for (; matchSequenceSize < 30; matchSequenceSize++) { + if (matchSequence.data[matchSequenceSize] == 0) { + break; + } + } + if (matchSequenceSize != clickCounter - 1) { + Serial.println(F("Sequence size doesn't match")); + runAction(ON_SEQUENCE_DOESNT_MATCH); + } else { + bool match = true; + for (int i = 0; i < clickCounter - 1; i++) { + unsigned int marginValue = calculateMargin(matchSequence.data[i]); + if (!(matchSequence.data[i] - marginValue <= currentSequence.data[i] && matchSequence.data[i] + marginValue >= currentSequence.data[i])) { + match = false; + break; + } + } + if (match) { + Serial.println(F("Sequence match")); + runAction(ON_SEQUENCE_MATCH); + } else { + Serial.println(F("Sequence doesn't match")); + runAction(ON_SEQUENCE_DOESNT_MATCH); + } + + } + clickCounter = 0; + } + } + } + +} + +unsigned int Supla::Control::SequenceButton::calculateMargin(unsigned int value) { + unsigned int result = margin*value; + if (result < 20) { + result = 20; + } + return result; +} + +void Supla::Control::SequenceButton::setMargin(float newMargin) { + margin = newMargin; + if (margin < 0) { + margin = 0; + } else if (margin > 1) { + margin = 1; + } +} + +void Supla::Control::SequenceButton::setSequence(uint16_t *sequence) { + uint16_t maxValue = 0; + for (int i = 0; i < SEQUENCE_MAX_SIZE; i++) { + matchSequence.data[i] = sequence[i]; + if (sequence[i] > maxValue) { + maxValue = sequence[i]; + } + } + maxValue *= 1.5; + if (maxValue < 500) { + maxValue = 500; + } + longestSequenceTimeDeltaWithMargin = maxValue; +} + +void Supla::Control::SequenceButton::getLastRecordedSequence(uint16_t *sequence) { + memcpy(sequence, currentSequence.data, sizeof(uint16_t [SEQUENCE_MAX_SIZE])); +} diff --git a/lib/SuplaDevice/src/supla/control/sequence_button.h b/lib/SuplaDevice/src/supla/control/sequence_button.h new file mode 100644 index 00000000..d4e12b12 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/sequence_button.h @@ -0,0 +1,58 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _sequence_button_h +#define _sequence_button_h + +#include "button.h" + +namespace Supla { +namespace Control { + +#define SEQUENCE_MAX_SIZE 30 + +struct ClickSequence { + uint16_t data[SEQUENCE_MAX_SIZE]; +}; + +class SequenceButton : public SimpleButton { + public: + SequenceButton(int pin, bool pullUp = false, bool invertLogic = false); + + void onTimer(); + + void setSequence(uint16_t *sequence); + void setMargin(float); + void getLastRecordedSequence(uint16_t *sequence); + + protected: + unsigned long lastStateChangeMs; + uint16_t longestSequenceTimeDeltaWithMargin; + uint8_t clickCounter; + bool sequenceDetectecion; + + ClickSequence currentSequence; + ClickSequence matchSequence; + + float margin; + unsigned int calculateMargin(unsigned int); + +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/simple_button.cpp b/lib/SuplaDevice/src/supla/control/simple_button.cpp new file mode 100644 index 00000000..7714f675 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/simple_button.cpp @@ -0,0 +1,108 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "button.h" +#include "../io.h" + +Supla::Control::ButtonState::ButtonState(int pin, bool pullUp, bool invertLogic) + : debounceTimeMs(0), + filterTimeMs(0), + debounceDelayMs(50), + swNoiseFilterDelayMs(20), + pin(pin), + newStatusCandidate(LOW), + prevState(LOW), + pullUp(pullUp), + invertLogic(invertLogic) { +} + +int Supla::Control::ButtonState::update() { + if (millis() - debounceTimeMs > debounceDelayMs) { + int currentState = Supla::Io::digitalRead(pin); + if (currentState != prevState) { + // If status is changed, then make sure that it will be kept at + // least swNoiseFilterDelayMs ms to avoid noise + if (currentState != newStatusCandidate) { + newStatusCandidate = currentState; + filterTimeMs = millis(); + } else if (millis() - filterTimeMs > swNoiseFilterDelayMs) { + // If new status is kept at least swNoiseFilterDelayMs ms, then apply + // change of status + debounceTimeMs = millis(); + prevState = currentState; + if (currentState == valueOnPress()) { + return TO_PRESSED; + } else { + return TO_RELEASED; + } + } + } else { + // If current status is the same as prevState, then reset + // new status candidate + newStatusCandidate = prevState; + } + } + if (prevState == valueOnPress()) { + return PRESSED; + } else { + return RELEASED; + } +} + +Supla::Control::SimpleButton::SimpleButton(int pin, bool pullUp, bool invertLogic) + : state(pin, pullUp, invertLogic) { +} + +void Supla::Control::SimpleButton::onTimer() { + int stateResult = state.update(); + if (stateResult == TO_PRESSED) { + runAction(ON_PRESS); + runAction(ON_CHANGE); + } else if (stateResult == TO_RELEASED) { + runAction(ON_RELEASE); + runAction(ON_CHANGE); + } +} + +void Supla::Control::SimpleButton::onInit() { + state.init(); +} + +void Supla::Control::ButtonState::init() { + Supla::Io::pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); + prevState = Supla::Io::digitalRead(pin); + newStatusCandidate = prevState; +} + +int Supla::Control::ButtonState::valueOnPress() { + return invertLogic ? LOW : HIGH; +} + +void Supla::Control::SimpleButton::setSwNoiseFilterDelay(unsigned int newDelayMs) { + state.setSwNoiseFilterDelay(newDelayMs); +} +void Supla::Control::ButtonState::setSwNoiseFilterDelay(unsigned int newDelayMs) { + swNoiseFilterDelayMs = newDelayMs; +} + +void Supla::Control::SimpleButton::setDebounceDelay(unsigned int newDelayMs) { + state.setDebounceDelay(newDelayMs); +} + +void Supla::Control::ButtonState::setDebounceDelay(unsigned int newDelayMs) { + debounceDelayMs = newDelayMs; +} + diff --git a/lib/SuplaDevice/src/supla/control/simple_button.h b/lib/SuplaDevice/src/supla/control/simple_button.h new file mode 100644 index 00000000..8b66fb7e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/simple_button.h @@ -0,0 +1,71 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _simple_button_h +#define _simple_button_h + +#include + +#include "../element.h" +#include "../events.h" +#include "../local_action.h" + +namespace Supla { +namespace Control { + +enum StateResults {PRESSED, RELEASED, TO_PRESSED, TO_RELEASED}; + +class ButtonState { + public: + ButtonState(int pin, bool pullUp, bool invertLogic); + int update(); + void init(); + + void setSwNoiseFilterDelay(unsigned int newDelayMs); + void setDebounceDelay(unsigned int newDelayMs); + + protected: + int valueOnPress(); + + unsigned long debounceTimeMs; + unsigned long filterTimeMs; + unsigned int debounceDelayMs; + unsigned int swNoiseFilterDelayMs; + int pin; + int8_t newStatusCandidate; + int8_t prevState; + bool pullUp; + bool invertLogic; +}; + +class SimpleButton : public Element, + public LocalAction { + public: + SimpleButton(int pin, bool pullUp = false, bool invertLogic = false); + + void onTimer(); + void onInit(); + void setSwNoiseFilterDelay(unsigned int newDelayMs); + void setDebounceDelay(unsigned int newDelayMs); + + protected: + ButtonState state; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/virtual_relay.cpp b/lib/SuplaDevice/src/supla/control/virtual_relay.cpp new file mode 100644 index 00000000..9cfc3ea8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/virtual_relay.cpp @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "virtual_relay.h" + +Supla::Control::VirtualRelay::VirtualRelay(_supla_int_t functions) + : Relay(-1, true, functions), state(false) { +} + +void Supla::Control::VirtualRelay::onInit() { + if (stateOnInit == STATE_ON_INIT_ON || + stateOnInit == STATE_ON_INIT_RESTORED_ON) { + turnOn(); + } else { + turnOff(); + } +} + +void Supla::Control::VirtualRelay::turnOn(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + if (keepTurnOnDurationMs) { + durationMs = storedTurnOnDurationMs; + } + state = true; + + channel.setNewValue(state); + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +void Supla::Control::VirtualRelay::turnOff(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + state = false; + + channel.setNewValue(state); + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +bool Supla::Control::VirtualRelay::isOn() { + return state; +} diff --git a/lib/SuplaDevice/src/supla/control/virtual_relay.h b/lib/SuplaDevice/src/supla/control/virtual_relay.h new file mode 100644 index 00000000..ee51affd --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/virtual_relay.h @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _virtual_relay_h +#define _virtual_relay_h + +#include "relay.h" + +namespace Supla { +namespace Control { +class VirtualRelay : public Relay { + public: + VirtualRelay(_supla_int_t functions = + (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)); + + void onInit(); + void turnOn(_supla_int_t duration = 0); + void turnOff(_supla_int_t duration = 0); + bool isOn(); + + protected: + bool state; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/crc16.h b/lib/SuplaDevice/src/supla/crc16.h new file mode 100644 index 00000000..5713f7bd --- /dev/null +++ b/lib/SuplaDevice/src/supla/crc16.h @@ -0,0 +1,14 @@ + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} diff --git a/lib/SuplaDevice/src/supla/element.cpp b/lib/SuplaDevice/src/supla/element.cpp new file mode 100644 index 00000000..70fc8a11 --- /dev/null +++ b/lib/SuplaDevice/src/supla/element.cpp @@ -0,0 +1,133 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "supla/element.h" + +namespace Supla { +Element *Element::firstPtr = nullptr; + +Element::Element() : nextPtr(nullptr) { + if (firstPtr == nullptr) { + firstPtr = this; + } else { + last()->nextPtr = this; + } +} + +Element::~Element() { + if (begin() == this) { + firstPtr = next(); + return; + } + + auto ptr = begin(); + while (ptr->next() != this) { + ptr = ptr->next(); + } + + ptr->nextPtr = ptr->next()->next(); +} + +Element *Element::begin() { + return firstPtr; +} + +Element *Element::last() { + Element *ptr = firstPtr; + while (ptr && ptr->nextPtr) { + ptr = ptr->nextPtr; + } + return ptr; +} + +Element *Element::getElementByChannelNumber(int channelNumber) { + Element *element = begin(); + while (element != nullptr && element->getChannelNumber() != channelNumber) { + element = element->next(); + } + + return element; +} + +Element *Element::next() { + return nextPtr; +} + +void Element::onInit(){}; + +void Element::onLoadState(){}; + +void Element::onSaveState(){}; + +void Element::iterateAlways(){}; + +bool Element::iterateConnected(void *srpc) { + Channel *channel = getChannel(); + if (channel && channel->isUpdateReady() && + millis() - channel->lastCommunicationTimeMs > 100) { + channel->lastCommunicationTimeMs = millis(); + channel->sendUpdate(srpc); + return false; + } + return true; +} + +void Element::onTimer(){}; + +void Element::onFastTimer(){}; + +int Element::handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + (void)(newValue); + return -1; +} + +int Element::getChannelNumber() { + int result = -1; + Channel *channel = getChannel(); + if (channel) { + result = channel->getChannelNumber(); + } + return result; +} + +Channel *Element::getChannel() { + return nullptr; +} + +Channel *Element::getSecondaryChannel() { + return nullptr; +} + +void Element::handleGetChannelState(TDSC_ChannelState &channelState) { + (void)(channelState); + return; +} + +int Element::handleCalcfgFromServer(TSD_DeviceCalCfgRequest *request) { + (void)(request); + return SUPLA_CALCFG_RESULT_NOT_SUPPORTED; +} + +Element & Element::disableChannelState() { + if (getChannel()) { + getChannel()->unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); + } + return *this; +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/element.h b/lib/SuplaDevice/src/supla/element.h new file mode 100644 index 00000000..34e48cc5 --- /dev/null +++ b/lib/SuplaDevice/src/supla/element.h @@ -0,0 +1,92 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _element_h +#define _element_h + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#include +#endif +#include "channel.h" + +namespace Supla { + +class Element { + public: + Element(); + virtual ~Element(); + static Element *begin(); + static Element *last(); + static Element *getElementByChannelNumber(int channelNumber); + Element *next(); + + // method called during SuplaDevice initialization. I.e. load initial state, + // initialize pins etc. + virtual void onInit(); + + // method called during Config initialization (i.e. read from EEPROM, FRAM). + // Called only if Storage class is configured + virtual void onLoadState(); + + // method called during periodically during SuplaDevice iteration + // Called only if Storage class is configured + virtual void onSaveState(); + + // method called on each SuplaDevice iteration (before Network layer + // iteration). When Device is connected, both iterateAlways() and + // iterateConnected() are called. + virtual void iterateAlways(); + + // method called on each Supla::Device iteration when Device is connected and + // registered to Supla server + virtual bool iterateConnected(void *srpc); + + // method called on timer interupt + // Include all actions that have to be executed periodically regardless of + // other SuplaDevice activities + virtual void onTimer(); + + // method called on fast timer interupt + // Include all actions that have to be executed periodically regardless of + // other SuplaDevice activities + virtual void onFastTimer(); + + // return value: + // -1 - don't send reply to server + // 0 - success==false + // 1 - success==true + virtual int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + + // Handles "get channel state" request from server + // channelState is prefilled with network and device status informations + virtual void handleGetChannelState(TDSC_ChannelState &channelState); + + virtual int handleCalcfgFromServer(TSD_DeviceCalCfgRequest *request); + + int getChannelNumber(); + virtual Channel *getChannel(); + virtual Channel *getSecondaryChannel(); + + Element &disableChannelState(); + + protected: + static Element *firstPtr; + Element *nextPtr; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/events.h b/lib/SuplaDevice/src/supla/events.h new file mode 100644 index 00000000..7bda4e79 --- /dev/null +++ b/lib/SuplaDevice/src/supla/events.h @@ -0,0 +1,47 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _events_h +#define _events_h + +namespace Supla { + +enum Event { +// Supla::Control::Button events + ON_PRESS, // Triggered on transition to valueOnPress() + ON_RELEASE, // Triggered on transition from valueOnPress() + ON_CHANGE, // Triggered on all transitions + ON_HOLD, // Triggered when button is hold + ON_CLICK_1, // ON_MULTI_x is triggered when multiclick is detected + ON_CLICK_2, + ON_CLICK_3, + ON_CLICK_4, + ON_CLICK_5, + ON_CLICK_6, + ON_CLICK_7, + ON_CLICK_8, + ON_CLICK_9, + ON_CLICK_10, + ON_CRAZY_CLICKER, // triggered on >= 10 clicks + ON_SEQUENCE_MATCH, // triggered by SequenceButton + ON_SEQUENCE_DOESNT_MATCH, // triggered by SequenceButton + ON_TURN_ON, + ON_TURN_OFF +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/io.cpp b/lib/SuplaDevice/src/supla/io.cpp new file mode 100644 index 00000000..6132709c --- /dev/null +++ b/lib/SuplaDevice/src/supla/io.cpp @@ -0,0 +1,88 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "io.h" + +#include + +namespace Supla { +void Io::pinMode(uint8_t pin, uint8_t mode) { + return pinMode(-1, pin, mode); +} + +int Io::digitalRead(uint8_t pin) { + return digitalRead(-1, pin); +} + +void Io::digitalWrite(uint8_t pin, uint8_t val) { + digitalWrite(-1, pin, val); +} + +void Io::pinMode(int channelNumber, uint8_t pin, uint8_t mode) { + if (ioInstance) { + ioInstance->customPinMode(channelNumber, pin, mode); + } else { + ::pinMode(pin, mode); + } +} + +int Io::digitalRead(int channelNumber, uint8_t pin) { + if (ioInstance) { + return ioInstance->customDigitalRead(channelNumber, pin); + } + return ::digitalRead(pin); +} + +void Io::digitalWrite(int channelNumber, uint8_t pin, uint8_t val) { + Serial.print(" **** Digital write["); + Serial.print(channelNumber); + Serial.print("], pin: "); + Serial.print(pin); + Serial.print("; value: "); + Serial.println(val); + if (ioInstance) { + ioInstance->customDigitalWrite(channelNumber, pin, val); + return; + } + ::digitalWrite(pin, val); +} + +Io *Io::ioInstance = 0; + +Io::Io() { + ioInstance = this; +} + +Io::~Io() { + ioInstance = nullptr; +} + +int Io::customDigitalRead(int channelNumber, uint8_t pin) { + (void)(channelNumber); + return ::digitalRead(pin); +} + +void Io::customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val) { + (void)(channelNumber); + ::digitalWrite(pin, val); +} + +void Io::customPinMode(int channelNumber, uint8_t pin, uint8_t mode) { + (void)(channelNumber); + ::pinMode(pin, mode); +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/io.h b/lib/SuplaDevice/src/supla/io.h new file mode 100644 index 00000000..918ceb0b --- /dev/null +++ b/lib/SuplaDevice/src/supla/io.h @@ -0,0 +1,50 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_io_h +#define _supla_io_h + +#include + +namespace Supla { +// This class can be used to override digitalRead and digitalWrite methods. +// If you want to add custom behavior i.e. during read/write from some +// digital pin, you can inherit from Supla::Io class, implement your +// own customDigitalRead and customDigitalWrite methods and create instance +// of this class. It will automatically register and SuplaDevice will use it. +// +// Example use: implement some additional logic, when relay state is +// changed. +class Io { + public: + static void pinMode(uint8_t pin, uint8_t mode); + static int digitalRead(uint8_t pin); + static void digitalWrite(uint8_t pin, uint8_t val); + static void pinMode(int channelNumber, uint8_t pin, uint8_t mode); + static int digitalRead(int channelNumber, uint8_t pin); + static void digitalWrite(int channelNumber, uint8_t pin, uint8_t val); + + static Io *ioInstance; + + Io(); + virtual ~Io(); + virtual void customPinMode(int channelNumber, uint8_t pin, uint8_t mode); + virtual int customDigitalRead(int channelNumber, uint8_t pin); + virtual void customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val); +}; +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/local_action.cpp b/lib/SuplaDevice/src/supla/local_action.cpp new file mode 100644 index 00000000..c7c70076 --- /dev/null +++ b/lib/SuplaDevice/src/supla/local_action.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "supla/local_action.h" + +namespace Supla { + +class ActionHandlerClient; + +class ActionHandlerClient { + public: + ActionHandlerClient() + : trigger(nullptr), + client(nullptr), + next(nullptr), + onEvent(0), + action(0) { + if (begin == nullptr) { + begin = this; + } else { + auto ptr = begin; + while (ptr->next) { + ptr = ptr->next; + } + ptr->next = this; + } + } + + ~ActionHandlerClient() { + if (begin == this) { + begin = next; + return; + } + + auto ptr = begin; + while (ptr->next != this) { + ptr = ptr->next; + } + + ptr->next = ptr->next->next; + } + + LocalAction *trigger; + ActionHandler *client; + ActionHandlerClient *next; + uint8_t onEvent; + uint8_t action; + static ActionHandlerClient *begin; +}; + +ActionHandlerClient *ActionHandlerClient::begin = nullptr; + +LocalAction::~LocalAction() { + auto ptr = ActionHandlerClient::begin; + while (ptr) { + if (ptr->trigger == this) { + auto tbdptr = ptr; + ptr = ptr->next; + delete tbdptr; + } else { + ptr = ptr->next; + } + } +} + +void LocalAction::addAction(int action, ActionHandler &client, int event) { + auto ptr = new ActionHandlerClient; + ptr->trigger = this; + ptr->client = &client; + ptr->onEvent = event; + ptr->action = action; +} + +void LocalAction::addAction(int action, ActionHandler *client, int event) { + addAction(action, *client, event); +} + +void LocalAction::runAction(int event) { + auto ptr = ActionHandlerClient::begin; + while (ptr) { + if (ptr->trigger == this && ptr->onEvent == event) { + ptr->client->handleAction(event, ptr->action); + } + ptr = ptr->next; + } +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/local_action.h b/lib/SuplaDevice/src/supla/local_action.h new file mode 100644 index 00000000..1d5ff129 --- /dev/null +++ b/lib/SuplaDevice/src/supla/local_action.h @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _local_action_h +#define _local_action_h + +#include +#include "action_handler.h" + +namespace Supla { + +class LocalAction { + public: + virtual ~LocalAction(); + virtual void addAction(int action, ActionHandler &client, int event); + virtual void addAction(int action, ActionHandler *client, int event); + + virtual void runAction(int event); + +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/network/ENC28J60.h b/lib/SuplaDevice/src/supla/network/ENC28J60.h new file mode 100644 index 00000000..e1241375 --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/ENC28J60.h @@ -0,0 +1,113 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef ENC28J60_h_ +#define ENC28J60_h_ + +#include +#include + +#include "../supla_lib_config.h" +#include "network.h" + +// TODO: change logs to supla_log + +namespace Supla { +class ENC28J60 : public Supla::Network { + public: + ENC28J60(uint8_t mac[6], IPAddress *ip = NULL) : Network(ip) { + memcpy(this->mac, mac, 6); + } + + int read(void *buf, int count) { + _supla_int_t size = client.available(); + + if (size > 0) { + if (size > count) size = count; + long readSize = client.read((uint8_t *)buf, size); +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Received: [")); + for (int i = 0; i < readSize; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + return readSize; + }; + + return -1; + } + + int write(void *buf, int count) { +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Sending: [")); + for (int i = 0; i < count; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + long sendSize = client.write((const uint8_t *)buf, count); + return sendSize; + } + + int connect(const char *server, int port = -1) { + int connectionPort = (port == -1 ? 2015 : port); + supla_log( + LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); + + return client.connect(server, connectionPort); + } + + bool connected() { + return client.connected(); + } + + void disconnect() { + client.stop(); + } + + bool isReady() { + return true; + } + + void setup(uint8_t mac[6]) { + Serial.println(F("Connecting to network...")); + if (useLocalIp) { + Ethernet.begin(mac, localIp); + } else { + Ethernet.begin(mac); + } + + Serial.print(F("localIP: ")); + Serial.println(Ethernet.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(Ethernet.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(Ethernet.gatewayIP()); + Serial.print(F("dnsServerIP: ")); + Serial.println(Ethernet.dnsServerIP()); + } + + protected: + EthernetClient client; + uint8_t mac[6]; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/network/esp32_wifi.h b/lib/SuplaDevice/src/supla/network/esp32_wifi.h new file mode 100644 index 00000000..e45ee3cc --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/esp32_wifi.h @@ -0,0 +1,28 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +// DEPRECATED: please use esp_wifi.h instead + +#ifndef esp32_wifi_h__ +#define esp32_wifi_h__ + +#include "esp_wifi.h" + +namespace Supla { +typedef ESPWifi ESP32Wifi; +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/network/esp_wifi.h b/lib/SuplaDevice/src/supla/network/esp_wifi.h new file mode 100644 index 00000000..adbeac69 --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/esp_wifi.h @@ -0,0 +1,277 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef esp_wifi_h__ +#define esp_wifi_h__ + +#include + +#ifdef ARDUINO_ARCH_ESP8266 +#include +#else +#include +#endif + +#include + +#include "../supla_lib_config.h" +#include "network.h" + +#define MAX_SSID_SIZE 32 +#define MAX_WIFI_PASSWORD_SIZE 64 + +#ifdef ARDUINO_ARCH_ESP8266 +WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; +#endif + +// TODO: change logs to supla_log + +namespace Supla { +class ESPWifi : public Supla::Network { + public: + ESPWifi(const char *wifiSsid = nullptr, + const char *wifiPassword = nullptr, + IPAddress *ip = nullptr) + : Network(ip), client(nullptr), isSecured(true), wifiConfigured(false) { + ssid[0] = '\0'; + password[0] = '\0'; + setSsid(wifiSsid); + setPassword(wifiPassword); +#ifdef ARDUINO_ARCH_ESP32 + enableSSL( + false); // current ESP32 WiFiClientSecure does not suport "setInsecure" +#endif + } + + int read(void *buf, int count) { + _supla_int_t size = client->available(); + + if (size > 0) { + if (size > count) size = count; + long readSize = client->read((uint8_t *)buf, size); +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Received: [")); + for (int i = 0; i < readSize; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + + return readSize; + } + return -1; + } + + int write(void *buf, int count) { +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Sending: [")); + for (int i = 0; i < count; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + long sendSize = client->write((const uint8_t *)buf, count); + return sendSize; + } + + int connect(const char *server, int port = -1) { + String message; + if (client == NULL) { + if (isSecured) { + message = "Secured connection"; + client = new WiFiClientSecure(); + if (fingerprint.length() > 0) { + message += " with certificate matching"; +#ifdef ARDUINO_ARCH_ESP8266 + ((WiFiClientSecure *)client)->setFingerprint(fingerprint.c_str()); +#else + message += " - NOT SUPPORTED ON ESP32 implmentation"; +#endif + } else { + message += " without certificate matching"; +#ifdef ARDUINO_ARCH_ESP8266 + ((WiFiClientSecure *)client)->setInsecure(); +#else + message += " - NOT SUPPORTED ON ESP32 implmentation"; +#endif + } + } else { + message = "unsecured connection"; + client = new WiFiClient(); + } + } + + int connectionPort = (isSecured ? 2016 : 2015); + if (port != -1) { + connectionPort = port; + } + + supla_log(LOG_DEBUG, + "Establishing %s with: %s (port: %d)", + message.c_str(), + server, + connectionPort); + + // static_cast(client)->setBufferSizes(512, 512); // + // EXPERIMENTAL + + bool result = client->connect(server, connectionPort); + + if (result && isSecured) { + if (!((WiFiClientSecure *)client)->verify(fingerprint.c_str(), server)) { + supla_log(LOG_DEBUG, "Provided certificates doesn't match!"); + client->stop(); + return false; + } + }; + + return result; + } + + bool connected() { + return (client != NULL) && client->connected(); + } + + bool isReady() { + return WiFi.status() == WL_CONNECTED; + } + + void disconnect() { + if (client != nullptr) { + client->stop(); + } + } + + // TODO: add handling of custom local ip + void setup() { + if (!wifiConfigured) { + wifiConfigured = true; +#ifdef ARDUINO_ARCH_ESP8266 + gotIpEventHandler = + WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { + (void)(event); + Serial.print(F("local IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(WiFi.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(WiFi.gatewayIP()); + long rssi = WiFi.RSSI(); + Serial.print(F("Signal strength (RSSI): ")); + Serial.print(rssi); + Serial.println(F(" dBm")); + }); + disconnectedEventHandler = WiFi.onStationModeDisconnected( + [](const WiFiEventStationModeDisconnected &event) { + (void)(event); + Serial.println(F("WiFi station disconnected")); + }); +#else + WiFiEventId_t event_gotIP = WiFi.onEvent( + [](WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.print(F("local IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(WiFi.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(WiFi.gatewayIP()); + long rssi = WiFi.RSSI(); + Serial.print(F("Signal Strength (RSSI): ")); + Serial.print(rssi); + Serial.println(F(" dBm")); + }, + WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); + + WiFiEventId_t event_disconnected = WiFi.onEvent( + [](WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.println(F("wifi Station disconnected")); + }, + WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); +#endif + Serial.print(F("WiFi: establishing connection with SSID: \"")); + Serial.print(ssid); + Serial.println(F("\"")); + WiFi.begin(ssid, password); + } else { + Serial.println(F("WiFi: resetting WiFi connection")); + if (client) { + delete client; + client = nullptr; + } + WiFi.reconnect(); + } + + yield(); + } + + void enableSSL(bool value) { + isSecured = value; + } + + void setServersCertFingerprint(String value) { + fingerprint = value; + } + + void setSsid(const char *wifiSsid) { + if (wifiSsid) { + strncpy(ssid, wifiSsid, MAX_SSID_SIZE); + } + } + + void setPassword(const char *wifiPassword) { + if (wifiPassword) { + strncpy(password, wifiPassword, MAX_WIFI_PASSWORD_SIZE); + } + } + + void setTimeout(int timeoutMs) { + if (client) { + client->setTimeout(timeoutMs); + } + } + + void fillStateData(TDSC_ChannelState &channelState) { + channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_IPV4 | + SUPLA_CHANNELSTATE_FIELD_MAC | + SUPLA_CHANNELSTATE_FIELD_WIFIRSSI | + SUPLA_CHANNELSTATE_FIELD_WIFISIGNALSTRENGTH; + channelState.IPv4 = WiFi.localIP(); + WiFi.macAddress(channelState.MAC); + int rssi = WiFi.RSSI(); + channelState.WiFiRSSI = rssi; + if (rssi > -50) { + channelState.WiFiSignalStrength = 100; + } else if (rssi <= -100) { + channelState.WiFiSignalStrength = 0; + } else { + channelState.WiFiSignalStrength = 2 * (rssi + 100); + } + } + + protected: + WiFiClient *client = NULL; + bool isSecured; + bool wifiConfigured; + String fingerprint; + char ssid[MAX_SSID_SIZE]; + char password[MAX_WIFI_PASSWORD_SIZE]; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/network/ethernet_shield.h b/lib/SuplaDevice/src/supla/network/ethernet_shield.h new file mode 100644 index 00000000..7d5e7e7a --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/ethernet_shield.h @@ -0,0 +1,130 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef ethernet_shield_h__ +#define ethernet_shield_h__ + +#include +#include + +#include "../supla_lib_config.h" +#include "network.h" + +// TODO: change logs to supla_log + +namespace Supla { +class EthernetShield : public Supla::Network { + public: + EthernetShield(uint8_t mac[6], IPAddress *ip = NULL) : Network(ip), isDeviceReady(false) { + memcpy(this->mac, mac, 6); + } + + int read(void *buf, int count) { + _supla_int_t size = client.available(); + if (size > 0) { + if (size > count) size = count; + long readSize = client.read((uint8_t *)buf, size); +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Received: [")); + for (int i = 0; i < readSize; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + return readSize; + }; + + return -1; + } + + int write(void *buf, int count) { +#ifdef SUPLA_COMM_DEBUG + Serial.print(F("Sending: [")); + for (int i = 0; i < count; i++) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + Serial.println(F("]")); +#endif + long sendSize = client.write((const uint8_t *)buf, count); + return sendSize; + } + + int connect(const char *server, int port = -1) { + int connectionPort = (port == -1 ? 2015 : port); + supla_log( + LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); + + return client.connect(server, connectionPort); + } + + bool connected() { + return client.connected(); + } + + void disconnect() { + client.stop(); + } + + bool isReady() { + return isDeviceReady; + } + + void setup() { + Serial.println(F("Connecting to network...")); + if (useLocalIp) { + Ethernet.begin(mac, localIp); + isDeviceReady = true; + } else { + int result = false; + result = Ethernet.begin(mac, 10000, 4000); + Serial.print(F("DHCP connection result: ")); + Serial.println(result); + isDeviceReady = result == 1 ? true : false; + } + + Serial.print(F("localIP: ")); + Serial.println(Ethernet.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(Ethernet.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(Ethernet.gatewayIP()); + Serial.print(F("dnsServerIP: ")); + Serial.println(Ethernet.dnsServerIP()); + } + + bool iterate() { + Ethernet.maintain(); + return true; + } + + void fillStateData(TDSC_ChannelState &channelState) { + channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_IPV4 | + SUPLA_CHANNELSTATE_FIELD_MAC; + channelState.IPv4 = Ethernet.localIP(); + Ethernet.MACAddress(channelState.MAC); + } + + protected: + EthernetClient client; + uint8_t mac[6]; + bool isDeviceReady; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/network/network.cpp b/lib/SuplaDevice/src/supla/network/network.cpp new file mode 100644 index 00000000..4cf98681 --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/network.cpp @@ -0,0 +1,218 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include + +#include "SuplaDevice.h" +#include "supla-common/log.h" +#include "supla-common/srpc.h" +#include "supla/element.h" +#include "supla/network/network.h" + +namespace Supla { + +Network *Network::netIntf = NULL; + +_supla_int_t data_read(void *buf, _supla_int_t count, void *userParams) { + (void)(userParams); + return Supla::Network::Read(buf, count); +} + +_supla_int_t data_write(void *buf, _supla_int_t count, void *userParams) { + (void)(userParams); + _supla_int_t r = Supla::Network::Write(buf, count); + if (r > 0) { + Network::Instance()->updateLastSent(); + } + return r; +} + +void message_received(void *_srpc, + unsigned _supla_int_t rr_id, + unsigned _supla_int_t call_type, + void *_sdc, + unsigned char proto_version) { + (void)(rr_id); + (void)(call_type); + (void)(proto_version); + TsrpcReceivedData rd; + char getDataResult; + + Network::Instance()->updateLastResponse(); + + if (SUPLA_RESULT_TRUE == (getDataResult = srpc_getdata(_srpc, &rd, 0))) { + switch (rd.call_type) { + case SUPLA_SDC_CALL_VERSIONERROR: + ((SuplaDeviceClass *)_sdc)->onVersionError(rd.data.sdc_version_error); + break; + case SUPLA_SD_CALL_REGISTER_DEVICE_RESULT: + ((SuplaDeviceClass *)_sdc) + ->onRegisterResult(rd.data.sd_register_device_result); + break; + case SUPLA_SD_CALL_CHANNEL_SET_VALUE: { + auto element = Supla::Element::getElementByChannelNumber( + rd.data.sd_channel_new_value->ChannelNumber); + if (element) { + int actionResult = + element->handleNewValueFromServer(rd.data.sd_channel_new_value); + if (actionResult != -1) { + srpc_ds_async_set_channel_result( + _srpc, + rd.data.sd_channel_new_value->ChannelNumber, + rd.data.sd_channel_new_value->SenderID, + actionResult); + } + } else { + Serial.print(F("Error: couldn't find element for a requested channel [")); + Serial.print(rd.data.sd_channel_new_value->ChannelNumber); + Serial.println(F("]")); + } + break; + } + case SUPLA_SDC_CALL_SET_ACTIVITY_TIMEOUT_RESULT: + ((SuplaDeviceClass *)_sdc) + ->channelSetActivityTimeoutResult( + rd.data.sdc_set_activity_timeout_result); + break; + case SUPLA_CSD_CALL_GET_CHANNEL_STATE: { + TDSC_ChannelState state; + memset(&state, 0, sizeof(TDSC_ChannelState)); + state.ReceiverID = rd.data.csd_channel_state_request->SenderID; + state.ChannelNumber = rd.data.csd_channel_state_request->ChannelNumber; + Network::Instance()->fillStateData(state); + ((SuplaDeviceClass *)_sdc)->fillStateData(state); + auto element = Supla::Element::getElementByChannelNumber( + rd.data.csd_channel_state_request->ChannelNumber); + if (element) { + element->handleGetChannelState(state); + } + srpc_csd_async_channel_state_result(_srpc, &state); + break; + } + case SUPLA_SDC_CALL_PING_SERVER_RESULT: + break; + + case SUPLA_DCS_CALL_GET_USER_LOCALTIME_RESULT: { + ((SuplaDeviceClass *)_sdc)->onGetUserLocaltimeResult(rd.data.sdc_user_localtime_result); + break; + } + case SUPLA_SD_CALL_DEVICE_CALCFG_REQUEST: { + TDS_DeviceCalCfgResult result; + result.ReceiverID = rd.data.sd_device_calcfg_request->SenderID; + result.ChannelNumber = rd.data.sd_device_calcfg_request->ChannelNumber; + result.Command = rd.data.sd_device_calcfg_request->Command; + result.Result = SUPLA_CALCFG_RESULT_NOT_SUPPORTED; + result.DataSize = 0; + + if (rd.data.sd_device_calcfg_request->SuperUserAuthorized != 1) { + result.Result = SUPLA_CALCFG_RESULT_UNAUTHORIZED; + } else { + auto element = Supla::Element::getElementByChannelNumber( + rd.data.sd_device_calcfg_request->ChannelNumber); + if (element) { + result.Result = element->handleCalcfgFromServer(rd.data.sd_device_calcfg_request); + } else { + Serial.print(F("Error: couldn't find element for a requested channel [")); + Serial.print(rd.data.sd_channel_new_value->ChannelNumber); + Serial.println(F("]")); + } + } + + srpc_ds_async_device_calcfg_result(_srpc, &result); + break; + } + default: + supla_log(LOG_DEBUG, "Received unknown message from server!"); + break; + } + + srpc_rd_free(&rd); + + } else if (getDataResult == SUPLA_RESULT_DATA_ERROR) { + supla_log(LOG_DEBUG, "DATA ERROR!"); + } +} + +Network::Network(IPAddress *ip) { + lastSentMs = 0; + srpc = NULL; + lastPingTimeMs = 0; + serverActivityTimeoutS = 30; + lastResponseMs = 0; + + netIntf = this; + + if (ip == NULL) { + useLocalIp = false; + } else { + useLocalIp = true; + localIp = *ip; + } +} + +bool Network::iterate() { + return false; +} + +void Network::updateLastSent() { + lastSentMs = millis(); +} + +void Network::updateLastResponse() { + lastResponseMs = millis(); +} + +void Network::setSrpc(void *_srpc) { + srpc = _srpc; +} + +bool Network::ping() { + _supla_int64_t _millis = millis(); + // If time from last response is longer than "server_activity_timeout + 10 s", + // then inform about failure in communication + if ((_millis - lastResponseMs) / 1000 >= (serverActivityTimeoutS + 10)) { + return false; + } else if (_millis - lastPingTimeMs >= 5000 && + ((_millis - lastResponseMs) / 1000 >= + (serverActivityTimeoutS - 5) || + (_millis - lastSentMs) / 1000 >= (serverActivityTimeoutS - 5))) { + lastPingTimeMs = _millis; + srpc_dcs_async_ping_server(srpc); + } + return true; +} + +void Network::clearTimeCounters() { + _supla_int64_t currentTime = millis(); + lastSentMs = currentTime; + lastResponseMs = currentTime; + lastPingTimeMs = currentTime; +} + +void Network::setActivityTimeout(_supla_int_t activityTimeoutSec) { + serverActivityTimeoutS = activityTimeoutSec; +} + +void Network::setTimeout(int timeoutMs) { + (void)(timeoutMs); + supla_log(LOG_DEBUG, "setTimeout is not implemented for this interface"); +} + +void Network::fillStateData(TDSC_ChannelState &channelState) { + (void)(channelState); + supla_log(LOG_DEBUG, "fillStateData is not implemented for this interface"); +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/network/network.h b/lib/SuplaDevice/src/supla/network/network.h new file mode 100644 index 00000000..9316eca2 --- /dev/null +++ b/lib/SuplaDevice/src/supla/network/network.h @@ -0,0 +1,148 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _network_interface_h +#define _network_interface_h + +#include + +#include "supla-common/log.h" +#include "supla-common/proto.h" + +namespace Supla { +class Network { + public: + static Network *Instance() { + return netIntf; + } + + static bool Connected() { + if (Instance() != NULL) { + return Instance()->connected(); + } + return false; + } + + static int Read(void *buf, int count) { + if (Instance() != NULL) { + return Instance()->read(buf, count); + } + return -1; + } + + static int Write(void *buf, int count) { + if (Instance() != NULL) { + return Instance()->write(buf, count); + } + return -1; + } + + static int Connect(const char *server, int port = -1) { + if (Instance() != NULL) { + Instance()->clearTimeCounters(); + return Instance()->connect(server, port); + } + return 0; + } + + static void Disconnect() { + if (Instance() != NULL) { + return Instance()->disconnect(); + } + return; + } + + static void Setup() { + if (Instance() != NULL) { + return Instance()->setup(); + } + return; + } + + static bool IsReady() { + if (Instance() != NULL) { + return Instance()->isReady(); + } + return false; + } + + static bool Iterate() { + if (Instance() != NULL) { + return Instance()->iterate(); + } + return false; + } + + static void SetSrpc(void *_srpc) { + if (Instance() != NULL) { + Instance()->setSrpc(_srpc); + } + } + + static bool Ping() { + if (Instance() != NULL) { + return Instance()->ping(); + } + return false; + } + + Network(IPAddress *ip); + virtual int read(void *buf, int count) = 0; + virtual int write(void *buf, int count) = 0; + virtual int connect(const char *server, int port = -1) = 0; + virtual bool connected() = 0; + virtual void disconnect() = 0; + virtual void setup() = 0; + virtual void setTimeout(int); + + virtual bool isReady() = 0; + virtual bool iterate(); + virtual bool ping(); + + virtual void fillStateData(TDSC_ChannelState &channelState); + + void setSrpc(void *_srpc); + void updateLastSent(); + void updateLastResponse(); + void clearTimeCounters(); + void setActivityTimeout(_supla_int_t activityTimeoutSec); + + protected: + static Network *netIntf; + _supla_int64_t lastSentMs; + _supla_int64_t lastResponseMs; + _supla_int64_t lastPingTimeMs; + _supla_int_t serverActivityTimeoutS; + void *srpc; + + bool useLocalIp; + IPAddress localIp; +}; + +// Method passed to SRPC as a callback to read raw data from network interface +_supla_int_t data_read(void *buf, _supla_int_t count, void *sdc); +// Method passed to SRPC as a callback to write raw data to network interface +_supla_int_t data_write(void *buf, _supla_int_t count, void *sdc); +// Method passed to SRPC as a callback to handle response from Supla server +void message_received(void *_srpc, + unsigned _supla_int_t rr_id, + unsigned _supla_int_t call_type, + void *_sdc, + unsigned char proto_version); + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/pv/afore.cpp b/lib/SuplaDevice/src/supla/pv/afore.cpp new file mode 100644 index 00000000..06c0933f --- /dev/null +++ b/lib/SuplaDevice/src/supla/pv/afore.cpp @@ -0,0 +1,150 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "afore.h" + +using namespace Supla; +using namespace PV; + +Afore::Afore(IPAddress ip, int port, const char *loginAndPass) + : ip(ip), + port(port), + buf(), + totalGeneratedEnergy(0), + currentPower(0), + bytesCounter(0), + retryCounter(0), + vFound(false), + varFound(false), + dataIsReady(false), + dataFetchInProgress(false), + connectionTimeoutMs(0) { + refreshRateSec = 15; + int len = strlen(loginAndPass); + if (len > LOGIN_AND_PASSOWORD_MAX_LENGTH) { + len = LOGIN_AND_PASSOWORD_MAX_LENGTH; + } + strncpy(loginAndPassword, loginAndPass, len); +} + +void Afore::iterateAlways() { + if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("AFORE: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } + if (!pvClient.connected()) { + Serial.println(F("AFORE fetch completed")); + dataFetchInProgress = false; + dataIsReady = true; + } + if (pvClient.available()) { + Serial.print(F("Reading data from afore: ")); + Serial.println(pvClient.available()); + } + while (pvClient.available()) { + char c; + c = pvClient.read(); + if (c == '\n') { + if (varFound) { + if (bytesCounter > 79) bytesCounter = 79; + buf[bytesCounter] = '\0'; + char varName[80]; + char varValue[80]; + sscanf(buf, "%s = \"%s\";", varName, varValue); + if (strncmp(varName, "webdata_now_p", strlen("webdata_now_p")) == 0) { + float curPower = atof(varValue); + Serial.print(F("Current power: ")); + Serial.println(curPower); + currentPower = curPower * 100000; + } + if (strncmp(varName, "webdata_total_e", strlen("webdata_total_e")) == + 0) { + float totalProd = atof(varValue); + Serial.print(F("Total production: ")); + Serial.println(totalProd); + + totalGeneratedEnergy = totalProd * 100000; + } + } + bytesCounter = 0; + vFound = false; + varFound = false; + } else if (c == 'v' || vFound) { + vFound = true; + if (bytesCounter < 80) { + buf[bytesCounter] = c; + } + bytesCounter++; + if (bytesCounter == 4 && !varFound) { + if (strncmp(buf, "var ", 4) == 0) { + varFound = true; + bytesCounter = 0; + } + } + } + } + if (!pvClient.connected()) { + pvClient.stop(); + } + } + if (dataIsReady) { + dataIsReady = false; + setFwdActEnergy(0, totalGeneratedEnergy); + setPowerActive(0, currentPower); + updateChannelValues(); + } +} + +bool Afore::iterateConnected(void *srpc) { + if (!dataFetchInProgress) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + Serial.println(F("AFORE connecting")); + if (pvClient.connect(ip, port)) { + retryCounter = 0; + dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; + Serial.println(F("Succesful connect")); + + pvClient.print("GET /status.html HTTP/1.1\nAuthorization: Basic "); + pvClient.println(loginAndPassword); + pvClient.println("Connection: close"); + pvClient.println(); + + } else { // if connection wasn't successful, try few times. If it fails, + // then assume that inverter is off during the night + Serial.print(F("Failed to connect to Afore at: ")); + Serial.print(ip); + Serial.print(F(":")); + Serial.println(port); + retryCounter++; + if (retryCounter > 3) { + currentPower = 0; + dataIsReady = true; + } + } + } + } + return Element::iterateConnected(srpc); +} + +void Afore::readValuesFromDevice() { +} + diff --git a/lib/SuplaDevice/src/supla/pv/afore.h b/lib/SuplaDevice/src/supla/pv/afore.h new file mode 100644 index 00000000..bb46c26a --- /dev/null +++ b/lib/SuplaDevice/src/supla/pv/afore.h @@ -0,0 +1,63 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __afore_h +#define __afore_h + +#include +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#else +#include +#endif + +#define LOGIN_AND_PASSOWORD_MAX_LENGTH 100 + +namespace Supla { +namespace PV { +class Afore : public Supla::Sensor::OnePhaseElectricityMeter { + public: + Afore(IPAddress ip, int port, const char *loginAndPassword); + void readValuesFromDevice(); + void iterateAlways(); + bool iterateConnected(void *srpc); + + protected: +#if defined(ARDUINO_ARCH_AVR) + EthernetClient pvClient; +#else + WiFiClient pvClient; +#endif + IPAddress ip; + int port; + char loginAndPassword[LOGIN_AND_PASSOWORD_MAX_LENGTH]; + char buf[80]; + unsigned _supla_int64_t totalGeneratedEnergy; + _supla_int_t currentPower; + int bytesCounter; + int retryCounter; + bool vFound; + bool varFound; + bool dataIsReady; + bool dataFetchInProgress; + unsigned long connectionTimeoutMs; +}; +}; // namespace PV +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/pv/fronius.cpp b/lib/SuplaDevice/src/supla/pv/fronius.cpp new file mode 100644 index 00000000..871f80a3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/pv/fronius.cpp @@ -0,0 +1,202 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "fronius.h" + +using namespace Supla; +using namespace PV; + +enum ParametersToRead { NONE, TOTAL_ENERGY, FAC, IAC, PAC, UAC }; + +Fronius::Fronius(IPAddress ip, int port, int deviceId) + : ip(ip), + port(port), + buf(), + totalGeneratedEnergy(0), + currentPower(0), + currentCurrent(0), + currentFreq(0), + currentVoltage(0), + bytesCounter(0), + retryCounter(0), + valueToFetch(NONE), + deviceId(deviceId), + startCharFound(false), + dataIsReady(false), + dataFetchInProgress(false), + connectionTimeoutMs(0) { + refreshRateSec = 15; +} + +void Fronius::iterateAlways() { + if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("Fronius: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } + if (!pvClient.connected()) { + Serial.println(F("Fronius fetch completed")); + dataFetchInProgress = false; + dataIsReady = true; + } + if (pvClient.available()) { + Serial.print(F("Reading data from Fronius: ")); + Serial.println(pvClient.available()); + } + while (pvClient.available()) { + char c; + c = pvClient.read(); + if (c == '\n') { + if (startCharFound) { + if (bytesCounter > 79) bytesCounter = 79; + buf[bytesCounter] = '\0'; + char varName[80]; + char varValue[80]; + sscanf(buf, " %s : %s", varName, varValue); + if (valueToFetch != NONE && strncmp(varName, "Value", strlen("Value")) == 0) { + switch (valueToFetch) { + case TOTAL_ENERGY: { + float totalProd = atof(varValue); + Serial.print(F("Total production: ")); + Serial.print(totalProd); + Serial.println(F(" Wh")); + totalGeneratedEnergy = totalProd * 100; + + break; + } + case PAC: { + float curPower = atof(varValue); + Serial.print(F("Current power: ")); + Serial.println(curPower); + currentPower = curPower * 100000; + + break; + } + case IAC: { + float curCurrent = atof(varValue); + Serial.print(F("Current: ")); + Serial.println(curCurrent); + currentCurrent = curCurrent * 1000; + + break; + } + case FAC: { + float curFreq = atof(varValue); + Serial.print(F("Frequency: ")); + Serial.println(curFreq); + currentFreq = curFreq * 100; + + break; + } + case UAC: { + float curVoltage = atof(varValue); + Serial.print(F("Voltage: ")); + Serial.println(curVoltage); + currentVoltage = curVoltage * 100; + + break; + } + } + valueToFetch = NONE; + } else if (strncmp(varName, "TOTAL_ENERGY\"", strlen("TOTAL_ENERGY")) == 0) { + valueToFetch = TOTAL_ENERGY; + } else if (strncmp(varName, "FAC", strlen("FAC")) == 0) { + valueToFetch = FAC; + } else if (strncmp(varName, "UAC", strlen("UAC")) == 0) { + valueToFetch = UAC; + } else if (strncmp(varName, "IAC", strlen("IAC")) == 0) { + valueToFetch = IAC; + } else if (strncmp(varName, "PAC", strlen("PAC")) == 0) { + valueToFetch = PAC; + } + } + bytesCounter = 0; + startCharFound = false; + } else if (c == '"' || startCharFound) { + startCharFound = true; + if (c == '"') { + c = ' '; + } + if (bytesCounter < 80) { + buf[bytesCounter] = c; + } + bytesCounter++; + } + } + if (!pvClient.connected()) { + pvClient.stop(); + } + } + if (dataIsReady) { + dataIsReady = false; + setFwdActEnergy(0, totalGeneratedEnergy); + setPowerActive(0, currentPower); + setCurrent(0, currentCurrent); + setVoltage(0, currentVoltage); + setFreq(currentFreq); + updateChannelValues(); + } +} + +bool Fronius::iterateConnected(void *srpc) { + if (!dataFetchInProgress) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + Serial.print(F("Fronius connecting ")); + Serial.println(deviceId); + if (pvClient.connect(ip, port)) { + retryCounter = 0; + dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; + Serial.println(F("Succesful connect")); + + char buf[100]; + strcpy(buf, "GET /solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceID="); + char idBuf[20]; + sprintf(idBuf, "%d", deviceId); + strcat(buf, idBuf); + strcat(buf, "&DataCollection=CommonInverterData HTTP/1.1"); + pvClient.println(buf); + pvClient.println("Host: localhost"); + pvClient.println("Connection: close"); + pvClient.println(); + + } else { // if connection wasn't successful, try few times. If it fails, + // then assume that inverter is off during the night + Serial.print(F("Failed to connect to Fronius at: ")); + Serial.print(ip); + Serial.print(F(":")); + Serial.println(port); + retryCounter++; + if (retryCounter > 3) { + currentPower = 0; + currentFreq = 0; + currentCurrent = 0; + currentVoltage = 0; + dataIsReady = true; + } + } + } + } + return Element::iterateConnected(srpc); +} + +void Fronius::readValuesFromDevice() { +} + diff --git a/lib/SuplaDevice/src/supla/pv/fronius.h b/lib/SuplaDevice/src/supla/pv/fronius.h new file mode 100644 index 00000000..f32d4699 --- /dev/null +++ b/lib/SuplaDevice/src/supla/pv/fronius.h @@ -0,0 +1,64 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __fronius_h +#define __fronius_h + +#include +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#else +#include +#endif + +namespace Supla { +namespace PV { +class Fronius : public Supla::Sensor::OnePhaseElectricityMeter { + public: + Fronius(IPAddress ip, int port = 80, int deviceId = 1); + void readValuesFromDevice(); + void iterateAlways(); + bool iterateConnected(void *srpc); + + protected: +#if defined(ARDUINO_ARCH_AVR) + EthernetClient pvClient; +#else + WiFiClient pvClient; +#endif + IPAddress ip; + int port; + char buf[80]; + unsigned _supla_int64_t totalGeneratedEnergy; + _supla_int_t currentPower; + unsigned _supla_int16_t currentCurrent; + unsigned _supla_int16_t currentFreq; + unsigned _supla_int16_t currentVoltage; + int bytesCounter; + int retryCounter; + int valueToFetch; + int deviceId; + bool startCharFound; + bool dataIsReady; + bool dataFetchInProgress; + unsigned long connectionTimeoutMs; +}; +}; // namespace PV +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/BME280.h b/lib/SuplaDevice/src/supla/sensor/BME280.h new file mode 100644 index 00000000..1b8cedb7 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/BME280.h @@ -0,0 +1,104 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _bme280_h +#define _bme280_h + +// Dependency: Adafruid BME280 library - use library manager to install it +#include + +#include "therm_hygro_press_meter.h" + +namespace Supla { +namespace Sensor { +class BME280 : public ThermHygroPressMeter { + public: + BME280(int8_t address = 0x77, float altitude = NAN) : address(address), sensorStatus(false), altitude(altitude) { + } + + double getTemp() { + float value = TEMPERATURE_NOT_AVAILABLE; + bool retryDone = false; + do { + if (!sensorStatus || isnan(value)) { + sensorStatus = bme.begin(address); + retryDone = true; + } + value = TEMPERATURE_NOT_AVAILABLE; + if (sensorStatus) { + value = bme.readTemperature(); + } + } while (isnan(value) && !retryDone); + return value; + } + + double getHumi() { + float value = HUMIDITY_NOT_AVAILABLE; + bool retryDone = false; + do { + if (!sensorStatus || isnan(value)) { + sensorStatus = bme.begin(address); + retryDone = true; + } + value = HUMIDITY_NOT_AVAILABLE; + if (sensorStatus) { + value = bme.readHumidity(); + } + } while (isnan(value) && !retryDone); + return value; + } + + double getPressure() { + float value = PRESSURE_NOT_AVAILABLE; + bool retryDone = false; + do { + if (!sensorStatus || isnan(value)) { + sensorStatus = bme.begin(address); + retryDone = true; + } + value = PRESSURE_NOT_AVAILABLE; + if (sensorStatus) { + value = bme.readPressure() / 100.0; + } + } while (isnan(value) && !retryDone); + if (!isnan(altitude)) { + value = bme.seaLevelForAltitude(altitude, value); + } + return value; + } + + void onInit() { + sensorStatus = bme.begin(address); + + pressureChannel.setNewValue(getPressure()); + channel.setNewValue(getTemp(), getHumi()); + } + + void setAltitude(float newAltitude) { + altitude = newAltitude; + } + + protected: + int8_t address; + bool sensorStatus; + float altitude; + Adafruit_BME280 bme; // I2C +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/DHT.h b/lib/SuplaDevice/src/supla/sensor/DHT.h new file mode 100644 index 00000000..65697bc7 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/DHT.h @@ -0,0 +1,104 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _dht_h +#define _dht_h + +#include + +#include "therm_hygro_meter.h" + +namespace Supla { +namespace Sensor { +class DHT: public ThermHygroMeter { + public: + DHT(int pin, int dhtType) : dht(pin, dhtType) { + dht.begin(); + delay(100); + retryCountTemp = 0; + retryCountHumi = 0; + lastValidTemp = TEMPERATURE_NOT_AVAILABLE; + lastValidHumi = HUMIDITY_NOT_AVAILABLE; + } + + double getTemp() { + double value = TEMPERATURE_NOT_AVAILABLE; + value = dht.readTemperature(); + if (isnan(value)) { + value = TEMPERATURE_NOT_AVAILABLE; + } + + if (value == TEMPERATURE_NOT_AVAILABLE) { + retryCountTemp++; + if (retryCountTemp > 3) { + retryCountTemp = 0; + } else { + value = lastValidTemp; + } + } else { + retryCountTemp = 0; + } + lastValidTemp = value; + + return value; + } + + double getHumi() { + double value = HUMIDITY_NOT_AVAILABLE; + value = dht.readHumidity(); + if (isnan(value)) { + value = HUMIDITY_NOT_AVAILABLE; + } + + if (value == HUMIDITY_NOT_AVAILABLE) { + retryCountHumi++; + if (retryCountHumi > 3) { + retryCountHumi = 0; + } else { + value = lastValidHumi; + } + } else { + retryCountHumi = 0; + } + lastValidHumi = value; + + return value; + } + + void iterateAlways() { + if (lastReadTime + 10000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getTemp(), getHumi()); + } + } + + void onInit() { + channel.setNewValue(getTemp(), getHumi()); + } + + protected: + ::DHT dht; + double lastValidTemp; + double lastValidHumi; + int8_t retryCountTemp; + int8_t retryCountHumi; + +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/DS18B20.h b/lib/SuplaDevice/src/supla/sensor/DS18B20.h new file mode 100644 index 00000000..7b27d211 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/DS18B20.h @@ -0,0 +1,198 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _ds18b20_h +#define _ds18b20_h + +#include +#include +#include + +#include "supla-common/log.h" +#include "supla/sensor/thermometer.h" + +namespace Supla { +namespace Sensor { + +class OneWireBus { + public: + OneWireBus(uint8_t pinNumber) + : pin(pinNumber), nextBus(nullptr), lastReadTime(0), oneWire(pinNumber) { + supla_log(LOG_DEBUG, "Initializing OneWire bus at pin %d", pinNumber); + sensors.setOneWire(&oneWire); + sensors.begin(); + if (sensors.isParasitePowerMode()) { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is ON", pinNumber); + } else { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is OFF", pinNumber); + } + + supla_log(LOG_DEBUG, + "OneWire(pin %d) Found %d devices:", + pinNumber, + sensors.getDeviceCount()); + + // report parasite power requirements + + DeviceAddress address; + char strAddr[64]; + for (int i = 0; i < sensors.getDeviceCount(); i++) { + if (!sensors.getAddress(address, i)) { + supla_log(LOG_DEBUG, "Unable to find address for Device %d", i); + } else { + sprintf( + strAddr, + "{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}", + address[0], + address[1], + address[2], + address[3], + address[4], + address[5], + address[6], + address[7]); + supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr); + sensors.setResolution(address, 12); + } + delay(0); + } + sensors.setWaitForConversion(true); + sensors.requestTemperatures(); + sensors.setWaitForConversion(false); + } + + int8_t getIndex(uint8_t *deviceAddress) { + DeviceAddress address; + for (int i = 0; i < sensors.getDeviceCount(); i++) { + if (sensors.getAddress(address, i)) { + bool found = true; + for (int j = 0; j < 8; j++) { + if (deviceAddress[j] != address[j]) { + found = false; + } + } + if (found) { + return i; + } + } + } + return -1; + } + + uint8_t pin; + OneWireBus *nextBus; + unsigned long lastReadTime; + DallasTemperature sensors; + + protected: + OneWire oneWire; +}; + +class DS18B20 : public Thermometer { + public: + DS18B20(uint8_t pin, uint8_t *deviceAddress = nullptr) { + OneWireBus *bus = oneWireBus; + OneWireBus *prevBus = nullptr; + address[0] = 0; + lastValidValue = TEMPERATURE_NOT_AVAILABLE; + retryCounter = 0; + + if (bus) { + while (bus) { + if (bus->pin == pin) { + myBus = bus; + break; + } + prevBus = bus; + bus = bus->nextBus; + } + } + + // There is no OneWire bus created yet for this pin + if (!bus) { + supla_log(LOG_DEBUG, "Creating OneWire bus for pin: %d", pin); + myBus = new OneWireBus(pin); + if (prevBus) { + prevBus->nextBus = myBus; + } else { + oneWireBus = myBus; + } + } + if (deviceAddress == nullptr) { + supla_log(LOG_DEBUG, + "Device address not provided. Using device from index 0"); + } else { + memcpy(address, deviceAddress, 8); + } + } + + void iterateAlways() { + if (myBus->lastReadTime + 10000 < millis()) { + myBus->sensors.requestTemperatures(); + myBus->lastReadTime = millis(); + } + if (myBus->lastReadTime + 5000 < millis() && + (lastReadTime != myBus->lastReadTime)) { + channel.setNewValue(getValue()); + lastReadTime = myBus->lastReadTime; + } + } + + double getValue() { + double value = TEMPERATURE_NOT_AVAILABLE; + if (address[0] == 0) { + value = myBus->sensors.getTempCByIndex(0); + } else { + value = myBus->sensors.getTempC(address); + } + + if (value == DEVICE_DISCONNECTED_C || value == 85.0) { + value = TEMPERATURE_NOT_AVAILABLE; + } + + if (value == TEMPERATURE_NOT_AVAILABLE) { + retryCounter++; + if (retryCounter > 3) { + retryCounter = 0; + } else { + value = lastValidValue; + } + } else { + retryCounter = 0; + } + lastValidValue = value; + + return value; + } + + void onInit() { + channel.setNewValue(getValue()); + } + + protected: + static OneWireBus *oneWireBus; + OneWireBus *myBus; + DeviceAddress address; + int8_t retryCounter; + double lastValidValue; +}; + +OneWireBus *DS18B20::oneWireBus = nullptr; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/HC_SR04.h b/lib/SuplaDevice/src/supla/sensor/HC_SR04.h new file mode 100644 index 00000000..780aad11 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HC_SR04.h @@ -0,0 +1,97 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _hc_sr04_h +#define _hc_sr04_h + +#include "supla/channel.h" +#include "supla/sensor/distance.h" + +#define DURATION_COUNT 2 + +namespace Supla { +namespace Sensor { +class HC_SR04 : public Distance { + public: + HC_SR04(int8_t trigPin, int8_t echoPin, int16_t minIn = 0, + int16_t maxIn = 500, int16_t minOut = 0, int16_t maxOut = 500) + : failCount(0), lastDuration(0) { + _trigPin = trigPin; + _echoPin = echoPin; + _minIn = minIn; + _maxIn = maxIn; + _minOut = minOut; + _maxOut = maxOut; + } + void onInit() { + pinMode(_trigPin, OUTPUT); + pinMode(_echoPin, INPUT); + digitalWrite(_trigPin, LOW); + delayMicroseconds(2); + + channel.setNewValue(getValue()); + channel.setNewValue(getValue()); + } + + virtual double getValue() { + digitalWrite(_trigPin, HIGH); + delayMicroseconds(10); + digitalWrite(_trigPin, LOW); + unsigned long duration = pulseIn(_echoPin, HIGH, 60000); + if (duration > 50) { + lastDuration = duration; + failCount = 0; + } else { + duration = lastDuration; + failCount++; + } + + long distance = (duration / 2) / 29.1; + long value = map(distance, _minIn, _maxIn, _minOut, _maxOut); + if (_minOut < _maxOut) { + value = constrain(value, _minOut, _maxOut); + } else { + value = constrain(value, _maxOut, _minOut); + } + return failCount <= 3 ? (float)value / 100 : DISTANCE_NOT_AVAILABLE; + } + + void setMinMaxIn(int16_t minIn, int16_t maxIn) { + _minIn = minIn; + _maxIn = maxIn; + } + + void setMinMaxOut(int16_t minOut, int16_t maxOut) { + _minOut = minOut; + _maxOut = maxOut; + } + +protected: + int8_t _trigPin; + int8_t _echoPin; + int16_t _minIn; + int16_t _maxIn; + int16_t _minOut; + int16_t _maxOut; + char failCount; + bool ready; + unsigned long lastDuration; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/HJ101.cpp b/lib/SuplaDevice/src/supla/sensor/HJ101.cpp new file mode 100644 index 00000000..13b3f082 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HJ101.cpp @@ -0,0 +1,151 @@ +#include "hj101.h" + +namespace Supla { +namespace Sensor { + +HJ101::HJ101(int8_t pinCF, int8_t pinCF1, int8_t pinSEL, bool currentWhen, bool use_interrupts) + : pinCF(pinCF), pinCF1(pinCF1), pinSEL(pinSEL), currentWhen(currentWhen), use_interrupts(use_interrupts) { + hj101 = new HLW8012(); + hj101->begin(pinCF, pinCF1, pinSEL, currentWhen, use_interrupts); + + attachInterrupt(pinCF, hjl01_cf_interrupt, FALLING); + attachInterrupt(pinCF1, hjl01_cf1_interrupt, FALLING); +} + +void HJ101::onInit() { + readValuesFromDevice(); + updateChannelValues(); +} + +void HJ101::readValuesFromDevice() { + energy = _energy + (hj101->getEnergy() / 36); // ---------- current energy value = value at start + (hj101->getEnergy() / 36) ---- + unsigned int _reactive = 0; + double _pf = 0; + double _current = hj101->getCurrent(); + unsigned int _voltage = hj101->getVoltage(); + unsigned int _active = hj101->getActivePower(); + unsigned int _apparent = _voltage * _current; + if (_apparent > _active) { + _reactive = sqrt(_apparent * _apparent - _active * _active); + } + else { + _reactive = 0; + } + if (_active > _apparent) { + _pf = 1; + } + if (_apparent == 0) { + _pf = 0; + } + else { + _pf = (double)_active / _apparent; + } + setVoltage(0, _voltage * 100); // voltage in 0.01 V + setCurrent(0, _current * 1000); // current in 0.001 A + setPowerActive(0, _active * 100000); // power in 0.00001 kW + setFwdActEnergy(0, energy); // energy in 0.00001 kWh + setPowerApparent(0, _apparent * 100000); // power in 0.00001 kVA + setPowerReactive(0, _reactive * 100000); // power in 0.00001 kvar + setPowerFactor(0, _pf * 1000); // power in 0.001 +} + +void HJ101::onSaveState() { + double current_multiplier = getCurrentMultiplier(); + double voltage_multiplier = getVoltageMultiplier(); + double power_multiplier = getPowerMultiplier(); + + Supla::Storage::WriteState((unsigned char *)&energy, sizeof(energy)); + Supla::Storage::WriteState((unsigned char *)¤t_multiplier, sizeof(current_multiplier)); + Supla::Storage::WriteState((unsigned char *)&voltage_multiplier, sizeof(voltage_multiplier)); + Supla::Storage::WriteState((unsigned char *)&power_multiplier, sizeof(power_multiplier)); +} + +void HJ101::onLoadState() { + _supla_int64_t data; + double current_multiplier; + double voltage_multiplier; + double power_multiplier; + + if (Supla::Storage::ReadState((unsigned char *)&data, sizeof(data))) { + setCounter(data); + } + + if (Supla::Storage::ReadState((unsigned char *)¤t_multiplier, sizeof(current_multiplier))) { + setCurrentMultiplier(current_multiplier); + } + + if (Supla::Storage::ReadState((unsigned char *)&voltage_multiplier, sizeof(voltage_multiplier))) { + setVoltageMultiplier(voltage_multiplier); + } + + if (Supla::Storage::ReadState((unsigned char *)&power_multiplier, sizeof(power_multiplier))) { + setPowerMultiplier(power_multiplier); + } +} + +_supla_int64_t HJ101::getCounter() { + return energy; +} + +void HJ101::setCounter(_supla_int64_t value) { + _energy = value; // ---------- energy value read from memory at startup ---- + setFwdActEnergy(0, value); +} + +// When using interrupts we have to call the library entry point +// whenever an interrupt is triggered +void ICACHE_RAM_ATTR HJ101::hjl01_cf1_interrupt() { + hj101->cf1_interrupt(); +} + +void ICACHE_RAM_ATTR HJ101::hjl01_cf_interrupt() { + hj101->cf_interrupt(); +} + +void HJ101::calibrate(double calibPower, double calibVoltage) { + unsigned long timeout1 = millis(); + while ((millis() - timeout1) < 10000) { + delay(10); + } + + Serial.print(F("[HLW] Active Power (W) : ")); + Serial.println(hj101->getActivePower()); + Serial.print(F("[HLW] Voltage (V) : ")); + Serial.println(hj101->getVoltage()); + Serial.print(F("[HLW] Current (A) : ")); + Serial.println(hj101->getCurrent()); + + hj101->expectedActivePower(calibPower); + hj101->expectedVoltage(calibVoltage); + hj101->expectedCurrent(calibPower / calibVoltage); + + unsigned long timeout2 = millis(); + while ((millis() - timeout2) < 2000) { + delay(10); + } + + double current_multi = getCurrentMultiplier(); + double voltage_multi = getVoltageMultiplier(); + double power_multi = getPowerMultiplier(); + + Serial.print(F("[HLW] New current multiplier : ")); + Serial.println(current_multi); + Serial.print(F("[HLW] New voltage multiplier : ")); + Serial.println(voltage_multi); + Serial.print(F("[HLW] New power multiplier : ")); + Serial.println(power_multi); + saveState(); + yield(); +} + +void HJ101::saveState() { + Supla::Storage::PrepareState(); + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + element->onSaveState(); + } + Supla::Storage::FinalizeSaveState(); +} + +HLW8012 *HJ101::hj101 = nullptr; +}; // namespace Sensor +}; // namespace Supla \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/HJ101.h b/lib/SuplaDevice/src/supla/sensor/HJ101.h new file mode 100644 index 00000000..c4814604 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HJ101.h @@ -0,0 +1,66 @@ +#ifndef _Hj101_h +#define _Hj101_h + +#include +#include + +// https://github.com/xoseperez/hlw8012 +#include +#include +#include +#include "one_phase_electricity_meter.h" + +namespace Supla { +namespace Sensor { + +class HJ101 : public OnePhaseElectricityMeter, public Element { + public: + HJ101(int8_t pinCF, int8_t pinCF1, int8_t pinSEL, bool currentWhen = LOW, bool use_interrupts = true); + + void onInit(); + void readValuesFromDevice(); + void onSaveState(); + void onLoadState(); + _supla_int64_t getCounter(); + void setCounter(_supla_int64_t value); + static void ICACHE_RAM_ATTR hjl01_cf1_interrupt(); + static void ICACHE_RAM_ATTR hjl01_cf_interrupt(); + void calibrate(double calibPower, double calibVoltage); + void saveState(); + + double getCurrentMultiplier() { + return hj101->getCurrentMultiplier(); + }; + double getVoltageMultiplier() { + return hj101->getVoltageMultiplier(); + }; + double getPowerMultiplier() { + return hj101->getPowerMultiplier(); + }; + + void setCurrentMultiplier(double current_multiplier) { + hj101->setCurrentMultiplier(current_multiplier); + }; + void setVoltageMultiplier(double voltage_multiplier) { + hj101->setVoltageMultiplier(voltage_multiplier); + }; + void setPowerMultiplier(double power_multiplier) { + hj101->setPowerMultiplier(power_multiplier); + }; + + protected: + static HLW8012 *hj101; + int8_t pinCF; + int8_t pinCF1; + int8_t pinSEL; + bool currentWhen; + bool use_interrupts; + + unsigned _supla_int64_t energy; + unsigned _supla_int64_t _energy; // ---------- energy value read from memory at startup ---- +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp new file mode 100644 index 00000000..4f610197 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp @@ -0,0 +1,74 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "MAX6675_K.h" + +namespace Supla { +namespace Sensor { +MAX6675_K::MAX6675_K(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO) + : pin_CLK(pin_CLK), pin_CS(pin_CS), pin_DO(pin_DO) { +} + +double MAX6675_K::getValue() { + uint16_t value; + + digitalWrite(pin_CS, LOW); + delay(1); + + value = spiRead(); + value <<= 8; + value |= spiRead(); + + digitalWrite(pin_CS, HIGH); + + if (value & 0x4) { // this means there is no probe connected to Max6675 + Serial.print(F("no probe connected to Max6675")); + return TEMPERATURE_NOT_AVAILABLE; + } + value >>= 3; + + return value * 0.25; +} + +void MAX6675_K::onInit() { + digitalWrite(pin_CS, HIGH); + + pinMode(pin_CS, OUTPUT); + pinMode(pin_CLK, OUTPUT); + pinMode(pin_DO, INPUT); + + channel.setNewValue(getValue()); +} + +byte MAX6675_K::spiRead() { + int i; + byte d = 0; + + for (i = 7; i >= 0; i--) { + digitalWrite(pin_CLK, LOW); + delay(1); + if (digitalRead(pin_DO)) { + d |= (1 << i); + } + + digitalWrite(pin_CLK, HIGH); + delay(1); + } + return d; +} + +}; // namespace Sensor +}; // namespace Supla \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h new file mode 100644 index 00000000..868a9bff --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _max6675_k_h +#define _max6675_k_h + +#include +#include + +namespace Supla { +namespace Sensor { +class MAX6675_K : public Thermometer { + public: + MAX6675_K(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO); + double getValue(); + + private: + void onInit(); + byte spiRead(); + + protected: + int8_t pin_CLK; + int8_t pin_CS; + int8_t pin_DO; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/PzemV2.h b/lib/SuplaDevice/src/supla/sensor/PzemV2.h new file mode 100644 index 00000000..f4728c07 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/PzemV2.h @@ -0,0 +1,88 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _PzemV2_h +#define _PzemV2_h + +#include +// dependence: Arduino communication library for Peacefair PZEM-004T Energy +// monitor https://github.com/olehs/PZEM004T +#include +#include + +#include "one_phase_electricity_meter.h" + +namespace Supla { +namespace Sensor { + +class PZEMv2 : public OnePhaseElectricityMeter { + public: + PZEMv2(int8_t pinRX, int8_t pinTX) : pzem(pinRX, pinTX), ip(192, 168, 1, 1) { + } + + PZEMv2(HardwareSerial *serial) : pzem(serial), ip(192, 168, 1, 1) { + } + + void onInit() { + pzem.setAddress(ip); + readValuesFromDevice(); + updateChannelValues(); + } + + virtual void readValuesFromDevice() { + + float current = pzem.current(ip); + // If current reading is NAN, we assume that PZEM there is no valid communication + // with PZEM. Sensor shouldn't show any data + if (current == PZEM_ERROR_VALUE) { + resetReadParameters(); + return; + } + + float powerFactor = 0; + float reactive = 0; + float voltage = pzem.voltage(ip); + float active = pzem.power(ip); + float apparent = (voltage * current); + if (apparent > active) { + reactive = sqrt(apparent * apparent - active * active); + } else { + reactive = 0; + } + if (active > apparent) { + powerFactor = 1; + } else if (apparent == 0) { + powerFactor = 0; + } else { + powerFactor = (active / apparent); + } + + setVoltage(0, voltage * 100); + setCurrent(0, current * 1000); + setPowerActive(0, active * 100000); + setFwdActEnergy(0, pzem.energy(ip) * 100); + setPowerApparent(0, apparent * 100000); + setPowerReactive(0, reactive * 10000); + setPowerFactor(0, powerFactor * 1000); + } + + PZEM004T pzem; + IPAddress ip; +}; +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/PzemV3.h b/lib/SuplaDevice/src/supla/sensor/PzemV3.h new file mode 100644 index 00000000..263e6d6f --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/PzemV3.h @@ -0,0 +1,84 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _PzemV3_h +#define _PzemV3_h + +#include +// dependence: Arduino library for the Updated PZEM-004T v3.0 Power and Energy +// meter https://github.com/mandulaj/PZEM-004T-v30 +#include +#include + +#include "one_phase_electricity_meter.h" + +namespace Supla { +namespace Sensor { + +class PZEMv3 : public OnePhaseElectricityMeter { + public: + PZEMv3(int8_t pinRX, int8_t pinTX) : pzem(pinRX, pinTX) { + } + + PZEMv3(HardwareSerial *serial) : pzem(serial) { + } + + void onInit() { + readValuesFromDevice(); + updateChannelValues(); + } + + virtual void readValuesFromDevice() { + float current = pzem.current(); + // If current reading is NAN, we assume that PZEM there is no valid communication + // with PZEM. Sensor shouldn't show any data + if (isnan(current)) { + resetReadParameters(); + return; + } + + float voltage = pzem.voltage(); + float active = pzem.power(); + float apparent = (voltage * current); + float reactive = 0; + if (apparent > active) { + reactive = sqrt(apparent * apparent - active * active); + } else { + reactive = 0; + } + + setVoltage(0, voltage * 100); + setCurrent(0, current * 1000); + setPowerActive(0, active * 100000); + setFwdActEnergy(0, pzem.energy() * 100000); + setFreq(pzem.frequency() * 100); + setPowerFactor(0, pzem.pf() * 1000); + setPowerApparent(0, apparent * 100000); + setPowerReactive(0, reactive * 10000); + } + + void resetStorage() { + pzem.resetEnergy(); + } + + protected: + PZEM004Tv30 pzem; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/SHT3x.h b/lib/SuplaDevice/src/supla/sensor/SHT3x.h new file mode 100644 index 00000000..3406ac14 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/SHT3x.h @@ -0,0 +1,93 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _sht3x_h +#define _sht3x_h + +// Dependency: ClosedCube SHT3x library - use library manager to install it +// https://github.com/closedcube/ClosedCube_SHT31D_Arduino + +#include + +#include "therm_hygro_meter.h" + + +namespace Supla { +namespace Sensor { +class SHT3x : public ThermHygroMeter { + public: + SHT3x(int8_t address = 0x44) + : temperature(TEMPERATURE_NOT_AVAILABLE), + humidity(HUMIDITY_NOT_AVAILABLE), + address(address), + retryCount(0) { + } + + double getTemp() { + return temperature; + } + + double getHumi() { + return humidity; + } + + private: + void iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + readValuesFromDevice(); + channel.setNewValue(getTemp(), getHumi()); + } + } + + void onInit() { + sht.begin(address); + readValuesFromDevice(); + channel.setNewValue(getTemp(), getHumi()); + } + + void readValuesFromDevice() { + SHT31D result = sht.readTempAndHumidity( + SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50); + + if (result.error != SHT3XD_NO_ERROR) { + Serial.print(F("SHT [ERROR] Code #")); + Serial.println(result.error); + retryCount++; + if (retryCount > 3) { + retryCount = 0; + temperature = TEMPERATURE_NOT_AVAILABLE; + humidity = HUMIDITY_NOT_AVAILABLE; + } + } else { + retryCount = 0; + temperature = result.t; + humidity = result.rh; + } + } + + protected: + int8_t address; + double temperature; + double humidity; + int8_t retryCount; + ::ClosedCube_SHT31D sht; // I2C +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021.h b/lib/SuplaDevice/src/supla/sensor/Si7021.h new file mode 100644 index 00000000..055e3f6f --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/Si7021.h @@ -0,0 +1,87 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _si7021_h +#define _si7021_h + +// Dependency: Adafruid Si7021 library - use library manager to install it +// https://github.com/adafruit/Adafruit_Si7021 + +#include +#include "therm_hygro_meter.h" + +namespace Supla { +namespace Sensor { +class Si7021 : public ThermHygroMeter { + public: + Si7021() { + } + + double getTemp() { + float value = TEMPERATURE_NOT_AVAILABLE; + value = sensor.readTemperature(); + + if (isnan(value)) { + value = TEMPERATURE_NOT_AVAILABLE; + } + + return value; + } + + double getHumi() { + float value = HUMIDITY_NOT_AVAILABLE; + value = sensor.readHumidity(); + + if (isnan(value)) { + value = HUMIDITY_NOT_AVAILABLE; + } + + return value; + } + + void onInit() { + sensor.begin(); + + Serial.print(F("Found model ")); + switch (sensor.getModel()) { + case SI_Engineering_Samples: + Serial.print(F("SI engineering samples")); + break; + case SI_7013: + Serial.print(F("Si7013")); + break; + case SI_7020: + Serial.print(F("Si7020")); + break; + case SI_7021: + Serial.print(F("Si7021")); + break; + case SI_UNKNOWN: + default: + Serial.print(F("Unknown")); + } + + channel.setNewValue(getTemp(), getHumi()); + } + + protected: + ::Adafruit_Si7021 sensor; // I2C +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp new file mode 100644 index 00000000..c3391e25 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp @@ -0,0 +1,118 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "Si7021_sonoff.h" + +namespace Supla { +namespace Sensor { +Si7021Sonoff::Si7021Sonoff(int pin) + : temperature(TEMPERATURE_NOT_AVAILABLE), + humidity(HUMIDITY_NOT_AVAILABLE), + pin(pin), + retryCount(0) { +} + +double Si7021Sonoff::getTemp() { + return temperature; +} + +double Si7021Sonoff::getHumi() { + return humidity; +} + +void Si7021Sonoff::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + read(); + channel.setNewValue(getTemp(), getHumi()); + } +} + +void Si7021Sonoff::onInit() { + pinMode(pin, INPUT); + + delay(100); + read(); + channel.setNewValue(getTemp(), getHumi()); +} + +double Si7021Sonoff::readTemp(uint8_t* data) { + double temp = (((data[2] & 0x7F) << 8) | data[3]) * 0.1; + if (data[2] & 0x80) { + temp *= -1; + } + return temp; +} + +double Si7021Sonoff::readHumi(uint8_t* data) { + double humi = ((data[0] << 8) | data[1]) * 0.1; + return humi; +} + +bool Si7021Sonoff::read() { + uint8_t data[5] = {0}; + + yield(); + + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + delayMicroseconds(500); + digitalWrite(pin, HIGH); + delayMicroseconds(20); + pinMode(pin, INPUT); + + uint32_t i = 0; + if (waitState(0) && waitState(1) && waitState(0)) { + for (i = 0; i < 40; i++) { + if (!waitState(1)) { + break; + } + delayMicroseconds(35); + if (digitalRead(pin) == HIGH) { + data[i / 8] |= (1 << (7 - i % 8)); + } + if (!waitState(0)) { + break; + } + } + } + + uint8_t checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF; + if (i < 40 || data[4] != checksum) { + retryCount++; + if (retryCount > 3) { + retryCount = 0; + temperature = TEMPERATURE_NOT_AVAILABLE; + humidity = HUMIDITY_NOT_AVAILABLE; + } + } else { + retryCount = 0; + temperature = readTemp(data); + humidity = readHumi(data); + } +} + +bool Si7021Sonoff::waitState(bool state) { + unsigned long timeout = micros(); + while (micros() - timeout < 100) { + if (digitalRead(pin) == state) return true; + delayMicroseconds(1); + } + return false; +} + +}; // namespace Sensor +}; // namespace Supla \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h new file mode 100644 index 00000000..bac9667e --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h @@ -0,0 +1,50 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _si7021_sonoff_h +#define _si7021_sonoff_h + +#include +#include + + +namespace Supla { +namespace Sensor { +class Si7021Sonoff : public ThermHygroMeter { + public: + Si7021Sonoff(int pin); + double getTemp(); + double getHumi(); + + private: + void iterateAlways(); + void onInit(); + double readTemp(uint8_t* data); + double readHumi(uint8_t* data); + bool read(); + bool waitState(bool state); + + protected: + int8_t pin; + double temperature; + double humidity; + int8_t retryCount; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/binary.cpp b/lib/SuplaDevice/src/supla/sensor/binary.cpp new file mode 100644 index 00000000..162b9d0c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/binary.cpp @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "binary.h" +#include "../io.h" + +Supla::Sensor::Binary::Binary(int pin, bool pullUp = false) + : pin(pin), pullUp(pullUp), lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_SENSORNO); +} + +bool Supla::Sensor::Binary::getValue() { + return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == LOW ? false + : true; +} + +void Supla::Sensor::Binary::iterateAlways() { + if (lastReadTime + 100 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } +} + +void Supla::Sensor::Binary::onInit() { + Supla::Io::pinMode( + channel.getChannelNumber(), pin, pullUp ? INPUT_PULLUP : INPUT); + channel.setNewValue(getValue()); +} diff --git a/lib/SuplaDevice/src/supla/sensor/binary.h b/lib/SuplaDevice/src/supla/sensor/binary.h new file mode 100644 index 00000000..994ca7ae --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/binary.h @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _binary_h +#define _binary_h + +#include + +#include "../channel_element.h" + +namespace Supla { +namespace Sensor { +class Binary : public ChannelElement { + public: + Binary(int pin, bool pullUp); + bool getValue(); + void iterateAlways(); + void onInit(); + + protected: + int pin; + bool pullUp; + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/distance.h b/lib/SuplaDevice/src/supla/sensor/distance.h new file mode 100644 index 00000000..29488800 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/distance.h @@ -0,0 +1,52 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _distance_h +#define _distance_h + +#include "supla/channel_element.h" + +#define DISTANCE_NOT_AVAILABLE -1.0 + +namespace Supla { +namespace Sensor { +class Distance : public ChannelElement { + public: + Distance() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_DISTANCESENSOR); + channel.setDefault(SUPLA_CHANNELFNC_DISTANCESENSOR); + channel.setNewValue(DISTANCE_NOT_AVAILABLE); + } + + virtual double getValue() { + return DISTANCE_NOT_AVAILABLE; + } + + void iterateAlways() { + if (lastReadTime + 500 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp b/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp new file mode 100644 index 00000000..13fbf36a --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp @@ -0,0 +1,260 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "electricity_meter.h" + +Supla::Sensor::ElectricityMeter::ElectricityMeter() + : valueChanged(false), lastReadTime(0), refreshRateSec(5) { + extChannel.setType(SUPLA_CHANNELTYPE_ELECTRICITY_METER); + extChannel.setDefault(SUPLA_CHANNELFNC_ELECTRICITY_METER); + memset(&emValue, 0, sizeof(emValue)); + emValue.period = 5; + for (int i = 0; i < MAX_PHASES; i++) { + rawCurrent[i] = 0; + } + currentMeasurementAvailable = false; +} + +void Supla::Sensor::ElectricityMeter::updateChannelValues() { + if (!valueChanged) { + return; + } + valueChanged = false; + + emValue.m_count = 1; + + // Update current messurement precision based on last updates + if (currentMeasurementAvailable) { + bool over65A = false; + for (int i = 0; i < MAX_PHASES; i++) { + if (rawCurrent[i] > 65000) { + over65A = true; + } + } + + for (int i = 0; i < MAX_PHASES; i++) { + if (over65A) { + emValue.m[0].current[i] = rawCurrent[i] / 10; + } else { + emValue.m[0].current[i] = rawCurrent[i]; + } + } + + if (over65A) { + emValue.measured_values ^= (!EM_VAR_CURRENT); + emValue.measured_values |= EM_VAR_CURRENT_OVER_65A; + } else { + emValue.measured_values ^= (!EM_VAR_CURRENT_OVER_65A); + emValue.measured_values |= EM_VAR_CURRENT; + } + } + + // Prepare extended channel value + srpc_evtool_v2_emextended2extended(&emValue, extChannel.getExtValue()); + extChannel.setNewValue(emValue); +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setFwdActEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_forward_active_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_forward_active_energy[phase] = energy; + emValue.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setRvrActEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_reverse_active_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_reverse_active_energy[phase] = energy; + emValue.measured_values |= EM_VAR_REVERSE_ACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setFwdReactEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_forward_reactive_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_forward_reactive_energy[phase] = energy; + emValue.measured_values |= EM_VAR_FORWARD_REACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setRvrReactEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_reverse_reactive_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_reverse_reactive_energy[phase] = energy; + emValue.measured_values |= EM_VAR_REVERSE_REACTIVE_ENERGY; + } +} + +// voltage in 0.01 V +void Supla::Sensor::ElectricityMeter::setVoltage( + int phase, unsigned _supla_int16_t voltage) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].voltage[phase] != voltage) { + valueChanged = true; + } + emValue.m[0].voltage[phase] = voltage; + emValue.measured_values |= EM_VAR_VOLTAGE; + } +} + +// current in 0.001 A +void Supla::Sensor::ElectricityMeter::setCurrent( + int phase, unsigned _supla_int_t current) { + if (phase >= 0 && phase < MAX_PHASES) { + if (rawCurrent[phase] != current) { + valueChanged = true; + } + rawCurrent[phase] = current; + currentMeasurementAvailable = true; + } +} + +// Frequency in 0.01 Hz +void Supla::Sensor::ElectricityMeter::setFreq(unsigned _supla_int16_t freq) { + if (emValue.m[0].freq != freq) { + valueChanged = true; + } + emValue.m[0].freq = freq; + emValue.measured_values |= EM_VAR_FREQ; +} + +// power in 0.00001 kW +void Supla::Sensor::ElectricityMeter::setPowerActive(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_active[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_active[phase] = power; + emValue.measured_values |= EM_VAR_POWER_ACTIVE; + } +} + +// power in 0.00001 kvar +void Supla::Sensor::ElectricityMeter::setPowerReactive(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_reactive[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_reactive[phase] = power; + emValue.measured_values |= EM_VAR_POWER_REACTIVE; + } +} + +// power in 0.00001 kVA +void Supla::Sensor::ElectricityMeter::setPowerApparent(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_apparent[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_apparent[phase] = power; + emValue.measured_values |= EM_VAR_POWER_APPARENT; + } +} + +// power in 0.001 +void Supla::Sensor::ElectricityMeter::setPowerFactor(int phase, + _supla_int_t powerFactor) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_factor[phase] != powerFactor) { + valueChanged = true; + } + emValue.m[0].power_factor[phase] = powerFactor; + emValue.measured_values |= EM_VAR_POWER_FACTOR; + } +} + +// phase angle in 0.1 degree +void Supla::Sensor::ElectricityMeter::setPhaseAngle(int phase, + _supla_int_t phaseAngle) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].phase_angle[phase] != phaseAngle) { + valueChanged = true; + } + emValue.m[0].phase_angle[phase] = phaseAngle; + emValue.measured_values |= EM_VAR_PHASE_ANGLE; + } +} + +void Supla::Sensor::ElectricityMeter::resetReadParameters() { + if (emValue.measured_values != 0) { + emValue.measured_values = 0; + memset(&emValue.m[0], 0, sizeof(TElectricityMeter_Measurement)); + valueChanged = true; + } +} + +// Please implement this class for reading value from elecricity meter device. +// It will be called every 5 s. Use set methods defined above in order to +// set values on channel. Don't use any other method to modify channel values. +void Supla::Sensor::ElectricityMeter::readValuesFromDevice() { +} + +// Put here initialization code for electricity meter device. +// It will be called within SuplaDevce.begin method. +// It should also read first data set, so at the end it should call those two +// methods: +// readValuesFromDevice(); +// updateChannelValues(); +void Supla::Sensor::ElectricityMeter::onInit() { +} + +void Supla::Sensor::ElectricityMeter::iterateAlways() { + if (millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + readValuesFromDevice(); + updateChannelValues(); + } +} + +// Implement this method to reset stored energy value (i.e. to set energy +// counter back to 0 kWh +void Supla::Sensor::ElectricityMeter::resetStorage() { +} + +Supla::Channel *Supla::Sensor::ElectricityMeter::getChannel() { + return &extChannel; +} + +void Supla::Sensor::ElectricityMeter::setResreshRate(unsigned int sec) { + refreshRateSec = sec; + if (refreshRateSec == 0) { + refreshRateSec = 1; + } +} + + diff --git a/lib/SuplaDevice/src/supla/sensor/electricity_meter.h b/lib/SuplaDevice/src/supla/sensor/electricity_meter.h new file mode 100644 index 00000000..e67bd517 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/electricity_meter.h @@ -0,0 +1,111 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _electricity_meter_h +#define _electricity_meter_h + +#include + +#include "../channel_extended.h" +#include "../element.h" +#include + +#define MAX_PHASES 3 + +namespace Supla { +namespace Sensor { +class ElectricityMeter : public Element { + public: + ElectricityMeter(); + + virtual void updateChannelValues(); + + // energy in 0.00001 kWh + void setFwdActEnergy(int phase, unsigned _supla_int64_t energy); + + // energy in 0.00001 kWh + void setRvrActEnergy(int phase, unsigned _supla_int64_t energy); + + // energy in 0.00001 kWh + void setFwdReactEnergy(int phase, unsigned _supla_int64_t energy); + + // energy in 0.00001 kWh + void setRvrReactEnergy(int phase, unsigned _supla_int64_t energy); + + // voltage in 0.01 V + void setVoltage(int phase, unsigned _supla_int16_t voltage); + + // current in 0.001 A + void setCurrent(int phase, unsigned _supla_int_t current); + + // Frequency in 0.01 Hz + void setFreq(unsigned _supla_int16_t freq); + + // power in 0.00001 kW + void setPowerActive(int phase, _supla_int_t power); + + // power in 0.00001 kvar + void setPowerReactive(int phase, _supla_int_t power); + + // power in 0.00001 kVA + void setPowerApparent(int phase, _supla_int_t power); + + // power in 0.001 + void setPowerFactor(int phase, _supla_int_t powerFactor); + + // phase angle in 0.1 degree + void setPhaseAngle(int phase, _supla_int_t phaseAngle); + + void resetReadParameters(); + + // Please implement this class for reading value from elecricity meter device. + // It will be called every 5 s. Use set methods defined above in order to + // set values on channel. Don't use any other method to modify channel values. + virtual void readValuesFromDevice(); + + // Put here initialization code for electricity meter device. + // It will be called within SuplaDevce.begin method. + // It should also read first data set, so at the end it should call those two + // methods: + // readValuesFromDevice(); + // updateChannelValues(); + void onInit(); + + void iterateAlways(); + + // Implement this method to reset stored energy value (i.e. to set energy + // counter back to 0 kWh + virtual void resetStorage(); + + void setResreshRate(unsigned int sec); + + Channel *getChannel(); + + protected: + TElectricityMeter_ExtendedValue_V2 emValue; + ChannelExtended extChannel; + unsigned _supla_int_t rawCurrent[MAX_PHASES]; + bool valueChanged; + bool currentMeasurementAvailable; + unsigned long lastReadTime; + unsigned int refreshRateSec; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif + diff --git a/lib/SuplaDevice/src/supla/sensor/esp_free_heap.h b/lib/SuplaDevice/src/supla/sensor/esp_free_heap.h new file mode 100644 index 00000000..1e42910d --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/esp_free_heap.h @@ -0,0 +1,40 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _esp_free_heap_h +#define _esp_free_heap_h + +#include "supla/sensor/thermometer.h" + +namespace Supla { +namespace Sensor { +class EspFreeHeap : public Thermometer { + public: + void onInit() { + channel.setNewValue(getValue()); + } + + double getValue() { + return ESP.getFreeHeap() / 1024.0; + } + + protected: +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h b/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h new file mode 100644 index 00000000..96afc2ec --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h @@ -0,0 +1,46 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _general_purpose_measurement_h +#define _general_purpose_measurement_h + +#include "supla/channel_element.h" + +namespace Supla { +namespace Sensor { +class GeneralPurposeMeasurementBase : public ChannelElement { + public: + GeneralPurposeMeasurementBase() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_GENERAL_PURPOSE_MEASUREMENT); + } + + virtual double getValue() = 0; + + void iterateAlways() { + if (lastReadTime + 1000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp b/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp new file mode 100644 index 00000000..9e641c4a --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp @@ -0,0 +1,111 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include + +#include "impulse_counter.h" + +using namespace Supla::Sensor; + +ImpulseCounter::ImpulseCounter(int _impulsePin, + bool _detectLowToHigh, + bool _inputPullup, + unsigned int _debounceDelay) + : impulsePin(_impulsePin), + lastImpulseMillis(0), + debounceDelay(_debounceDelay), + detectLowToHigh(_detectLowToHigh), + inputPullup(_inputPullup), + counter(0) { + channel.setType(SUPLA_CHANNELTYPE_IMPULSE_COUNTER); + + prevState = (detectLowToHigh == true ? LOW : HIGH); + + supla_log(LOG_DEBUG, + "Creating Impulse Counter: impulsePin(%d), " + "delay(%d ms)", + impulsePin, + debounceDelay); + if (impulsePin <= 0) { + supla_log(LOG_DEBUG, + "SuplaImpulseCounter ERROR - incorrect impulse pin number"); + return; + } +} + +void ImpulseCounter::onInit() { + if (inputPullup) { + Supla::Io::pinMode(channel.getChannelNumber(), impulsePin, INPUT_PULLUP); + } else { + Supla::Io::pinMode(channel.getChannelNumber(), impulsePin, INPUT); + } +} + +_supla_int64_t ImpulseCounter::getCounter() { + return counter; +} + +void ImpulseCounter::onSaveState() { + Supla::Storage::WriteState((unsigned char *)&counter, sizeof(counter)); +} + +void ImpulseCounter::onLoadState() { + _supla_int64_t data; + if (Supla::Storage::ReadState((unsigned char *)&data, sizeof(data))) { + setCounter(data); + } +} + +void ImpulseCounter::setCounter(_supla_int64_t value) { + counter = value; + channel.setNewValue(value); + supla_log(LOG_DEBUG, + "ImpulseCounter[%d] - set counter to %d", + channel.getChannelNumber(), + static_cast(counter)); +} + +void ImpulseCounter::incCounter() { + counter++; + channel.setNewValue(getCounter()); +} + +void ImpulseCounter::onFastTimer() { + int currentState = Supla::Io::digitalRead(channel.getChannelNumber(), impulsePin); + if (prevState == (detectLowToHigh == true ? LOW : HIGH)) { + if (millis() - lastImpulseMillis > debounceDelay) { + if (currentState == (detectLowToHigh == true ? HIGH : LOW)) { + incCounter(); + lastImpulseMillis = millis(); + } + } + } + prevState = currentState; +} + +void ImpulseCounter::handleAction(int event, int action) { + (void)(event); + switch (action) { + case RESET: { + setCounter(0); + break; + } + } +} diff --git a/lib/SuplaDevice/src/supla/sensor/impulse_counter.h b/lib/SuplaDevice/src/supla/sensor/impulse_counter.h new file mode 100644 index 00000000..ef99708c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/impulse_counter.h @@ -0,0 +1,66 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_impulse_counter_h_ +#define _supla_impulse_counter_h_ + +#include +#include +#include + +namespace Supla { +namespace Sensor { +class ImpulseCounter : public ChannelElement, public ActionHandler { + public: + ImpulseCounter(int _impulsePin, + bool _detectLowToHigh = false, + bool inputPullup = true, + unsigned int _debounceDelay = 10); + + void onInit(); + void onLoadState(); + void onSaveState(); + void onFastTimer(); + void handleAction(int event, int action); + + // Returns value of a counter at given Supla channel + _supla_int64_t getCounter(); + + // Set counter to a given value + void setCounter(_supla_int64_t value); + + // Increment the counter by 1 + void incCounter(); + + protected: + int prevState; // Store previous state of pin (LOW/HIGH). It is used to track + // changes on pin state. + int impulsePin; // Pin where impulses are counted + + unsigned long + lastImpulseMillis; // Stores timestamp of last impulse (used to ignore + // changes of state during 10 ms timeframe) + unsigned int debounceDelay; + bool detectLowToHigh; // defines if we count raining (LOW to HIGH) or falling + // (HIGH to LOW) edge + bool inputPullup; + + unsigned _supla_int64_t counter; // Actual count of impulses +}; +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/normally_open.h b/lib/SuplaDevice/src/supla/sensor/normally_open.h new file mode 100644 index 00000000..bfa605ac --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/normally_open.h @@ -0,0 +1,33 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _normally_open_h +#define _normally_open_h + +#include "binary.h" + +namespace Supla { +namespace Sensor { +class NormallyOpen : public Binary { + public: + NormallyOpen(int pin, bool pullUp = false) : Binary(pin, pullUp) { + } +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/one_phase_electricity_meter.h b/lib/SuplaDevice/src/supla/sensor/one_phase_electricity_meter.h new file mode 100644 index 00000000..3d271582 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/one_phase_electricity_meter.h @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _one_phase_electricity_meter_h +#define _one_phase_electricity_meter_h + +#include "electricity_meter.h" + +namespace Supla { +namespace Sensor { +class OnePhaseElectricityMeter : public ElectricityMeter { + public: + OnePhaseElectricityMeter() { + extChannel.setFlag(SUPLA_CHANNEL_FLAG_PHASE2_UNSUPPORTED); + extChannel.setFlag(SUPLA_CHANNEL_FLAG_PHASE3_UNSUPPORTED); + } + + virtual void readValuesFromDevice() { + } + + void onInit() { + } + +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/pressure.h b/lib/SuplaDevice/src/supla/sensor/pressure.h new file mode 100644 index 00000000..b9c2653c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/pressure.h @@ -0,0 +1,52 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _pressure_h +#define _pressure_h + +#include "supla/channel_element.h" + +#define PRESSURE_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class Pressure : public ChannelElement { + public: + Pressure() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); + channel.setDefault(SUPLA_CHANNELFNC_PRESSURESENSOR); + channel.setNewValue(PRESSURE_NOT_AVAILABLE); + } + + virtual double getValue() { + return PRESSURE_NOT_AVAILABLE; + } + + void iterateAlways() { + if (lastReadTime + 10000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/rain.h b/lib/SuplaDevice/src/supla/sensor/rain.h new file mode 100644 index 00000000..4fdbe177 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/rain.h @@ -0,0 +1,52 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _rain_h +#define _rain_h + +#include "supla/channel_element.h" + +#define RAIN_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class Rain: public ChannelElement { + public: + Rain() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_RAINSENSOR); + channel.setDefault(SUPLA_CHANNELFNC_RAINSENSOR); + channel.setNewValue(RAIN_NOT_AVAILABLE); + } + + virtual double getValue() { + return RAIN_NOT_AVAILABLE; + } + + void iterateAlways() { + if (lastReadTime + 10000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp new file mode 100644 index 00000000..b9604cfd --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "therm_hygro_meter.h" + +Supla::Sensor::ThermHygroMeter::ThermHygroMeter() { + channel.setType(SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR); + channel.setDefault(SUPLA_CHANNELFNC_HUMIDITYANDTEMPERATURE); +} + +double Supla::Sensor::ThermHygroMeter::getTemp() { + return TEMPERATURE_NOT_AVAILABLE; +} + +double Supla::Sensor::ThermHygroMeter::getHumi() { + return HUMIDITY_NOT_AVAILABLE; +} + +void Supla::Sensor::ThermHygroMeter::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + channel.setNewValue(getTemp(), getHumi()); + } +} diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h new file mode 100644 index 00000000..ab79fea1 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h @@ -0,0 +1,38 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _therm_hygro_meter_h +#define _therm_hygro_meter_h + +#include "thermometer.h" + +#define HUMIDITY_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class ThermHygroMeter : public Thermometer { + public: + ThermHygroMeter(); + virtual double getTemp(); + virtual double getHumi(); + void iterateAlways(); + +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp new file mode 100644 index 00000000..409fc14c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "therm_hygro_press_meter.h" + +Supla::Sensor::ThermHygroPressMeter::ThermHygroPressMeter() { + pressureChannel.setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); + pressureChannel.setDefault(SUPLA_CHANNELFNC_PRESSURESENSOR); +} + +double Supla::Sensor::ThermHygroPressMeter::getPressure() { + return PRESSURE_NOT_AVAILABLE; +} + +void Supla::Sensor::ThermHygroPressMeter::iterateAlways() { + if (millis() - lastReadTime > 10000) { + pressureChannel.setNewValue(getPressure()); + } + ThermHygroMeter::iterateAlways(); +} + +bool Supla::Sensor::ThermHygroPressMeter::iterateConnected(void *srpc) { + bool response = true; + if (pressureChannel.isUpdateReady() && + millis() - pressureChannel.lastCommunicationTimeMs > 100) { + pressureChannel.lastCommunicationTimeMs = millis(); + pressureChannel.sendUpdate(srpc); + response = false; + } + + if (!Element::iterateConnected(srpc)) { + response = false; + } + return response; +} + +Supla::Element &Supla::Sensor::ThermHygroPressMeter::disableChannelState() { + pressureChannel.unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); + return ThermHygroMeter::disableChannelState(); +} + +Supla::Channel *Supla::Sensor::ThermHygroPressMeter::getSecondaryChannel() { + return &pressureChannel; +} diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h new file mode 100644 index 00000000..77d1156a --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _therm_hygro_press_meter_h +#define _therm_hygro_press_meter_h + +#include "therm_hygro_meter.h" + +#define PRESSURE_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class ThermHygroPressMeter : public ThermHygroMeter { + public: + ThermHygroPressMeter(); + virtual double getPressure(); + void iterateAlways(); + bool iterateConnected(void *srpc); + Element &disableChannelState(); + Channel *getSecondaryChannel(); + + protected: + Channel pressureChannel; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/thermometer.cpp b/lib/SuplaDevice/src/supla/sensor/thermometer.cpp new file mode 100644 index 00000000..e9e8ccbb --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/thermometer.cpp @@ -0,0 +1,34 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "thermometer.h" + +Supla::Sensor::Thermometer::Thermometer() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_THERMOMETER); + channel.setDefault(SUPLA_CHANNELFNC_THERMOMETER); +} + +double Supla::Sensor::Thermometer::getValue() { + return TEMPERATURE_NOT_AVAILABLE; +} + +void Supla::Sensor::Thermometer::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } +} + diff --git a/lib/SuplaDevice/src/supla/sensor/thermometer.h b/lib/SuplaDevice/src/supla/sensor/thermometer.h new file mode 100644 index 00000000..8be2f28a --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/thermometer.h @@ -0,0 +1,40 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _thermometer_h +#define _thermometer_h + +#include +#include "supla/channel_element.h" + +#define TEMPERATURE_NOT_AVAILABLE -275 + +namespace Supla { +namespace Sensor { +class Thermometer : public ChannelElement { + public: + Thermometer(); + virtual double getValue(); + void iterateAlways(); + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/three_phase_PzemV3.h b/lib/SuplaDevice/src/supla/sensor/three_phase_PzemV3.h new file mode 100644 index 00000000..52fb5eeb --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/three_phase_PzemV3.h @@ -0,0 +1,108 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _three_phase_PzemV3_h +#define _three_phase_PzemV3_h + +#include +// dependence: Arduino library for the Updated PZEM-004T v3.0 Power and Energy +// meter https://github.com/mandulaj/PZEM-004T-v30 +#include +#include + +#include "electricity_meter.h" + +namespace Supla { +namespace Sensor { + +class ThreePhasePZEMv3 : public ElectricityMeter { + public: + ThreePhasePZEMv3(int8_t pinRX1, + int8_t pinTX1, + int8_t pinRX2, + int8_t pinTX2, + int8_t pinRX3, + int8_t pinTX3) + : pzem{PZEM004Tv30(pinRX1, pinTX1), + PZEM004Tv30(pinRX2, pinTX2), + PZEM004Tv30(pinRX3, pinTX3)} { + } + + ThreePhasePZEMv3(HardwareSerial *serial1, + HardwareSerial *serial2, + HardwareSerial *serial3) + : pzem{PZEM004Tv30(serial1), + PZEM004Tv30(serial2), + PZEM004Tv30(serial3)} { + } + + void onInit() { + readValuesFromDevice(); + updateChannelValues(); + } + + virtual void readValuesFromDevice() { + bool atLeatOnePzemWasRead = false; + for (int i = 0; i < 3; i++) { + float current = pzem[i].current(); + // If current reading is NAN, we assume that PZEM there is no valid + // communication with PZEM. Sensor shouldn't show any data + if (isnan(current)) { + continue; + } + + atLeatOnePzemWasRead = true; + + float voltage = pzem[i].voltage(); + float active = pzem[i].power(); + float apparent = (voltage * current); + float reactive = 0; + if (apparent > active) { + reactive = sqrt(apparent * apparent - active * active); + } else { + reactive = 0; + } + + setVoltage(i, voltage * 100); + setCurrent(i, current * 1000); + setPowerActive(i, active * 100000); + setFwdActEnergy(i, pzem[i].energy() * 100000); + setPowerFactor(i, pzem[i].pf() * 1000); + setPowerApparent(i, apparent * 100000); + setPowerReactive(i, reactive * 10000); + + setFreq(pzem[i].frequency() * 100); + } + + if (!atLeatOnePzemWasRead) { + resetReadParameters(); + } + } + + void resetStorage() { + for (int i = 0; i < 3; i++) { + pzem[i].resetEnergy(); + } + } + + protected: + PZEM004Tv30 pzem[3]; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp b/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp new file mode 100644 index 00000000..04240e0d --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "virtual_binary.h" + +namespace Supla { +namespace Sensor { + +VirtualBinary::VirtualBinary() : state(false), lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_SENSORNO); +} + +bool VirtualBinary::getValue() { + return state; +} + +void VirtualBinary::iterateAlways() { + if (millis() - lastReadTime > 100) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } +} + +void VirtualBinary::onInit() { + channel.setNewValue(getValue()); +} + +void VirtualBinary::handleAction(int event, int action) { + (void)(event); + switch (action) { + case SET: { + state = true; + break; + } + case CLEAR: { + state = false; + break; + } + case TOGGLE: { + state = !state; + break; + } + } +} + +}; // namespace Sensor +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/virtual_binary.h b/lib/SuplaDevice/src/supla/sensor/virtual_binary.h new file mode 100644 index 00000000..94521b89 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/virtual_binary.h @@ -0,0 +1,44 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _virtual_binary_h +#define _virtual_binary_h + +#include + +#include "../channel_element.h" +#include "../action_handler.h" +#include "../actions.h" + +namespace Supla { +namespace Sensor { +class VirtualBinary : public ChannelElement, public ActionHandler { + public: + VirtualBinary(); + bool getValue(); + void iterateAlways(); + void onInit(); + void handleAction(int event, int action); + + protected: + bool state; + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/weight.h b/lib/SuplaDevice/src/supla/sensor/weight.h new file mode 100644 index 00000000..89a8efb6 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/weight.h @@ -0,0 +1,53 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _weight_h +#define _weight_h + +#include "supla/channel_element.h" +#include "supla/element.h" + +#define WEIGHT_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class Weight : public ChannelElement { + public: + Weight() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_WEIGHTSENSOR); + channel.setDefault(SUPLA_CHANNELFNC_WEIGHTSENSOR); + channel.setNewValue(WEIGHT_NOT_AVAILABLE); + } + + virtual double getValue() { + return WEIGHT_NOT_AVAILABLE; + } + + void iterateAlways() { + if (lastReadTime + 10000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/wind.h b/lib/SuplaDevice/src/supla/sensor/wind.h new file mode 100644 index 00000000..c8c147a7 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/wind.h @@ -0,0 +1,52 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _wind_h +#define _wind_h + +#include "supla/channel_element.h" + +#define WIND_NOT_AVAILABLE -1 + +namespace Supla { +namespace Sensor { +class Wind: public ChannelElement { + public: + Wind() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_WINDSENSOR); + channel.setDefault(SUPLA_CHANNELFNC_WINDSENSOR); + channel.setNewValue(WIND_NOT_AVAILABLE); + } + + virtual double getValue() { + return WIND_NOT_AVAILABLE; + } + + void iterateAlways() { + if (lastReadTime + 10000 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } + } + + protected: + unsigned long lastReadTime; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/status.cpp b/lib/SuplaDevice/src/supla/status.cpp new file mode 100644 index 00000000..03550de5 --- /dev/null +++ b/lib/SuplaDevice/src/supla/status.cpp @@ -0,0 +1,15 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ diff --git a/lib/SuplaDevice/src/supla/status.h b/lib/SuplaDevice/src/supla/status.h new file mode 100644 index 00000000..4cbec2b9 --- /dev/null +++ b/lib/SuplaDevice/src/supla/status.h @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_status_h +#define _supla_status_h + +namespace Supla { +class Status { + public: + void notInitialized(); + void initialized(); + + void alreadyInitialized(); + void channelLimitExceeded(); + void missingNetworkInterface(); + void invalidGuid(); + void missingCredentials(); + void unknownServerAddress(); + + void connectingToNetworkInterface(); + void connectingToSuplaServer(); + void registerInProgress(); + void registeredAndReady(); + + void networkDisconnected(); + void serverDisconnected(); + + + void iterateFail(); + void protocolVersionError(); + void badCredentials(); + void temporarilyUnavailable(); + void locationConflict(); + void channelConflict(); + void deviceIsDisabled(); + void locationIsDisabled(); + void deviceLimitExceeded(); + void registrationDisabled(); + + +}; +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/storage/eeprom.cpp b/lib/SuplaDevice/src/supla/storage/eeprom.cpp new file mode 100644 index 00000000..79cd9f7b --- /dev/null +++ b/lib/SuplaDevice/src/supla/storage/eeprom.cpp @@ -0,0 +1,83 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include "eeprom.h" + +using namespace Supla; + +// By default, write to EEPROM every 3 min +#define SUPLA_EEPROM_WRITING_PERIOD 3*60*1000 + +Eeprom::Eeprom(unsigned int storageStartingOffset) + : Storage(storageStartingOffset), + dataChanged(false) { + setStateSavePeriod((unsigned long)SUPLA_EEPROM_WRITING_PERIOD); +} + +bool Eeprom::init() { +#if defined(ARDUINO_ARCH_ESP8266) + EEPROM.begin(1024); +#elif defined(ARDUINO_ARCH_ESP32) + EEPROM.begin(512); +#endif + delay(15); + + return Storage::init(); +} + +int Eeprom::readStorage(unsigned int offset, unsigned char *buf, int size, bool logs) { + if (logs) { + Serial.print(F("readStorage: ")); + Serial.print(size); + Serial.print(F("; Read: [")); + } + for (int i = 0; i < size; i++) { + buf[i] = EEPROM.read(offset + i); + if (logs) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + } + if (logs) { + Serial.println(F("]")); + } + return size; +} + +int Eeprom::writeStorage(unsigned int offset, const unsigned char *buf, int size) { + dataChanged = true; + for (int i = 0; i < size; i++) { + EEPROM.write(offset + i, buf[i]); + } + Serial.print(F("Wrote ")); + Serial.print(size); + Serial.print(F(" bytes to storage at ")); + Serial.println(offset); + return size; +} + +void Eeprom::commit() { +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + if (dataChanged) { + EEPROM.commit(); + Serial.println(F("Commit")); + } +#endif + dataChanged = false; +} diff --git a/lib/SuplaDevice/src/supla/storage/eeprom.h b/lib/SuplaDevice/src/supla/storage/eeprom.h new file mode 100644 index 00000000..b9699ada --- /dev/null +++ b/lib/SuplaDevice/src/supla/storage/eeprom.h @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_eeprom_h +#define _supla_eeprom_h + +#include "storage.h" + +namespace Supla { + +class Eeprom : public Storage { + public: + Eeprom(unsigned int storageStartingOffset = 0); + bool init(); + void commit(); + + protected: + int readStorage(unsigned int, unsigned char *, int, bool); + int writeStorage(unsigned int, const unsigned char *, int); + + bool dataChanged; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/storage/fram_spi.h b/lib/SuplaDevice/src/supla/storage/fram_spi.h new file mode 100644 index 00000000..d35e02a3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/storage/fram_spi.h @@ -0,0 +1,99 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This extension depends on Adafruit FRAM SPI library + * Please install it from librarary manager in Arduino + */ + +#ifndef _supla_fram_spi_h +#define _supla_fram_spi_h + +#include + +#include "Adafruit_FRAM_SPI.h" +#include "storage.h" + +#define SUPLA_FRAM_WRITING_PERIOD 1000 + +namespace Supla { + +class FramSpi : public Storage { + public: + FramSpi(int8_t clk, + int8_t miso, + int8_t mosi, + int8_t framCs, + unsigned int storageStartingOffset = 0) + : Storage(storageStartingOffset), + fram(clk, miso, mosi, framCs) { + setStateSavePeriod(SUPLA_FRAM_WRITING_PERIOD); + } + + FramSpi(int8_t framCs, unsigned int storageStartingOffset = 0) + : Storage(storageStartingOffset), fram(framCs) { + setStateSavePeriod(SUPLA_FRAM_WRITING_PERIOD); + } + + bool init() { + if (fram.begin()) { + Serial.println(F("Storage: FRAM found")); + } else { + Serial.println(F("Storage: FRAM not found")); + } + + return Storage::init(); + } + + void commit(){}; + + protected: + int readStorage(unsigned int offset, unsigned char *buf, int size, bool logs) { + if (logs) { + Serial.print(F("readStorage: ")); + Serial.print(size); + Serial.print(F("; Read: [")); + } + for (int i = 0; i < size; i++) { + buf[i] = fram.read8(offset + i); + if (logs) { + Serial.print(static_cast(buf)[i], HEX); + Serial.print(F(" ")); + } + } + if (logs) { + Serial.println(F("]")); + } + return size; + } + + int writeStorage(unsigned int offset, const unsigned char *buf, int size) { + fram.writeEnable(true); + fram.write(offset, const_cast(buf), size); + fram.writeEnable(false); + Serial.print(F("Wrote ")); + Serial.print(size); + Serial.print(F(" bytes to storage at ")); + Serial.println(offset); + return size; + } + + Adafruit_FRAM_SPI fram; +}; + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/storage/storage.cpp b/lib/SuplaDevice/src/supla/storage/storage.cpp new file mode 100644 index 00000000..8e21d098 --- /dev/null +++ b/lib/SuplaDevice/src/supla/storage/storage.cpp @@ -0,0 +1,335 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "storage.h" + +#define SUPLA_STORAGE_VERSION 1 + +using namespace Supla; + +Storage *Storage::instance = nullptr; + +Storage *Storage::Instance() { + return instance; +} + +bool Storage::Init() { + if (Instance()) { + return Instance()->init(); + } + return false; +} + +bool Storage::ReadState(unsigned char *buf, int size) { + if (Instance()) { + return Instance()->readState(buf, size); + } + return false; +} + +bool Storage::WriteState(const unsigned char *buf, int size) { + if (Instance()) { + return Instance()->writeState(buf, size); + } + return false; +} + +bool Storage::LoadDeviceConfig() { + if (Instance()) { + return Instance()->loadDeviceConfig(); + } + return false; +} + +bool Storage::LoadElementConfig() { + if (Instance()) { + return Instance()->loadElementConfig(); + } + return false; +} + +void Storage::PrepareState(bool dryRun) { + if (Instance()) { + Instance()->prepareState(dryRun); + } +} + +bool Storage::FinalizeSaveState() { + if (Instance()) { + return Instance()->finalizeSaveState(); + } + return false; +} + +bool Storage::SaveStateAllowed(unsigned long ms) { + if (Instance()) { + return Instance()->saveStateAllowed(ms); + } + return false; +} + +void Storage::ScheduleSave(unsigned long delayMs) { + if (Instance()) { + Instance()->scheduleSave(delayMs); + } +} + +Storage::Storage(unsigned int storageStartingOffset) + : storageStartingOffset(storageStartingOffset), + deviceConfigOffset(0), + elementConfigOffset(0), + elementStateOffset(0), + deviceConfigSize(0), + elementConfigSize(0), + elementStateSize(0), + currentStateOffset(0), + newSectionSize(0), + sectionsCount(0), + dryRun(false), + saveStatePeriod(1000), + lastWriteTimestamp(0) { + instance = this; +} + +void Storage::prepareState(bool performDryRun) { + dryRun = performDryRun; + newSectionSize = 0; + currentStateOffset = elementStateOffset + sizeof(SectionPreamble); +} + +bool Storage::readState(unsigned char *buf, int size) { + if (elementStateOffset + sizeof(SectionPreamble) + elementStateSize < + currentStateOffset + size) { + Serial.println(F("Warning! Attempt to read state outside of section size")); + return false; + } + currentStateOffset += readStorage(currentStateOffset, buf, size); + return true; +} + +bool Storage::writeState(const unsigned char *buf, int size) { + newSectionSize += size; + + if (size == 0) { + return true; + } + + if (elementStateSize > 0 && + elementStateOffset + sizeof(SectionPreamble) + elementStateSize < + currentStateOffset + size) { + Serial.println( + F("Warning! Attempt to write state outside of section size.")); + Serial.println( + F("Storage: rewriting element state section. All data will be lost.")); + elementStateSize = 0; + elementStateOffset = 0; + return false; + } + + if (dryRun) { + currentStateOffset += size; + return true; + } + + // Calculation of offset for section data - in case sector is missing + if (elementStateOffset == 0) { + Serial.print(F("Initialization of elementStateOffset: ")); + elementStateOffset = storageStartingOffset + sizeof(Preamble); + if (deviceConfigOffset != 0) { + elementStateOffset += sizeof(SectionPreamble) + deviceConfigSize; + } + if (elementConfigOffset != 0) { + elementStateOffset += sizeof(SectionPreamble) + elementConfigSize; + } + Serial.println(elementStateOffset); + + currentStateOffset = elementStateOffset + sizeof(SectionPreamble); + + sectionsCount++; + + // Update Storage preamble with new section count + Serial.println(F("Update Storage preamble")); + unsigned char suplaTag[] = {'S', 'U', 'P', 'L', 'A'}; + Preamble preamble; + memcpy(preamble.suplaTag, suplaTag, 5); + preamble.version = SUPLA_STORAGE_VERSION; + preamble.sectionsCount = sectionsCount; + + updateStorage( + storageStartingOffset, (unsigned char *)&preamble, sizeof(preamble)); + } + + currentStateOffset += updateStorage(currentStateOffset, buf, size); + + return true; +} + +bool Storage::finalizeSaveState() { + if (dryRun) { + dryRun = false; + if (elementStateSize != newSectionSize) { + Serial.println( + F("Element state section size doesn't match current device " + "configuration")); + elementStateOffset = 0; + elementStateSize = 0; + return false; + } + return true; + } + + SectionPreamble preamble; + preamble.type = STORAGE_SECTION_TYPE_ELEMENT_STATE; + preamble.size = newSectionSize; + preamble.crc1 = 0; + preamble.crc2 = 0; + // TODO add crc calculation + + updateStorage( + elementStateOffset, (unsigned char *)&preamble, sizeof(preamble)); + + commit(); + return true; +} + +bool Storage::init() { + Serial.println(F("Storage initialization")); + unsigned int currentOffset = storageStartingOffset; + Preamble preamble; + currentOffset += + readStorage(currentOffset, (unsigned char *)&preamble, sizeof(preamble)); + + unsigned char suplaTag[] = {'S', 'U', 'P', 'L', 'A'}; + + if (memcmp(suplaTag, preamble.suplaTag, 5)) { + Serial.println(F("Storage: missing Supla tag. Rewriting...")); + + memcpy(preamble.suplaTag, suplaTag, 5); + preamble.version = SUPLA_STORAGE_VERSION; + preamble.sectionsCount = 0; + + writeStorage( + storageStartingOffset, (unsigned char *)&preamble, sizeof(preamble)); + commit(); + + } else if (preamble.version != SUPLA_STORAGE_VERSION) { + Serial.print(F("Storage: storage version [")); + Serial.print(preamble.version); + Serial.println(F("] is not supported. Storage not initialized")); + return false; + } else { + Serial.print(F("Storage: Number of sections ")); + Serial.println(preamble.sectionsCount); + } + + if (preamble.sectionsCount == 0) { + return true; + } + + for (int i = 0; i < preamble.sectionsCount; i++) { + Serial.print(F("Reading section: ")); + Serial.println(i); + SectionPreamble section; + unsigned int sectionOffset = currentOffset; + currentOffset += + readStorage(currentOffset, (unsigned char *)§ion, sizeof(section)); + + Serial.print(F("Section type: ")); + Serial.print(static_cast(section.type)); + Serial.print(F("; size: ")); + Serial.println(section.size); + + if (section.crc1 != section.crc2) { + Serial.println( + F("Warning! CRC copies on section doesn't match. Please check your " + "storage hardware")); + } + + switch (section.type) { + case STORAGE_SECTION_TYPE_DEVICE_CONFIG: { + deviceConfigOffset = sectionOffset; + deviceConfigSize = section.size; + break; + } + case STORAGE_SECTION_TYPE_ELEMENT_CONFIG: { + elementConfigOffset = sectionOffset; + elementConfigSize = section.size; + break; + } + case STORAGE_SECTION_TYPE_ELEMENT_STATE: { + elementStateOffset = sectionOffset; + elementStateSize = section.size; + break; + } + default: { + Serial.println(F("Warning! Unknown section type")); + break; + } + } + currentOffset += section.size; + } + + return true; +} + +bool Storage::loadDeviceConfig() { + return true; +} + +bool Storage::loadElementConfig() { + return true; +} + +int Storage::updateStorage(unsigned int offset, const unsigned char *buf, int size) { + if (offset < storageStartingOffset) { + return 0; + } + + unsigned char currentData[size]; + readStorage(offset, currentData, size, false); + + if (memcmp(currentData, buf, size)) { + return writeStorage(offset, buf, size); + } + return size; +} + +void Storage::setStateSavePeriod(unsigned long periodMs) { + if (periodMs < 1000) { + saveStatePeriod = 1000; + } else { + saveStatePeriod = periodMs; + } +} + +bool Storage::saveStateAllowed(unsigned long ms) { + if (ms - lastWriteTimestamp > saveStatePeriod) { + lastWriteTimestamp = ms; + return true; + } + return false; +} + +void Storage::scheduleSave(unsigned long delayMs) { + unsigned long currentMs = millis(); + unsigned long newTimestamp = currentMs - saveStatePeriod - 1 + delayMs; + + if (currentMs - lastWriteTimestamp < currentMs - newTimestamp) { + lastWriteTimestamp = newTimestamp; + } +} diff --git a/lib/SuplaDevice/src/supla/storage/storage.h b/lib/SuplaDevice/src/supla/storage/storage.h new file mode 100644 index 00000000..f25a4091 --- /dev/null +++ b/lib/SuplaDevice/src/supla/storage/storage.h @@ -0,0 +1,102 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_storage_h +#define _supla_storage_h + +#include + +#define STORAGE_SECTION_TYPE_DEVICE_CONFIG 1 +#define STORAGE_SECTION_TYPE_ELEMENT_CONFIG 2 +#define STORAGE_SECTION_TYPE_ELEMENT_STATE 3 + +namespace Supla { + +class Storage { + public: + static Storage *Instance(); + static bool Init(); + static bool ReadState(unsigned char *, int); + static bool WriteState(const unsigned char *, int); + static bool LoadDeviceConfig(); + static bool LoadElementConfig(); + static void PrepareState(bool dryRun = false); + static bool FinalizeSaveState(); + static bool SaveStateAllowed(unsigned long); + static void ScheduleSave(unsigned long delayMs); + + Storage(unsigned int storageStartingOffset = 0); + + // Changes default state save period time + virtual void setStateSavePeriod(unsigned long periodMs); + + virtual bool init(); + virtual bool readState(unsigned char *, int); + virtual bool writeState(const unsigned char *, int); + + virtual bool loadDeviceConfig(); + virtual bool loadElementConfig(); + virtual void prepareState(bool performDryRun); + virtual bool finalizeSaveState(); + virtual bool saveStateAllowed(unsigned long); + virtual void scheduleSave(unsigned long delayMs); + + virtual void commit() = 0; + + protected: + virtual int readStorage(unsigned int, unsigned char *, int, bool = true) = 0; + virtual int writeStorage(unsigned int, const unsigned char *, int) = 0; + virtual int updateStorage(unsigned int, const unsigned char *, int); + + unsigned int storageStartingOffset; + unsigned int deviceConfigOffset; + unsigned int elementConfigOffset; + unsigned int elementStateOffset; + + unsigned int deviceConfigSize; + unsigned int elementConfigSize; + unsigned int elementStateSize; + + unsigned int currentStateOffset; + + unsigned int newSectionSize; + int sectionsCount; + bool dryRun; + + unsigned long saveStatePeriod; + unsigned long lastWriteTimestamp; + + static Storage *instance; +}; + +#pragma pack(push, 1) +struct Preamble { + unsigned char suplaTag[5]; + uint16_t version; + uint8_t sectionsCount; +}; + +struct SectionPreamble { + unsigned char type; + uint16_t size; + uint16_t crc1; + uint16_t crc2; +}; +#pragma pack(pop) + +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/supla_lib_config.h b/lib/SuplaDevice/src/supla/supla_lib_config.h new file mode 100644 index 00000000..4e8071bb --- /dev/null +++ b/lib/SuplaDevice/src/supla/supla_lib_config.h @@ -0,0 +1,12 @@ +/* + * Put here all custom defines to customize library functionality + * Supported defines: + * SUPLA_COMM_DEBUG - enables logging of send and received data to/from server + * + */ +#ifndef supla_lib_config_h_ +#define supla_lib_config_h_ + +#define SUPLA_COMM_DEBUG + +#endif diff --git a/lib/SuplaDevice/src/supla/timer.cpp b/lib/SuplaDevice/src/supla/timer.cpp new file mode 100644 index 00000000..a0a50ec7 --- /dev/null +++ b/lib/SuplaDevice/src/supla/timer.cpp @@ -0,0 +1,108 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include "timer.h" + +#if defined(ARDUINO_ARCH_ESP32) +#include +#endif + +namespace { +#if defined(ARDUINO_ARCH_ESP8266) +ETSTimer supla_esp_timer; +ETSTimer supla_esp_fastTimer; + +void esp_timer_cb(void *timer_arg) { + (void)(timer_arg); + SuplaDevice.onTimer(); +} + +void esp_fastTimer_cb(void *timer_arg) { + (void)(timer_arg); + SuplaDevice.onFastTimer(); +} +#elif defined(ARDUINO_ARCH_ESP32) +Ticker supla_esp_timer; +Ticker supla_esp_fastTimer; + +void esp_timer_cb() { + SuplaDevice.onTimer(); +} + +void esp_fastTimer_cb() { + SuplaDevice.onFastTimer(); +} +#else +ISR(TIMER1_COMPA_vect) { + SuplaDevice.onTimer(); +} +ISR(TIMER2_COMPA_vect) { + SuplaDevice.onFastTimer(); +} +#endif +}; // namespace + +namespace Supla { +void initTimers() { +#if defined(ARDUINO_ARCH_ESP8266) + + os_timer_disarm(&supla_esp_timer); + os_timer_setfn(&supla_esp_timer, (os_timer_func_t *)esp_timer_cb, NULL); + os_timer_arm(&supla_esp_timer, 10, 1); + + os_timer_disarm(&supla_esp_fastTimer); + os_timer_setfn(&supla_esp_fastTimer, (os_timer_func_t *)esp_fastTimer_cb, NULL); + os_timer_arm(&supla_esp_fastTimer, 1, 1); + +#elif defined(ARDUINO_ARCH_ESP32) + supla_esp_timer.attach_ms(10, esp_timer_cb); + supla_esp_fastTimer.attach_ms(1, esp_fastTimer_cb); +#else + // Timer 1 for interrupt frequency 100 Hz (10 ms) + TCCR1A = 0; // set entire TCCR1A register to 0 + TCCR1B = 0; // same for TCCR1B + TCNT1 = 0; // initialize counter value to 0 + // set compare match register for 1hz increments + OCR1A = 155; // (16*10^6) / (100*1024) - 1 (must be <65536) == 155.25 + // turn on CTC mode + TCCR1B |= (1 << WGM12); + // Set CS12 and CS10 bits for 1024 prescaler + TCCR1B |= (1 << CS12) | (1 << CS10); + // enable timer compare interrupt + TIMSK1 |= (1 << OCIE1A); + sei(); // enable interrupts + + // TIMER 2 for interrupt frequency 2000 Hz (0.5 ms) + cli(); // stop interrupts + TCCR2A = 0; // set entire TCCR2A register to 0 + TCCR2B = 0; // same for TCCR2B + TCNT2 = 0; // initialize counter value to 0 + // set compare match register for 2000 Hz increments + OCR2A = 249; // = 16000000 / (32 * 2000) - 1 (must be <256) + // turn on CTC mode + TCCR2B |= (1 << WGM21); + // Set CS22, CS21 and CS20 bits for 32 prescaler + TCCR2B |= (0 << CS22) | (1 << CS21) | (1 << CS20); + // enable timer compare interrupt + TIMSK2 |= (1 << OCIE2A); + sei(); // allow interrupts +#endif +} + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/timer.h b/lib/SuplaDevice/src/supla/timer.h new file mode 100644 index 00000000..a2fa2049 --- /dev/null +++ b/lib/SuplaDevice/src/supla/timer.h @@ -0,0 +1,24 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _supla_timer_h +#define _supla_timer_h + +namespace Supla { + void initTimers(); +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/tools.cpp b/lib/SuplaDevice/src/supla/tools.cpp new file mode 100644 index 00000000..f053ad41 --- /dev/null +++ b/lib/SuplaDevice/src/supla/tools.cpp @@ -0,0 +1,56 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "tools.h" + +void float2DoublePacked(float number, uint8_t *bar, int byteOrder) { + (void)(byteOrder); + _FLOATCONV fl; + fl.f = number; + _DBLCONV dbl; + dbl.p.s = fl.p.s; + dbl.p.e = fl.p.e - 127 + 1023; // exponent adjust + dbl.p.m = fl.p.m; + +#ifdef IEEE754_ENABLE_MSB + if (byteOrder == LSBFIRST) { +#endif + for (int i = 0; i < 8; i++) { + bar[i] = dbl.b[i]; + } +#ifdef IEEE754_ENABLE_MSB + } else { + for (int i = 0; i < 8; i++) { + bar[i] = dbl.b[7 - i]; + } + } +#endif +} + +float doublePacked2float(uint8_t *bar) { + _FLOATCONV fl; + _DBLCONV dbl; + for (int i = 0; i < 8; i++) { + dbl.b[i] = bar[i]; + } + fl.p.s = dbl.p.s; + fl.p.m = dbl.p.m; + fl.p.e = dbl.p.e + 127 - 1023; // exponent adjust + + return fl.f; +} diff --git a/lib/SuplaDevice/src/supla/tools.h b/lib/SuplaDevice/src/supla/tools.h new file mode 100644 index 00000000..ab2ac31f --- /dev/null +++ b/lib/SuplaDevice/src/supla/tools.h @@ -0,0 +1,29 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _tools_H_ +#define _tools_H_ + +#include +#include "supla-common/IEEE754tools.h" + +void float2DoublePacked(float number, uint8_t *bar, int byteOrder = LSBFIRST); +float doublePacked2float(uint8_t *bar); + + +#endif diff --git a/lib/SuplaDevice/src/supla/triggerable.h b/lib/SuplaDevice/src/supla/triggerable.h new file mode 100644 index 00000000..1614de6b --- /dev/null +++ b/lib/SuplaDevice/src/supla/triggerable.h @@ -0,0 +1,28 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _triggerable_h +#define _triggerable_h + +namespace Supla { +class Triggerable { + public: + virtual void runAction(int event, int action) = 0; +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/uptime.cpp b/lib/SuplaDevice/src/supla/uptime.cpp new file mode 100644 index 00000000..09b624b1 --- /dev/null +++ b/lib/SuplaDevice/src/supla/uptime.cpp @@ -0,0 +1,63 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "uptime.h" + +namespace Supla { + +Uptime::Uptime() + : lastMillis(0), + deviceUptime(0), + connectionUptime(0), + lastConnectionResetCause(SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN), + acceptConnectionLostCause(false) { +} + +void Uptime::iterate(unsigned long millis) { + int seconds = (millis - lastMillis) / 1000; + if (seconds > 0) { + lastMillis = millis - ((millis - lastMillis) % 1000); + deviceUptime += seconds; + connectionUptime += seconds; + } +} + +void Uptime::resetConnectionUptime() { + connectionUptime = 0; + acceptConnectionLostCause = true; +} + +void Uptime::setConnectionLostCause(unsigned char cause) { + if (acceptConnectionLostCause) { + lastConnectionResetCause = cause; + acceptConnectionLostCause = false; + } +} + +unsigned _supla_int_t Uptime::getUptime() { + return deviceUptime; +} + +unsigned _supla_int_t Uptime::getConnectionUptime() { + return connectionUptime; +} + +unsigned char Uptime::getLastResetCause() { + return lastConnectionResetCause; +} + + +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/uptime.h b/lib/SuplaDevice/src/supla/uptime.h new file mode 100644 index 00000000..8f9ad7ec --- /dev/null +++ b/lib/SuplaDevice/src/supla/uptime.h @@ -0,0 +1,47 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _uptime_h +#define _uptime_h + +#include "../supla-common/proto.h" + +namespace Supla { + +class Uptime { + public: + Uptime(); + + void iterate(unsigned long millis); + void resetConnectionUptime(); + void setConnectionLostCause(unsigned char cause); + + unsigned _supla_int_t getUptime(); + unsigned _supla_int_t getConnectionUptime(); + unsigned char getLastResetCause(); + + + protected: + unsigned long lastMillis; + unsigned _supla_int_t deviceUptime; + unsigned _supla_int_t connectionUptime; + unsigned char lastConnectionResetCause; + bool acceptConnectionLostCause; +}; + +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/.editorconfig b/lib/esp8266-oled-ssd1306-master/.editorconfig new file mode 100644 index 00000000..79b24ae3 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.editorconfig @@ -0,0 +1,14 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 +max_line_length = 120 +curly_bracket_next_line = false diff --git a/lib/esp8266-oled-ssd1306-master/.gitignore b/lib/esp8266-oled-ssd1306-master/.gitignore new file mode 100644 index 00000000..80e74646 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.gitignore @@ -0,0 +1,2 @@ +.vscode +.pio diff --git a/lib/esp8266-oled-ssd1306-master/.travis.yml b/lib/esp8266-oled-ssd1306-master/.travis.yml new file mode 100644 index 00000000..49ea5b62 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.travis.yml @@ -0,0 +1,29 @@ +# documentation at https://docs.platformio.org/en/latest/integration/ci/travis.html + +language: python +python: + - "3.7" + +# Cache PlatformIO packages using Travis CI container-based infrastructure +sudo: false +cache: + directories: + - "~/.platformio" + - $HOME/.cache/pip + +env: + - PLATFORMIO_CI_SRC=examples/SSD1306UiDemo + - PLATFORMIO_CI_SRC=examples/SSD1306SimpleDemo + - PLATFORMIO_CI_SRC=examples/SSD1306DrawingDemo + - PLATFORMIO_CI_SRC=examples/SSD1306OTADemo + - PLATFORMIO_CI_SRC=examples/SSD1306ClockDemo + - PLATFORMIO_CI_SRC=examples/SSD1306TwoScreenDemo + + +install: + - pip install -U platformio + - pio update + - platformio lib -g install "paulstoffregen/Time@^1.6" + +script: + - platformio ci --lib="." --board=nodemcuv2 diff --git a/lib/esp8266-oled-ssd1306-master/CMakeLists.txt b/lib/esp8266-oled-ssd1306-master/CMakeLists.txt new file mode 100644 index 00000000..705d9a07 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_ADD_INCLUDEDIRS src) +set(COMPONENT_PRIV_REQUIRES arduino-esp32) +set(COMPONENT_SRCDIRS src) +register_component() diff --git a/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md b/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md new file mode 100644 index 00000000..6ed01ad3 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# Contributing to ThingPulse OLED SSD1306 + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to the ThingPulse OLED SSD1306 library on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. + +It is appreciated if you raise an issue _before_ you start changing the code, discussing the proposed change; emphasizing that you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. + +### Table Of Contents +* [General remarks](#general-remarks) +* [Writing Documentation](#writing-documentation) +* [Working with Git and GitHub](#working-with-git-and-github) + * [General flow](#general-flow) + * [Keeping your fork in sync](#keeping-your-fork-in-sync) + * [Commit messages](#commit-messages) + +## General remarks +We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of code. +If you made something really cool but won't spend the time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issue we'll take a look at it anyway. + +## Writing Documentation +ThingPulse maintains documentation for its products at [https://github.com/thingpulse/docs/](https://github.com/thingpulse/docs/). If you contribute features for this project that require altering the respective product guide then we ask you to prepare a pull request with the necessary documentation changes as well. + +## Working with Git and GitHub + +Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) your feature branch onto `master` to pull updates and verify your local changes against them before placing the pull request. + +### General flow +1. [Fork](https://help.github.com/articles/fork-a-repo) this repository on GitHub. +1. [Create a branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/#creating-a-branch) in your fork on GitHub **based on the `master` branch**. +1. Clone the fork on your machine with `git clone https://github.com//.git` +1. `cd ` then run `git remote add upstream https://github.com/ThingPulse/esp8266-oled-ssd1306` +1. `git checkout ` +1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'` +1. When you're done bring your fork up-to-date with the upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `master` running `git rebase master`. +1. `git push` +1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. + +This is just one way of doing things. If you're proficient in Git matters you're free to choose your own. If you want to read more then the [GitHub chapter in the Git book](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project#The-GitHub-Flow) is a way to start. [GitHub's own documentation](https://help.github.com/categories/collaborating/) contains a wealth of information as well. + +### Keeping your fork in sync +You need to sync your fork with the upstream repository from time to time, latest before you rebase (see flow above). + +1. `git fetch upstream` +1. `git checkout master` +1. `git merge upstream/master` + +### Commit messages + +From: [http://git-scm.com/book/ch5-2.html](http://git-scm.com/book/ch5-2.html) +
+Short (50 chars or less) summary of changes
+
+More detailed explanatory text, if necessary.  Wrap it to about 72
+characters or so.  In some contexts, the first line is treated as the
+subject of an email and the rest of the text as the body.  The blank
+line separating the summary from the body is critical (unless you omit
+the body entirely); tools like rebase can get confused if you run the
+two together.
+
+Further paragraphs come after blank lines.
+
+- Bullet points are okay, too
+- Typically a hyphen or asterisk is used for the bullet, preceded by a
+   single space, with blank lines in between, but conventions vary here
+
+ +Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub. + +[Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible. diff --git a/lib/esp8266-oled-ssd1306-master/README.md b/lib/esp8266-oled-ssd1306-master/README.md new file mode 100644 index 00000000..ed2d8b13 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/README.md @@ -0,0 +1,431 @@ +[![Build Status](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306.svg?branch=master)](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306) + +# ThingPulse OLED SSD1306 (ESP8266/ESP32/Mbed-OS) + +This is a driver for SSD1306 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms. +Can be used with either the I2C or SPI version of the display. + +This library drives the OLED display included in the [ThingPulse IoT starter kit](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) aka classic kit aka weather station kit. + +[![ThingPulse ESP8266 WeatherStation Classic Kit](https://github.com/ThingPulse/esp8266-weather-station/blob/master/resources/ThingPulse-ESP8266-Weather-Station.jpeg?raw=true)](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) + +You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". For mbed-os a copy of the files are available as an mbed-os library. + +It is also available as a [PlatformIO library](https://platformio.org/lib/show/562/ESP8266%20and%20ESP32%20OLED%20driver%20for%20SSD1306%20displays/examples). Just execute the following command: +``` +platformio lib install 562 +``` + +## Service level promise + +
+This is a ThingPulse prime project. See our open-source commitment declaration for what this means.
+ +## Credits + +This library has initially been written by Daniel Eichhorn ([@squix78](https://github.com/squix78)). Many thanks go to Fabrice Weinberg ([@FWeinb](https://github.com/FWeinb)) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. Mbed-OS support and other improvements were contributed by Helmut Tschemernjak ([@helmut64](https://github.com/helmut64)). + +The init sequence for the SSD1306 was inspired by Adafruit's library for the same display. + +## mbed-os +This library has been adopted to support the ARM mbed-os environment. A copy of this library is available in mbed-os under the name OLED_SSD1306 by Helmut Tschemernjak. An alternate installation option is to copy the following files into your mbed-os project: OLEDDisplay.cpp OLEDDisplay.h OLEDDisplayFonts.h OLEDDisplayUi.cpp OLEDDisplayUi.h SSD1306I2C.h + +## Usage + +Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information. + +## Upgrade + +The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md). + +Going from 3.x version to 4.0 a lot of internals changed and compatibility for more displays was added. Please read the [Upgrade Guide](UPGRADE-4.0.md). + +## Features + +* Draw pixels at given coordinates +* Draw lines from given coordinates to given coordinates +* Draw or fill a rectangle with given dimensions +* Draw Text at given coordinates: + * Define Alignment: Left, Right and Center + * Set the Fontface you want to use (see section Fonts below) + * Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible +* Display content in automatically side scrolling carousel + * Define transition cycles + * Define how long one frame will be displayed + * Draw the different frames in callback methods + * One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once + +## Fonts + +Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list +of open sourced Fonts from this web app: http://oleddisplay.squix.ch +Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file. + + +![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png) + +## Hardware Abstraction + +The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster [BRZO I2C library](https://github.com/pasko-zh/brzo_i2c) written in assembler and it also supports displays which come with the SPI interface. + +### I2C with Wire.h + +```C++ +#include +#include "SSD1306Wire.h" + +// for 128x64 displays: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// for 128x32 displays: +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, GEOMETRY_128_32 (or 128_64) +// for using 2nd Hardware I2C (if available) +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_TWO); //default value is I2C_ONE if not mentioned +// By default SD1306Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +for a SH1106: +```C++ +#include +#include "SH1106Wire.h" + +SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// By default SH1106Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +### I2C with brzo_i2c + +```C++ +#include +#include "SSD1306Brzo.h" + +SSD1306Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` +or for the SH1106: +```C++ +#include +#include "SH1106Brzo.h" + +SH1106Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` + +### SPI + +```C++ +#include +#include "SSD1306Spi.h" + +SSD1306Spi display(D0, D2, D8); // RES, DC, CS +``` +or for the SH1106: +```C++ +#include +#include "SH1106Spi.h" + +SH1106Spi display(D0, D2); // RES, DC +``` + +## API + +### Display Control + +```C++ +// Initialize the display +void init(); + +// Free the memory used by the display +void end(); + +// Cycle through the initialization +void resetDisplay(void); + +// Connect again to the display through I2C +void reconnect(void); + +// Turn the display on +void displayOn(void); + +// Turn the display offs +void displayOff(void); + +// Clear the local pixel buffer +void clear(void); + +// Write the buffer to the display memory +void display(void); + +// Inverted display mode +void invertDisplay(void); + +// Normal display mode +void normalDisplay(void); + +// Set display contrast +// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 +// normal brightness & contrast: contrast = 100 +void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + +// Convenience method to access +void setBrightness(uint8_t); + +// Turn the display upside down +void flipScreenVertically(); + +// Draw the screen mirrored +void mirrorScreen(); +``` + +## Pixel drawing + +```C++ + +/* Drawing functions */ +// Sets the color of all pixel operations +// color : BLACK, WHITE, INVERSE +void setColor(OLEDDISPLAY_COLOR color); + +// Draw a pixel at given position +void setPixel(int16_t x, int16_t y); + +// Draw a line from position 0 to position 1 +void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + +// Draw the border of a rectangle at the given location +void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Fill the rectangle +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Draw the border of a circle +void drawCircle(int16_t x, int16_t y, int16_t radius); + +// Fill circle +void fillCircle(int16_t x, int16_t y, int16_t radius); + +// Draw a line horizontally +void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + +// Draw a lin vertically +void drawVerticalLine(int16_t x, int16_t y, int16_t length); + +// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is +// a unsigned byte value between 0 and 100 +void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + +// Draw a bitmap in the internal image format +void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + +// Draw a XBM +void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm); +``` + +## Text operations + +``` C++ +void drawString(int16_t x, int16_t y, String text); + +// Draws a String with a maximum width at the given location. +// If the given String is wider than the specified width +// The text will be wrapped to the next line at a space or dash +void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text); + +// Returns the width of the const char* with the current +// font settings +uint16_t getStringWidth(const char* text, uint16_t length); + +// Convencience method for the const char version +uint16_t getStringWidth(String text); + +// Specifies relative to which anchor point +// the text is rendered. Available constants: +// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH +void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + +// Sets the current font. Available default fonts +// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 +// Or create one with the font tool at http://oleddisplay.squix.ch +void setFont(const uint8_t* fontData); +``` + +## Ui Library (OLEDDisplayUi) + +The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide +information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position. + + +```C++ +/** + * Initialise the display + */ +void init(); + +/** + * Configure the internal used target FPS + */ +void setTargetFPS(uint8_t fps); + +/** + * Enable automatic transition to next frame after the some time can be configured with + * `setTimePerFrame` and `setTimePerTransition`. + */ +void enableAutoTransition(); + +/** + * Disable automatic transition to next frame. + */ +void disableAutoTransition(); + +/** + * Set the direction if the automatic transitioning + */ +void setAutoTransitionForwards(); +void setAutoTransitionBackwards(); + +/** + * Set the approx. time a frame is displayed + */ +void setTimePerFrame(uint16_t time); + +/** + * Set the approx. time a transition will take + */ +void setTimePerTransition(uint16_t time); + +/** + * Draw the indicator. + * This is the default state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ +void enableIndicator(); + +/** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ +void disableIndicator(); + +/** + * Enable drawing of all indicators. + */ +void enableAllIndicators(); + +/** + * Disable drawing of all indicators. + */ +void disableAllIndicators(); + +/** + * Set the position of the indicator bar. + */ +void setIndicatorPosition(IndicatorPosition pos); + +/** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ +void setIndicatorDirection(IndicatorDirection dir); + +/** + * Set the symbol to indicate an active frame in the indicator bar. + */ +void setActiveSymbol(const char* symbol); + +/** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ +void setInactiveSymbol(const char* symbol); + +/** + * Configure what animation is used to transition from one frame to another + */ +void setFrameAnimation(AnimationDirection dir); + +/** + * Add frame drawing functions + */ +void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + +/** + * Add overlays drawing functions that are draw independent of the Frames + */ +void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + +/** + * Set the function that will draw each step + * in the loading animation + */ +void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction); + +/** + * Run the loading process + */ +void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + +// Manuell Controll +void nextFrame(); +void previousFrame(); + +/** + * Switch without transition to frame `frame`. + */ +void switchToFrame(uint8_t frame); + +/** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ +void transitionToFrame(uint8_t frame); + +// State Info +OLEDDisplayUiState* getUiState(); + +// This needs to be called in the main loop +// the returned value is the remaining time (in ms) +// you have to draw after drawing to keep the frame budget. +int8_t update(); +``` + +## Example: SSD1306Demo + +### Frame 1 +![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg) + +This frame shows three things: + * How to draw an xbm image + * How to draw a static text which is not moved by the frame transition + * The active/inactive frame indicators + +### Frame 2 +![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg) + +Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format. + +### Frame 3 + +![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg) + +This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered. + +### Frame 4 + +![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg) + +This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display. + +### SPI version + +![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg) + +This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used. + +## Selection of projects using this library + + * [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya) + * [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard) + * [ThingPulse Weather Station](https://github.com/ThingPulse/esp8266-weather-station) + * [Meshtastic](https://www.meshtastic.org/) - an open source GPS communicator mesh radio + * Yours? diff --git a/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md b/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md new file mode 100644 index 00000000..9b9bf912 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md @@ -0,0 +1,20 @@ +# GEOMETRY_64_48 + +The 64x48 geometry setting are working with the `Wire.h` and `brzo_i2c` libraries. + +I've tested it successfully with a WEMOS D1 mini Lite and a WEMOS OLED shield + +Initialization code: + +- Wire +``` +#include +#include +SSD1306Wire display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED shield +``` + +- BRZO i2c +``` +#include +SSD1306Brzo display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED Shield +``` diff --git a/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md b/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md new file mode 100644 index 00000000..e7a315bc --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md @@ -0,0 +1,125 @@ +# Upgrade from 2.0 to 3.0 + +While developing version 3.0 we made some breaking changes to the public +API of this library. This document will help you update your code to work with +version 3.0 + +## Font Definitions + +To get better performance and a smaller font definition format, we change the memory +layout of the font definition format. If you are using custom fonts not included in +this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home). +Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown. + + +## Architectural Changes + +To become a more versatile library for the SSD1306 chipset we abstracted the +hardware connection into subclasses of the base display class now called `OLEDDisplay`. +This library is currently shipping with three implementations: + + * `SSD1306Wire` implementing the I2C protocol using the Wire Library. + * `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library. + * `SSD1306Spi` implementing the SPI protocol. + +To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`. +If you are not using the UI components you don't have to change anything to keep your code working. + +## Name Changes + +[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library +we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`. +As a consequence the type definitions of all frame and overlay related functions changed. +This means that you have to update all your frame drawing callbacks from: + +```c +bool frame1(SSD1306 *display, SSD1306UiState* state, int x, int y); +``` + +too + +```c +void frame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +``` + +And your overlay drawing functions from: + +```c +bool overlay1(SSD1306 *display, SSD1306UiState* state); +``` + +too + +```c +void overlay1(OLEDDisplay *display, OLEDDisplayUiState* state); +``` + +## New Features + +### Loading Animation + +While using this library ourself we noticed a pattern emerging. We want to drawing +a loading progress while connecting to WiFi and updating weather data etc. + +The simplest thing was to add the function `drawProgressBar(x, y, width, height, progress)` +,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class. + +But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`. +You can define your loading process like this: + +```c++ +LoadingStage loadingStages[] = { + { + .process = "Connect to WiFi", + .callback = []() { + // Connect to WiFi + } + }, + { + .process = "Get time from NTP", + .callback = []() { + // Get current time via NTP + } + } + // more steps +}; + +int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage); +``` + +After defining your array of `LoadingStages` you can then run the loading process by using +`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a +nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918) +video. + +To further customize this you are free to define your own `LoadingDrawFunction` like this: + +```c +void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + // stage->process contains the text of the current progress e.q. "Connect to WiFi" + display->drawString(64, 18, stage->process); + // you could just print the current process without the progress bar + display->drawString(64, 28, progress); +} +``` + +After defining a function like that, you can pass it to the Ui library by use +`ui.setLoadingDrawFunction(myLoadingDraw)`. + + +### Text Logging + +It is always useful to display some text on the display without worrying to much +where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement +[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h) +so you can use it like you would use `Serial`. We calls this feature `LogBuffer` +and the only thing you have to do is to define how many lines you want to display +and how many characters there are on average on each. This is done by calling +`setLogBuffer(lines, chars);`. If there is not enough memory the function will +return false. + +After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`. +(Note: You have to call `display()` to update the screen) +We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action. diff --git a/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md b/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md new file mode 100644 index 00000000..4b17693f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md @@ -0,0 +1,27 @@ +# Upgrade from 3.x to 4.0 + +There are changes that breaks compatibility with older versions. + +1. You'll have to change data type for all your binary resources such as images and fonts from + + ```c + const char MySymbol[] PROGMEM = { + ``` + + to + + ```c + const uint8_t MySymbol[] PROGMEM = { + ``` + +1. Arguments of `setContrast` from `char` to `uint8_t` + + ```c++ + void OLEDDisplay::setContrast(char contrast, char precharge, char comdetect); + ``` + + to + + ```c++ + void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect); + ``` diff --git a/lib/esp8266-oled-ssd1306-master/component.mk b/lib/esp8266-oled-ssd1306-master/component.mk new file mode 100644 index 00000000..23a01a07 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -Wno-ignored-qualifiers diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino new file mode 100644 index 00000000..63102cf0 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino @@ -0,0 +1,214 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +int screenW = 128; +int screenH = 64; +int clockCenterX = screenW/2; +int clockCenterY = ((screenH-16)/2)+16; // top yellow part is 16 px height +int clockRadius = 23; + +// utility function for digital clock display: prints leading 0 +String twoDigits(int digits){ + if(digits < 10) { + String i = '0'+String(digits); + return i; + } + else { + return String(digits); + } +} + +void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + +} + +void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { +// ui.disableIndicator(); + + // Draw the clock face +// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius); + display->drawCircle(clockCenterX + x, clockCenterY + y, 2); + // + //hour ticks + for( int z=0; z < 360;z= z + 30 ){ + //Begin at 0° and stop at 360° + float angle = z ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) ); + int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) ); + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y); + } + + // display second hand + float angle = second() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display minute hand + angle = minute() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display hour hand + angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); +} + +void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_24); + display->drawString(clockCenterX + x , clockCenterY + y, timenow ); +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { analogClockFrame, digitalClockFrame }; + +// how many frames are there? +int frameCount = 2; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { clockOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(9600); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(TOP); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + + unsigned long secsSinceStart = millis(); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR; + setTime(epoch); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + + } + + +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h new file mode 100644 index 00000000..1889188f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h @@ -0,0 +1,21 @@ +const unsigned char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const unsigned char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino new file mode 100644 index 00000000..43bd9747 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino @@ -0,0 +1,233 @@ + /** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` + // or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + + // Use the corresponding display class: + + // Initialize the OLED display using SPI + // D5 -> CLK + // D7 -> MOSI (DOUT) + // D0 -> RES + // D2 -> DC + // D8 -> CS + // SSD1306Spi display(D0, D2, D8); + // or + // SH1106Spi display(D0, D2); + + // Initialize the OLED display using brzo_i2c + // D3 -> SDA + // D5 -> SCL + // SSD1306Brzo display(0x3c, D3, D5); + // or + // SH1106Brzo display(0x3c, D3, D5); + + // Initialize the OLED display using Wire library + SSD1306Wire display(0x3c, D3, D5); + // SH1106 display(0x3c, D3, D5); + +// Adapted from Adafruit_SSD1306 +void drawLines() { + for (int16_t i=0; i=0; i-=4) { + display.drawLine(0, display.getHeight()-1, display.getWidth()-1, i); + display.display(); + delay(10); + } + delay(250); + + display.clear(); + for (int16_t i=display.getWidth()-1; i>=0; i-=4) { + display.drawLine(display.getWidth()-1, display.getHeight()-1, i, 0); + display.display(); + delay(10); + } + for (int16_t i=display.getHeight()-1; i>=0; i-=4) { + display.drawLine(display.getWidth()-1, display.getHeight()-1, 0, i); + display.display(); + delay(10); + } + delay(250); + display.clear(); + for (int16_t i=0; i + + // OTA Includes + #include + #include + + const char *ssid = "[Your SSID]"; + const char *password = "[Your Password]"; + + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + + +void setup() { + WiFi.begin ( ssid, password ); + + // Wait for connection + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 10 ); + } + + display.init(); + display.flipScreenVertically(); + display.setContrast(255); + + ArduinoOTA.begin(); + ArduinoOTA.onStart([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth()/2, display.getHeight()/2 - 10, "OTA Update"); + display.display(); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) ); + display.display(); + }); + + ArduinoOTA.onEnd([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth()/2, display.getHeight()/2, "Restart"); + display.display(); + }); + + // Align text vertical/horizontal center + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.setFont(ArialMT_Plain_10); + display.drawString(display.getWidth()/2, display.getHeight()/2, "Ready for OTA:\n" + WiFi.localIP().toString()); + display.display(); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino new file mode 100644 index 00000000..8d715e44 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino @@ -0,0 +1,197 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +// Include the correct display library + +// For a connection via I2C using the Arduino Wire include: +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy: #include "SSD1306.h" +// OR #include "SH1106Wire.h" // legacy: #include "SH1106.h" + +// For a connection via I2C using brzo_i2c (must be installed) include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// OR #include "SH1106Brzo.h" + +// For a connection via SPI include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// OR #include "SH1106SPi.h" + + +// Optionally include custom images +#include "images.h" + + +// Initialize the OLED display using Arduino Wire: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h +// SSD1306Wire display(0x3c, D3, D5); // ADDRESS, SDA, SCL - If not, they can be specified manually. +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, OLEDDISPLAY_GEOMETRY - Extra param required for 128x32 displays. +// SH1106 display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL + +// Initialize the OLED display using brzo_i2c: +// SSD1306Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL +// or +// SH1106Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL + +// Initialize the OLED display using SPI: +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); // RES, DC, CS +// or +// SH1106Spi display(D0, D2); // RES, DC + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +int demoMode = 0; +int counter = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + +} + +void drawFontFaceDemo() { + // Font Demo1 + // create more fonts at http://oleddisplay.squix.ch/ + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, "Hello world"); + display.setFont(ArialMT_Plain_16); + display.drawString(0, 10, "Hello world"); + display.setFont(ArialMT_Plain_24); + display.drawString(0, 26, "Hello world"); +} + +void drawTextFlowDemo() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, + "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." ); +} + +void drawTextAlignmentDemo() { + // Text alignment demo + display.setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); +} + +void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); +} + +void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } +} + +void drawProgressBarDemo() { + int progress = (counter / 5) % 100; + // draw the progress bar + display.drawProgressBar(0, 32, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 15, String(progress) + "%"); +} + +void drawImageDemo() { + // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html + // on how to create xbm files + display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; +int demoLength = (sizeof(demos) / sizeof(Demo)); +long timeSinceLastModeSwitch = 0; + +void loop() { + // clear the display + display.clear(); + // draw the current demo method + demos[demoMode](); + + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(10, 128, String(millis())); + // write the buffer to the display + display.display(); + + if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { + demoMode = (demoMode + 1) % demoLength; + timeSinceLastModeSwitch = millis(); + } + counter++; + delay(10); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h new file mode 100644 index 00000000..50417990 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino new file mode 100644 index 00000000..b9595d8d --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino @@ -0,0 +1,75 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +#include "images.h" + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +SSD1306Wire display2(0x3c, D1, D2); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + display2.init(); + + // This will make sure that multiple instances of a display driver + // running on different ports will work together transparently + display.setI2cAutoInit(true); + display2.setI2cAutoInit(true); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + + display2.flipScreenVertically(); + display2.setFont(ArialMT_Plain_10); + display2.setTextAlignment(TEXT_ALIGN_LEFT); + +} + +void loop() { + display.clear(); + display.drawString(0, 0, "Hello world: " + String(millis())); + display.display(); + + display2.clear(); + display2.drawString(0, 0, "Hello world: " + String(millis())); + display2.display(); + + delay(10); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h new file mode 100644 index 00000000..50417990 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino new file mode 100644 index 00000000..72c836bf --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino @@ -0,0 +1,194 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier + #include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` + // or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images + +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106Wire display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); +} + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // draw an xbm image. + // Please note that everything that should be transitioned + // needs to be drawn relative to x and y + + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file + // Besides the default fonts there will be a program to convert TrueType fonts into this format + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(0 + x, 10 + y, "Arial 10"); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 20 + y, "Arial 16"); + + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 34 + y, "Arial 24"); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Text alignment demo + display->setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 11 + y, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 22 + y, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128 + x, 33 + y, "Right aligned (128,33)"); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demo for drawStringMaxWidth: + // with the third parameter you can define the width after which words will be wrapped. + // Currently only spaces and "-" are allowed for wrapping + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); +} + +void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 }; + +// how many frames are there? +int frameCount = 5; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { msOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h new file mode 100644 index 00000000..2489d1e5 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h @@ -0,0 +1,50 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const uint8_t activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const uint8_t inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/lib/esp8266-oled-ssd1306-master/keywords.txt b/lib/esp8266-oled-ssd1306-master/keywords.txt new file mode 100644 index 00000000..db59d6e8 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/keywords.txt @@ -0,0 +1,100 @@ +####################################### +# Syntax Coloring Map List +####################################### + + +####################################### +# Constants (LITERAL1) +####################################### +INVERSE LITERAL1 + +TEXT_ALIGN_LEFT LITERAL1 +TEXT_ALIGN_RIGHT LITERAL1 +TEXT_ALIGN_CENTER LITERAL1 +TEXT_ALIGN_CENTER_BOTH LITERAL1 + +GEOMETRY_128_64 LITERAL1 +GEOMETRY_128_32 LITERAL1 +GEOMETRY_RAWMODE LITERAL1 + +ArialMT_Plain_10 LITERAL1 +ArialMT_Plain_16 LITERAL1 +ArialMT_Plain_24 LITERAL1 + +SLIDE_UP LITERAL1 +SLIDE_DOWN LITERAL1 +SLIDE_LEFT LITERAL1 +SLIDE_RIGHT LITERAL1 + +TOP LITERAL1 +RIGHT LITERAL1 +BOTTOM LITERAL1 +LEFT LITERAL1 + +LEFT_RIGHT LITERAL1 +RIGHT_LEFT LITERAL1 + +IN_TRANSITION LITERAL1 +FIXED LITERAL1 + + +####################################### +# Datatypes (KEYWORD1) +####################################### +OLEDDisplay KEYWORD1 +OLEDDisplayUi KEYWORD1 + +SH1106Wire KEYWORD1 +SH1106Brzo KEYWORD1 +SH1106Spi KEYWORD1 + +SSD1306Wire KEYWORD1 +SSD1306Brzo KEYWORD1 +SSD1306I2C KEYWORD1 +SSD1306Spi KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +allocateBuffer KEYWORD2 +init KEYWORD2 +resetDisplay KEYWORD2 +setColor KEYWORD2 +getColor KEYWORD2 +setPixel KEYWORD2 +setPixelColor KEYWORD2 +clearPixel KEYWORD2 +drawLine KEYWORD2 +drawRect KEYWORD2 +fillRect KEYWORD2 +drawCircle KEYWORD2 +drawCircleQuads KEYWORD2 +fillCircle KEYWORD2 +fillRing KEYWORD2 +drawHorizontalLine KEYWORD2 +drawVerticalLine KEYWORD2 +drawProgressBar KEYWORD2 +drawFastImage KEYWORD2 +drawXbm KEYWORD2 +drawIco16x16 KEYWORD2 +drawString KEYWORD2 +drawStringMaxWidth KEYWORD2 +getStringWidth KEYWORD2 +setTextAlignment KEYWORD2 +setFont KEYWORD2 +setFontTableLookupFunction KEYWORD2 +displayOn KEYWORD2 +displayOff KEYWORD2 +invertDisplay KEYWORD2 +normalDisplay KEYWORD2 +setContrast KEYWORD2 +setBrightness KEYWORD2 +resetOrientation KEYWORD2 +flipScreenVertically KEYWORD2 +mirrorScreen KEYWORD2 +display KEYWORD2 +setLogBuffer KEYWORD2 +drawLogBuffer KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 diff --git a/lib/esp8266-oled-ssd1306-master/library.json b/lib/esp8266-oled-ssd1306-master/library.json new file mode 100644 index 00000000..acd11ce9 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/library.json @@ -0,0 +1,30 @@ +{ + "name": "ESP8266 and ESP32 OLED driver for SSD1306 displays", + "version": "4.2.0", + "keywords": "ssd1306, oled, display, i2c", + "description": "I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS", + "license": "MIT", + "repository": + { + "type": "git", + "url": "https://github.com/ThingPulse/esp8266-oled-ssd1306" + }, + "authors": + [ + { + "name": "Daniel Eichhorn, ThingPulse", + "email": "squix78@gmail.com", + "url": "https://thingpulse.com" + }, + { + "name": "Fabrice Weinberg", + "email": "fabrice@weinberg.me" + } + ], + "frameworks": "arduino", + "platforms": [ + "espressif8266", + "espressif32", + "nordicnrf52" + ] +} diff --git a/lib/esp8266-oled-ssd1306-master/library.properties b/lib/esp8266-oled-ssd1306-master/library.properties new file mode 100644 index 00000000..e931a3f6 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/library.properties @@ -0,0 +1,10 @@ +name=ESP8266 and ESP32 OLED driver for SSD1306 displays +version=4.2.0 +author=ThingPulse, Fabrice Weinberg +maintainer=ThingPulse +sentence=I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS +paragraph=The following geometries are currently supported: 128x64, 128x32, 64x48. The init sequence was inspired by Adafruit's library for the same display. +category=Display +url=https://github.com/ThingPulse/esp8266-oled-ssd1306 +architectures=esp8266,esp32 +license=MIT diff --git a/lib/esp8266-oled-ssd1306-master/license b/lib/esp8266-oled-ssd1306-master/license new file mode 100644 index 00000000..706c10fe --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/license @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2016 by Daniel Eichhorn +Copyright (c) 2016 by Fabrice Weinberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +See more at http://blog.squix.ch diff --git a/lib/esp8266-oled-ssd1306-master/platformio.ini b/lib/esp8266-oled-ssd1306-master/platformio.ini new file mode 100644 index 00000000..530c046e --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/page/projectconf.html + +[env:d1_mini] +platform = espressif8266 +board = d1_mini +framework = arduino +upload_speed = 921600 +board_build.f_cpu = 160000000L +upload_port = /dev/cu.SLAB_USBtoUART +monitor_port = /dev/cu.SLAB_USBtoUART +lib_deps = diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp new file mode 100644 index 00000000..89a80108 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp @@ -0,0 +1,1045 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + /* + * TODO Helmut + * - test/finish dislplay.printf() on mbed-os + * - Finish _putc with drawLogBuffer when running display + */ + +#include "OLEDDisplay.h" + +OLEDDisplay::OLEDDisplay() { + + displayWidth = 128; + displayHeight = 64; + displayBufferSize = displayWidth * displayHeight / 8; + color = WHITE; + geometry = GEOMETRY_128_64; + textAlignment = TEXT_ALIGN_LEFT; + fontData = ArialMT_Plain_10; + fontTableLookupFunction = DefaultFontTableLookup; + buffer = NULL; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + buffer_back = NULL; +#endif +} + +OLEDDisplay::~OLEDDisplay() { + end(); +} + +bool OLEDDisplay::allocateBuffer() { + + logBufferSize = 0; + logBufferFilled = 0; + logBufferLine = 0; + logBufferMaxLines = 0; + logBuffer = NULL; + + if (!connect()) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); + return false; + } + + if(this->buffer==NULL) { + this->buffer = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer += BufferOffset; + + if(!this->buffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); + return false; + } + } + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if(this->buffer_back==NULL) { + this->buffer_back = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer_back += BufferOffset; + + if(!this->buffer_back) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); + free(this->buffer - BufferOffset); + return false; + } + } + #endif + + return true; +} + +bool OLEDDisplay::init() { + + BufferOffset = getBufferOffset(); + + if(!allocateBuffer()) { + return false; + } + + sendInitCommands(); + resetDisplay(); + + return true; +} + +void OLEDDisplay::end() { + if (this->buffer) { free(this->buffer - BufferOffset); this->buffer = NULL; } + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if (this->buffer_back) { free(this->buffer_back - BufferOffset); this->buffer_back = NULL; } + #endif + if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } +} + +void OLEDDisplay::resetDisplay(void) { + clear(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + memset(buffer_back, 1, displayBufferSize); + #endif + display(); +} + +void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { + this->color = color; +} + +OLEDDISPLAY_COLOR OLEDDisplay::getColor() { + return this->color; +} + +void OLEDDisplay::setPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::clearPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case BLACK: buffer[x + (y >> 3) * this->width()] |= (1 << (y & 7)); break; + case WHITE: buffer[x + (y >> 3) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y >> 3) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + + +// Bresenham's algorithm - thx wikipedia and Adafruit_GFX +void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + setPixel(y0, x0); + } else { + setPixel(x0, y0); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { + drawHorizontalLine(x, y, width); + drawVerticalLine(x, y, height); + drawVerticalLine(x + width - 1, y, height); + drawHorizontalLine(x, y + height - 1, width); +} + +void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { + for (int16_t x = xMove; x < xMove + width; x++) { + drawVerticalLine(x, yMove, height); + } +} + +void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + setPixel(x0 + x, y0 + y); //For the 8 octants + setPixel(x0 - x, y0 + y); + setPixel(x0 + x, y0 - y); + setPixel(x0 - x, y0 - y); + setPixel(x0 + y, y0 + x); + setPixel(x0 - y, y0 + x); + setPixel(x0 + y, y0 - x); + setPixel(x0 - y, y0 - x); + + } while (x < y); + + setPixel(x0 + radius, y0); + setPixel(x0, y0 + radius); + setPixel(x0 - radius, y0); + setPixel(x0, y0 - radius); +} + +void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + while (x < y) { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + if (quads & 0x1) { + setPixel(x0 + x, y0 - y); + setPixel(x0 + y, y0 - x); + } + if (quads & 0x2) { + setPixel(x0 - y, y0 - x); + setPixel(x0 - x, y0 - y); + } + if (quads & 0x4) { + setPixel(x0 - y, y0 + x); + setPixel(x0 - x, y0 + y); + } + if (quads & 0x8) { + setPixel(x0 + x, y0 + y); + setPixel(x0 + y, y0 + x); + } + } + if (quads & 0x1 && quads & 0x8) { + setPixel(x0 + radius, y0); + } + if (quads & 0x4 && quads & 0x8) { + setPixel(x0, y0 + radius); + } + if (quads & 0x2 && quads & 0x4) { + setPixel(x0 - radius, y0); + } + if (quads & 0x1 && quads & 0x2) { + setPixel(x0, y0 - radius); + } +} + + +void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + drawHorizontalLine(x0 - x, y0 - y, 2*x); + drawHorizontalLine(x0 - x, y0 + y, 2*x); + drawHorizontalLine(x0 - y, y0 - x, 2*y); + drawHorizontalLine(x0 - y, y0 + x, 2*y); + + + } while (x < y); + drawHorizontalLine(x0 - radius, y0, 2 * radius); + +} + +void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { + if (y < 0 || y >= this->height()) { return; } + + if (x < 0) { + length += x; + x = 0; + } + + if ( (x + length) > this->width()) { + length = (this->width() - x); + } + + if (length <= 0) { return; } + + uint8_t * bufferPtr = buffer; + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + uint8_t drawBit = 1 << (y & 7); + + switch (color) { + case WHITE: while (length--) { + *bufferPtr++ |= drawBit; + }; break; + case BLACK: drawBit = ~drawBit; while (length--) { + *bufferPtr++ &= drawBit; + }; break; + case INVERSE: while (length--) { + *bufferPtr++ ^= drawBit; + }; break; + } +} + +void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { + if (x < 0 || x >= this->width()) return; + + if (y < 0) { + length += y; + y = 0; + } + + if ( (y + length) > this->height()) { + length = (this->height() - y); + } + + if (length <= 0) return; + + + uint8_t yOffset = y & 7; + uint8_t drawBit; + uint8_t *bufferPtr = buffer; + + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + if (yOffset) { + yOffset = 8 - yOffset; + drawBit = ~(0xFF >> (yOffset)); + + if (length < yOffset) { + drawBit &= (0xFF >> (yOffset - length)); + } + + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + + if (length < yOffset) return; + + length -= yOffset; + bufferPtr += this->width(); + } + + if (length >= 8) { + switch (color) { + case WHITE: + case BLACK: + drawBit = (color == WHITE) ? 0xFF : 0x00; + do { + *bufferPtr = drawBit; + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + case INVERSE: + do { + *bufferPtr = ~(*bufferPtr); + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + } + } + + if (length > 0) { + drawBit = (1 << (length & 7)) - 1; + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + } +} + +void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { + uint16_t radius = height / 2; + uint16_t xRadius = x + radius; + uint16_t yRadius = y + radius; + uint16_t doubleRadius = 2 * radius; + uint16_t innerRadius = radius - 2; + + setColor(WHITE); + drawCircleQuads(xRadius, yRadius, radius, 0b00000110); + drawHorizontalLine(xRadius, y, width - doubleRadius + 1); + drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); + drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); + + uint16_t maxProgressWidth = (width - doubleRadius + 1) * progress / 100; + + fillCircle(xRadius, yRadius, innerRadius); + fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); + fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); +} + +void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) { + drawInternal(xMove, yMove, width, height, image, 0, 0); +} + +void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) { + int16_t widthInXbm = (width + 7) / 8; + uint8_t data = 0; + + for(int16_t y = 0; y < height; y++) { + for(int16_t x = 0; x < width; x++ ) { + if (x & 7) { + data >>= 1; // Move a bit + } else { // Read new data every 8 bit + data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); + } + // if there is a bit draw it + if (data & 0x01) { + setPixel(xMove + x, yMove + y); + } + } + } +} + +void OLEDDisplay::drawIco16x16(int16_t xMove, int16_t yMove, const char *ico, bool inverse) { + uint16_t data; + + for(int16_t y = 0; y < 16; y++) { + data = pgm_read_byte(ico + (y << 1)) + (pgm_read_byte(ico + (y << 1) + 1) << 8); + for(int16_t x = 0; x < 16; x++ ) { + if ((data & 0x01) ^ inverse) { + setPixelColor(xMove + x, yMove + y, WHITE); + } else { + setPixelColor(xMove + x, yMove + y, BLACK); + } + data >>= 1; // Move a bit + } + } +} + +void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; + + uint16_t cursorX = 0; + uint16_t cursorY = 0; + + switch (textAlignment) { + case TEXT_ALIGN_CENTER_BOTH: + yMove -= textHeight >> 1; + // Fallthrough + case TEXT_ALIGN_CENTER: + xMove -= textWidth >> 1; // divide by 2 + break; + case TEXT_ALIGN_RIGHT: + xMove -= textWidth; + break; + case TEXT_ALIGN_LEFT: + break; + } + + // Don't draw anything if it is not on the screen. + if (xMove + textWidth < 0 || xMove > this->width() ) {return;} + if (yMove + textHeight < 0 || yMove > this->width() ) {return;} + + for (uint16_t j = 0; j < textLength; j++) { + int16_t xPos = xMove + cursorX; + int16_t yPos = yMove + cursorY; + + uint8_t code = text[j]; + if (code >= firstChar) { + uint8_t charCode = code - firstChar; + + // 4 Bytes per char code + uint8_t msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + uint8_t lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + uint8_t charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + uint8_t currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width + + // Test if the char is drawable + if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { + // Get the position of the char data + uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); + drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); + } + + cursorX += currentCharWidth; + } + } +} + + +void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + // char* text must be freed! + char* text = utf8ascii(strUser); + + uint16_t yOffset = 0; + // If the string should be centered vertically too + // we need to now how heigh the string is. + if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { + uint16_t lb = 0; + // Find number of linebreaks in text + for (uint16_t i=0;text[i] != 0; i++) { + lb += (text[i] == 10); + } + // Calculate center + yOffset = (lb * lineHeight) / 2; + } + + uint16_t line = 0; + char* textPart = strtok(text,"\n"); + while (textPart != NULL) { + uint16_t length = strlen(textPart); + drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); + textPart = strtok(NULL, "\n"); + } + free(text); +} + +void OLEDDisplay::drawStringf( int16_t x, int16_t y, char* buffer, String format, ... ) +{ + va_list myargs; + va_start(myargs, format); + vsprintf(buffer, format.c_str(), myargs); + va_end(myargs); + drawString( x, y, buffer ); +} + +void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + char* text = utf8ascii(strUser); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + + for (uint16_t i = 0; i < length; i++) { + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space or dash + if (text[i] == ' ' || text[i]== '-') { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint); + lastDrawnPos = preferredBreakpoint + 1; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + } + } + + // Draw last part if needed + if (lastDrawnPos < length) { + drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos)); + } + + free(text); +} + +uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + + uint16_t stringWidth = 0; + uint16_t maxWidth = 0; + + while (length--) { + stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + if (text[length] == 10) { + maxWidth = max(maxWidth, stringWidth); + stringWidth = 0; + } + } + + return max(maxWidth, stringWidth); +} + +uint16_t OLEDDisplay::getStringWidth(String strUser) { + char* text = utf8ascii(strUser); + uint16_t length = strlen(text); + uint16_t width = getStringWidth(text, length); + free(text); + return width; +} + +void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { + this->textAlignment = textAlignment; +} + +void OLEDDisplay::setFont(const uint8_t *fontData) { + this->fontData = fontData; +} + +void OLEDDisplay::displayOn(void) { + sendCommand(DISPLAYON); +} + +void OLEDDisplay::displayOff(void) { + sendCommand(DISPLAYOFF); +} + +void OLEDDisplay::invertDisplay(void) { + sendCommand(INVERTDISPLAY); +} + +void OLEDDisplay::normalDisplay(void) { + sendCommand(NORMALDISPLAY); +} + +void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) { + sendCommand(SETPRECHARGE); //0xD9 + sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F + sendCommand(SETCONTRAST); + sendCommand(contrast); // 0-255 + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(comdetect); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(DISPLAYON); +} + +void OLEDDisplay::setBrightness(uint8_t brightness) { + uint8_t contrast = brightness; + if (brightness < 128) { + // Magic values to get a smooth/ step-free transition + contrast = brightness * 1.171; + } else { + contrast = brightness * 1.171 - 43; + } + + uint8_t precharge = 241; + if (brightness == 0) { + precharge = 0; + } + uint8_t comdetect = brightness / 8; + + setContrast(contrast, precharge, comdetect); +} + +void OLEDDisplay::resetOrientation() { + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); //Reset screen rotation or mirroring +} + +void OLEDDisplay::flipScreenVertically() { + sendCommand(SEGREMAP | 0x01); + sendCommand(COMSCANDEC); //Rotate screen 180 Deg +} + +void OLEDDisplay::mirrorScreen() { + sendCommand(SEGREMAP); + sendCommand(COMSCANDEC); //Mirror screen +} + +void OLEDDisplay::clear(void) { + memset(buffer, 0, displayBufferSize); +} + +void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + // Always align left + setTextAlignment(TEXT_ALIGN_LEFT); + + // State values + uint16_t length = 0; + uint16_t line = 0; + uint16_t lastPos = 0; + + for (uint16_t i=0;ilogBufferFilled;i++){ + // Everytime we have a \n print + if (this->logBuffer[i] == 10) { + length++; + // Draw string on line `line` from lastPos to length + // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT + drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0); + // Remember last pos + lastPos = i; + // Reset length + length = 0; + } else { + // Count chars until next linebreak + length++; + } + } + // Draw the remaining string + if (length > 0) { + drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0); + } +} + +uint16_t OLEDDisplay::getWidth(void) { + return displayWidth; +} + +uint16_t OLEDDisplay::getHeight(void) { + return displayHeight; +} + +bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ + if (logBuffer != NULL) free(logBuffer); + uint16_t size = lines * chars; + if (size > 0) { + this->logBufferLine = 0; // Lines printed + this->logBufferFilled = 0; // Nothing stored yet + this->logBufferMaxLines = lines; // Lines max printable + this->logBufferSize = size; // Total number of characters the buffer can hold + this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); + if(!this->logBuffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); + return false; + } + } + return true; +} + +size_t OLEDDisplay::write(uint8_t c) { + if (this->logBufferSize > 0) { + // Don't waste space on \r\n line endings, dropping \r + if (c == 13) return 1; + + // convert UTF-8 character to font table index + c = (this->fontTableLookupFunction)(c); + // drop unknown character + if (c == 0) return 1; + + bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; + bool bufferNotFull = this->logBufferFilled < this->logBufferSize; + + // Can we write to the buffer? + if (bufferNotFull && maxLineNotReached) { + this->logBuffer[logBufferFilled] = c; + this->logBufferFilled++; + // Keep track of lines written + if (c == 10) this->logBufferLine++; + } else { + // Max line number is reached + if (!maxLineNotReached) this->logBufferLine--; + + // Find the end of the first line + uint16_t firstLineEnd = 0; + for (uint16_t i=0;ilogBufferFilled;i++) { + if (this->logBuffer[i] == 10){ + // Include last char too + firstLineEnd = i + 1; + break; + } + } + // If there was a line ending + if (firstLineEnd > 0) { + // Calculate the new logBufferFilled value + this->logBufferFilled = logBufferFilled - firstLineEnd; + // Now we move the lines infront of the buffer + memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); + } else { + // Let's reuse the buffer if it was full + if (!bufferNotFull) { + this->logBufferFilled = 0; + }// else { + // Nothing to do here + //} + } + write(c); + } + } + // We are always writing all uint8_t to the buffer + return 1; +} + +size_t OLEDDisplay::write(const char* str) { + if (str == NULL) return 0; + size_t length = strlen(str); + for (size_t i = 0; i < length; i++) { + write(str[i]); + } + return length; +} + +#ifdef __MBED__ +int OLEDDisplay::_putc(int c) { + + if (!fontData) + return 1; + if (!logBufferSize) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint16_t lines = this->displayHeight / textHeight; + uint16_t chars = 2 * (this->displayWidth / textHeight); + + if (this->displayHeight % textHeight) + lines++; + if (this->displayWidth % textHeight) + chars++; + setLogBuffer(lines, chars); + } + + return this->write((uint8_t)c); +} +#endif + +// Private functions +void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { + this->geometry = g; + + switch (g) { + case GEOMETRY_128_64: + this->displayWidth = 128; + this->displayHeight = 64; + break; + case GEOMETRY_128_32: + this->displayWidth = 128; + this->displayHeight = 32; + break; + case GEOMETRY_64_48: + this->displayWidth = 64; + this->displayHeight = 48; + break; + case GEOMETRY_64_32: + this->displayWidth = 64; + this->displayHeight = 32; + break; + case GEOMETRY_RAWMODE: + this->displayWidth = width > 0 ? width : 128; + this->displayHeight = height > 0 ? height : 64; + break; + } + this->displayBufferSize = displayWidth * displayHeight / 8; +} + +void OLEDDisplay::sendInitCommands(void) { + if (geometry == GEOMETRY_RAWMODE) + return; + sendCommand(DISPLAYOFF); + sendCommand(SETDISPLAYCLOCKDIV); + sendCommand(0xF0); // Increase speed of the display max ~96Hz + sendCommand(SETMULTIPLEX); + sendCommand(this->height() - 1); + sendCommand(SETDISPLAYOFFSET); + sendCommand(0x00); + if(geometry == GEOMETRY_64_32) + sendCommand(0x00); + else + sendCommand(SETSTARTLINE); + sendCommand(CHARGEPUMP); + sendCommand(0x14); + sendCommand(MEMORYMODE); + sendCommand(0x00); + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); + sendCommand(SETCOMPINS); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0x12); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x02); + } + + sendCommand(SETCONTRAST); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0xCF); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x8F); + } + + sendCommand(SETPRECHARGE); + sendCommand(0xF1); + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(0x40); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(0x2e); // stop scroll + sendCommand(DISPLAYON); +} + +void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) { + if (width < 0 || height < 0) return; + if (yMove + height < 0 || yMove > this->height()) return; + if (xMove + width < 0 || xMove > this->width()) return; + + uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) + int8_t yOffset = yMove & 7; + + bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; + + int16_t initYMove = yMove; + int8_t initYOffset = yOffset; + + + for (uint16_t i = 0; i < bytesInData; i++) { + + // Reset if next horizontal drawing phase is started. + if ( i % rasterHeight == 0) { + yMove = initYMove; + yOffset = initYOffset; + } + + uint8_t currentByte = pgm_read_byte(data + offset + i); + + int16_t xPos = xMove + (i / rasterHeight); + int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); + +// int16_t yScreenPos = yMove + yOffset; + int16_t dataPos = xPos + yPos; + + if (dataPos >= 0 && dataPos < displayBufferSize && + xPos >= 0 && xPos < this->width() ) { + + if (yOffset >= 0) { + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte << yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; + } + + if (dataPos < (displayBufferSize - this->width())) { + switch (this->color) { + case WHITE: buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break; + case BLACK: buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break; + case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break; + } + } + } else { + // Make new offset position + yOffset = -yOffset; + + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; + } + + // Prepare for next iteration by moving one block up + yMove -= 8; + + // and setting the new yOffset + yOffset = 8 - yOffset; + } +#ifndef __MBED__ + yield(); +#endif + } + } +} + +// You need to free the char! +char* OLEDDisplay::utf8ascii(String str) { + uint16_t k = 0; + uint16_t length = str.length() + 1; + + // Copy the string into a char array + char* s = (char*) malloc(length * sizeof(char)); + if(!s) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); + return (char*) str.c_str(); + } + str.toCharArray(s, length); + + length--; + + for (uint16_t i=0; i < length; i++) { + char c = (this->fontTableLookupFunction)(s[i]); + if (c!=0) { + s[k++]=c; + } + } + + s[k]=0; + + // This will leak 's' be sure to free it in the calling function. + return s; +} + +void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { + this->fontTableLookupFunction = function; +} + + +char DefaultFontTableLookup(const uint8_t ch) { + // UTF-8 to font table index converter + // Code form http://playground.arduino.cc/Main/Utf8ascii + static uint8_t LASTCHAR; + + if (ch < 128) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ch; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ch; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (uint8_t) ch; + case 0xC3: return (uint8_t) (ch | 0xC0); + case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol + } + + return (uint8_t) 0; // otherwise: return zero, if character has to be ignored +} diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h new file mode 100644 index 00000000..d43ee162 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h @@ -0,0 +1,384 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAY_h +#define OLEDDISPLAY_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + +#include +#define delay(x) wait_ms(x) +#define yield() void() + +/* + * This is a little Arduino String emulation to keep the OLEDDisplay + * library code in common between Arduino and mbed-os + */ +class String { +public: + String(const char *s) { _str = s; }; + int length() { return strlen(_str); }; + const char *c_str() { return _str; }; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + memcpy(buf, _str + index, std::min(bufsize, strlen(_str))); + }; +private: + const char *_str; +}; + +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplayFonts.h" + +//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) +//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAY +#define DEBUG_OLEDDISPLAY(...) +#endif + +// Use DOUBLE BUFFERING by default +#ifndef OLEDDISPLAY_REDUCE_MEMORY +#define OLEDDISPLAY_DOUBLE_BUFFER +#endif + +// Header Values +#define JUMPTABLE_BYTES 4 + +#define JUMPTABLE_LSB 1 +#define JUMPTABLE_SIZE 2 +#define JUMPTABLE_WIDTH 3 +#define JUMPTABLE_START 4 + +#define WIDTH_POS 0 +#define HEIGHT_POS 1 +#define FIRST_CHAR_POS 2 +#define CHAR_NUM_POS 3 + + +// Display commands +#define CHARGEPUMP 0x8D +#define COLUMNADDR 0x21 +#define COMSCANDEC 0xC8 +#define COMSCANINC 0xC0 +#define DISPLAYALLON 0xA5 +#define DISPLAYALLON_RESUME 0xA4 +#define DISPLAYOFF 0xAE +#define DISPLAYON 0xAF +#define EXTERNALVCC 0x1 +#define INVERTDISPLAY 0xA7 +#define MEMORYMODE 0x20 +#define NORMALDISPLAY 0xA6 +#define PAGEADDR 0x22 +#define SEGREMAP 0xA0 +#define SETCOMPINS 0xDA +#define SETCONTRAST 0x81 +#define SETDISPLAYCLOCKDIV 0xD5 +#define SETDISPLAYOFFSET 0xD3 +#define SETHIGHCOLUMN 0x10 +#define SETLOWCOLUMN 0x00 +#define SETMULTIPLEX 0xA8 +#define SETPRECHARGE 0xD9 +#define SETSEGMENTREMAP 0xA1 +#define SETSTARTLINE 0x40 +#define SETVCOMDETECT 0xDB +#define SWITCHCAPVCC 0x2 + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +enum OLEDDISPLAY_COLOR { + BLACK = 0, + WHITE = 1, + INVERSE = 2 +}; + +enum OLEDDISPLAY_TEXT_ALIGNMENT { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_RIGHT = 1, + TEXT_ALIGN_CENTER = 2, + TEXT_ALIGN_CENTER_BOTH = 3 +}; + + +enum OLEDDISPLAY_GEOMETRY { + GEOMETRY_128_64 = 0, + GEOMETRY_128_32 = 1, + GEOMETRY_64_48 = 2, + GEOMETRY_64_32 = 3, + GEOMETRY_RAWMODE = 4 +}; + +enum HW_I2C { + I2C_ONE, + I2C_TWO +}; + +typedef char (*FontTableLookupFunction)(const uint8_t ch); +char DefaultFontTableLookup(const uint8_t ch); + + +#ifdef ARDUINO +class OLEDDisplay : public Print { +#elif __MBED__ +class OLEDDisplay : public Stream { +#else +#error "Unkown operating system" +#endif + + public: + OLEDDisplay(); + virtual ~OLEDDisplay(); + + uint16_t width(void) const { return displayWidth; }; + uint16_t height(void) const { return displayHeight; }; + + // Use this to resume after a deep sleep without resetting the display (what init() would do). + // Returns true if connection to the display was established and the buffer allocated, false otherwise. + bool allocateBuffer(); + + // Allocates the buffer and initializes the driver & display. Resets the display! + // Returns false if buffer allocation failed, true otherwise. + bool init(); + + // Free the memory used by the display + void end(); + + // Cycle through the initialization + void resetDisplay(void); + + /* Drawing functions */ + // Sets the color of all pixel operations + void setColor(OLEDDISPLAY_COLOR color); + + // Returns the current color. + OLEDDISPLAY_COLOR getColor(); + + // Draw a pixel at given position + void setPixel(int16_t x, int16_t y); + + // Draw a pixel at given position and color + void setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color); + + // Clear a pixel at given position FIXME: INVERSE is untested with this function + void clearPixel(int16_t x, int16_t y); + + // Draw a line from position 0 to position 1 + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + + // Draw the border of a rectangle at the given location + void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Fill the rectangle + void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Draw the border of a circle + void drawCircle(int16_t x, int16_t y, int16_t radius); + + // Draw all Quadrants specified in the quads bit mask + void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); + + // Fill circle + void fillCircle(int16_t x, int16_t y, int16_t radius); + + // Draw a line horizontally + void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + + // Draw a line vertically + void drawVerticalLine(int16_t x, int16_t y, int16_t length); + + // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is + // a unsigned byte value between 0 and 100 + void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + + // Draw a bitmap in the internal image format + void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + + // Draw a XBM + void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); + + // Draw icon 16x16 xbm format + void drawIco16x16(int16_t x, int16_t y, const char *ico, bool inverse = false); + + /* Text functions */ + + // Draws a string at the given location + void drawString(int16_t x, int16_t y, String text); + + // Draws a formatted string (like printf) at the given location + void drawStringf(int16_t x, int16_t y, char* buffer, String format, ... ); + + // Draws a String with a maximum width at the given location. + // If the given String is wider than the specified width + // The text will be wrapped to the next line at a space or dash + void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text); + + // Returns the width of the const char* with the current + // font settings + uint16_t getStringWidth(const char* text, uint16_t length); + + // Convencience method for the const char version + uint16_t getStringWidth(String text); + + // Specifies relative to which anchor point + // the text is rendered. Available constants: + // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH + void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + + // Sets the current font. Available default fonts + // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 + void setFont(const uint8_t *fontData); + + // Set the function that will convert utf-8 to font table index + void setFontTableLookupFunction(FontTableLookupFunction function); + + /* Display functions */ + + // Turn the display on + void displayOn(void); + + // Turn the display offs + void displayOff(void); + + // Inverted display mode + void invertDisplay(void); + + // Normal display mode + void normalDisplay(void); + + // Set display contrast + // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 + // normal brightness & contrast: contrast = 100 + void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + + // Convenience method to access + void setBrightness(uint8_t); + + // Reset display rotation or mirroring + void resetOrientation(); + + // Turn the display upside down + void flipScreenVertically(); + + // Mirror the display (to be used in a mirror or as a projector) + void mirrorScreen(); + + // Write the buffer to the display memory + virtual void display(void) = 0; + + // Clear the local pixel buffer + void clear(void); + + // Log buffer implementation + + // This will define the lines and characters you can + // print to the screen. When you exeed the buffer size (lines * chars) + // the output may be truncated due to the size constraint. + bool setLogBuffer(uint16_t lines, uint16_t chars); + + // Draw the log buffer at position (x, y) + void drawLogBuffer(uint16_t x, uint16_t y); + + // Get screen geometry + uint16_t getWidth(void); + uint16_t getHeight(void); + + // Implement needed function to be compatible with Print class + size_t write(uint8_t c); + size_t write(const char* s); + + // Implement needed function to be compatible with Stream class +#ifdef __MBED__ + int _putc(int c); + int _getc() { return -1; }; +#endif + + + uint8_t *buffer; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t *buffer_back; + #endif + + protected: + + OLEDDISPLAY_GEOMETRY geometry; + + uint16_t displayWidth; + uint16_t displayHeight; + uint16_t displayBufferSize; + + // Set the correct height, width and buffer for the geometry + void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0); + + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment; + OLEDDISPLAY_COLOR color; + + const uint8_t *fontData; + + // State values for logBuffer + uint16_t logBufferSize; + uint16_t logBufferFilled; + uint16_t logBufferLine; + uint16_t logBufferMaxLines; + char *logBuffer; + + + // the header size of the buffer used, e.g. for the SPI command header + int BufferOffset; + virtual int getBufferOffset(void) = 0; + + // Send a command to the display (low level function) + virtual void sendCommand(uint8_t com) {(void)com;}; + + // Connect to the display + virtual bool connect() { return false; }; + + // Send all the init commands + void sendInitCommands(); + + // converts utf8 characters to extended ascii + char* utf8ascii(String s); + + void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); + + void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); + + FontTableLookupFunction fontTableLookupFunction; +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h new file mode 100644 index 00000000..abc61ba1 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h @@ -0,0 +1,1278 @@ +#ifndef OLEDDISPLAYFONTS_h +#define OLEDDISPLAYFONTS_h + +#ifdef __MBED__ +#define PROGMEM +#endif + +const uint8_t ArialMT_Plain_10[] PROGMEM = { + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x03, // 32:65535 + 0x00, 0x00, 0x04, 0x03, // 33:0 + 0x00, 0x04, 0x05, 0x04, // 34:4 + 0x00, 0x09, 0x09, 0x06, // 35:9 + 0x00, 0x12, 0x0A, 0x06, // 36:18 + 0x00, 0x1C, 0x10, 0x09, // 37:28 + 0x00, 0x2C, 0x0E, 0x07, // 38:44 + 0x00, 0x3A, 0x01, 0x02, // 39:58 + 0x00, 0x3B, 0x06, 0x03, // 40:59 + 0x00, 0x41, 0x06, 0x03, // 41:65 + 0x00, 0x47, 0x05, 0x04, // 42:71 + 0x00, 0x4C, 0x09, 0x06, // 43:76 + 0x00, 0x55, 0x04, 0x03, // 44:85 + 0x00, 0x59, 0x03, 0x03, // 45:89 + 0x00, 0x5C, 0x04, 0x03, // 46:92 + 0x00, 0x60, 0x05, 0x03, // 47:96 + 0x00, 0x65, 0x0A, 0x06, // 48:101 + 0x00, 0x6F, 0x08, 0x06, // 49:111 + 0x00, 0x77, 0x0A, 0x06, // 50:119 + 0x00, 0x81, 0x0A, 0x06, // 51:129 + 0x00, 0x8B, 0x0B, 0x06, // 52:139 + 0x00, 0x96, 0x0A, 0x06, // 53:150 + 0x00, 0xA0, 0x0A, 0x06, // 54:160 + 0x00, 0xAA, 0x09, 0x06, // 55:170 + 0x00, 0xB3, 0x0A, 0x06, // 56:179 + 0x00, 0xBD, 0x0A, 0x06, // 57:189 + 0x00, 0xC7, 0x04, 0x03, // 58:199 + 0x00, 0xCB, 0x04, 0x03, // 59:203 + 0x00, 0xCF, 0x0A, 0x06, // 60:207 + 0x00, 0xD9, 0x09, 0x06, // 61:217 + 0x00, 0xE2, 0x09, 0x06, // 62:226 + 0x00, 0xEB, 0x0B, 0x06, // 63:235 + 0x00, 0xF6, 0x14, 0x0A, // 64:246 + 0x01, 0x0A, 0x0E, 0x07, // 65:266 + 0x01, 0x18, 0x0C, 0x07, // 66:280 + 0x01, 0x24, 0x0C, 0x07, // 67:292 + 0x01, 0x30, 0x0B, 0x07, // 68:304 + 0x01, 0x3B, 0x0C, 0x07, // 69:315 + 0x01, 0x47, 0x09, 0x06, // 70:327 + 0x01, 0x50, 0x0D, 0x08, // 71:336 + 0x01, 0x5D, 0x0C, 0x07, // 72:349 + 0x01, 0x69, 0x04, 0x03, // 73:361 + 0x01, 0x6D, 0x08, 0x05, // 74:365 + 0x01, 0x75, 0x0E, 0x07, // 75:373 + 0x01, 0x83, 0x0C, 0x06, // 76:387 + 0x01, 0x8F, 0x10, 0x08, // 77:399 + 0x01, 0x9F, 0x0C, 0x07, // 78:415 + 0x01, 0xAB, 0x0E, 0x08, // 79:427 + 0x01, 0xB9, 0x0B, 0x07, // 80:441 + 0x01, 0xC4, 0x0E, 0x08, // 81:452 + 0x01, 0xD2, 0x0C, 0x07, // 82:466 + 0x01, 0xDE, 0x0C, 0x07, // 83:478 + 0x01, 0xEA, 0x0B, 0x06, // 84:490 + 0x01, 0xF5, 0x0C, 0x07, // 85:501 + 0x02, 0x01, 0x0D, 0x07, // 86:513 + 0x02, 0x0E, 0x11, 0x09, // 87:526 + 0x02, 0x1F, 0x0E, 0x07, // 88:543 + 0x02, 0x2D, 0x0D, 0x07, // 89:557 + 0x02, 0x3A, 0x0C, 0x06, // 90:570 + 0x02, 0x46, 0x06, 0x03, // 91:582 + 0x02, 0x4C, 0x06, 0x03, // 92:588 + 0x02, 0x52, 0x04, 0x03, // 93:594 + 0x02, 0x56, 0x09, 0x05, // 94:598 + 0x02, 0x5F, 0x0C, 0x06, // 95:607 + 0x02, 0x6B, 0x03, 0x03, // 96:619 + 0x02, 0x6E, 0x0A, 0x06, // 97:622 + 0x02, 0x78, 0x0A, 0x06, // 98:632 + 0x02, 0x82, 0x0A, 0x05, // 99:642 + 0x02, 0x8C, 0x0A, 0x06, // 100:652 + 0x02, 0x96, 0x0A, 0x06, // 101:662 + 0x02, 0xA0, 0x05, 0x03, // 102:672 + 0x02, 0xA5, 0x0A, 0x06, // 103:677 + 0x02, 0xAF, 0x0A, 0x06, // 104:687 + 0x02, 0xB9, 0x04, 0x02, // 105:697 + 0x02, 0xBD, 0x04, 0x02, // 106:701 + 0x02, 0xC1, 0x08, 0x05, // 107:705 + 0x02, 0xC9, 0x04, 0x02, // 108:713 + 0x02, 0xCD, 0x10, 0x08, // 109:717 + 0x02, 0xDD, 0x0A, 0x06, // 110:733 + 0x02, 0xE7, 0x0A, 0x06, // 111:743 + 0x02, 0xF1, 0x0A, 0x06, // 112:753 + 0x02, 0xFB, 0x0A, 0x06, // 113:763 + 0x03, 0x05, 0x05, 0x03, // 114:773 + 0x03, 0x0A, 0x08, 0x05, // 115:778 + 0x03, 0x12, 0x06, 0x03, // 116:786 + 0x03, 0x18, 0x0A, 0x06, // 117:792 + 0x03, 0x22, 0x09, 0x05, // 118:802 + 0x03, 0x2B, 0x0E, 0x07, // 119:811 + 0x03, 0x39, 0x0A, 0x05, // 120:825 + 0x03, 0x43, 0x09, 0x05, // 121:835 + 0x03, 0x4C, 0x0A, 0x05, // 122:844 + 0x03, 0x56, 0x06, 0x03, // 123:854 + 0x03, 0x5C, 0x04, 0x03, // 124:860 + 0x03, 0x60, 0x05, 0x03, // 125:864 + 0x03, 0x65, 0x09, 0x06, // 126:869 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 128:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 129:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 130:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 131:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 132:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 133:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 134:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 135:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 136:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 137:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 138:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 139:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 140:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 141:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 142:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 143:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 144:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 145:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 146:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 147:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 148:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 149:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 150:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 151:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 152:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 153:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 154:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 155:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 156:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 157:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 158:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 159:65535 + 0xFF, 0xFF, 0x00, 0x03, // 160:65535 + 0x03, 0x6E, 0x04, 0x03, // 161:878 + 0x03, 0x72, 0x0A, 0x06, // 162:882 + 0x03, 0x7C, 0x0C, 0x06, // 163:892 + 0x03, 0x88, 0x0A, 0x06, // 164:904 + 0x03, 0x92, 0x0A, 0x06, // 165:914 + 0x03, 0x9C, 0x04, 0x03, // 166:924 + 0x03, 0xA0, 0x0A, 0x06, // 167:928 + 0x03, 0xAA, 0x05, 0x03, // 168:938 + 0x03, 0xAF, 0x0D, 0x07, // 169:943 + 0x03, 0xBC, 0x07, 0x04, // 170:956 + 0x03, 0xC3, 0x0A, 0x06, // 171:963 + 0x03, 0xCD, 0x09, 0x06, // 172:973 + 0x03, 0xD6, 0x03, 0x03, // 173:982 + 0x03, 0xD9, 0x0D, 0x07, // 174:985 + 0x03, 0xE6, 0x0B, 0x06, // 175:998 + 0x03, 0xF1, 0x07, 0x04, // 176:1009 + 0x03, 0xF8, 0x0A, 0x05, // 177:1016 + 0x04, 0x02, 0x05, 0x03, // 178:1026 + 0x04, 0x07, 0x05, 0x03, // 179:1031 + 0x04, 0x0C, 0x05, 0x03, // 180:1036 + 0x04, 0x11, 0x0A, 0x06, // 181:1041 + 0x04, 0x1B, 0x09, 0x05, // 182:1051 + 0x04, 0x24, 0x03, 0x03, // 183:1060 + 0x04, 0x27, 0x06, 0x03, // 184:1063 + 0x04, 0x2D, 0x05, 0x03, // 185:1069 + 0x04, 0x32, 0x07, 0x04, // 186:1074 + 0x04, 0x39, 0x0A, 0x06, // 187:1081 + 0x04, 0x43, 0x10, 0x08, // 188:1091 + 0x04, 0x53, 0x10, 0x08, // 189:1107 + 0x04, 0x63, 0x10, 0x08, // 190:1123 + 0x04, 0x73, 0x0A, 0x06, // 191:1139 + 0x04, 0x7D, 0x0E, 0x07, // 192:1149 + 0x04, 0x8B, 0x0E, 0x07, // 193:1163 + 0x04, 0x99, 0x0E, 0x07, // 194:1177 + 0x04, 0xA7, 0x0E, 0x07, // 195:1191 + 0x04, 0xB5, 0x0E, 0x07, // 196:1205 + 0x04, 0xC3, 0x0E, 0x07, // 197:1219 + 0x04, 0xD1, 0x12, 0x0A, // 198:1233 + 0x04, 0xE3, 0x0C, 0x07, // 199:1251 + 0x04, 0xEF, 0x0C, 0x07, // 200:1263 + 0x04, 0xFB, 0x0C, 0x07, // 201:1275 + 0x05, 0x07, 0x0C, 0x07, // 202:1287 + 0x05, 0x13, 0x0C, 0x07, // 203:1299 + 0x05, 0x1F, 0x05, 0x03, // 204:1311 + 0x05, 0x24, 0x04, 0x03, // 205:1316 + 0x05, 0x28, 0x04, 0x03, // 206:1320 + 0x05, 0x2C, 0x05, 0x03, // 207:1324 + 0x05, 0x31, 0x0B, 0x07, // 208:1329 + 0x05, 0x3C, 0x0C, 0x07, // 209:1340 + 0x05, 0x48, 0x0E, 0x08, // 210:1352 + 0x05, 0x56, 0x0E, 0x08, // 211:1366 + 0x05, 0x64, 0x0E, 0x08, // 212:1380 + 0x05, 0x72, 0x0E, 0x08, // 213:1394 + 0x05, 0x80, 0x0E, 0x08, // 214:1408 + 0x05, 0x8E, 0x0A, 0x06, // 215:1422 + 0x05, 0x98, 0x0D, 0x08, // 216:1432 + 0x05, 0xA5, 0x0C, 0x07, // 217:1445 + 0x05, 0xB1, 0x0C, 0x07, // 218:1457 + 0x05, 0xBD, 0x0C, 0x07, // 219:1469 + 0x05, 0xC9, 0x0C, 0x07, // 220:1481 + 0x05, 0xD5, 0x0D, 0x07, // 221:1493 + 0x05, 0xE2, 0x0B, 0x07, // 222:1506 + 0x05, 0xED, 0x0C, 0x06, // 223:1517 + 0x05, 0xF9, 0x0A, 0x06, // 224:1529 + 0x06, 0x03, 0x0A, 0x06, // 225:1539 + 0x06, 0x0D, 0x0A, 0x06, // 226:1549 + 0x06, 0x17, 0x0A, 0x06, // 227:1559 + 0x06, 0x21, 0x0A, 0x06, // 228:1569 + 0x06, 0x2B, 0x0A, 0x06, // 229:1579 + 0x06, 0x35, 0x10, 0x09, // 230:1589 + 0x06, 0x45, 0x0A, 0x05, // 231:1605 + 0x06, 0x4F, 0x0A, 0x06, // 232:1615 + 0x06, 0x59, 0x0A, 0x06, // 233:1625 + 0x06, 0x63, 0x0A, 0x06, // 234:1635 + 0x06, 0x6D, 0x0A, 0x06, // 235:1645 + 0x06, 0x77, 0x05, 0x03, // 236:1655 + 0x06, 0x7C, 0x04, 0x03, // 237:1660 + 0x06, 0x80, 0x05, 0x03, // 238:1664 + 0x06, 0x85, 0x05, 0x03, // 239:1669 + 0x06, 0x8A, 0x0A, 0x06, // 240:1674 + 0x06, 0x94, 0x0A, 0x06, // 241:1684 + 0x06, 0x9E, 0x0A, 0x06, // 242:1694 + 0x06, 0xA8, 0x0A, 0x06, // 243:1704 + 0x06, 0xB2, 0x0A, 0x06, // 244:1714 + 0x06, 0xBC, 0x0A, 0x06, // 245:1724 + 0x06, 0xC6, 0x0A, 0x06, // 246:1734 + 0x06, 0xD0, 0x09, 0x05, // 247:1744 + 0x06, 0xD9, 0x0A, 0x06, // 248:1753 + 0x06, 0xE3, 0x0A, 0x06, // 249:1763 + 0x06, 0xED, 0x0A, 0x06, // 250:1773 + 0x06, 0xF7, 0x0A, 0x06, // 251:1783 + 0x07, 0x01, 0x0A, 0x06, // 252:1793 + 0x07, 0x0B, 0x09, 0x05, // 253:1803 + 0x07, 0x14, 0x0A, 0x06, // 254:1812 + 0x07, 0x1E, 0x09, 0x05, // 255:1822 + + // Font Data: + 0x00,0x00,0xF8,0x02, // 33 + 0x38,0x00,0x00,0x00,0x38, // 34 + 0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35 + 0x30,0x01,0x28,0x02,0xF8,0x07,0x48,0x02,0x90,0x01, // 36 + 0x00,0x00,0x30,0x00,0x48,0x00,0x30,0x03,0xC0,0x00,0xB0,0x01,0x48,0x02,0x80,0x01, // 37 + 0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02, // 38 + 0x38, // 39 + 0xE0,0x03,0x10,0x04,0x08,0x08, // 40 + 0x08,0x08,0x10,0x04,0xE0,0x03, // 41 + 0x28,0x00,0x18,0x00,0x28, // 42 + 0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43 + 0x00,0x00,0x00,0x06, // 44 + 0x80,0x00,0x80, // 45 + 0x00,0x00,0x00,0x02, // 46 + 0x00,0x03,0xE0,0x00,0x18, // 47 + 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 48 + 0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03, // 49 + 0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02, // 50 + 0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 51 + 0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52 + 0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01, // 53 + 0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01, // 54 + 0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55 + 0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 56 + 0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01, // 57 + 0x00,0x00,0x20,0x02, // 58 + 0x00,0x00,0x20,0x06, // 59 + 0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60 + 0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61 + 0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62 + 0x10,0x00,0x08,0x00,0x08,0x00,0xC8,0x02,0x48,0x00,0x30, // 63 + 0x00,0x00,0xC0,0x03,0x30,0x04,0xD0,0x09,0x28,0x0A,0x28,0x0A,0xC8,0x0B,0x68,0x0A,0x10,0x05,0xE0,0x04, // 64 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x88,0x00,0xB0,0x00,0xC0,0x01,0x00,0x02, // 65 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0xF0,0x01, // 66 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x10,0x01, // 67 + 0x00,0x00,0xF8,0x03,0x08,0x02,0x08,0x02,0x10,0x01,0xE0, // 68 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02, // 69 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x08, // 70 + 0x00,0x00,0xE0,0x00,0x10,0x01,0x08,0x02,0x48,0x02,0x50,0x01,0xC0, // 71 + 0x00,0x00,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03, // 72 + 0x00,0x00,0xF8,0x03, // 73 + 0x00,0x03,0x00,0x02,0x00,0x02,0xF8,0x01, // 74 + 0x00,0x00,0xF8,0x03,0x80,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 75 + 0x00,0x00,0xF8,0x03,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 76 + 0x00,0x00,0xF8,0x03,0x30,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x30,0x00,0xF8,0x03, // 77 + 0x00,0x00,0xF8,0x03,0x30,0x00,0x40,0x00,0x80,0x01,0xF8,0x03, // 78 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 79 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x48,0x00,0x30, // 80 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x03,0x08,0x03,0xF0,0x02, // 81 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0xC8,0x00,0x30,0x03, // 82 + 0x00,0x00,0x30,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x90,0x01, // 83 + 0x00,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,0x08,0x00,0x08, // 84 + 0x00,0x00,0xF8,0x01,0x00,0x02,0x00,0x02,0x00,0x02,0xF8,0x01, // 85 + 0x08,0x00,0x70,0x00,0x80,0x01,0x00,0x02,0x80,0x01,0x70,0x00,0x08, // 86 + 0x18,0x00,0xE0,0x01,0x00,0x02,0xF0,0x01,0x08,0x00,0xF0,0x01,0x00,0x02,0xE0,0x01,0x18, // 87 + 0x00,0x02,0x08,0x01,0x90,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 88 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC0,0x03,0x20,0x00,0x10,0x00,0x08, // 89 + 0x08,0x03,0x88,0x02,0xC8,0x02,0x68,0x02,0x38,0x02,0x18,0x02, // 90 + 0x00,0x00,0xF8,0x0F,0x08,0x08, // 91 + 0x18,0x00,0xE0,0x00,0x00,0x03, // 92 + 0x08,0x08,0xF8,0x0F, // 93 + 0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94 + 0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08, // 95 + 0x08,0x00,0x10, // 96 + 0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03, // 97 + 0x00,0x00,0xF8,0x03,0x20,0x02,0x20,0x02,0xC0,0x01, // 98 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0x40,0x01, // 99 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xF8,0x03, // 100 + 0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 101 + 0x20,0x00,0xF0,0x03,0x28, // 102 + 0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07, // 103 + 0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 104 + 0x00,0x00,0xE8,0x03, // 105 + 0x00,0x08,0xE8,0x07, // 106 + 0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02, // 107 + 0x00,0x00,0xF8,0x03, // 108 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 109 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 110 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01, // 111 + 0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 112 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F, // 113 + 0x00,0x00,0xE0,0x03,0x20, // 114 + 0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01, // 115 + 0x20,0x00,0xF8,0x03,0x20,0x02, // 116 + 0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03, // 117 + 0x20,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x20, // 118 + 0xE0,0x01,0x00,0x02,0xC0,0x01,0x20,0x00,0xC0,0x01,0x00,0x02,0xE0,0x01, // 119 + 0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02, // 120 + 0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121 + 0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02, // 122 + 0x80,0x00,0x78,0x0F,0x08,0x08, // 123 + 0x00,0x00,0xF8,0x0F, // 124 + 0x08,0x08,0x78,0x0F,0x80, // 125 + 0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126 + 0x00,0x00,0xA0,0x0F, // 161 + 0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01, // 162 + 0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02, // 163 + 0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01, // 164 + 0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01, // 165 + 0x00,0x00,0x38,0x0F, // 166 + 0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05, // 167 + 0x08,0x00,0x00,0x00,0x08, // 168 + 0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169 + 0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170 + 0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02, // 171 + 0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172 + 0x80,0x00,0x80, // 173 + 0xE0,0x00,0x10,0x01,0xE8,0x02,0x68,0x02,0xC8,0x02,0x10,0x01,0xE0, // 174 + 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 175 + 0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176 + 0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02, // 177 + 0x48,0x00,0x68,0x00,0x58, // 178 + 0x48,0x00,0x58,0x00,0x68, // 179 + 0x00,0x00,0x10,0x00,0x08, // 180 + 0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03, // 181 + 0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182 + 0x00,0x00,0x40, // 183 + 0x00,0x00,0x00,0x14,0x00,0x18, // 184 + 0x00,0x00,0x10,0x00,0x78, // 185 + 0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186 + 0x00,0x00,0x40,0x02,0x80,0x01,0x40,0x02,0x80,0x01, // 187 + 0x00,0x00,0x10,0x02,0x78,0x01,0xC0,0x00,0x20,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 188 + 0x00,0x00,0x10,0x02,0x78,0x01,0x80,0x00,0x60,0x00,0x50,0x02,0x48,0x03,0xC0,0x02, // 189 + 0x48,0x00,0x58,0x00,0x68,0x03,0x80,0x00,0x60,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 190 + 0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04, // 191 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 192 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 193 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 194 + 0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 195 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 196 + 0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02, // 197 + 0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02, // 198 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01, // 199 + 0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 200 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02, // 201 + 0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 202 + 0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02, // 203 + 0x00,0x00,0xF9,0x03,0x02, // 204 + 0x02,0x00,0xF9,0x03, // 205 + 0x01,0x00,0xFA,0x03, // 206 + 0x02,0x00,0xF8,0x03,0x02, // 207 + 0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208 + 0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03, // 209 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 210 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01, // 211 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01, // 212 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01, // 213 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 214 + 0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01, // 215 + 0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216 + 0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01, // 217 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01, // 218 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01, // 219 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01, // 220 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221 + 0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222 + 0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01, // 223 + 0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03, // 224 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03, // 225 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03, // 226 + 0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03, // 227 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03, // 228 + 0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03, // 229 + 0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 230 + 0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01, // 231 + 0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02, // 232 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02, // 233 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02, // 234 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02, // 235 + 0x00,0x00,0xE4,0x03,0x08, // 236 + 0x08,0x00,0xE4,0x03, // 237 + 0x08,0x00,0xE4,0x03,0x08, // 238 + 0x08,0x00,0xE0,0x03,0x08, // 239 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01, // 240 + 0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03, // 241 + 0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01, // 242 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01, // 243 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01, // 244 + 0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01, // 245 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01, // 246 + 0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247 + 0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01, // 248 + 0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 249 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03, // 250 + 0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 251 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03, // 252 + 0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253 + 0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 254 + 0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20 // 255 +}; + +const uint8_t ArialMT_Plain_16[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32:65535 + 0x00, 0x00, 0x08, 0x04, // 33:0 + 0x00, 0x08, 0x0D, 0x06, // 34:8 + 0x00, 0x15, 0x1A, 0x09, // 35:21 + 0x00, 0x2F, 0x17, 0x09, // 36:47 + 0x00, 0x46, 0x26, 0x0E, // 37:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38:108 + 0x00, 0x89, 0x04, 0x03, // 39:137 + 0x00, 0x8D, 0x0C, 0x05, // 40:141 + 0x00, 0x99, 0x0B, 0x05, // 41:153 + 0x00, 0xA4, 0x0D, 0x06, // 42:164 + 0x00, 0xB1, 0x17, 0x09, // 43:177 + 0x00, 0xC8, 0x09, 0x04, // 44:200 + 0x00, 0xD1, 0x0B, 0x05, // 45:209 + 0x00, 0xDC, 0x08, 0x04, // 46:220 + 0x00, 0xE4, 0x0A, 0x04, // 47:228 + 0x00, 0xEE, 0x17, 0x09, // 48:238 + 0x01, 0x05, 0x11, 0x09, // 49:261 + 0x01, 0x16, 0x17, 0x09, // 50:278 + 0x01, 0x2D, 0x17, 0x09, // 51:301 + 0x01, 0x44, 0x17, 0x09, // 52:324 + 0x01, 0x5B, 0x17, 0x09, // 53:347 + 0x01, 0x72, 0x17, 0x09, // 54:370 + 0x01, 0x89, 0x16, 0x09, // 55:393 + 0x01, 0x9F, 0x17, 0x09, // 56:415 + 0x01, 0xB6, 0x17, 0x09, // 57:438 + 0x01, 0xCD, 0x05, 0x04, // 58:461 + 0x01, 0xD2, 0x06, 0x04, // 59:466 + 0x01, 0xD8, 0x17, 0x09, // 60:472 + 0x01, 0xEF, 0x17, 0x09, // 61:495 + 0x02, 0x06, 0x17, 0x09, // 62:518 + 0x02, 0x1D, 0x16, 0x09, // 63:541 + 0x02, 0x33, 0x2F, 0x10, // 64:563 + 0x02, 0x62, 0x1D, 0x0B, // 65:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66:639 + 0x02, 0x9C, 0x20, 0x0C, // 67:668 + 0x02, 0xBC, 0x20, 0x0C, // 68:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69:732 + 0x02, 0xF9, 0x19, 0x0A, // 70:761 + 0x03, 0x12, 0x20, 0x0C, // 71:786 + 0x03, 0x32, 0x1D, 0x0C, // 72:818 + 0x03, 0x4F, 0x05, 0x04, // 73:847 + 0x03, 0x54, 0x14, 0x08, // 74:852 + 0x03, 0x68, 0x1D, 0x0B, // 75:872 + 0x03, 0x85, 0x17, 0x09, // 76:901 + 0x03, 0x9C, 0x23, 0x0D, // 77:924 + 0x03, 0xBF, 0x1D, 0x0C, // 78:959 + 0x03, 0xDC, 0x20, 0x0C, // 79:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80:1020 + 0x04, 0x18, 0x20, 0x0C, // 81:1048 + 0x04, 0x38, 0x1D, 0x0C, // 82:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83:1109 + 0x04, 0x72, 0x19, 0x0A, // 84:1138 + 0x04, 0x8B, 0x1D, 0x0C, // 85:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88:1263 + 0x05, 0x0F, 0x19, 0x0B, // 89:1295 + 0x05, 0x28, 0x1A, 0x0A, // 90:1320 + 0x05, 0x42, 0x0C, 0x04, // 91:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92:1358 + 0x05, 0x59, 0x09, 0x04, // 93:1369 + 0x05, 0x62, 0x14, 0x08, // 94:1378 + 0x05, 0x76, 0x1B, 0x09, // 95:1398 + 0x05, 0x91, 0x07, 0x05, // 96:1425 + 0x05, 0x98, 0x17, 0x09, // 97:1432 + 0x05, 0xAF, 0x17, 0x09, // 98:1455 + 0x05, 0xC6, 0x14, 0x08, // 99:1478 + 0x05, 0xDA, 0x17, 0x09, // 100:1498 + 0x05, 0xF1, 0x17, 0x09, // 101:1521 + 0x06, 0x08, 0x0A, 0x04, // 102:1544 + 0x06, 0x12, 0x17, 0x09, // 103:1554 + 0x06, 0x29, 0x14, 0x09, // 104:1577 + 0x06, 0x3D, 0x05, 0x04, // 105:1597 + 0x06, 0x42, 0x06, 0x04, // 106:1602 + 0x06, 0x48, 0x17, 0x08, // 107:1608 + 0x06, 0x5F, 0x05, 0x04, // 108:1631 + 0x06, 0x64, 0x23, 0x0D, // 109:1636 + 0x06, 0x87, 0x14, 0x09, // 110:1671 + 0x06, 0x9B, 0x17, 0x09, // 111:1691 + 0x06, 0xB2, 0x17, 0x09, // 112:1714 + 0x06, 0xC9, 0x18, 0x09, // 113:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114:1761 + 0x06, 0xEE, 0x14, 0x08, // 115:1774 + 0x07, 0x02, 0x0B, 0x04, // 116:1794 + 0x07, 0x0D, 0x14, 0x09, // 117:1805 + 0x07, 0x21, 0x13, 0x08, // 118:1825 + 0x07, 0x34, 0x1F, 0x0C, // 119:1844 + 0x07, 0x53, 0x14, 0x08, // 120:1875 + 0x07, 0x67, 0x13, 0x08, // 121:1895 + 0x07, 0x7A, 0x14, 0x08, // 122:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123:1934 + 0x07, 0x9D, 0x06, 0x04, // 124:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125:1955 + 0x07, 0xB1, 0x17, 0x09, // 126:1969 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x10, // 128:65535 + 0xFF, 0xFF, 0x00, 0x10, // 129:65535 + 0xFF, 0xFF, 0x00, 0x10, // 130:65535 + 0xFF, 0xFF, 0x00, 0x10, // 131:65535 + 0xFF, 0xFF, 0x00, 0x10, // 132:65535 + 0xFF, 0xFF, 0x00, 0x10, // 133:65535 + 0xFF, 0xFF, 0x00, 0x10, // 134:65535 + 0xFF, 0xFF, 0x00, 0x10, // 135:65535 + 0xFF, 0xFF, 0x00, 0x10, // 136:65535 + 0xFF, 0xFF, 0x00, 0x10, // 137:65535 + 0xFF, 0xFF, 0x00, 0x10, // 138:65535 + 0xFF, 0xFF, 0x00, 0x10, // 139:65535 + 0xFF, 0xFF, 0x00, 0x10, // 140:65535 + 0xFF, 0xFF, 0x00, 0x10, // 141:65535 + 0xFF, 0xFF, 0x00, 0x10, // 142:65535 + 0xFF, 0xFF, 0x00, 0x10, // 143:65535 + 0xFF, 0xFF, 0x00, 0x10, // 144:65535 + 0xFF, 0xFF, 0x00, 0x10, // 145:65535 + 0xFF, 0xFF, 0x00, 0x10, // 146:65535 + 0xFF, 0xFF, 0x00, 0x10, // 147:65535 + 0xFF, 0xFF, 0x00, 0x10, // 148:65535 + 0xFF, 0xFF, 0x00, 0x10, // 149:65535 + 0xFF, 0xFF, 0x00, 0x10, // 150:65535 + 0xFF, 0xFF, 0x00, 0x10, // 151:65535 + 0xFF, 0xFF, 0x00, 0x10, // 152:65535 + 0xFF, 0xFF, 0x00, 0x10, // 153:65535 + 0xFF, 0xFF, 0x00, 0x10, // 154:65535 + 0xFF, 0xFF, 0x00, 0x10, // 155:65535 + 0xFF, 0xFF, 0x00, 0x10, // 156:65535 + 0xFF, 0xFF, 0x00, 0x10, // 157:65535 + 0xFF, 0xFF, 0x00, 0x10, // 158:65535 + 0xFF, 0xFF, 0x00, 0x10, // 159:65535 + 0xFF, 0xFF, 0x00, 0x04, // 160:65535 + 0x07, 0xC8, 0x09, 0x05, // 161:1992 + 0x07, 0xD1, 0x17, 0x09, // 162:2001 + 0x07, 0xE8, 0x17, 0x09, // 163:2024 + 0x07, 0xFF, 0x14, 0x09, // 164:2047 + 0x08, 0x13, 0x1A, 0x09, // 165:2067 + 0x08, 0x2D, 0x06, 0x04, // 166:2093 + 0x08, 0x33, 0x17, 0x09, // 167:2099 + 0x08, 0x4A, 0x07, 0x05, // 168:2122 + 0x08, 0x51, 0x23, 0x0C, // 169:2129 + 0x08, 0x74, 0x0E, 0x06, // 170:2164 + 0x08, 0x82, 0x14, 0x09, // 171:2178 + 0x08, 0x96, 0x17, 0x09, // 172:2198 + 0x08, 0xAD, 0x0B, 0x05, // 173:2221 + 0x08, 0xB8, 0x23, 0x0C, // 174:2232 + 0x08, 0xDB, 0x19, 0x09, // 175:2267 + 0x08, 0xF4, 0x0D, 0x06, // 176:2292 + 0x09, 0x01, 0x17, 0x09, // 177:2305 + 0x09, 0x18, 0x0E, 0x05, // 178:2328 + 0x09, 0x26, 0x0D, 0x05, // 179:2342 + 0x09, 0x33, 0x0A, 0x05, // 180:2355 + 0x09, 0x3D, 0x17, 0x09, // 181:2365 + 0x09, 0x54, 0x19, 0x09, // 182:2388 + 0x09, 0x6D, 0x08, 0x05, // 183:2413 + 0x09, 0x75, 0x0C, 0x05, // 184:2421 + 0x09, 0x81, 0x0B, 0x05, // 185:2433 + 0x09, 0x8C, 0x0D, 0x06, // 186:2444 + 0x09, 0x99, 0x17, 0x09, // 187:2457 + 0x09, 0xB0, 0x26, 0x0D, // 188:2480 + 0x09, 0xD6, 0x26, 0x0D, // 189:2518 + 0x09, 0xFC, 0x26, 0x0D, // 190:2556 + 0x0A, 0x22, 0x1A, 0x0A, // 191:2594 + 0x0A, 0x3C, 0x1D, 0x0B, // 192:2620 + 0x0A, 0x59, 0x1D, 0x0B, // 193:2649 + 0x0A, 0x76, 0x1D, 0x0B, // 194:2678 + 0x0A, 0x93, 0x1D, 0x0B, // 195:2707 + 0x0A, 0xB0, 0x1D, 0x0B, // 196:2736 + 0x0A, 0xCD, 0x1D, 0x0B, // 197:2765 + 0x0A, 0xEA, 0x2C, 0x10, // 198:2794 + 0x0B, 0x16, 0x20, 0x0C, // 199:2838 + 0x0B, 0x36, 0x1D, 0x0B, // 200:2870 + 0x0B, 0x53, 0x1D, 0x0B, // 201:2899 + 0x0B, 0x70, 0x1D, 0x0B, // 202:2928 + 0x0B, 0x8D, 0x1D, 0x0B, // 203:2957 + 0x0B, 0xAA, 0x05, 0x04, // 204:2986 + 0x0B, 0xAF, 0x07, 0x04, // 205:2991 + 0x0B, 0xB6, 0x0A, 0x04, // 206:2998 + 0x0B, 0xC0, 0x07, 0x04, // 207:3008 + 0x0B, 0xC7, 0x20, 0x0C, // 208:3015 + 0x0B, 0xE7, 0x1D, 0x0C, // 209:3047 + 0x0C, 0x04, 0x20, 0x0C, // 210:3076 + 0x0C, 0x24, 0x20, 0x0C, // 211:3108 + 0x0C, 0x44, 0x20, 0x0C, // 212:3140 + 0x0C, 0x64, 0x20, 0x0C, // 213:3172 + 0x0C, 0x84, 0x20, 0x0C, // 214:3204 + 0x0C, 0xA4, 0x17, 0x09, // 215:3236 + 0x0C, 0xBB, 0x20, 0x0C, // 216:3259 + 0x0C, 0xDB, 0x1D, 0x0C, // 217:3291 + 0x0C, 0xF8, 0x1D, 0x0C, // 218:3320 + 0x0D, 0x15, 0x1D, 0x0C, // 219:3349 + 0x0D, 0x32, 0x1D, 0x0C, // 220:3378 + 0x0D, 0x4F, 0x19, 0x0B, // 221:3407 + 0x0D, 0x68, 0x1D, 0x0B, // 222:3432 + 0x0D, 0x85, 0x17, 0x0A, // 223:3461 + 0x0D, 0x9C, 0x17, 0x09, // 224:3484 + 0x0D, 0xB3, 0x17, 0x09, // 225:3507 + 0x0D, 0xCA, 0x17, 0x09, // 226:3530 + 0x0D, 0xE1, 0x17, 0x09, // 227:3553 + 0x0D, 0xF8, 0x17, 0x09, // 228:3576 + 0x0E, 0x0F, 0x17, 0x09, // 229:3599 + 0x0E, 0x26, 0x29, 0x0E, // 230:3622 + 0x0E, 0x4F, 0x14, 0x08, // 231:3663 + 0x0E, 0x63, 0x17, 0x09, // 232:3683 + 0x0E, 0x7A, 0x17, 0x09, // 233:3706 + 0x0E, 0x91, 0x17, 0x09, // 234:3729 + 0x0E, 0xA8, 0x17, 0x09, // 235:3752 + 0x0E, 0xBF, 0x05, 0x04, // 236:3775 + 0x0E, 0xC4, 0x07, 0x04, // 237:3780 + 0x0E, 0xCB, 0x0A, 0x04, // 238:3787 + 0x0E, 0xD5, 0x07, 0x04, // 239:3797 + 0x0E, 0xDC, 0x17, 0x09, // 240:3804 + 0x0E, 0xF3, 0x14, 0x09, // 241:3827 + 0x0F, 0x07, 0x17, 0x09, // 242:3847 + 0x0F, 0x1E, 0x17, 0x09, // 243:3870 + 0x0F, 0x35, 0x17, 0x09, // 244:3893 + 0x0F, 0x4C, 0x17, 0x09, // 245:3916 + 0x0F, 0x63, 0x17, 0x09, // 246:3939 + 0x0F, 0x7A, 0x17, 0x09, // 247:3962 + 0x0F, 0x91, 0x17, 0x0A, // 248:3985 + 0x0F, 0xA8, 0x14, 0x09, // 249:4008 + 0x0F, 0xBC, 0x14, 0x09, // 250:4028 + 0x0F, 0xD0, 0x14, 0x09, // 251:4048 + 0x0F, 0xE4, 0x14, 0x09, // 252:4068 + 0x0F, 0xF8, 0x13, 0x08, // 253:4088 + 0x10, 0x0B, 0x17, 0x09, // 254:4107 + 0x10, 0x22, 0x13, 0x08, // 255:4130 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F, // 33 + 0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34 + 0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08, // 35 + 0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36 + 0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C, // 37 + 0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38 + 0x00,0x00,0x00,0x78, // 39 + 0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02, // 40 + 0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41 + 0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, // 46 + 0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 47 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49 + 0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50 + 0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51 + 0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52 + 0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54 + 0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18, // 55 + 0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56 + 0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57 + 0x00,0x00,0x00,0x40,0x40, // 58 + 0x00,0x00,0x00,0x40,0xC0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60 + 0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62 + 0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 63 + 0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 67 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 68 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E, // 71 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72 + 0x00,0x00,0x00,0xF8,0x7F, // 73 + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F, // 74 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 79 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 80 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F, // 81 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82 + 0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83 + 0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85 + 0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18, // 86 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87 + 0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40, // 88 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89 + 0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40, // 90 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02, // 91 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93 + 0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01, // 94 + 0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95 + 0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20, // 99 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48, // 102 + 0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 104 + 0x00,0x00,0x00,0xC8,0x7F, // 105 + 0x00,0x00,0x02,0xC8,0xFF,0x01, // 106 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107 + 0x00,0x00,0x00,0xF8,0x7F, // 108 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 110 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03, // 113 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114 + 0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38, // 115 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 117 + 0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118 + 0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119 + 0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40, // 120 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121 + 0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40, // 122 + 0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123 + 0x00,0x00,0x00,0xF8,0xFF,0x03, // 124 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04, // 125 + 0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162 + 0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163 + 0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B, // 164 + 0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A, // 165 + 0x00,0x00,0x00,0xF8,0xF1,0x03, // 166 + 0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167 + 0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169 + 0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01, // 170 + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20, // 171 + 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174 + 0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175 + 0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176 + 0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177 + 0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01, // 178 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08, // 180 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181 + 0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, // 183 + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03, // 184 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185 + 0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 188 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C, // 189 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 190 + 0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0, // 191 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197 + 0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41, // 198 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 199 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203 + 0x01,0x00,0x00,0xFA,0x7F, // 204 + 0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205 + 0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02, // 206 + 0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207 + 0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 208 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 210 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 211 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 212 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 213 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 214 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215 + 0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F, // 216 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222 + 0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20, // 231 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235 + 0x08,0x00,0x00,0xD0,0x7F, // 236 + 0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237 + 0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10, // 238 + 0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239 + 0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F, // 241 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247 + 0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 249 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F, // 250 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F, // 251 + 0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 252 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0 // 255 +}; +const uint8_t ArialMT_Plain_24[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32:65535 + 0x00, 0x00, 0x13, 0x07, // 33:0 + 0x00, 0x13, 0x1A, 0x09, // 34:19 + 0x00, 0x2D, 0x33, 0x0D, // 35:45 + 0x00, 0x60, 0x2F, 0x0D, // 36:96 + 0x00, 0x8F, 0x4F, 0x15, // 37:143 + 0x00, 0xDE, 0x3B, 0x10, // 38:222 + 0x01, 0x19, 0x0A, 0x05, // 39:281 + 0x01, 0x23, 0x1C, 0x08, // 40:291 + 0x01, 0x3F, 0x1B, 0x08, // 41:319 + 0x01, 0x5A, 0x21, 0x09, // 42:346 + 0x01, 0x7B, 0x32, 0x0E, // 43:379 + 0x01, 0xAD, 0x10, 0x07, // 44:429 + 0x01, 0xBD, 0x1B, 0x08, // 45:445 + 0x01, 0xD8, 0x0F, 0x07, // 46:472 + 0x01, 0xE7, 0x19, 0x07, // 47:487 + 0x02, 0x00, 0x2F, 0x0D, // 48:512 + 0x02, 0x2F, 0x23, 0x0D, // 49:559 + 0x02, 0x52, 0x2F, 0x0D, // 50:594 + 0x02, 0x81, 0x2F, 0x0D, // 51:641 + 0x02, 0xB0, 0x2F, 0x0D, // 52:688 + 0x02, 0xDF, 0x2F, 0x0D, // 53:735 + 0x03, 0x0E, 0x2F, 0x0D, // 54:782 + 0x03, 0x3D, 0x2D, 0x0D, // 55:829 + 0x03, 0x6A, 0x2F, 0x0D, // 56:874 + 0x03, 0x99, 0x2F, 0x0D, // 57:921 + 0x03, 0xC8, 0x0F, 0x07, // 58:968 + 0x03, 0xD7, 0x10, 0x07, // 59:983 + 0x03, 0xE7, 0x2F, 0x0E, // 60:999 + 0x04, 0x16, 0x2F, 0x0E, // 61:1046 + 0x04, 0x45, 0x2E, 0x0E, // 62:1093 + 0x04, 0x73, 0x2E, 0x0D, // 63:1139 + 0x04, 0xA1, 0x5B, 0x18, // 64:1185 + 0x04, 0xFC, 0x3B, 0x10, // 65:1276 + 0x05, 0x37, 0x3B, 0x10, // 66:1335 + 0x05, 0x72, 0x3F, 0x11, // 67:1394 + 0x05, 0xB1, 0x3F, 0x11, // 68:1457 + 0x05, 0xF0, 0x3B, 0x10, // 69:1520 + 0x06, 0x2B, 0x35, 0x0F, // 70:1579 + 0x06, 0x60, 0x43, 0x13, // 71:1632 + 0x06, 0xA3, 0x3B, 0x11, // 72:1699 + 0x06, 0xDE, 0x0F, 0x07, // 73:1758 + 0x06, 0xED, 0x27, 0x0C, // 74:1773 + 0x07, 0x14, 0x3F, 0x10, // 75:1812 + 0x07, 0x53, 0x2F, 0x0D, // 76:1875 + 0x07, 0x82, 0x43, 0x14, // 77:1922 + 0x07, 0xC5, 0x3B, 0x11, // 78:1989 + 0x08, 0x00, 0x47, 0x13, // 79:2048 + 0x08, 0x47, 0x3A, 0x10, // 80:2119 + 0x08, 0x81, 0x47, 0x13, // 81:2177 + 0x08, 0xC8, 0x3F, 0x11, // 82:2248 + 0x09, 0x07, 0x3B, 0x10, // 83:2311 + 0x09, 0x42, 0x35, 0x0F, // 84:2370 + 0x09, 0x77, 0x3B, 0x11, // 85:2423 + 0x09, 0xB2, 0x39, 0x10, // 86:2482 + 0x09, 0xEB, 0x59, 0x17, // 87:2539 + 0x0A, 0x44, 0x3B, 0x10, // 88:2628 + 0x0A, 0x7F, 0x3D, 0x10, // 89:2687 + 0x0A, 0xBC, 0x37, 0x0F, // 90:2748 + 0x0A, 0xF3, 0x14, 0x07, // 91:2803 + 0x0B, 0x07, 0x1B, 0x07, // 92:2823 + 0x0B, 0x22, 0x18, 0x07, // 93:2850 + 0x0B, 0x3A, 0x2A, 0x0B, // 94:2874 + 0x0B, 0x64, 0x34, 0x0D, // 95:2916 + 0x0B, 0x98, 0x11, 0x08, // 96:2968 + 0x0B, 0xA9, 0x2F, 0x0D, // 97:2985 + 0x0B, 0xD8, 0x33, 0x0D, // 98:3032 + 0x0C, 0x0B, 0x2B, 0x0C, // 99:3083 + 0x0C, 0x36, 0x2F, 0x0D, // 100:3126 + 0x0C, 0x65, 0x2F, 0x0D, // 101:3173 + 0x0C, 0x94, 0x1A, 0x07, // 102:3220 + 0x0C, 0xAE, 0x2F, 0x0D, // 103:3246 + 0x0C, 0xDD, 0x2F, 0x0D, // 104:3293 + 0x0D, 0x0C, 0x0F, 0x05, // 105:3340 + 0x0D, 0x1B, 0x10, 0x05, // 106:3355 + 0x0D, 0x2B, 0x2F, 0x0C, // 107:3371 + 0x0D, 0x5A, 0x0F, 0x05, // 108:3418 + 0x0D, 0x69, 0x47, 0x14, // 109:3433 + 0x0D, 0xB0, 0x2F, 0x0D, // 110:3504 + 0x0D, 0xDF, 0x2F, 0x0D, // 111:3551 + 0x0E, 0x0E, 0x33, 0x0D, // 112:3598 + 0x0E, 0x41, 0x30, 0x0D, // 113:3649 + 0x0E, 0x71, 0x1E, 0x08, // 114:3697 + 0x0E, 0x8F, 0x2B, 0x0C, // 115:3727 + 0x0E, 0xBA, 0x1B, 0x07, // 116:3770 + 0x0E, 0xD5, 0x2F, 0x0D, // 117:3797 + 0x0F, 0x04, 0x2A, 0x0C, // 118:3844 + 0x0F, 0x2E, 0x42, 0x11, // 119:3886 + 0x0F, 0x70, 0x2B, 0x0C, // 120:3952 + 0x0F, 0x9B, 0x2A, 0x0C, // 121:3995 + 0x0F, 0xC5, 0x2B, 0x0C, // 122:4037 + 0x0F, 0xF0, 0x1C, 0x08, // 123:4080 + 0x10, 0x0C, 0x10, 0x06, // 124:4108 + 0x10, 0x1C, 0x1B, 0x08, // 125:4124 + 0x10, 0x37, 0x32, 0x0E, // 126:4151 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x18, // 128:65535 + 0xFF, 0xFF, 0x00, 0x18, // 129:65535 + 0xFF, 0xFF, 0x00, 0x18, // 130:65535 + 0xFF, 0xFF, 0x00, 0x18, // 131:65535 + 0xFF, 0xFF, 0x00, 0x18, // 132:65535 + 0xFF, 0xFF, 0x00, 0x18, // 133:65535 + 0xFF, 0xFF, 0x00, 0x18, // 134:65535 + 0xFF, 0xFF, 0x00, 0x18, // 135:65535 + 0xFF, 0xFF, 0x00, 0x18, // 136:65535 + 0xFF, 0xFF, 0x00, 0x18, // 137:65535 + 0xFF, 0xFF, 0x00, 0x18, // 138:65535 + 0xFF, 0xFF, 0x00, 0x18, // 139:65535 + 0xFF, 0xFF, 0x00, 0x18, // 140:65535 + 0xFF, 0xFF, 0x00, 0x18, // 141:65535 + 0xFF, 0xFF, 0x00, 0x18, // 142:65535 + 0xFF, 0xFF, 0x00, 0x18, // 143:65535 + 0xFF, 0xFF, 0x00, 0x18, // 144:65535 + 0xFF, 0xFF, 0x00, 0x18, // 145:65535 + 0xFF, 0xFF, 0x00, 0x18, // 146:65535 + 0xFF, 0xFF, 0x00, 0x18, // 147:65535 + 0xFF, 0xFF, 0x00, 0x18, // 148:65535 + 0xFF, 0xFF, 0x00, 0x18, // 149:65535 + 0xFF, 0xFF, 0x00, 0x18, // 150:65535 + 0xFF, 0xFF, 0x00, 0x18, // 151:65535 + 0xFF, 0xFF, 0x00, 0x18, // 152:65535 + 0xFF, 0xFF, 0x00, 0x18, // 153:65535 + 0xFF, 0xFF, 0x00, 0x18, // 154:65535 + 0xFF, 0xFF, 0x00, 0x18, // 155:65535 + 0xFF, 0xFF, 0x00, 0x18, // 156:65535 + 0xFF, 0xFF, 0x00, 0x18, // 157:65535 + 0xFF, 0xFF, 0x00, 0x18, // 158:65535 + 0xFF, 0xFF, 0x00, 0x18, // 159:65535 + 0xFF, 0xFF, 0x00, 0x07, // 160:65535 + 0x10, 0x69, 0x14, 0x08, // 161:4201 + 0x10, 0x7D, 0x2B, 0x0D, // 162:4221 + 0x10, 0xA8, 0x2F, 0x0D, // 163:4264 + 0x10, 0xD7, 0x33, 0x0D, // 164:4311 + 0x11, 0x0A, 0x31, 0x0D, // 165:4362 + 0x11, 0x3B, 0x10, 0x06, // 166:4411 + 0x11, 0x4B, 0x2F, 0x0D, // 167:4427 + 0x11, 0x7A, 0x19, 0x08, // 168:4474 + 0x11, 0x93, 0x46, 0x12, // 169:4499 + 0x11, 0xD9, 0x1A, 0x09, // 170:4569 + 0x11, 0xF3, 0x27, 0x0D, // 171:4595 + 0x12, 0x1A, 0x2F, 0x0E, // 172:4634 + 0x12, 0x49, 0x1B, 0x08, // 173:4681 + 0x12, 0x64, 0x46, 0x12, // 174:4708 + 0x12, 0xAA, 0x31, 0x0D, // 175:4778 + 0x12, 0xDB, 0x1E, 0x0A, // 176:4827 + 0x12, 0xF9, 0x33, 0x0D, // 177:4857 + 0x13, 0x2C, 0x1A, 0x08, // 178:4908 + 0x13, 0x46, 0x1A, 0x08, // 179:4934 + 0x13, 0x60, 0x19, 0x08, // 180:4960 + 0x13, 0x79, 0x2F, 0x0E, // 181:4985 + 0x13, 0xA8, 0x31, 0x0D, // 182:5032 + 0x13, 0xD9, 0x12, 0x08, // 183:5081 + 0x13, 0xEB, 0x18, 0x08, // 184:5099 + 0x14, 0x03, 0x16, 0x08, // 185:5123 + 0x14, 0x19, 0x1E, 0x09, // 186:5145 + 0x14, 0x37, 0x2E, 0x0D, // 187:5175 + 0x14, 0x65, 0x4F, 0x14, // 188:5221 + 0x14, 0xB4, 0x4B, 0x14, // 189:5300 + 0x14, 0xFF, 0x4B, 0x14, // 190:5375 + 0x15, 0x4A, 0x33, 0x0F, // 191:5450 + 0x15, 0x7D, 0x3B, 0x10, // 192:5501 + 0x15, 0xB8, 0x3B, 0x10, // 193:5560 + 0x15, 0xF3, 0x3B, 0x10, // 194:5619 + 0x16, 0x2E, 0x3B, 0x10, // 195:5678 + 0x16, 0x69, 0x3B, 0x10, // 196:5737 + 0x16, 0xA4, 0x3B, 0x10, // 197:5796 + 0x16, 0xDF, 0x5B, 0x18, // 198:5855 + 0x17, 0x3A, 0x3F, 0x11, // 199:5946 + 0x17, 0x79, 0x3B, 0x10, // 200:6009 + 0x17, 0xB4, 0x3B, 0x10, // 201:6068 + 0x17, 0xEF, 0x3B, 0x10, // 202:6127 + 0x18, 0x2A, 0x3B, 0x10, // 203:6186 + 0x18, 0x65, 0x11, 0x07, // 204:6245 + 0x18, 0x76, 0x11, 0x07, // 205:6262 + 0x18, 0x87, 0x15, 0x07, // 206:6279 + 0x18, 0x9C, 0x15, 0x07, // 207:6300 + 0x18, 0xB1, 0x3F, 0x11, // 208:6321 + 0x18, 0xF0, 0x3B, 0x11, // 209:6384 + 0x19, 0x2B, 0x47, 0x13, // 210:6443 + 0x19, 0x72, 0x47, 0x13, // 211:6514 + 0x19, 0xB9, 0x47, 0x13, // 212:6585 + 0x1A, 0x00, 0x47, 0x13, // 213:6656 + 0x1A, 0x47, 0x47, 0x13, // 214:6727 + 0x1A, 0x8E, 0x2B, 0x0E, // 215:6798 + 0x1A, 0xB9, 0x47, 0x13, // 216:6841 + 0x1B, 0x00, 0x3B, 0x11, // 217:6912 + 0x1B, 0x3B, 0x3B, 0x11, // 218:6971 + 0x1B, 0x76, 0x3B, 0x11, // 219:7030 + 0x1B, 0xB1, 0x3B, 0x11, // 220:7089 + 0x1B, 0xEC, 0x3D, 0x10, // 221:7148 + 0x1C, 0x29, 0x3A, 0x10, // 222:7209 + 0x1C, 0x63, 0x37, 0x0F, // 223:7267 + 0x1C, 0x9A, 0x2F, 0x0D, // 224:7322 + 0x1C, 0xC9, 0x2F, 0x0D, // 225:7369 + 0x1C, 0xF8, 0x2F, 0x0D, // 226:7416 + 0x1D, 0x27, 0x2F, 0x0D, // 227:7463 + 0x1D, 0x56, 0x2F, 0x0D, // 228:7510 + 0x1D, 0x85, 0x2F, 0x0D, // 229:7557 + 0x1D, 0xB4, 0x53, 0x15, // 230:7604 + 0x1E, 0x07, 0x2B, 0x0C, // 231:7687 + 0x1E, 0x32, 0x2F, 0x0D, // 232:7730 + 0x1E, 0x61, 0x2F, 0x0D, // 233:7777 + 0x1E, 0x90, 0x2F, 0x0D, // 234:7824 + 0x1E, 0xBF, 0x2F, 0x0D, // 235:7871 + 0x1E, 0xEE, 0x11, 0x07, // 236:7918 + 0x1E, 0xFF, 0x11, 0x07, // 237:7935 + 0x1F, 0x10, 0x15, 0x07, // 238:7952 + 0x1F, 0x25, 0x15, 0x07, // 239:7973 + 0x1F, 0x3A, 0x2F, 0x0D, // 240:7994 + 0x1F, 0x69, 0x2F, 0x0D, // 241:8041 + 0x1F, 0x98, 0x2F, 0x0D, // 242:8088 + 0x1F, 0xC7, 0x2F, 0x0D, // 243:8135 + 0x1F, 0xF6, 0x2F, 0x0D, // 244:8182 + 0x20, 0x25, 0x2F, 0x0D, // 245:8229 + 0x20, 0x54, 0x2F, 0x0D, // 246:8276 + 0x20, 0x83, 0x32, 0x0D, // 247:8323 + 0x20, 0xB5, 0x33, 0x0F, // 248:8373 + 0x20, 0xE8, 0x2F, 0x0D, // 249:8424 + 0x21, 0x17, 0x2F, 0x0D, // 250:8471 + 0x21, 0x46, 0x2F, 0x0D, // 251:8518 + 0x21, 0x75, 0x2F, 0x0D, // 252:8565 + 0x21, 0xA4, 0x2A, 0x0C, // 253:8612 + 0x21, 0xCE, 0x2F, 0x0D, // 254:8654 + 0x21, 0xFD, 0x2A, 0x0C, // 255:8701 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 34 + 0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35 + 0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 39 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04, // 40 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01, // 44 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47 + 0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49 + 0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50 + 0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51 + 0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52 + 0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56 + 0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60 + 0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61 + 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20, // 62 + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07, // 63 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73 + 0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F, // 80 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82 + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83 + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85 + 0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86 + 0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87 + 0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89 + 0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90 + 0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 91 + 0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07, // 93 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20, // 94 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 95 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06, // 102 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03, // 106 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07, // 113 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 114 + 0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117 + 0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 118 + 0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E, // 119 + 0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 121 + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122 + 0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 123 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F, // 124 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07, // 161 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162 + 0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163 + 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164 + 0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F, // 166 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 169 + 0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F, // 170 + 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171 + 0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 174 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175 + 0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03, // 176 + 0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177 + 0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21, // 178 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181 + 0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 183 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01, // 184 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F, // 185 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80, // 187 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204 + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205 + 0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207 + 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8, // 222 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229 + 0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237 + 0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238 + 0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246 + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 247 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 253 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255 +}; +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp new file mode 100644 index 00000000..778a2e77 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp @@ -0,0 +1,471 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include "OLEDDisplayUi.h" + +void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); +}; + + +OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { + this->display = display; + + indicatorPosition = BOTTOM; + indicatorDirection = LEFT_RIGHT; + activeSymbol = ANIMATION_activeSymbol; + inactiveSymbol = ANIMATION_inactiveSymbol; + frameAnimationDirection = SLIDE_RIGHT; + lastTransitionDirection = 1; + frameCount = 0; + nextFrameNumber = -1; + overlayCount = 0; + indicatorDrawState = 1; + loadingDrawFunction = LoadingDrawDefault; + updateInterval = 33; + state.lastUpdate = 0; + state.ticksSinceLastStateSwitch = 0; + state.frameState = FIXED; + state.currentFrame = 0; + state.frameTransitionDirection = 1; + state.isIndicatorDrawen = true; + state.manuelControll = false; + state.userData = NULL; + shouldDrawIndicators = true; + autoTransition = true; + setTimePerFrame(5000); + setTimePerTransition(500); +} + +void OLEDDisplayUi::init() { + this->display->init(); +} + +void OLEDDisplayUi::setTargetFPS(uint8_t fps){ + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + + this->ticksPerFrame = timePerFrame / updateInterval; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Automatic controll ------\- + +void OLEDDisplayUi::enableAutoTransition(){ + this->autoTransition = true; +} +void OLEDDisplayUi::disableAutoTransition(){ + this->autoTransition = false; +} +void OLEDDisplayUi::setAutoTransitionForwards(){ + this->state.frameTransitionDirection = 1; + this->lastTransitionDirection = 1; +} +void OLEDDisplayUi::setAutoTransitionBackwards(){ + this->state.frameTransitionDirection = -1; + this->lastTransitionDirection = -1; +} +void OLEDDisplayUi::setTimePerFrame(uint16_t time){ + this->timePerFrame = time; + this->ticksPerFrame = timePerFrame / updateInterval; +} +void OLEDDisplayUi::setTimePerTransition(uint16_t time){ + this->timePerTransition = time; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Customize indicator position and style -------\- +void OLEDDisplayUi::enableIndicator(){ + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::disableIndicator(){ + this->state.isIndicatorDrawen = false; +} + +void OLEDDisplayUi::enableAllIndicators(){ + this->shouldDrawIndicators = true; +} + +void OLEDDisplayUi::disableAllIndicators(){ + this->shouldDrawIndicators = false; +} + +void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; +} +void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { + this->activeSymbol = symbol; +} +void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { + this->inactiveSymbol = symbol; +} + + +// -/----- Frame settings -----\- +void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { + this->frameFunctions = frameFunctions; + this->frameCount = frameCount; + this->resetState(); +} + +// -/----- Overlays ------\- +void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ + this->overlayFunctions = overlayFunctions; + this->overlayCount = overlayCount; +} + +// -/----- Loading Process -----\- + +void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { + this->loadingDrawFunction = loadingDrawFunction; +} + +void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { + uint8_t progress = 0; + uint8_t increment = 100 / stagesCount; + + for (uint8_t i = 0; i < stagesCount; i++) { + display->clear(); + this->loadingDrawFunction(this->display, &stages[i], progress); + display->display(); + + stages[i].callback(); + + progress += increment; + yield(); + } + + display->clear(); + this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); + display->display(); + + delay(150); +} + +// -/----- Manuel control -----\- +void OLEDDisplayUi::nextFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = 1; + } +} +void OLEDDisplayUi::previousFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = -1; + } +} + +void OLEDDisplayUi::switchToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->state.frameState = FIXED; + this->state.currentFrame = frame; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::transitionToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->nextFrameNumber = frame; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; +} + + +// -/----- State information -----\- +OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ + return &this->state; +} + +int16_t OLEDDisplayUi::update(){ +#ifdef ARDUINO + unsigned long frameStart = millis(); +#elif __MBED__ + Timer t; + t.start(); + unsigned long frameStart = t.read_ms(); +#else +#error "Unkown operating system" +#endif + int32_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); + if ( timeBudget <= 0) { + // Implement frame skipping to ensure time budget is keept + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval); + + this->state.lastUpdate = frameStart; + this->tick(); + } +#ifdef ARDUINO + return this->updateInterval - (millis() - frameStart); +#elif __MBED__ + return this->updateInterval - (t.read_ms() - frameStart); +#else +#error "Unkown operating system" +#endif +} + + +void OLEDDisplayUi::tick() { + this->state.ticksSinceLastStateSwitch++; + + switch (this->state.frameState) { + case IN_TRANSITION: + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->state.frameState = FIXED; + this->state.currentFrame = getNextFrameNumber(); + this->state.ticksSinceLastStateSwitch = 0; + this->nextFrameNumber = -1; + } + break; + case FIXED: + // Revert manuelControll + if (this->state.manuelControll) { + this->state.frameTransitionDirection = this->lastTransitionDirection; + this->state.manuelControll = false; + } + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->state.frameState = IN_TRANSITION; + } + this->state.ticksSinceLastStateSwitch = 0; + } + break; + } + + this->display->clear(); + this->drawFrame(); + if (shouldDrawIndicators) { + this->drawIndicator(); + } + this->drawOverlays(); + this->display->display(); +} + +void OLEDDisplayUi::resetState() { + this->state.lastUpdate = 0; + this->state.ticksSinceLastStateSwitch = 0; + this->state.frameState = FIXED; + this->state.currentFrame = 0; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::drawFrame(){ + switch (this->state.frameState){ + case IN_TRANSITION: { + float progress = 0.f; + if (this->ticksPerTransition > 0u) { + progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + } + int16_t x = 0, y = 0, x1 = 0, y1 = 0; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -this->display->width() * progress; + y = 0; + x1 = x + this->display->width(); + y1 = 0; + break; + case SLIDE_RIGHT: + x = this->display->width() * progress; + y = 0; + x1 = x - this->display->width(); + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -this->display->height() * progress; + x1 = 0; + y1 = y + this->display->height(); + break; + case SLIDE_DOWN: + default: + x = 0; + y = this->display->height() * progress; + x1 = 0; + y1 = y - this->display->height(); + break; + } + + // Invert animation if direction is reversed. + int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + bool drawenCurrentFrame; + + + // Prope each frameFunction for the indicator Drawen state + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); + drawenCurrentFrame = this->state.isIndicatorDrawen; + + this->enableIndicator(); + (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); + + // Build up the indicatorDrawState + if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Drawen now but not next + this->indicatorDrawState = 2; + } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { + // Not drawen now but next + this->indicatorDrawState = 1; + } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Not drawen in both frames + this->indicatorDrawState = 3; + } + + // If the indicator isn't draw in the current frame + // reflect it in state.isIndicatorDrawen + if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; + + break; + } + case FIXED: + // Always assume that the indicator is drawn! + // And set indicatorDrawState to "not known yet" + this->indicatorDrawState = 0; + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); + break; + } +} + +void OLEDDisplayUi::drawIndicator() { + + // Only draw if the indicator is invisible + // for both frames or + // the indiactor is shown and we are IN_TRANSITION + if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { + return; + } + + uint8_t posOfHighlightFrame = 0; + float indicatorFadeProgress = 0; + + // if the indicator needs to be slided in we want to + // highlight the next frame in the transition + uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; + + // Calculate the frame that needs to be highlighted + // based on the Direction the indiactor is drawn + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfHighlightFrame = frameToHighlight; + break; + case RIGHT_LEFT: + default: + posOfHighlightFrame = this->frameCount - frameToHighlight; + break; + } + + switch (this->indicatorDrawState) { + case 1: // Indicator was not drawn in this frame but will be in next + // Slide IN + indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + case 2: // Indicator was drawn in this frame but not in next + // Slide OUT + indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + } + + //Space between indicators - reduce for small screen sizes + uint16_t indicatorSpacing = 12; + if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { + indicatorSpacing = 6; + } + + uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); + const uint8_t *image; + + uint16_t x = 0,y = 0; + + + for (uint8_t i = 0; i < this->frameCount; i++) { + + switch (this->indicatorPosition){ + case TOP: + y = 0 - (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case BOTTOM: + y = (this->display->height() - 8) + (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case RIGHT: + x = (this->display->width() - 8) + (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; + break; + case LEFT: + default: + x = 0 - (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; + break; + } + + if (posOfHighlightFrame == i) { + image = this->activeSymbol; + } else { + image = this->inactiveSymbol; + } + + this->display->drawFastImage(x, y, 8, 8, image); + } +} + +void OLEDDisplayUi::drawOverlays() { + for (uint8_t i=0;ioverlayCount;i++){ + (this->overlayFunctions[i])(this->display, &this->state); + } +} + +uint8_t OLEDDisplayUi::getNextFrameNumber(){ + if (this->nextFrameNumber != -1) return this->nextFrameNumber; + return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; +} diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h new file mode 100644 index 00000000..9aa3f320 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h @@ -0,0 +1,315 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAYUI_h +#define OLEDDISPLAYUI_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#include +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplay.h" + +//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAYUI +#define DEBUG_OLEDDISPLAYUI(...) +#endif + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + + +const uint8_t ANIMATION_activeSymbol[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + + +// Structure of the UiState +struct OLEDDisplayUiState { + uint64_t lastUpdate; + uint16_t ticksSinceLastStateSwitch; + + FrameState frameState; + uint8_t currentFrame; + + bool isIndicatorDrawen; + + // Normal = 1, Inverse = -1; + int8_t frameTransitionDirection; + + bool manuelControll; + + // Custom data that can be used by the user + void* userData; +}; + +struct LoadingStage { + const char* process; + void (*callback)(); +}; + +typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); +typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); + +class OLEDDisplayUi { + private: + OLEDDisplay *display; + + // Symbols for the Indicator + IndicatorPosition indicatorPosition; + IndicatorDirection indicatorDirection; + + const uint8_t* activeSymbol; + const uint8_t* inactiveSymbol; + + bool shouldDrawIndicators; + + // Values for the Frames + AnimationDirection frameAnimationDirection; + + int8_t lastTransitionDirection; + + uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition; // ~ 500ms at 30 FPS + + bool autoTransition; + + FrameCallback* frameFunctions; + uint8_t frameCount; + + // Internally used to transition to a specific frame + int8_t nextFrameNumber; + + // Values for Overlays + OverlayCallback* overlayFunctions; + uint8_t overlayCount; + + // Will the Indicator be drawen + // 3 Not drawn in both frames + // 2 Drawn this frame but not next + // 1 Not drown this frame but next + // 0 Not known yet + uint8_t indicatorDrawState; + + // Loading screen + LoadingDrawFunction loadingDrawFunction; + + // UI State + OLEDDisplayUiState state; + + // Bookeeping for update + uint16_t updateInterval = 33; + + uint16_t timePerFrame; + uint16_t timePerTransition; + + uint8_t getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + void resetState(); + + public: + + OLEDDisplayUi(OLEDDisplay *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(uint8_t fps); + + // Automatic Controll + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approx. time a frame is displayed + */ + void setTimePerFrame(uint16_t time); + + /** + * Set the approx. time a transition will take + */ + void setTimePerTransition(uint16_t time); + + // Customize indicator position and style + + /** + * Draw the indicator. + * This is the defaut state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ + void enableIndicator(); + + /** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ + void disableIndicator(); + + /** + * Enable drawing of indicators + */ + void enableAllIndicators(); + + /** + * Disable draw of indicators. + */ + void disableAllIndicators(); + + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbol to indicate an active frame in the indicator bar. + */ + void setActiveSymbol(const uint8_t* symbol); + + /** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbol(const uint8_t* symbol); + + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + + + // Loading animation + /** + * Set the function that will draw each step + * in the loading animation + */ + void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); + + + /** + * Run the loading process + */ + void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + + + // Manual Control + void nextFrame(); + void previousFrame(); + + /** + * Switch without transition to frame `frame`. + */ + void switchToFrame(uint8_t frame); + + /** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ + void transitionToFrame(uint8_t frame); + + // State Info + OLEDDisplayUiState* getUiState(); + + int16_t update(); +}; +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106.h b/lib/esp8266-oled-ssd1306-master/src/SH1106.h new file mode 100644 index 00000000..47188d1f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106_h +#define SH1106_h +#include "SH1106Wire.h" + +// For make SH1106 an alias for SH1106Wire +typedef SH1106Wire SH1106; + + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h b/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h new file mode 100644 index 00000000..be9c0c74 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h @@ -0,0 +1,141 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Brzo_h +#define SH1106Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SH1106Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + byte k = 0; + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * displayWidth]; + if (k == 16) { + brzo_i2c_write(sendBuffer, 17, true); + k = 0; + } + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + k = 0; + } + yield(); + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + } + brzo_i2c_end_transaction(); + #else + #endif + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t com) __attribute__((always_inline)){ + uint8_t command[2] = {0x80 /* command mode */, com}; + brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); + brzo_i2c_write(command, 2, true); + brzo_i2c_end_transaction(); + } +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h b/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h new file mode 100644 index 00000000..23693bc4 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h @@ -0,0 +1,135 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Spi_h +#define SH1106Spi_h + +#include "OLEDDisplay.h" +#include + +class SH1106Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + + public: + SH1106Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + digitalWrite(_dc, HIGH); // data mode + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + yield(); + } + #else + for (uint8_t y=0; y + +#define SH1106_SET_PUMP_VOLTAGE 0X30 +#define SH1106_SET_PUMP_MODE 0XAD +#define SH1106_PUMP_ON 0X8B +#define SH1106_PUMP_OFF 0X8A +//-------------------------------------- + +class SH1106Wire : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + _wire->write(buffer[x + y * displayWidth]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + if (k != 0) { + _wire->endTransmission(); + k = 0; + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + uint8_t * p = &buffer[0]; + for (uint8_t y=0; y<8; y++) { + sendCommand(0xB0+y); + sendCommand(0x02); + sendCommand(0x10); + for( uint8_t x=0; x<8; x++) { + _wire->beginTransmission(_address); + _wire->write(0x40); + for (uint8_t k = 0; k < 16; k++) { + _wire->write(*p++); + } + _wire->endTransmission(); + } + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#ifdef ARDUINO_ARCH_AVR + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306.h new file mode 100644 index 00000000..f6bd554c --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306_h +#define SSD1306_h +#include "SSD1306Wire.h" + +// For legacy support make SSD1306 an alias for SSD1306 +typedef SSD1306Wire SSD1306; + + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h new file mode 100644 index 00000000..fbcffcda --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h @@ -0,0 +1,167 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Brzo_h +#define SSD1306Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * this->width()]; + if (k == (buflen-1)) { + brzo_i2c_write(sendBuffer, buflen, true); + k = 0; + } + } + yield(); + } + brzo_i2c_write(sendBuffer, k + 1, true); + brzo_i2c_end_transaction(); + #else + // No double buffering + sendCommand(COLUMNADDR); + + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand((this->height() / 8) - 1); + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (uint16_t i=0; i + +#ifndef UINT8_MAX + #define UINT8_MAX 0xff +#endif + +class SSD1306I2C : public OLEDDisplay { +public: + SSD1306I2C(uint8_t _address, PinName _sda, PinName _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address << 1; // convert from 7 to 8 bit for mbed. + this->_sda = _sda; + this->_scl = _scl; + _i2c = new I2C(_sda, _scl); + } + + bool connect() { + // mbed supports 100k and 400k some device maybe 1000k +#ifdef TARGET_STM32L4 + _i2c->frequency(1000000); +#else + _i2c->frequency(400000); +#endif + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); // column start address (0 = reset) + sendCommand(x_offset + maxBoundX); // column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(minBoundY); // page start address + sendCommand(maxBoundY); // page end address + + for (y = minBoundY; y <= maxBoundY; y++) { + uint8_t *start = &buffer[(minBoundX + y * this->width())-1]; + uint8_t save = *start; + + *start = 0x40; // control + _i2c->write(_address, (char *)start, (maxBoundX-minBoundX) + 1 + 1); + *start = save; + } +#else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); // column start address (0 = reset) + sendCommand(x_offset + (this->width() - 1));// column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(0x0); // page start address (0 = reset) + + if (geometry == GEOMETRY_128_64) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + buffer[-1] = 0x40; // control + _i2c->write(_address, (char *)&buffer[-1], displayBufferSize + 1); +#endif + } + +private: + int getBufferOffset(void) { + return 0; + } + + inline void sendCommand(uint8_t command) __attribute__((always_inline)) { + char _data[2]; + _data[0] = 0x80; // control + _data[1] = command; + _i2c->write(_address, _data, sizeof(_data)); + } + + uint8_t _address; + PinName _sda; + PinName _scl; + I2C *_i2c; +}; + +#endif + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h new file mode 100644 index 00000000..08cb4a80 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h @@ -0,0 +1,163 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Spi_h +#define SSD1306Spi_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + + public: + SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + yield(); + } + digitalWrite(_cs, HIGH); + #else + // No double buffering + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32 ) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (uint16_t i=0; i +#include + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_STM32) +#define _min min +#define _max max +#endif +//-------------------------------------- + +class SSD1306Wire : public OLEDDisplay { + private: + uint8_t _address; + int _sda; + int _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SSD1306Wire(uint8_t _address, int _sda = -1, int _scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + const int x_offset = (128 - this->width()) / 2; + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + + _wire->write(buffer[x + y * this->width()]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + for (uint16_t i=0; i < displayBufferSize; i++) { + _wire->beginTransmission(this->_address); + _wire->write(0x40); + for (uint8_t x = 0; x < 16; x++) { + _wire->write(buffer[i]); + i++; + } + i--; + _wire->endTransmission(); + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + initI2cIfNeccesary(); + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 00000000..3a0ff0bd --- /dev/null +++ b/platformio.ini @@ -0,0 +1,154 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = + GUI_Generic_1M +; GUI_Generic_1M-en +; GUI_Generic_4M +; GUI_Generic_minimal +; GUI_Generic_lite +; GUI_Generic_sensors +; GUI_Generic_DEBUG +; GUI_Generic_blank +; lib_extra_dirs = ~/Documents/Arduino/libraries + + +[common] +build_flags = -D BUILD_VERSION='"GUI 1.1.16"' + -w + -DATOMIC_FS_UPDATE + -DBEARSSL_SSL_BASIC + -D SUPLA_OTA + -D SUPLA_RELAY + -D SUPLA_BUTTON + -D SUPLA_LIMIT_SWITCH + -D SUPLA_ROLLERSHUTTER + -D SUPLA_CONFIG + -D SUPLA_DS18B20 + -D SUPLA_DHT11 + -D SUPLA_DHT22 + -D SUPLA_SI7021_SONOFF + -D SUPLA_BME280 + -D SUPLA_SHT3x + -D SUPLA_SI7021 + -D SUPLA_MAX6675 + -D SUPLA_HC_SR04 + -D SUPLA_IMPULSE_COUNTER + -D SUPLA_OLED + -D SUPLA_HLW8012 + -D SUPLA_MCP23017 + +[env] +framework = arduino +platform = espressif8266@2.6.0 +upload_speed = 256000 +monitor_speed = 74880 +upload_resetmethod = nodemcu +board_build.flash_mode = dout +; set frequency to 160MHz +board_build.f_cpu = 160000000L +lib_deps = + milesburton/DallasTemperature@^3.9.1 + adafruit/DHT sensor library@^1.4.0 + paulstoffregen/OneWire@^2.3.5 + adafruit/Adafruit BME280 Library@^2.1.1 + datacute/DoubleResetDetector@^1.0.3 + closedcube/ClosedCube SHT31D@^1.5.1 + adafruit/Adafruit Si7021 Library@^1.3.0 + xoseperez/HLW8012 @ ^1.1.1 +extra_scripts = tools/copy_files.py + +[env:GUI_Generic_1M] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} + +[env:GUI_Generic_1M-en] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} + -D UI_LANGUAGE=en + +[env:GUI_Generic_4M] +board = esp12e +board_build.ldscript = eagle.flash.4m1m.ld +build_flags = ${common.build_flags} + +[env:GUI_Generic_minimal] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} +build_unflags = -D SUPLA_DS18B20 + -D SUPLA_DHT11 + -D SUPLA_DHT22 + -D SUPLA_SI7021_SONOFF + -D SUPLA_BME280 + -D SUPLA_SHT3x + -D SUPLA_SI7021 + -D SUPLA_MAX6675 + -D SUPLA_HC_SR04 + -D SUPLA_IMPULSE_COUNTER + +[env:GUI_Generic_lite] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} +build_unflags = + -D SUPLA_DHT11 + -D SUPLA_BME280 + -D SUPLA_SHT3x + -D SUPLA_SI7021 + -D SUPLA_MAX6675 + -D SUPLA_HC_SR04 + -D SUPLA_IMPULSE_COUNTER + +[env:GUI_Generic_sensors] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} +build_unflags = -D SUPLA_RELAY + -D SUPLA_BUTTON + -D SUPLA_LIMIT_SWITCH + -D SUPLA_ROLLERSHUTTER + -D SUPLA_CONFIG + +[env:GUI_Generic_DEBUG] +board = nodemcuv2 +board_build.ldscript = eagle.flash.4m1m.ld +build_flags = ${common.build_flags} + -D DEBUG_MODE + +[env:GUI_Generic_blank] +board = nodemcuv2 +board_build.ldscript = eagle.flash.4m1m.ld +build_flags = ${common.build_flags} +build_unflags = -D SUPLA_OTA + -D SUPLA_RELAY + -D SUPLA_BUTTON + -D SUPLA_LIMIT_SWITCH + -D SUPLA_ROLLERSHUTTER + -D SUPLA_CONFIG + -D SUPLA_DS18B20 + -D SUPLA_DHT11 + -D SUPLA_DHT22 + -D SUPLA_SI7021_SONOFF + -D SUPLA_BME280 + -D SUPLA_SHT3x + -D SUPLA_SI7021 + -D SUPLA_MAX6675 + -D SUPLA_HC_SR04 + -D SUPLA_IMPULSE_COUNTER + +[env:GUI_Generic_MCP23017] +board = esp8285 +board_build.ldscript = eagle.flash.1m64.ld +build_flags = ${common.build_flags} +build_unflags = -D SUPLA_IMPULSE_COUNTER \ No newline at end of file diff --git a/GUI-Generic.ino b/src/GUI-Generic.ino similarity index 59% rename from GUI-Generic.ino rename to src/GUI-Generic.ino index eeed7c91..d43920d0 100644 --- a/GUI-Generic.ino +++ b/src/GUI-Generic.ino @@ -1,204 +1,205 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#include "GUI-Generic_Config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef SUPLA_BME280 -#include -#include "SuplaWebPageSensor.h" -#endif -#ifdef SUPLA_SHT30 -#include -#endif -#ifdef SUPLA_SI7021 -#include -#endif -#ifdef SUPLA_SI7021_SONOFF -#include -#endif -#ifdef SUPLA_MAX6675 -#include -#endif - -#include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" - -#define DRD_TIMEOUT 5 // Number of seconds after reset during which a subseqent reset will be considered a double reset. -#define DRD_ADDRESS 0 // RTC Memory Address for the DoubleResetDetector to use -DoubleResetDetector drd(DRD_TIMEOUT, DRD_ADDRESS); - -void setup() { - Serial.begin(74880); - - if (drd.detectDoubleReset()) { - drd.stop(); - ConfigESP->factoryReset(); - } - - uint8_t nr, gpio; - String key; - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) - uint8_t rollershutters = ConfigManager->get(KEY_MAX_ROLLERSHUTTER)->getValueInt(); - - if (ConfigESP->getGpio(FUNCTION_RELAY) != OFF_GPIO && ConfigManager->get(KEY_MAX_RELAY)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { -#ifdef SUPLA_ROLLERSHUTTER - if (rollershutters > 0) { -#ifdef SUPLA_BUTTON - if (ConfigESP->getLevel(nr, FUNCTION_BUTTON) == Supla::ON_CHANGE && ConfigESP->getLevel(nr + 1, FUNCTION_BUTTON) == Supla::ON_CHANGE) { - Supla::GUI::addRolleShutterMomentary(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr + 1, FUNCTION_RELAY), - ConfigESP->getGpio(nr, FUNCTION_BUTTON), ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); - } - else { -#endif - Supla::GUI::addRolleShutter(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr + 1, FUNCTION_RELAY), - ConfigESP->getGpio(nr, FUNCTION_BUTTON), ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); -#ifdef SUPLA_BUTTON - } -#endif - rollershutters--; - nr++; - } - else { -#endif - Supla::GUI::addRelayButton(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); -#ifdef SUPLA_ROLLERSHUTTER - } -#endif - } - } -#endif - -#ifdef SUPLA_LIMIT_SWITCH - if (ConfigESP->getGpio(FUNCTION_LIMIT_SWITCH) != OFF_GPIO && ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { - new Supla::Sensor::Binary(ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH), true); - } - } -#endif - -#ifdef SUPLA_CONFIG - Supla::GUI::addConfigESP(ConfigESP->getGpio(FUNCTION_CFG_BUTTON), ConfigESP->getGpio(FUNCTION_CFG_LED), CONFIG_MODE_10_ON_PRESSES, - ConfigESP->getLevel(FUNCTION_CFG_LED)); -#endif - -#ifdef SUPLA_DHT11 - if (ConfigESP->getGpio(FUNCTION_DHT11) != OFF_GPIO && ConfigManager->get(KEY_MAX_DHT11)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { - new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT11), DHT11); - } - } -#endif - -#ifdef SUPLA_DHT22 - if (ConfigESP->getGpio(FUNCTION_DHT22) != OFF_GPIO && ConfigManager->get(KEY_MAX_DHT22)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { - new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT22), DHT22); - } - } -#endif - -#ifdef SUPLA_DS18B20 - if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { - Supla::GUI::addDS18B20MultiThermometer(ConfigESP->getGpio(FUNCTION_DS18B20)); - } -#endif - -#ifdef SUPLA_SI7021_SONOFF - if (ConfigESP->getGpio(FUNCTION_SI7021_SONOFF) != OFF_GPIO) { - new Supla::Sensor::Si7021Sonoff(ConfigESP->getGpio(FUNCTION_SI7021_SONOFF)); - } -#endif - -#ifdef SUPLA_HC_SR04 - if (ConfigESP->getGpio(FUNCTION_TRIG) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_ECHO) != OFF_GPIO) { - new Supla::Sensor::HC_SR04(ConfigESP->getGpio(FUNCTION_TRIG), ConfigESP->getGpio(FUNCTION_ECHO)); - } -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT30) || defined(SUPLA_HTU21D) || defined(SUPLA_SHT71) || \ - defined(SUPLA_BH1750) || defined(SUPLA_MAX44009) - if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { - Wire.begin(ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL)); - -#ifdef SUPLA_BME280 - switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_BME280).toInt()) { - case BME280_ADDRESS_0X76: - new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; - case BME280_ADDRESS_0X77: - new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; - case BME280_ADDRESS_0X76_AND_0X77: - new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; - } -#endif - -#ifdef SUPLA_SHT30 - switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SHT30).toInt()) { - case SHT30_ADDRESS_0X44: - new Supla::Sensor::SHT3x(0x44); - break; - case SHT30_ADDRESS_0X45: - new Supla::Sensor::SHT3x(0x45); - break; - case SHT30_ADDRESS_0X44_AND_0X45: - new Supla::Sensor::SHT3x(0x44); - new Supla::Sensor::SHT3x(0x45); - break; - } -#endif - -#ifdef SUPLA_SI7021 - if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SI7021).toInt()) { - new Supla::Sensor::Si7021(); - } -#endif - } -#endif - -#ifdef SUPLA_MAX6675 - if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { - new Supla::Sensor::MAX6675_K(ConfigESP->getGpio(FUNCTION_CLK), ConfigESP->getGpio(FUNCTION_CS), - ConfigESP->getGpio(FUNCTION_D0)); - } - -#endif - - Supla::GUI::begin(); -} - -void loop() { - SuplaDevice.iterate(); - delay(25); - drd.loop(); -} +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SuplaDeviceGUI.h" + +#include + +#define DRD_TIMEOUT 5 // Number of seconds after reset during which a subseqent reset will be considered a double reset. +#define DRD_ADDRESS 0 // RTC Memory Address for the DoubleResetDetector to use +DoubleResetDetector drd(DRD_TIMEOUT, DRD_ADDRESS); + +void setup() { + Serial.begin(74880); + + if (drd.detectDoubleReset()) { + drd.stop(); + ConfigESP->factoryReset(); + } + + uint8_t nr, gpio; + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + uint8_t rollershutters = ConfigManager->get(KEY_MAX_ROLLERSHUTTER)->getValueInt(); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_RELAY) != OFF_GPIO) { +#ifdef SUPLA_ROLLERSHUTTER + if (rollershutters > 0) { +#ifdef SUPLA_BUTTON + if (ConfigESP->getLevel(nr, FUNCTION_BUTTON) == Supla::ON_CHANGE && ConfigESP->getLevel(nr + 1, FUNCTION_BUTTON) == Supla::ON_CHANGE) { + Supla::GUI::addRolleShutterMomentary(nr); + } + else { +#endif + Supla::GUI::addRolleShutter(nr); +#ifdef SUPLA_BUTTON + } +#endif + rollershutters--; + nr++; + } + else { +#endif + Supla::GUI::addRelayButton(nr); +#ifdef SUPLA_ROLLERSHUTTER + } +#endif + } + } +#endif + +#ifdef SUPLA_LIMIT_SWITCH + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH) != OFF_GPIO) { + new Supla::Sensor::Binary(ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH), true); + } + } +#endif + +#ifdef SUPLA_CONFIG + Supla::GUI::addConfigESP(ConfigESP->getGpio(FUNCTION_CFG_BUTTON), ConfigESP->getGpio(FUNCTION_CFG_LED), + ConfigManager->get(KEY_CFG_MODE)->getValueInt(), ConfigESP->getLevel(FUNCTION_CFG_LED)); +#endif + +#ifdef SUPLA_DS18B20 + if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { + Supla::GUI::addDS18B20MultiThermometer(ConfigESP->getGpio(FUNCTION_DS18B20)); + } +#endif + +#ifdef SUPLA_DHT11 + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_DHT11) != OFF_GPIO) { + new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT11), DHT11); + } + } +#endif + +#ifdef SUPLA_DHT22 + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_DHT22) != OFF_GPIO) { + new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT22), DHT22); + } + } +#endif + +#ifdef SUPLA_SI7021_SONOFF + if (ConfigESP->getGpio(FUNCTION_SI7021_SONOFF) != OFF_GPIO) { + new Supla::Sensor::Si7021Sonoff(ConfigESP->getGpio(FUNCTION_SI7021_SONOFF)); + } +#endif + +#ifdef SUPLA_HC_SR04 + if (ConfigESP->getGpio(FUNCTION_TRIG) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_ECHO) != OFF_GPIO) { + new Supla::Sensor::HC_SR04(ConfigESP->getGpio(FUNCTION_TRIG), ConfigESP->getGpio(FUNCTION_ECHO)); + } +#endif + +#ifdef SUPLA_MAX6675 + if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { + new Supla::Sensor::MAX6675_K(ConfigESP->getGpio(FUNCTION_CLK), ConfigESP->getGpio(FUNCTION_CS), ConfigESP->getGpio(FUNCTION_D0)); + } +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + if (ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt() > 0) { + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { + Supla::GUI::addImpulseCounter(ConfigESP->getGpio(nr, FUNCTION_IMPULSE_COUNTER), ConfigESP->getLevel(nr, FUNCTION_IMPULSE_COUNTER), + ConfigESP->getMemory(nr, FUNCTION_IMPULSE_COUNTER), + ConfigManager->get(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT)->getValueInt()); + } + } + } + +#endif + +#ifdef SUPLA_HLW8012 + if (ConfigESP->getGpio(FUNCTION_CF) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CF1) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SEL) != OFF_GPIO) { + Supla::GUI::addHLW8012(ConfigESP->getGpio(FUNCTION_CF), ConfigESP->getGpio(FUNCTION_CF1), ConfigESP->getGpio(FUNCTION_SEL)); + } +#endif + +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_HTU21D) || defined(SUPLA_SHT71) || \ + defined(SUPLA_BH1750) || defined(SUPLA_MAX44009) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) + if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { + Wire.begin(ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL)); + +#ifdef SUPLA_BME280 + switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_BME280).toInt()) { + case BME280_ADDRESS_0X76: + new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + case BME280_ADDRESS_0X77: + new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + case BME280_ADDRESS_0X76_AND_0X77: + new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + } +#endif + +#ifdef SUPLA_SHT3x + switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SHT3x).toInt()) { + case SHT3x_ADDRESS_0X44: + new Supla::Sensor::SHT3x(0x44); + break; + case SHT3x_ADDRESS_0X45: + new Supla::Sensor::SHT3x(0x45); + break; + case SHT3x_ADDRESS_0X44_AND_0X45: + new Supla::Sensor::SHT3x(0x44); + new Supla::Sensor::SHT3x(0x45); + break; + } +#endif + +#ifdef SUPLA_SI7021 + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SI7021).toInt()) { + new Supla::Sensor::Si7021(); + } +#endif + +#ifdef SUPLA_MCP23017 + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + if (!mcp1.begin(0)) + Serial.println(F("MCP23017 1 not found!")); // begin(uint8_t address) "Pin 100 - 115" + if (!mcp2.begin(1)) + Serial.println(F("MCP23017 2 not found!")); // begin(uint8_t address) "Pin 116 - 131" + if (!mcp3.begin(2)) + Serial.println(F("MCP23017 3 not found!")); // begin(uint8_t address) "Pin 132 - 147" + if (!mcp4.begin(3)) + Serial.println(F("MCP23017 4 not found!")); // begin(uint8_t address) "Pin 148 - 163" + } +#endif + +#ifdef SUPLA_OLED + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_OLED).toInt()) { + SuplaOled *oled = new SuplaOled(); + oled->addButtonOled(ConfigESP->getGpio(FUNCTION_CFG_BUTTON)); + } +#endif + } +#endif + Supla::GUI::begin(); +} + +void loop() { + SuplaDevice.iterate(); + delay(25); + drd.loop(); +} diff --git a/GUI-Generic_Config.h b/src/GUI-Generic_Config.h similarity index 52% rename from GUI-Generic_Config.h rename to src/GUI-Generic_Config.h index c7c456f4..c3d7cb17 100644 --- a/GUI-Generic_Config.h +++ b/src/GUI-Generic_Config.h @@ -1,37 +1,54 @@ -#ifndef GUI_Generic_Config_h -#define GUI_Generic_Config_h - -// #define DEBUG_MODE - -// Language en - english, pl - polish (default if not defined UI_LANGUAGE) -// #define UI_LANGUAGE en - -#define SUPLA_RELAY -#define SUPLA_BUTTON -#define SUPLA_LIMIT_SWITCH -#define SUPLA_ROLLERSHUTTER -#define SUPLA_CONFIG - -// 1Wire -#define SUPLA_DS18B20 -#define SUPLA_DHT11 -#define SUPLA_DHT22 -#define SUPLA_SI7021_SONOFF - -// i2c -#define SUPLA_BME280 -#define SUPLA_SHT30 -#define SUPLA_SI7021 -// #define SUPLA_HTU21D // 0x40 NOT SUPPORTED -// #define SUPLA_SHT71 // 0x44 AND 0x45 NOT SUPPORTED -// #define SUPLA_BH1750 // 0x23 AND 0x5C NOT SUPPORTED -// #define SUPLA_MAX44009 // A0 NOT SUPPORTED - -// SPI -#define SUPLA_MAX6675 - -// Other -#define SUPLA_HC_SR04 -#define SUPLA_IMPULSE_COUNTER // NOT SUPPORTED - -#endif // GUI-Generic_Config_h +#ifndef GUI_Generic_Config_h +#define GUI_Generic_Config_h + +#define supla_lib_config_h_ // silences unnecessary debug messages "should be disabled by default" + +//#define USE_CUSTOM + +// User configuration +#ifdef USE_CUSTOM +#ifdef BUILD_VERSION +#undef BUILD_VERSION +#endif // ifdef BUILD_VERSION + +#define BUILD_VERSION "User GUI 1.0.1" + +//#define DEBUG_MODE +#define SUPLA_OTA + +// Language en - english, pl - polish (default if not defined UI_LANGUAGE), es- spanish, fr - french, de - german, +//#define UI_LANGUAGE de + +#define SUPLA_RELAY +#define SUPLA_BUTTON +#define SUPLA_LIMIT_SWITCH +#define SUPLA_ROLLERSHUTTER +#define SUPLA_CONFIG + +// ##### 1Wire ##### +#define SUPLA_DS18B20 +#define SUPLA_DHT11 +#define SUPLA_DHT22 +#define SUPLA_SI7021_SONOFF + +// ##### i2c ##### +#define SUPLA_BME280 +#define SUPLA_SHT3x +#define SUPLA_SI7021 +#define SUPLA_OLED +//#define SUPLA_MCP23017 +// #define SUPLA_HTU21D // 0x40 NOT SUPPORTED +// #define SUPLA_SHT71 // 0x44 AND 0x45 NOT SUPPORTED +// #define SUPLA_BH1750 // 0x23 AND 0x5C NOT SUPPORTED +// #define SUPLA_MAX44009 // A0 NOT SUPPORTED + +// ##### SPI ##### +#define SUPLA_MAX6675 + +// ##### Other ##### +#define SUPLA_HC_SR04 +#define SUPLA_IMPULSE_COUNTER +#define SUPLA_HLW8012 + +#endif +#endif // GUI-Generic_Config_h diff --git a/src/GUIGenericCommon.cpp b/src/GUIGenericCommon.cpp new file mode 100644 index 00000000..30113cdf --- /dev/null +++ b/src/GUIGenericCommon.cpp @@ -0,0 +1,56 @@ +#include "GUIGenericCommon.h" +#include "SuplaDeviceGUI.h" + +uint8_t *HexToBytes(String _value) { + int size = 16; + uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t) * (size)); + + for (int i = 0; i < size; i++) { + sscanf(&_value[i * 2], "%2hhx", &buffer[i]); + } + return buffer; +} + +int getCountSensorChannels() { + int maxFrame = 0; + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_THERMOMETER) { + maxFrame += 1; + } + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR) { + maxFrame += 2; + } + } + + if (element->getSecondaryChannel()) { + auto channel = element->getSecondaryChannel(); + if (channel->getChannelType() == SUPLA_CHANNELTYPE_PRESSURESENSOR) { + maxFrame += 1; + } + } + } + + return maxFrame; +} + +uint32_t lowestRAM = 0; +uint32_t lowestFreeStack = 0; + +void checkRAM() { + uint32_t freeRAM = ESP.getFreeHeap(); + Serial.print(F("freeRAM: ")); + Serial.println(freeRAM); + if (freeRAM <= lowestRAM) { + lowestRAM = freeRAM; + } + uint32_t freeStack = ESP.getFreeContStack(); + Serial.print(F("freeStack: ")); + Serial.println(freeStack); + if (freeStack <= lowestFreeStack) { + lowestFreeStack = freeStack; + } +} \ No newline at end of file diff --git a/GUIGenericCommon.h b/src/GUIGenericCommon.h similarity index 63% rename from GUIGenericCommon.h rename to src/GUIGenericCommon.h index 62672230..09dd8308 100644 --- a/GUIGenericCommon.h +++ b/src/GUIGenericCommon.h @@ -6,8 +6,13 @@ #else #define QUOTE(x) QUOTE_1(x) #define QUOTE_1(x) #x -#define INCLUDE_FILE(x) QUOTE(language/x.h) +#define INCLUDE_FILE(x) QUOTE(language / x.h) #include INCLUDE_FILE(UI_LANGUAGE) #endif +#include "SuplaDeviceGUI.h" + +uint8_t *HexToBytes(String _value); +int getCountSensorChannels(); + #endif // GUI_GENERIC_COMMON_H diff --git a/src/Markup.cpp b/src/Markup.cpp new file mode 100644 index 00000000..c00b434c --- /dev/null +++ b/src/Markup.cpp @@ -0,0 +1,387 @@ +#include "Markup.h" +#include "SuplaCommonPROGMEM.h" + +void addForm(String& html, const String& method, const String& action) { + html += "
"; +} + +void addFormEnd(String& html) { + html += "
"; +} + +void addFormHeader(String& html, const String& name) { + html += F("
"); + if (name != "\n") { + html += F("

"); + html += name; + html += F("

"); + } +} + +void addFormHeaderEnd(String& html) { + html += F("
"); +} + +void addTextBox(String& html, + const String& input_id, + const String& name, + const String& value, + const String& placeholder, + int minlength, + int maxlength, + bool required, + bool readonly, + bool password) { + html += F(" 0) { + html += F("' length='"); + html += maxlength; + html += F("'"); + } + if (readonly) { + html += F(" readonly"); + } + + if (required) { + html += F(" required"); + } + + html += F(" > "); +} + +void addTextBox(String& html, + const String& input_id, + const String& name, + uint8_t value_key, + const String& placeholder, + int minlength, + int maxlength, + bool required, + bool readonly, + bool password) { + String value = String(ConfigManager->get(value_key)->getValue()); + return addTextBox(html, input_id, name, value, placeholder, minlength, maxlength, required, readonly, password); +} + +void addTextBox( + String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required, bool readonly) { + return addTextBox(html, input_id, name, value_key, "", minlength, maxlength, required, readonly, false); +} + +void addTextBox( + String& html, const String& input_id, const String& name, const String& value, int minlength, int maxlength, bool required, bool readonly) { + return addTextBox(html, input_id, name, value, "", minlength, maxlength, required, readonly, false); +} + +void addTextBoxPassword(String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required) { + return addTextBox(html, input_id, name, value_key, "", minlength, maxlength, required, false, true); +} + +void addNumberBox(String& html, const String& input_id, const String& name, uint8_t value_key, uint16_t max) { + html += F(""); +} + +void addNumberBox(String& html, const String& input_id, const String& name, const String& placeholder, bool required, const String& value) { + html += F(""); +} + +void addLinkBox(String& html, const String& name, const String& url) { + html += F(""); + html += F(""); + html += F(""); +} + +void addListGPIOBox(String& html, const String& input_id, const String& name, uint8_t function, uint8_t nr) { + html += F(""); + addListGPIOSelect(html, input_id, function, nr); + html += F(""); +} + +void addListMCP23017GPIOBox(String& html, const String& input_id, const String& name, uint8_t function, uint8_t nr) { + if (nr == 1) { + uint8_t address = ConfigESP->getAdressMCP23017(nr, function); + addListBox(html, INPUT_ADRESS_MCP23017, F("MCP23017 Adres"), MCP23017_P, 3, address); + } + + html += F(""); + addListMCP23017GPIO(html, input_id, function, nr); + html += F(""); +} + +void addListMCP23017GPIOLinkBox(String& html, const String& input_id, const String& name, uint8_t function, const String& url, uint8_t nr) { + if (nr == 1) { + uint8_t address = ConfigESP->getAdressMCP23017(nr, function); + addListBox(html, INPUT_ADRESS_MCP23017, F("MCP23017 Adres"), MCP23017_P, 3, address); + } + + html += F(""); + html += F(""); + addListMCP23017GPIO(html, input_id, function, nr); + html += F(""); +} + +void addListGPIOLinkBox(String& html, const String& input_id, const String& name, uint8_t function, const String& url, uint8_t nr) { + uint8_t _nr; + if (nr == 0) { + _nr = 1; + } + else { + _nr = nr; + } + html += F(""); + html += F(""); + addListGPIOSelect(html, input_id, function, nr); + html += F(""); +} + +void addListBox(String& html, const String& input_id, const String& name, const char* const* array_P, uint8_t size, uint8_t selected, uint8_t nr) { + html += F(""); +} + +void addButton(String& html, const String& name, const String& url) { + html += F(""); + html += F("

"); +} + +void addButtonSubmit(String& html, const String& name) { + html += F(""); + html += F("

"); +} + +void addListGPIOSelect(String& html, const String& input_id, uint8_t function, uint8_t nr) { + html += F(""); +} + +void addListMCP23017GPIO(String& html, const String& input_id, uint8_t function, uint8_t nr) { + ; + html += F(""); +} + +String getURL(const String& url) { + return String(F(PATH_START)) + url; +} + +const String SuplaJavaScript(const String& java_return) { + String java_script = + F("\n"); +#ifdef SUPLA_OTA + java_script += + F("\n"); +#endif + return java_script; +} + +const String SuplaSaveResult(int save) { + if (save == 0) + return F(""); + String saveresult = ""; + saveresult += F("
"); + if (save == 1) { + saveresult += S_DATA_SAVED; + } + else if (save == 2) { + saveresult += S_RESTART_MODULE; + } + else if (save == 3) { + saveresult += S_DATA_ERASED_RESTART_DEVICE; + } + else if (save == 4) { + saveresult += S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING; + } + else if (save == 5) { + saveresult += S_DATA_SAVED_RESTART_MODULE; + } + else if (save == 6) { + saveresult += S_WRITE_ERROR_BAD_DATA; + } + else if (save == 7) { + saveresult += F("data saved"); + } + saveresult += F("
"); + return saveresult; +} \ No newline at end of file diff --git a/src/Markup.h b/src/Markup.h new file mode 100644 index 00000000..f04c9200 --- /dev/null +++ b/src/Markup.h @@ -0,0 +1,71 @@ +#ifndef Markup_h +#define Markup_h + +#include "SuplaDeviceGUI.h" + +void addForm(String& html, const String& method, const String& action = "/"); +void addFormEnd(String& html); + +void addFormHeader(String& html, const String& name = "\n"); +void addFormHeaderEnd(String& html); + +void addTextBox(String& html, + const String& input_id, + const String& name, + const String& value, + const String& placeholder, + int minlength, + int maxlength, + bool required, + bool readonly = false, + bool password = false); + +void addTextBox(String& html, + const String& input_id, + const String& name, + uint8_t value_key, + const String& placeholder, + int minlength, + int maxlength, + bool required, + bool readonly = false, + bool password = false); + +void addTextBox( + String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required, bool readonly = false); + +void addTextBox( + String& html, const String& input_id, const String& name, const String& value, int minlength, int maxlength, bool required, bool readonly = false); + +void addTextBoxPassword(String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required); + +void addNumberBox(String& html, const String& input_id, const String& name, uint8_t value_key, uint16_t max); + +void addNumberBox(String& html, const String& input_id, const String& name, const String& placeholder, bool required, const String& value = "\n"); + +void addLinkBox(String& html, const String& name, const String& url); + +void addListGPIOBox(String& html, const String& input_id, const String& name, uint8_t function, uint8_t nr = 0); + +void addListMCP23017GPIOBox(String& html, const String& input_id, const String& name, uint8_t function, uint8_t nr); + +void addListMCP23017GPIOLinkBox(String& html, const String& input_id, const String& name, uint8_t function, const String& url, uint8_t nr = 0); + +void addListGPIOLinkBox(String& html, const String& input_id, const String& name, uint8_t function, const String& url, uint8_t nr = 0); + +void addListBox(String& html, const String& input_id, const String& name, const char* const* list_P, uint8_t size, uint8_t selected, uint8_t nr = 0); + +void addButton(String& html, const String& name, const String& url); + +void addButtonSubmit(String& html, const String& name); + +void addListGPIOSelect(String& html, const String& input_id, uint8_t function, uint8_t nr = 0); + +void addListMCP23017GPIO(String& html, const String& input_id, uint8_t function, uint8_t nr); + +String getURL(const String& url); + +const String SuplaJavaScript(const String& java_return = PATH_START); + +const String SuplaSaveResult(int save); +#endif // Markup_h diff --git a/SuplaCommonPROGMEM.h b/src/SuplaCommonPROGMEM.h similarity index 57% rename from SuplaCommonPROGMEM.h rename to src/SuplaCommonPROGMEM.h index f997582a..b0c7e11c 100644 --- a/SuplaCommonPROGMEM.h +++ b/src/SuplaCommonPROGMEM.h @@ -2,6 +2,9 @@ #define SuplaCommonPROGMEM_h #include #include "SuplaDeviceGUI.h" +#include "GUIGenericCommon.h" + +#define PGMT(pgm_ptr) (reinterpret_cast(pgm_ptr)) const char HTTP_META[] PROGMEM = ""; + "12px;color:#00d151;font-size:13px;position:relative;line-height:18px}i input,select{width:calc(100% - " + "10px);font-size:16px;line-height:28px;padding:0 5px;border-bottom:solid 1px " + "#00d151}select{width:100%;float:none;margin:0}}iframe{margin:auto;display:block;}.formcenter{color:#000;width:50%;margin: 25px auto 25px " + "auto;} "; const char HTTP_LOGO[] PROGMEM = "
"; -const char HTTP_SUMMARY[] PROGMEM = "

{h}

LAST STATE: {s}
Firmware: SuplaDevice {v}
GUID: {g}
MAC: {m}
\n"; +const char HTTP_SUMMARY[] PROGMEM = "

{h}

LAST STATE: {s}
Firmware: SuplaDevice {v}
GUID: {g}
MAC: {m}
Free Mem: {f}KB
\n"; const char HTTP_COPYRIGHT[] PROGMEM = "https://forum.supla.org/\n"; +const char HTTP_FAVICON[] PROGMEM = "\n"; + +const char HTTP_RBT[] PROGMEM = "
"; + +const char ICON_EDIT[] PROGMEM = + ""; + const char GPIO0[] PROGMEM = "GPIO0-D3"; const char GPIO1[] PROGMEM = "GPIO1-TX"; const char GPIO2[] PROGMEM = "GPIO2-D4"; @@ -62,13 +81,33 @@ const char GPIO13[] PROGMEM = "GPIO13-D7"; const char GPIO14[] PROGMEM = "GPIO14-D5"; const char GPIO15[] PROGMEM = "GPIO15-D8"; const char GPIO16[] PROGMEM = "GPIO16-D0"; -const char OFF[] PROGMEM = "OFF"; -const char ON[] PROGMEM = "ON"; +const char OFF[] PROGMEM = S_OFF; +const char ON[] PROGMEM = S_ON; const char GPIONULL[] PROGMEM = ""; const char* const GPIO_P[] PROGMEM = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIONULL, GPIONULL, GPIONULL, GPIO9, GPIO10, GPIONULL, GPIO12, GPIO13, GPIO14, GPIO15, GPIO16, OFF}; +const char GPIO_A0[] PROGMEM = "A0"; +const char GPIO_A1[] PROGMEM = "A1"; +const char GPIO_A2[] PROGMEM = "A2"; +const char GPIO_A3[] PROGMEM = "A3"; +const char GPIO_A4[] PROGMEM = "A4"; +const char GPIO_A5[] PROGMEM = "A5"; +const char GPIO_A6[] PROGMEM = "A6"; +const char GPIO_A7[] PROGMEM = "A7"; +const char GPIO_B0[] PROGMEM = "B0"; +const char GPIO_B1[] PROGMEM = "B1"; +const char GPIO_B2[] PROGMEM = "B2"; +const char GPIO_B3[] PROGMEM = "B3"; +const char GPIO_B4[] PROGMEM = "B4"; +const char GPIO_B5[] PROGMEM = "B5"; +const char GPIO_B6[] PROGMEM = "B6"; +const char GPIO_B7[] PROGMEM = "B7"; + +const char* const GPIO_MCP23017_P[] PROGMEM = {GPIO_A0, GPIO_A1, GPIO_A2, GPIO_A3, GPIO_A4, GPIO_A5, GPIO_A6, GPIO_A7, GPIO_B0, + GPIO_B1, GPIO_B2, GPIO_B3, GPIO_B4, GPIO_B5, GPIO_B6, GPIO_B7, GPIONULL, OFF}; + const char ADR44[] PROGMEM = "0x44"; const char ADR45[] PROGMEM = "0x45"; const char ADR44_ADR45[] PROGMEM = "0x44 & 0x45"; @@ -77,29 +116,45 @@ const char ADR77[] PROGMEM = "0x77"; const char ADR76_ADR77[] PROGMEM = "0x76 & 0x77"; const char* const BME280_P[] PROGMEM = {OFF, ADR76, ADR77, ADR76_ADR77}; -const char* const SHT30_P[] PROGMEM = {OFF, ADR44, ADR45, ADR44_ADR45}; +const char* const SHT3x_P[] PROGMEM = {OFF, ADR44, ADR45, ADR44_ADR45}; + +const char ADR20[] PROGMEM = "0x20"; +const char ADR21[] PROGMEM = "0x21"; +const char ADR22[] PROGMEM = "0x22"; +const char ADR23[] PROGMEM = "0x23"; +const char* const MCP23017_P[] PROGMEM = {ADR20, ADR21, OFF}; const char* const STATE_P[] PROGMEM = {OFF, ON}; -const char ODWROCONE[] PROGMEM = "LOW"; -const char NORMALNE[] PROGMEM = "HIGH"; -const char* const LEVEL_P[] PROGMEM = {ODWROCONE, NORMALNE}; - -const char PAMIETAJSTAN[] PROGMEM = "PAMIĘTAJ STAN"; -const char* const MEMORY_P[] PROGMEM = {OFF, ON, PAMIETAJSTAN}; - -const char WCISNIECIE[] PROGMEM = "WCIŚNIĘCIE"; -const char ZWOLNIENIE[] PROGMEM = "ZWOLNIENIE"; -const char ZMIANASTANU[] PROGMEM = "ZMIANA STANU"; -const char* const TRIGGER_P[] PROGMEM = {WCISNIECIE, ZWOLNIENIE, ZMIANASTANU}; - -String GIPOString(uint8_t gpio); -String BME280String(uint8_t adr); -String SHT30String(uint8_t adr); -String StateString(uint8_t adr); -String LevelString(uint8_t nr); -String MemoryString(uint8_t nr); -String TriggerString(uint8_t nr); -String BoardString(uint8_t board); +const char LOW_STATE_CONTROL[] PROGMEM = S_LOW; +const char HIGH_STATE_CONTROL[] PROGMEM = S_HIGH; +const char* const LEVEL_P[] PROGMEM = {LOW_STATE_CONTROL, HIGH_STATE_CONTROL}; + +const char POSITION_MEMORY[] PROGMEM = S_POSITION_MEMORY; +const char* const MEMORY_P[] PROGMEM = {OFF, ON, POSITION_MEMORY}; + +const char REACTION_ON_PRESS[] PROGMEM = S_REACTION_ON_PRESS; +const char REACTION_ON_RELEASE[] PROGMEM = S_REACTION_ON_RELEASE; +const char REACTION_ON_CHANGE[] PROGMEM = S_REACTION_ON_CHANGE; +const char* const TRIGGER_P[] PROGMEM = {REACTION_ON_PRESS, REACTION_ON_RELEASE, REACTION_ON_CHANGE}; + +const char ACTION_TOGGLE[] PROGMEM = S_TOGGLE; +const char* const ACTION_P[] PROGMEM = {ON, OFF, ACTION_TOGGLE}; + +const char CFG_10_PRESSES[] PROGMEM = S_CFG_10_PRESSES; +const char CFG_5SEK_HOLD[] PROGMEM = S_5SEK_HOLD; +const char* const CFG_MODE_P[] PROGMEM = {CFG_10_PRESSES, CFG_5SEK_HOLD}; + +#ifdef SUPLA_OLED +const char SSD1306[] PROGMEM = "SSD1306 - 0,96''"; +const char SH1106[] PROGMEM = "SH1106 - 1,3''"; +const char SSD1306_WEMOS_SHIELD[] PROGMEM = "SSD1306 - 0,66'' WEMOS OLED shield"; +const char* const OLED_P[] PROGMEM = {OFF, SSD1306, SH1106, SSD1306_WEMOS_SHIELD}; + +const char CONTROLL_NORMAL[] PROGMEM = "NORMALNE"; +const char CONTROLL_SLOW[] PROGMEM = "WOLNE"; +const char CONTROLL_MANUAL[] PROGMEM = "RĘCZNE"; +const char* const OLED_CONTROLL_P[] PROGMEM = {CONTROLL_NORMAL, CONTROLL_SLOW, CONTROLL_MANUAL}; +#endif #endif // SuplaCommonPROGMEM_h diff --git a/src/SuplaConfigESP.cpp b/src/SuplaConfigESP.cpp new file mode 100644 index 00000000..9bfff53b --- /dev/null +++ b/src/SuplaConfigESP.cpp @@ -0,0 +1,691 @@ +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "SuplaConfigESP.h" +#include "SuplaConfigManager.h" +#include "SuplaDeviceGUI.h" +#include "GUIGenericCommon.h" +#include "SuplaWebPageSensor.h" + +SuplaConfigESP::SuplaConfigESP() { + configModeESP = NORMAL_MODE; + + if (ConfigManager->isDeviceConfigured()) { + ConfigManager->setGUIDandAUTHKEY(); + if (String(ConfigManager->get(KEY_LOGIN)->getValue()) == 0) { + ConfigManager->set(KEY_LOGIN, DEFAULT_LOGIN); + } + if (String(ConfigManager->get(KEY_LOGIN_PASS)->getValue()) == 0) { + ConfigManager->set(KEY_LOGIN_PASS, DEFAULT_LOGIN_PASS); + } + if (String(ConfigManager->get(KEY_HOST_NAME)->getValue()) == 0) { + ConfigManager->set(KEY_HOST_NAME, DEFAULT_HOSTNAME); + } + if (String(ConfigManager->get(KEY_SUPLA_SERVER)->getValue()) == 0) { + ConfigManager->set(KEY_SUPLA_SERVER, DEFAULT_SERVER); + } + if (String(ConfigManager->get(KEY_SUPLA_EMAIL)->getValue()) == 0) { + ConfigManager->set(KEY_SUPLA_EMAIL, DEFAULT_EMAIL); + } + ConfigManager->save(); + + configModeInit(); + } + + SuplaDevice.setStatusFuncImpl(&status_func); +} + +void SuplaConfigESP::addConfigESP(int _pinNumberConfig, int _pinLedConfig, int _modeConfigButton, bool _ledHighIsOn) { + pinNumberConfig = _pinNumberConfig; + pinLedConfig = _pinLedConfig; + modeConfigButton = _modeConfigButton; + ledHighIsOn = _ledHighIsOn; + + if (ConfigESP->getGpio(FUNCTION_CFG_LED) != OFF_GPIO) { + pinMode(pinLedConfig, OUTPUT); + digitalWrite(pinLedConfig, pinOffValue()); + } + + if (ConfigESP->getGpio(FUNCTION_CFG_BUTTON) != OFF_GPIO) { + Supla::Control::Button *buttonConfig = new Supla::Control::Button(pinNumberConfig, true, true); + buttonConfig->setMulticlickTime(1000); + buttonConfig->addAction(Supla::TURN_ON, *ConfigESP, Supla::ON_CLICK_1); + + if (modeConfigButton == CONFIG_MODE_10_ON_PRESSES) { + buttonConfig->addAction(CONFIG_MODE_10_ON_PRESSES, *ConfigESP, Supla::ON_CLICK_10); + } + if (modeConfigButton == CONFIG_MODE_5SEK_HOLD) { + buttonConfig->setHoldTime(5000); + buttonConfig->addAction(CONFIG_MODE_5SEK_HOLD, *ConfigESP, Supla::ON_HOLD); + } + } +} + +void SuplaConfigESP::handleAction(int event, int action) { + if (action == CONFIG_MODE_10_ON_PRESSES) { + if (event == Supla::ON_CLICK_10) { + configModeInit(); + } + } + if (action == CONFIG_MODE_5SEK_HOLD) { + if (event == Supla::ON_HOLD) { + configModeInit(); + } + } + + if (configModeESP == CONFIG_MODE) { + if (event == Supla::ON_CLICK_1) { + rebootESP(); + } + } +} + +void SuplaConfigESP::rebootESP() { + ESP.restart(); +} + +void SuplaConfigESP::configModeInit() { + configModeESP = CONFIG_MODE; + ledBlinking(100); + + WiFi.softAPdisconnect(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_AP_STA); +} + +void SuplaConfigESP::iterateAlways() { + if (configModeESP == CONFIG_MODE) { + WiFi.softAP(getConfigNameAP(), ""); + } +} + +const String SuplaConfigESP::getConfigNameAP() { + String name = F("SUPLA-ESP8266-"); + return name += getMacAddress(false); +} +const char *SuplaConfigESP::getLastStatusSupla() { + return supla_status.msg; +} + +void SuplaConfigESP::ledBlinking(int time) { + os_timer_disarm(&led_timer); + os_timer_setfn(&led_timer, ledBlinking_func, NULL); + os_timer_arm(&led_timer, time, true); +} + +void SuplaConfigESP::ledBlinkingStop(void) { + os_timer_disarm(&led_timer); + digitalWrite(pinLedConfig, pinOffValue()); +} + +int SuplaConfigESP::getPinLedConfig() { + return pinLedConfig; +} + +String SuplaConfigESP::getMacAddress(bool formating) { + byte mac[6]; + WiFi.macAddress(mac); + char baseMacChr[18] = {0}; + + if (formating) + sprintf(baseMacChr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + else + sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + return String(baseMacChr); +} + +void ledBlinking_func(void *timer_arg) { + int val = digitalRead(ConfigESP->getPinLedConfig()); + digitalWrite(ConfigESP->getPinLedConfig(), val == HIGH ? 0 : 1); +} + +void status_func(int status, const char *msg) { +#ifndef UI_LANGUAGE + switch (status) { + case 2: + ConfigESP->supla_status.msg = S_ALREADY_INITIATED; + break; + case 3: + ConfigESP->supla_status.msg = S_NOT_ASSIGNED_CB; + break; + case 4: + ConfigESP->supla_status.msg = S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE; + break; + case 5: + ConfigESP->supla_status.msg = S_UNKNOWN_SEVER_ADDRESS; + break; + case 6: + ConfigESP->supla_status.msg = S_UNKNOWN_ID; + break; + case 7: + ConfigESP->supla_status.msg = S_INITIATED; + break; + case 8: + ConfigESP->supla_status.msg = S_CHANNEL_LIMIT_EXCEEDED; + break; + case 9: + ConfigESP->supla_status.msg = S_DISCONNECTED; + break; + case 10: + ConfigESP->supla_status.msg = S_REGISTRATION_IS_PENDING; + break; + case 11: + ConfigESP->supla_status.msg = S_VARIABLE_ERROR; + break; + case 12: + ConfigESP->supla_status.msg = S_PROTOCOL_VERSION_ERROR; + break; + case 13: + ConfigESP->supla_status.msg = S_BAD_CREDENTIALS; + break; + case 14: + ConfigESP->supla_status.msg = S_TEMPORARILY_UNAVAILABLE; + break; + case 15: + ConfigESP->supla_status.msg = S_LOCATION_CONFLICT; + break; + case 16: + ConfigESP->supla_status.msg = S_CHANNEL_CONFLICT; + break; + case 17: + ConfigESP->supla_status.msg = S_REGISTERED_AND_READY; + break; + case 18: + ConfigESP->supla_status.msg = S_DEVICE_IS_DISCONNECTED; + break; + case 19: + ConfigESP->supla_status.msg = S_LOCATION_IS_DISABLED; + break; + case 20: + ConfigESP->supla_status.msg = S_DEVICE_LIMIT_EXCEEDED; + } +#else + ConfigESP->supla_status.msg = msg; +#endif + + static int lock; + if (status == 17 && ConfigESP->configModeESP == NORMAL_MODE) { + ConfigESP->ledBlinkingStop(); + lock = 0; + } + else if (status != 17 && lock == 0 && ConfigESP->configModeESP == NORMAL_MODE) { + ConfigESP->ledBlinking(500); + lock = 1; + } + + if (ConfigESP->supla_status.old_msg != ConfigESP->supla_status.msg) { + ConfigESP->supla_status.old_msg = ConfigESP->supla_status.msg; + ConfigESP->supla_status.status = status; + // Serial.println(ConfigESP->supla_status.msg); + } +} + +int SuplaConfigESP::getGpio(int nr, int function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + if (function == FUNCTION_CFG_BUTTON) { + if (checkBusyCfg(gpio)) { + return gpio; + } + } + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + if (ConfigManager->get(key)->getElement(NR).toInt() == nr) { + return gpio; + } + } + // return OFF_GPIO; + //"Pin 100 - 115" + // Pin 116 - 131" + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + switch (getAdressMCP23017(nr, function)) { + case 0: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function && + ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + return gpio + 100; + } + break; + case 1: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function && + ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + return gpio + 100 + 16; + } + break; + case 2: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function && + ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + return gpio + 100 + 16 + 16; + } + break; + } + } + } + return OFF_GPIO; +} + +int SuplaConfigESP::getLevel(int nr, int function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + if (ConfigManager->get(key)->getElement(NR).toInt() == nr) { + return ConfigManager->get(key)->getElement(LEVEL).toInt(); + } + } + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + switch (getAdressMCP23017(nr, function)) { + case 0: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + return ConfigManager->get(key)->getElement(LEVEL).toInt(); + ; + } + } + break; + case 1: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + return ConfigManager->get(key)->getElement(LEVEL).toInt(); + ; + } + } + break; + case 2: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + return ConfigManager->get(key)->getElement(LEVEL).toInt(); + ; + } + } + break; + } + } + } + return OFF_GPIO; +} + +int SuplaConfigESP::getMemory(int nr, int function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + if (ConfigManager->get(key)->getElement(NR).toInt() == nr) { + return ConfigManager->get(key)->getElement(MEMORY).toInt(); + } + } + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + switch (getAdressMCP23017(nr, function)) { + case 0: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + return ConfigManager->get(key)->getElement(MEMORY).toInt(); + ; + } + } + break; + case 1: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + return ConfigManager->get(key)->getElement(MEMORY).toInt(); + ; + } + } + break; + case 2: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + return ConfigManager->get(key)->getElement(MEMORY).toInt(); + ; + } + } + break; + } + } + } + return OFF_GPIO; +} + +int SuplaConfigESP::getAction(int nr, int function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + if (ConfigManager->get(key)->getElement(NR).toInt() == nr) { + return ConfigManager->get(key)->getElement(ACTION).toInt(); + ; + } + } + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + switch (getAdressMCP23017(nr, function)) { + case 0: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + return ConfigManager->get(key)->getElement(ACTION).toInt(); + ; + } + } + break; + case 1: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + return ConfigManager->get(key)->getElement(ACTION).toInt(); + ; + } + } + break; + case 2: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + return ConfigManager->get(key)->getElement(ACTION).toInt(); + ; + } + } + break; + } + } + } + return OFF_GPIO; +} + +bool SuplaConfigESP::checkBusyCfg(int gpio) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(FUNCTION_CFG_LED).toInt() == 1) { + return true; + } + return false; +} + +int SuplaConfigESP::checkBusyGpio(int gpio, int function) { + if (gpio == 6 || gpio == 7 || gpio == 8 || gpio == 11) { + return false; + } + else { + uint8_t key = KEY_GPIO + gpio; + + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == FUNCTION_BUTTON) { + if (function == FUNCTION_CFG_BUTTON) { + return false; + } + } + if (checkBusyCfg(gpio)) { + if (function != FUNCTION_BUTTON) { + return false; + } + } + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() != FUNCTION_OFF) { + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() != function) { + return false; + } + } + return true; + } +} + +void SuplaConfigESP::setGpio(uint8_t gpio, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory) { + uint8_t key = KEY_GPIO + gpio; + + if (function == FUNCTION_CFG_BUTTON) { + ConfigManager->setElement(key, CFG, 1); + return; + } + + ConfigManager->setElement(key, NR, nr); + ConfigManager->setElement(key, FUNCTION, function); + ConfigManager->setElement(key, LEVEL, level); + ConfigManager->setElement(key, MEMORY, memory); + ConfigManager->setElement(key, ACTION, Supla::TOGGLE); + + // ConfigManager->setElement(key.c_str(), MEMORY, memory); + // ConfigManager->setElement(key.c_str(), CFG, cfg); +} + +void SuplaConfigESP::clearGpio(uint8_t gpio, uint8_t function) { + uint8_t key = KEY_GPIO + gpio; + + if (function == FUNCTION_CFG_BUTTON) { + ConfigManager->setElement(key, CFG, 0); + return; + } + ConfigManager->setElement(key, NR, 0); + ConfigManager->setElement(key, FUNCTION, FUNCTION_OFF); + ConfigManager->setElement(key, LEVEL, 0); + ConfigManager->setElement(key, MEMORY, 2); + ConfigManager->setElement(key, ACTION, Supla::TOGGLE); +} + +uint8_t SuplaConfigESP::countFreeGpio(uint8_t exception) { + uint8_t count = 0; + for (uint8_t gpio = 0; gpio < OFF_GPIO; gpio++) { + if (gpio != 6 && gpio != 7 && gpio != 8 && gpio != 11) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == FUNCTION_OFF || + ConfigManager->get(key)->getElement(FUNCTION).toInt() == exception) { + count++; + } + } + } + return count; +} + +bool SuplaConfigESP::checkBusyGpioMCP23017(uint8_t gpio, uint8_t function) { + if (gpio == OFF_GPIO) { + return true; + } + else if (gpio == 16) { + return false; + } + else { + uint8_t key = KEY_GPIO + gpio; + uint8_t address = ConfigESP->getAdressMCP23017(1, function); + + if (address == OFF_MCP23017) { + return true; + } + + if (ConfigManager->get(key)->getElement(getFunctionMCP23017(address)).toInt() != FUNCTION_OFF) { + return false; + } + } + return true; +} + +uint8_t SuplaConfigESP::getGpioMCP23017(uint8_t nr, uint8_t function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + + switch (getAdressMCP23017(nr, function)) { + case 0: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + return gpio; + } + } + break; + case 1: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + return gpio; + } + } + break; + case 2: + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function) { + if (ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + return gpio; + } + } + break; + } + } + return OFF_GPIO; +} + +uint8_t SuplaConfigESP::getAdressMCP23017(uint8_t nr, uint8_t function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + if (ConfigManager->get(key)->getElement(MCP23017_NR_1).toInt() == nr) { + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_1).toInt() == function) { + return 0; + } + } + if (ConfigManager->get(key)->getElement(MCP23017_NR_2).toInt() == nr) { + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_2).toInt() == function) { + return 1; + } + } + if (ConfigManager->get(key)->getElement(MCP23017_NR_3).toInt() == nr) { + if (ConfigManager->get(key)->getElement(MCP23017_FUNCTION_3).toInt() == function) { + return 2; + } + } + } + return OFF_MCP23017; +} + +void SuplaConfigESP::setGpioMCP23017(uint8_t gpio, uint8_t adress, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory) { + uint8_t key = KEY_GPIO + gpio; + + uint8_t _gpio = ConfigESP->getGpioMCP23017(nr, function); + ConfigESP->clearGpioMCP23017(_gpio, nr, function); + + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == FUNCTION_OFF) { + ConfigManager->setElement(key, LEVEL, 0); + ConfigManager->setElement(key, MEMORY, 2); + ConfigManager->setElement(key, ACTION, Supla::TOGGLE); + } + else { + ConfigManager->setElement(key, LEVEL, level); + ConfigManager->setElement(key, MEMORY, memory); + ConfigManager->setElement(key, ACTION, Supla::TOGGLE); + } + + switch (adress) { + case 0: + ConfigManager->setElement(key, MCP23017_NR_1, nr); + ConfigManager->setElement(key, MCP23017_FUNCTION_1, function); + break; + case 1: + ConfigManager->setElement(key, MCP23017_NR_2, nr); + ConfigManager->setElement(key, MCP23017_FUNCTION_2, function); + break; + case 2: + ConfigManager->setElement(key, MCP23017_NR_3, nr); + ConfigManager->setElement(key, MCP23017_FUNCTION_3, function); + break; + } +} + +void SuplaConfigESP::clearGpioMCP23017(uint8_t gpio, uint8_t nr, uint8_t function) { + uint8_t key = KEY_GPIO + gpio; + uint8_t adress = getAdressMCP23017(nr, function); + + if (adress != OFF_MCP23017) { + ConfigManager->setElement(key, getNrMCP23017(adress), 0); + ConfigManager->setElement(key, getFunctionMCP23017(adress), FUNCTION_OFF); + } +} + +void SuplaConfigESP::clearFunctionGpio(uint8_t function) { + for (uint8_t gpio = 0; gpio <= OFF_GPIO; gpio++) { + uint8_t key = KEY_GPIO + gpio; + + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + ConfigManager->setElement(key, NR, 0); + ConfigManager->setElement(key, FUNCTION, FUNCTION_OFF); + } + } +} + +uint8_t SuplaConfigESP::getFunctionMCP23017(uint8_t adress) { + switch (adress) { + case 0: + return MCP23017_FUNCTION_1; + break; + case 1: + return MCP23017_FUNCTION_2; + break; + case 2: + return MCP23017_FUNCTION_3; + break; + } + return FUNCTION_OFF; +} + +uint8_t SuplaConfigESP::getNrMCP23017(uint8_t adress) { + switch (adress) { + case 0: + return MCP23017_NR_1; + break; + case 1: + return MCP23017_NR_2; + break; + case 2: + return MCP23017_NR_3; + break; + } + return FUNCTION_OFF; +} + +void SuplaConfigESP::factoryReset(bool forceReset) { + delay(2000); + pinMode(0, INPUT_PULLUP); + if (digitalRead(0) != HIGH || forceReset) { + Serial.println(F("FACTORY RESET!!!")); + + EEPROM.begin(1024); + delay(15); + for (int i = 1; i < 1024; ++i) { + EEPROM.write(i, 0); + } + EEPROM.end(); + + ConfigManager->set(KEY_WIFI_SSID, ""); + ConfigManager->set(KEY_WIFI_PASS, ""); + ConfigManager->set(KEY_SUPLA_SERVER, DEFAULT_SERVER); + ConfigManager->set(KEY_SUPLA_EMAIL, DEFAULT_EMAIL); + ConfigManager->set(KEY_HOST_NAME, DEFAULT_HOSTNAME); + ConfigManager->set(KEY_LOGIN, DEFAULT_LOGIN); + ConfigManager->set(KEY_LOGIN_PASS, DEFAULT_LOGIN_PASS); + ConfigManager->set(KEY_MAX_RELAY, "1"); + ConfigManager->set(KEY_MAX_BUTTON, "1"); + ConfigManager->set(KEY_MAX_LIMIT_SWITCH, "0"); + ConfigManager->set(KEY_MAX_DHT22, "1"); + ConfigManager->set(KEY_MAX_DHT11, "1"); + ConfigManager->set(KEY_MULTI_MAX_DS18B20, "1"); + ConfigManager->set(KEY_MAX_ROLLERSHUTTER, "0"); + ConfigManager->set(KEY_ALTITUDE_BME280, "0"); + ConfigManager->set(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, "0"); + ConfigManager->set(KEY_MAX_IMPULSE_COUNTER, "0"); + ConfigManager->set(KEY_ACTIVE_SENSOR, ""); + ConfigManager->set(KEY_BOARD, "0"); + ConfigManager->set(KEY_CFG_MODE, "0"); + + uint8_t nr, key; + for (nr = 0; nr <= 17; nr++) { + key = KEY_GPIO + nr; + ConfigManager->set(key, ""); + } + + ConfigManager->set(KEY_ADDR_DS18B20, ""); + ConfigManager->set(KEY_NAME_SENSOR, ""); + + ConfigManager->save(); + + // rebootESP(); + } +} diff --git a/SuplaConfigESP.h b/src/SuplaConfigESP.h similarity index 62% rename from SuplaConfigESP.h rename to src/SuplaConfigESP.h index 8a400913..61c3e614 100644 --- a/SuplaConfigESP.h +++ b/src/SuplaConfigESP.h @@ -1,94 +1,128 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef SuplaConfigESP_h -#define SuplaConfigESP_h - -#include - -#include "GUI-Generic_Config.h" - -enum _configModeESP { NORMAL_MODE, CONFIG_MODE }; - -typedef struct { - int status; - const char *msg; - const char *old_msg; -} _supla_status; - -class SuplaConfigESP : public Supla::Triggerable { - public: - SuplaConfigESP(); - - void addConfigESP(int _pinNumberConfig, int _pinLedConfig, int _modeConfigButton, bool _highIsOn = true); - void runAction(int event, int action); - void rebootESP(); - - const char *getLastStatusSupla(); - - void ledBlinking(int time); - void ledBlinkingStop(void); - int getPinLedConfig(); - virtual uint8_t pinOnValue() { - return highIsOn ? HIGH : LOW; - } - - virtual uint8_t pinOffValue() { - return highIsOn ? LOW : HIGH; - } - - String getMacAddress(bool formating); - - _configModeESP configModeESP; - _supla_status supla_status; - - int getGpio(int nr, int function); - int getGpio(int function) { - return getGpio(1, function); - } - int getLevel(int nr, int function); - int getLevel(int function) { - return getLevel(1, function); - } - bool checkBusyCfg(int gpio); - int checkBusyGpio(int gpio, int function); - uint8_t countFreeGpio(uint8_t exception = 0); - void setGpio(uint8_t gpio, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory = 0); - void setGpio(uint8_t gpio, uint8_t function, uint8_t level = 0) { - setGpio(gpio, 1, function, level); - } - void clearGpio(uint8_t gpio, uint8_t function = 0); - - int getMemoryRelay(int nr); - void factoryReset(); - - private: - void configModeInit(); - - int pinNumberConfig; - int pinLedConfig; - int modeConfigButton; - int countPresses = 0; - unsigned long cnfigChangeTimeMs = 0; - bool highIsOn; - - ETSTimer led_timer; -}; - -void ledBlinking_func(void *timer_arg); -void status_func(int status, const char *msg); - -#endif // SuplaConfigESP_h +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SuplaConfigESP_h +#define SuplaConfigESP_h + +#include "Arduino.h" +#include +#include +#include "GUI-Generic_Config.h" + +#include +#include + +enum _configModeESP +{ + NORMAL_MODE, + CONFIG_MODE +}; +enum _ConfigMode +{ + CONFIG_MODE_10_ON_PRESSES, + CONFIG_MODE_5SEK_HOLD, + FACTORYRESET +}; + +#define OFF_GPIO 17 +#define OFF_MCP23017 2 + +typedef struct { + int status; + const char *msg; + const char *old_msg; +} _supla_status; + +class SuplaConfigESP : public Supla::ActionHandler, public Supla::Element { + public: + SuplaConfigESP(); + + void addConfigESP(int _pinNumberConfig, int _pinLedConfig, int _modeConfigButton, bool _highIsOn = true); + void handleAction(int event, int action); + void rebootESP(); + + const char *getLastStatusSupla(); + + void ledBlinking(int time); + void ledBlinkingStop(void); + int getPinLedConfig(); + virtual uint8_t pinOnValue() { + return ledHighIsOn ? HIGH : LOW; + } + + virtual uint8_t pinOffValue() { + return ledHighIsOn ? LOW : HIGH; + } + + String getMacAddress(bool formating); + + _configModeESP configModeESP; + _supla_status supla_status; + + int getGpio(int nr, int function); + int getGpio(int function) { + return getGpio(1, function); + } + int getLevel(int nr, int function); + int getLevel(int function) { + return getLevel(1, function); + } + bool checkBusyCfg(int gpio); + int checkBusyGpio(int gpio, int function); + uint8_t countFreeGpio(uint8_t exception = 0); + void setGpio(uint8_t gpio, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory = 0); + void setGpio(uint8_t gpio, uint8_t function, uint8_t level = 0) { + setGpio(gpio, 1, function, level); + } + void clearGpio(uint8_t gpio, uint8_t function = 0); + + int getMemory(int nr, int function); + int getMemory(int function) { + return getMemory(1, function); + } + + int getAction(int nr, int function); + void factoryReset(bool forceReset = false); + const String getConfigNameAP(); + + bool checkBusyGpioMCP23017(uint8_t gpio, uint8_t function); + uint8_t getGpioMCP23017(uint8_t nr, uint8_t function); + uint8_t getAdressMCP23017(uint8_t nr, uint8_t function); + void setGpioMCP23017(uint8_t gpio, uint8_t adress, uint8_t nr, uint8_t function, uint8_t level, uint8_t memory); + void clearGpioMCP23017(uint8_t gpio, uint8_t nr, uint8_t function); + void clearFunctionGpio(uint8_t function); + uint8_t getFunctionMCP23017(uint8_t adress); + uint8_t getNrMCP23017(uint8_t adress); + + private: + void configModeInit(); + void iterateAlways(); + int pinNumberConfig; + int pinLedConfig; + int modeConfigButton; + bool ledHighIsOn; + + ETSTimer led_timer; +}; + +void ledBlinking_func(void *timer_arg); +void status_func(int status, const char *msg); + +uint32_t getFreeStackWatermark(); +unsigned long FreeMem(); +void checkRAM(); + +#endif // SuplaConfigESP_h diff --git a/SuplaConfigManager.cpp b/src/SuplaConfigManager.cpp similarity index 72% rename from SuplaConfigManager.cpp rename to src/SuplaConfigManager.cpp index ce40aff3..3c8ffe7f 100644 --- a/SuplaConfigManager.cpp +++ b/src/SuplaConfigManager.cpp @@ -1,437 +1,454 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include -#include -#include - -#include -#include - -#include "FS.h" -#include "SuplaConfigManager.h" - -#define CONFIG_FILE_PATH "/dat" - -ConfigOption::ConfigOption(const char *key, const char *value, int maxLength) { - // size_t size = strlen(key) + 1; - // _key = (char *)malloc(sizeof(char) * size); - // memcpy(_key, key, size - 1); - // _key[size - 1] = 0; - size_t size = strlen(key) + 1; - _key = new char[size]; - strncpy(_key, key, size); - _key[size - 1] = '\0'; - - _maxLength = maxLength + 1; - setValue(value); -} - -const char *ConfigOption::getKey() { - return _key; -} - -const char *ConfigOption::getValue() { - return _value; -} - -int ConfigOption::getValueInt() { - return atoi(_value); -} - -uint8_t *ConfigOption::getValueBin(size_t size) { - uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t) * (size)); - - for (int i = 0; i < size; i++) { - sscanf(&_value[i * 2], "%2hhx", &buffer[i]); - } - return buffer; -} - -const char *ConfigOption::getValueHex(size_t size) { - char *buffer = (char *)malloc(sizeof(char) * (size * 2)); - int a, b; - - buffer[0] = 0; - b = 0; - - for (a = 0; a < size; a++) { - snprintf(&buffer[b], 3, "%02X", (unsigned char)_value[a]); // NOLINT - b += 2; - } - return buffer; -} - -int ConfigOption::getValueElement(int element) { - return _value[element] - 48; -} - -int ConfigOption::getLength() { - return _maxLength; -} - -String ConfigOption::getElement(int index) { - String data = _value; - int found = 0; - int strIndex[] = {0, -1}; - int maxIndex = data.length() - 1; - for (int i = 0; i <= maxIndex && found <= index; i++) { - if (data.charAt(i) == SEPARATOR || i == maxIndex) { - found++; - strIndex[0] = strIndex[1] + 1; - strIndex[1] = (i == maxIndex) ? i + 1 : i; - } - } - return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; -} - -String ConfigOption::replaceElement(int index, int newvalue) { - String data = _value; - int lenght = 5; - String table; - for (int i = 0; i < lenght; i++) { - if (i == index) { - table += newvalue; - } else { - table += this->getElement(i); - } - if (i < lenght - 1) table += SEPARATOR; - } - return table; -} - -void ConfigOption::setValue(const char *value) { - //size_t size = _maxLength + 1; - //_value = (char *)malloc(sizeof(char) * (size)); - - //if (value != NULL) { - // memcpy(_value, value, size - 1); - // _value[size - 1] = 0; - //} - if (value != NULL) { - size_t size = getLength(); - _value = new char[size]; - strncpy(_value, value, size); - _value[size - 1] = '\0'; - } -} - -// -// class SuplaConfigManager -// -SuplaConfigManager::SuplaConfigManager() { - if (SPIFFS.begin()) { - // Serial.println(F("\nSPIFFS mounted")); - // } else { - // Serial.println(F("\nFailed to mount SPIFFS")); - } - _optionCount = 0; - - this->addKey(KEY_SUPLA_GUID, MAX_GUID); - this->addKey(KEY_SUPLA_AUTHKEY, MAX_AUTHKEY); - this->addKey(KEY_WIFI_SSID, MAX_SSID); - this->addKey(KEY_WIFI_PASS, MAX_PASSWORD); - this->addKey(KEY_LOGIN, MAX_MLOGIN); - this->addKey(KEY_LOGIN_PASS, MAX_MPASSWORD); - this->addKey(KEY_HOST_NAME, DEFAULT_HOSTNAME, MAX_HOSTNAME); - this->addKey(KEY_SUPLA_SERVER, DEFAULT_SERVER, MAX_SUPLA_SERVER); - this->addKey(KEY_SUPLA_EMAIL, DEFAULT_EMAIL, MAX_EMAIL); - this->addKey(KEY_MAX_ROLLERSHUTTER, "0", 2); - this->addKey(KEY_MAX_RELAY, "1", 2); - this->addKey(KEY_MAX_BUTTON, "1", 2); - this->addKey(KEY_MAX_LIMIT_SWITCH, "0", 2); - this->addKey(KEY_MAX_DHT22, "1", 2); - this->addKey(KEY_MAX_DHT11, "1", 2); - this->addKey(KEY_MULTI_MAX_DS18B20, "1", 2); - this->addKey(KEY_ALTITUDE_BME280, "0", 4); - - int nr; - String key; - - for (nr = 0; nr <= 17; nr++) { - key = GPIO; - key += nr; - this->addKey(key.c_str(), "0,0,0,0,0", 14); - } - - this->addKey(KEY_ACTIVE_SENSOR, "0,0,0,0,0", 14); - this->addKey(KEY_BOARD, "0", 2); - - for (nr = 0; nr <= MAX_DS18B20; nr++) { - key = KEY_DS; - key += nr; - this->addKey(key.c_str(), MAX_DS18B20_ADDRESS_HEX); - key = KEY_DS_NAME; - key += nr; - this->addKey(key.c_str(), MAX_DS18B20_NAME); - } - this->load(); - // switch (this->load()) { - // case E_CONFIG_OK: - // Serial.println(F("Config read")); - // return; - // case E_CONFIG_FS_ACCESS: - // Serial.println(F("E_CONFIG_FS_ACCESS: Couldn't access file system")); - // return; - // case E_CONFIG_FILE_NOT_FOUND: - // Serial.println(F("E_CONFIG_FILE_NOT_FOUND: File not found")); - // return; - // case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - // return; - // case E_CONFIG_PARSE_ERROR: - // Serial.println(F("E_CONFIG_PARSE_ERROR: File was not parsable")); - // return; - // } -} - -uint8_t SuplaConfigManager::addKey(const char *key, int maxLength) { - return addKey(key, "", maxLength); -} - -uint8_t SuplaConfigManager::addKey(const char *key, const char *value, int maxLength) { - if (_optionCount == CONFIG_MAX_OPTIONS) { - return E_CONFIG_MAX; - } - _options[_optionCount] = new ConfigOption(key, value, maxLength); - _optionCount += 1; - - return E_CONFIG_OK; -} - -uint8_t SuplaConfigManager::addKeyAndRead(const char *key, const char *value, int maxLength) { - addKey(key, maxLength); - if (this->loadItem(key) != E_CONFIG_OK) { - this->set(key, value); - } - return E_CONFIG_OK; -} - -uint8_t SuplaConfigManager::deleteKey(const char *key) { - for (int i = 0; i < _optionCount; i++) { - if (strcmp(_options[i]->getKey(), key) == 0) { - delete _options[_optionCount]; - _optionCount -= 1; - } - } - - return E_CONFIG_OK; -} - -uint8_t SuplaConfigManager::load() { - - if (SPIFFS.begin()) { - if (SPIFFS.exists(CONFIG_FILE_PATH)) { - File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); - - if (configFile) { - int i = 0; - int offset = 0; - int length = 0; - - for (i = 0; i < _optionCount; i++) { - length += _options[i]->getLength(); - } - - // if (length != configFile.size()) { - // return E_CONFIG_PARSE_ERROR; - // } - - uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); - configFile.read(content, length); - - for (i = 0; i < _optionCount; i++) { - _options[i]->setValue((const char *)(content + offset)); - offset += _options[i]->getLength(); - } - - configFile.close(); - free(content); - - return E_CONFIG_OK; - } else { - configFile.close(); - return E_CONFIG_FILE_OPEN; - } - } else { - return E_CONFIG_FILE_NOT_FOUND; - } - } else { - return E_CONFIG_FS_ACCESS; - } -} - -uint8_t SuplaConfigManager::loadItem(const char *key) { - - if (SPIFFS.begin()) { - if (SPIFFS.exists(CONFIG_FILE_PATH)) { - File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); - - if (configFile) { - int i = 0; - int offset = 0; - int length = 0; - - for (i = 0; i < _optionCount; i++) { - length += _options[i]->getLength(); - } - - uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); - configFile.read(content, length); - - for (i = 0; i < _optionCount; i++) { - if (strcmp(_options[i]->getKey(), key) == 0) { - _options[i]->setValue((const char *)(content + offset)); - } - offset += _options[i]->getLength(); - } - - configFile.close(); - free(content); - - return E_CONFIG_OK; - } else { - configFile.close(); - return E_CONFIG_FILE_OPEN; - } - } else { - return E_CONFIG_FILE_NOT_FOUND; - } - } else { - return E_CONFIG_FS_ACCESS; - } -} - -uint8_t SuplaConfigManager::save() { - if (SPIFFS.begin()) { - int i = 0; - int offset = 0; - int length = 0; - - for (i = 0; i < _optionCount; i++) { - length += _options[i]->getLength(); - } - - File configFile = SPIFFS.open(CONFIG_FILE_PATH, "w+"); - if (configFile) { - uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); - for (i = 0; i < _optionCount; i++) { - Serial.println("Save key " + String(_options[i]->getKey()) + ": " + String(_options[i]->getValue())); - memcpy(content + offset, _options[i]->getValue(), _options[i]->getLength()); - offset += _options[i]->getLength(); - } - - configFile.write(content, length); - configFile.close(); - - free(content); - return E_CONFIG_OK; - } else { - return E_CONFIG_FILE_OPEN; - } - } - - return E_CONFIG_FS_ACCESS; -} - -void SuplaConfigManager::showAllValue() { - for (int i = 0; i < _optionCount; i++) { - Serial.println("Key: " + String(_options[i]->getKey()) + " Value: " + String(_options[i]->getValue())); - } -} - -bool SuplaConfigManager::isDeviceConfigured() { - return strcmp(this->get(KEY_SUPLA_GUID)->getValue(), "") == 0 || - strcmp(this->get(KEY_SUPLA_AUTHKEY)->getValue(), "") == 0 || - strcmp(this->get(KEY_WIFI_SSID)->getValue(), "") == 0 || - strcmp(this->get(KEY_WIFI_PASS)->getValue(), "") == 0 || - strcmp(this->get(KEY_LOGIN)->getValue(), "") == 0; -} - -ConfigOption *SuplaConfigManager::get(const char *key) { - for (int i = 0; i < _optionCount; i++) { - if (strcmp(_options[i]->getKey(), key) == 0) { - return _options[i]; - } - } - return NULL; -} - -bool SuplaConfigManager::set(const char *key, const char *value) { - for (int i = 0; i < _optionCount; i++) { - if (strcmp(key, _options[i]->getKey()) == 0) { - _options[i]->setValue(value); - return true; - } - } - return false; -} - -bool SuplaConfigManager::setElement(const char *key, int index, int newvalue) { - for (int i = 0; i < _optionCount; i++) { - if (strcmp(key, _options[i]->getKey()) == 0) { - String data = _options[i]->replaceElement(index, newvalue); - _options[i]->setValue(data.c_str()); - return true; - } - } - return false; -} - -void SuplaConfigManager::setGUIDandAUTHKEY() { - if (strcmp(this->get(KEY_SUPLA_GUID)->getValue(), "") != 0 || - strcmp(this->get(KEY_SUPLA_AUTHKEY)->getValue(), "") != 0) { - return; - } - - char mac[6]; - int a; - - char GUID[SUPLA_GUID_SIZE]; - char AUTHKEY[SUPLA_AUTHKEY_SIZE]; - - memset(GUID, 0, SUPLA_GUID_SIZE); - memset(AUTHKEY, 0, SUPLA_AUTHKEY_SIZE); - - os_get_random((unsigned char *)GUID, SUPLA_GUID_SIZE); - os_get_random((unsigned char *)AUTHKEY, SUPLA_AUTHKEY_SIZE); - - if (SUPLA_GUID_SIZE >= 6) { - wifi_get_macaddr(STATION_IF, (unsigned char *)mac); - - for (a = 0; a < 6; a++) - GUID[a] = (GUID[a] * mac[a]) % 255; - } - - if (SUPLA_GUID_SIZE >= 12) { - wifi_get_macaddr(SOFTAP_IF, (unsigned char *)mac); - - for (a = 0; a < 6; a++) GUID[a + 6] = (GUID[a + 6] * mac[a]) % 255; - } - - for (a = 0; a < SUPLA_GUID_SIZE; a++) { - GUID[a] = (GUID[a] + system_get_time() + spi_flash_get_id() + system_get_chip_id() + system_get_rtc_time()) % 255; - } - - a = SUPLA_GUID_SIZE > SUPLA_AUTHKEY_SIZE ? SUPLA_AUTHKEY_SIZE : SUPLA_GUID_SIZE; - a--; - for (; a > 0; a--) { - AUTHKEY[a] += GUID[a]; - } - - this->set(KEY_SUPLA_GUID, GUID); - this->set(KEY_SUPLA_AUTHKEY, AUTHKEY); -} +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +#include +#include + +#include "FS.h" +#include "SuplaConfigManager.h" + +ConfigOption::ConfigOption(uint8_t key, const char *value, int maxLength) { + // size_t size = strlen(key) + 1; + // _key = (char *)malloc(sizeof(char) * size); + // memcpy(_key, key, size - 1); + // _key[size - 1] = 0; + /*size_t size = strlen(key) + 1; + _key = new char[size]; + strncpy(_key, key, size); + _key[size - 1] = '\0'; +*/ + _key = key; + _maxLength = maxLength + 1; + + _value = new char[_maxLength]; + setValue(value); +} + +uint8_t ConfigOption::getKey() { + return _key; +} + +const char *ConfigOption::getValue() { + return _value; +} + +int ConfigOption::getValueInt() { + return atoi(_value); +} + +const char *ConfigOption::getValueHex(size_t size) { + char *buffer = (char *)malloc(sizeof(char) * (size * 2)); + int a, b; + + buffer[0] = 0; + b = 0; + + for (a = 0; a < size; a++) { + snprintf(&buffer[b], 3, "%02X", (unsigned char)_value[a]); // NOLINT + b += 2; + } + return buffer; +} + +int ConfigOption::getValueElement(int element) { + return _value[element] - 48; +} + +int ConfigOption::getLength() { + return _maxLength; +} + +const String ConfigOption::getElement(int index) { + String data = _value; + data.reserve(_maxLength); + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length() - 1; + for (int i = 0; i <= maxIndex && found <= index; i++) { + if (data.charAt(i) == SEPARATOR || i == maxIndex) { + found++; + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + } + } + return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; +} + +const String ConfigOption::replaceElement(int index, int newvalue) { + String data = _value; + data.reserve(_maxLength); + int lenght = SETTINGSCOUNT; + String table; + for (int i = 0; i <= lenght; i++) { + if (i == index) { + table += newvalue; + } + else { + table += this->getElement(i); + } + if (i < lenght - 1) + table += SEPARATOR; + } + return table; +} + +const String ConfigOption::replaceElement(int index, const char *newvalue) { + int lenght = SETTINGSCOUNT; + String table; + for (int i = 0; i <= lenght; i++) { + if (i == index) { + table += newvalue; + } + else { + table += this->getElement(i); + } + if (i < lenght - 1) + table += SEPARATOR; + } + return table; +} + +void ConfigOption::setValue(const char *value) { + if (value != NULL) { + size_t size = getLength(); + strncpy(_value, value, size); + _value[size - 1] = '\0'; + } +} + +// +// class SuplaConfigManager +// +SuplaConfigManager::SuplaConfigManager() { + if (SPIFFS.begin()) { + // Serial.println(F("\nSPIFFS mounted")); + // } else { + // Serial.println(F("\nFailed to mount SPIFFS")); + } + _optionCount = 0; + + this->addKey(KEY_SUPLA_GUID, MAX_GUID); + this->addKey(KEY_SUPLA_AUTHKEY, MAX_AUTHKEY); + this->addKey(KEY_WIFI_SSID, MAX_SSID); + this->addKey(KEY_WIFI_PASS, MAX_PASSWORD); + this->addKey(KEY_LOGIN, MAX_MLOGIN); + this->addKey(KEY_LOGIN_PASS, MAX_MPASSWORD); + this->addKey(KEY_HOST_NAME, DEFAULT_HOSTNAME, MAX_HOSTNAME); + this->addKey(KEY_SUPLA_SERVER, DEFAULT_SERVER, MAX_SUPLA_SERVER); + this->addKey(KEY_SUPLA_EMAIL, DEFAULT_EMAIL, MAX_EMAIL); + this->addKey(KEY_MAX_ROLLERSHUTTER, "0", 2); + this->addKey(KEY_MAX_RELAY, "0", 2); + this->addKey(KEY_MAX_BUTTON, "0", 2); + this->addKey(KEY_MAX_LIMIT_SWITCH, "0", 2); + this->addKey(KEY_MAX_DHT22, "1", 2); + this->addKey(KEY_MAX_DHT11, "1", 2); + this->addKey(KEY_MULTI_MAX_DS18B20, "1", 2); + this->addKey(KEY_ALTITUDE_BME280, "0", 4); + this->addKey(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, "10", 4); + this->addKey(KEY_MAX_IMPULSE_COUNTER, "0", 2); + + uint8_t nr, key; + + for (nr = 0; nr <= 17; nr++) { + key = KEY_GPIO + nr; + this->addKey(key, "", SETTINGSCOUNT * 2); + } + + this->addKey(KEY_ACTIVE_SENSOR, "", 16); + this->addKey(KEY_BOARD, "0", 2); + this->addKey(KEY_CFG_MODE, "0", 2); + this->addKey(KEY_ADDR_DS18B20, MAX_DS18B20_ADDRESS_HEX * MAX_DS18B20); + this->addKey(KEY_NAME_SENSOR, MAX_DS18B20_NAME * MAX_DS18B20); + this->addKey(KEY_LEVEL_LED, "0", 1); + this->addKey(KEY_OLED_ANIMATION, "0", 1); + this->addKey(KEY_OLED_BACK_LIGHT_TIME, "5", 2); + + this->load(); + // switch (this->load()) { + // case E_CONFIG_OK: + // Serial.println(F("Config read")); + // return; + // case E_CONFIG_FS_ACCESS: + // Serial.println(F("E_CONFIG_FS_ACCESS: Couldn't access file system")); + // return; + // case E_CONFIG_FILE_NOT_FOUND: + // Serial.println(F("E_CONFIG_FILE_NOT_FOUND: File not found")); + // return; + // case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + // return; + // case E_CONFIG_PARSE_ERROR: + // Serial.println(F("E_CONFIG_PARSE_ERROR: File was not parsable")); + // return; + // } +} + +uint8_t SuplaConfigManager::addKey(uint8_t key, int maxLength) { + return addKey(key, "", maxLength); +} + +uint8_t SuplaConfigManager::addKey(uint8_t key, const char *value, int maxLength) { + if (_optionCount == CONFIG_MAX_OPTIONS) { + return E_CONFIG_MAX; + } + _options[_optionCount] = new ConfigOption(key, value, maxLength); + _optionCount += 1; + + return E_CONFIG_OK; +} + +uint8_t SuplaConfigManager::addKeyAndRead(uint8_t key, const char *value, int maxLength) { + addKey(key, maxLength); + if (this->loadItem(key) != E_CONFIG_OK) { + this->set(key, value); + } + return E_CONFIG_OK; +} + +uint8_t SuplaConfigManager::deleteKey(uint8_t key) { + for (int i = 0; i < _optionCount; i++) { + if (_options[i]->getKey() == key) { + delete _options[_optionCount]; + _optionCount -= 1; + } + } + + return E_CONFIG_OK; +} + +uint8_t SuplaConfigManager::load() { + if (SPIFFS.begin()) { + if (SPIFFS.exists(CONFIG_FILE_PATH)) { + File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); + + if (configFile) { + int i = 0; + int offset = 0; + int length = 0; + + for (i = 0; i < _optionCount; i++) { + length += _options[i]->getLength(); + } + + // if (length != configFile.size()) { + // return E_CONFIG_PARSE_ERROR; + // } + + uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); + configFile.read(content, length); + + for (i = 0; i < _optionCount; i++) { + _options[i]->setValue((const char *)(content + offset)); + offset += _options[i]->getLength(); + } + + configFile.close(); + free(content); + + return E_CONFIG_OK; + } + else { + configFile.close(); + return E_CONFIG_FILE_OPEN; + } + } + else { + return E_CONFIG_FILE_NOT_FOUND; + } + } + else { + return E_CONFIG_FS_ACCESS; + } +} + +uint8_t SuplaConfigManager::loadItem(uint8_t key) { + if (SPIFFS.begin()) { + if (SPIFFS.exists(CONFIG_FILE_PATH)) { + File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); + + if (configFile) { + int i = 0; + int offset = 0; + int length = 0; + + for (i = 0; i < _optionCount; i++) { + length += _options[i]->getLength(); + } + + uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); + configFile.read(content, length); + + for (i = 0; i < _optionCount; i++) { + if (_options[i]->getKey() == key) { + _options[i]->setValue((const char *)(content + offset)); + } + offset += _options[i]->getLength(); + } + + configFile.close(); + free(content); + + return E_CONFIG_OK; + } + else { + configFile.close(); + return E_CONFIG_FILE_OPEN; + } + } + else { + return E_CONFIG_FILE_NOT_FOUND; + } + } + else { + return E_CONFIG_FS_ACCESS; + } +} + +uint8_t SuplaConfigManager::save() { + if (SPIFFS.begin()) { + int i = 0; + int offset = 0; + int length = 0; + + for (i = 0; i < _optionCount; i++) { + length += _options[i]->getLength(); + } + + File configFile = SPIFFS.open(CONFIG_FILE_PATH, "w+"); + if (configFile) { + uint8_t *content = (uint8_t *)malloc(sizeof(uint8_t) * length); + for (i = 0; i < _optionCount; i++) { + Serial.printf_P(PSTR("Save key=%d"), _options[i]->getKey()); + Serial.printf_P(PSTR(" value=%s\n"), _options[i]->getValue()); + memcpy(content + offset, _options[i]->getValue(), _options[i]->getLength()); + offset += _options[i]->getLength(); + } + + configFile.write(content, length); + configFile.close(); + + free(content); + return E_CONFIG_OK; + } + else { + return E_CONFIG_FILE_OPEN; + } + } + + return E_CONFIG_FS_ACCESS; +} + +void SuplaConfigManager::showAllValue() { + for (int i = 0; i < _optionCount; i++) { + // Serial.println("Key: " + String(_options[i]->getKey()) + " Value: " + String(_options[i]->getValue())); + Serial.printf_P(PSTR("Key=%d"), _options[i]->getKey()); + Serial.printf_P(PSTR(" value=%s\n"), _options[i]->getValue()); + } +} + +bool SuplaConfigManager::isDeviceConfigured() { + return strcmp(this->get(KEY_SUPLA_GUID)->getValue(), "") == 0 || strcmp(this->get(KEY_SUPLA_AUTHKEY)->getValue(), "") == 0 || + strcmp(this->get(KEY_WIFI_SSID)->getValue(), "") == 0 || strcmp(this->get(KEY_WIFI_PASS)->getValue(), "") == 0 || + strcmp(this->get(KEY_LOGIN)->getValue(), "") == 0; +} + +ConfigOption *SuplaConfigManager::get(uint8_t key) { + for (int i = 0; i < _optionCount; i++) { + if (_options[i]->getKey() == key) { + return _options[i]; + } + } + return NULL; +} + +bool SuplaConfigManager::set(uint8_t key, const char *value) { + for (int i = 0; i < _optionCount; i++) { + if (key == _options[i]->getKey()) { + _options[i]->setValue(value); + return true; + } + } + return false; +} + +bool SuplaConfigManager::setElement(uint8_t key, int index, int newvalue) { + for (int i = 0; i < _optionCount; i++) { + if (key == _options[i]->getKey()) { + String data = _options[i]->replaceElement(index, newvalue); + _options[i]->setValue(data.c_str()); + return true; + } + } + return false; +} + +bool SuplaConfigManager::setElement(uint8_t key, int index, const char *newvalue) { + for (int i = 0; i < _optionCount; i++) { + if (key == _options[i]->getKey()) { + String data = _options[i]->replaceElement(index, newvalue); + _options[i]->setValue(data.c_str()); + return true; + } + } + return false; +} + +void SuplaConfigManager::setGUIDandAUTHKEY() { + if (strcmp(this->get(KEY_SUPLA_GUID)->getValue(), "") != 0 || strcmp(this->get(KEY_SUPLA_AUTHKEY)->getValue(), "") != 0) { + return; + } + + char mac[6]; + int a; + + char GUID[SUPLA_GUID_SIZE]; + char AUTHKEY[SUPLA_AUTHKEY_SIZE]; + + memset(GUID, 0, SUPLA_GUID_SIZE); + memset(AUTHKEY, 0, SUPLA_AUTHKEY_SIZE); + + os_get_random((unsigned char *)GUID, SUPLA_GUID_SIZE); + os_get_random((unsigned char *)AUTHKEY, SUPLA_AUTHKEY_SIZE); + + if (SUPLA_GUID_SIZE >= 6) { + wifi_get_macaddr(STATION_IF, (unsigned char *)mac); + + for (a = 0; a < 6; a++) GUID[a] = (GUID[a] * mac[a]) % 255; + } + + if (SUPLA_GUID_SIZE >= 12) { + wifi_get_macaddr(SOFTAP_IF, (unsigned char *)mac); + + for (a = 0; a < 6; a++) GUID[a + 6] = (GUID[a + 6] * mac[a]) % 255; + } + + for (a = 0; a < SUPLA_GUID_SIZE; a++) { + GUID[a] = (GUID[a] + system_get_time() + spi_flash_get_id() + system_get_chip_id() + system_get_rtc_time()) % 255; + } + + a = SUPLA_GUID_SIZE > SUPLA_AUTHKEY_SIZE ? SUPLA_AUTHKEY_SIZE : SUPLA_GUID_SIZE; + a--; + for (; a > 0; a--) { + AUTHKEY[a] += GUID[a]; + } + + this->set(KEY_SUPLA_GUID, GUID); + this->set(KEY_SUPLA_AUTHKEY, AUTHKEY); +} diff --git a/SuplaConfigManager.h b/src/SuplaConfigManager.h similarity index 59% rename from SuplaConfigManager.h rename to src/SuplaConfigManager.h index a728d9d5..dcd0f50f 100644 --- a/SuplaConfigManager.h +++ b/src/SuplaConfigManager.h @@ -1,158 +1,190 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef SuplaConfigManager_h -#define SuplaConfigManager_h - -#define DEFAULT_HOSTNAME "GUI Generic" - -#define DEFAULT_LOGIN "admin" -#define DEFAULT_LOGIN_PASS "pass" - -#define DEFAULT_SERVER "svrX.supla.org" -#define DEFAULT_EMAIL "email@address.com" - -#define KEY_SUPLA_GUID "GUID" -#define KEY_SUPLA_AUTHKEY "AUTHKEY" -#define KEY_WIFI_SSID "wifiSSID" -#define KEY_WIFI_PASS "wifiPass" -#define KEY_LOGIN "login" -#define KEY_LOGIN_PASS "loginPass" -#define KEY_HOST_NAME "hostName" -#define KEY_SUPLA_SERVER "suplaServer" -#define KEY_SUPLA_EMAIL "suplaEmail" -#define KEY_DS "ds" -#define KEY_DS_NAME "dsName" -#define KEY_MULTI_MAX_DS18B20 "multiMaxDs" -#define KEY_SUPLA_FUNCTION "function" -#define KEY_MAX_RELAY "maxRelay" -#define KEY_MAX_BUTTON "maxButton" -#define KEY_MAX_LIMIT_SWITCH "maxLimitSwitch" -#define KEY_MAX_DHT11 "maxDht11" -#define KEY_MAX_DHT22 "maxDht22" -#define KEY_MAX_ROLLERSHUTTER "maxRollerShutter" -#define KEY_ALTITUDE_BME280 "altbme280" -#define KEY_ACTIVE_SENSOR "sensor" -#define KEY_BOARD "board" - -#define GPIO "GPIO" -#define SEPARATOR ',' - -enum _settings -{ - NR, - FUNCTION, - LEVEL, - MEMORY, - CFG -}; - -enum _function -{ - FUNCTION_OFF, - FUNCTION_RELAY, - FUNCTION_BUTTON, - FUNCTION_LIMIT_SWITCH, - FUNCTION_CFG_LED, - FUNCTION_CFG_BUTTON, - FUNCTION_DS18B20, - FUNCTION_DHT11, - FUNCTION_DHT22, - FUNCTION_SDA, - FUNCTION_SCL, - FUNCTION_TRIG, - FUNCTION_ECHO, - FUNCTION_SI7021_SONOFF, - FUNCTION_CLK, - FUNCTION_CS, - FUNCTION_D0 -}; - -#define MAX_GUID SUPLA_GUID_SIZE -#define MAX_AUTHKEY SUPLA_GUID_SIZE -#define MAX_SSID 32 -#define MAX_PASSWORD 64 -#define MIN_PASSWORD 8 -#define MAX_MLOGIN 32 -#define MAX_MPASSWORD 64 -#define MAX_HOSTNAME 32 -#define MAX_SUPLA_SERVER SUPLA_SERVER_NAME_MAXSIZE -#define MAX_EMAIL SUPLA_EMAIL_MAXSIZE -#define MAX_DS18B20_ADDRESS_HEX 16 -#define MAX_DS18B20_ADDRESS 8 -#define MAX_DS18B20_NAME 8 -#define MAX_TYPE_BUTTON 4 -#define MAX_MONOSTABLE_TRIGGER 1 -#define MAX_FUNCTION 1 -#define MAX_DS18B20 10 - -enum _e_onfig -{ - E_CONFIG_OK, - E_CONFIG_FS_ACCESS, - E_CONFIG_FILE_NOT_FOUND, - E_CONFIG_FILE_OPEN, - E_CONFIG_PARSE_ERROR, - E_CONFIG_MAX -}; - -#define CONFIG_MAX_OPTIONS 88 - -class ConfigOption { - public: - ConfigOption(const char *key, const char *value, int maxLength); - const char *getKey(); - const char *getValue(); - int getValueInt(); - uint8_t *getValueBin(size_t size); - const char *getValueHex(size_t size); - int getValueElement(int element); - - int getLength(); - void setValue(const char *value); - String getElement(int index); - String replaceElement(int index, int value); - - private: - char *_key; - char *_value; - int _maxLength; -}; - -class SuplaConfigManager { - public: - SuplaConfigManager(); - uint8_t addKey(const char *key, int maxLength); - uint8_t addKey(const char *key, const char *value, int maxLength); - uint8_t addKeyAndRead(const char *key, const char *value, int maxLength); - uint8_t deleteKey(const char *key); - uint8_t load(); - uint8_t loadItem(const char *key); - uint8_t save(); - void showAllValue(); - - ConfigOption *get(const char *key); - bool set(const char *key, const char *value); - bool setElement(const char *key, int index, int newvalue); - - bool isDeviceConfigured(); - void setGUIDandAUTHKEY(); - - private: - int _optionCount; - ConfigOption *_options[CONFIG_MAX_OPTIONS]; -}; -#endif +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SuplaConfigManager_h +#define SuplaConfigManager_h + +#define CONFIG_FILE_PATH "/dat" + +#define DEFAULT_HOSTNAME "GUI Generic" + +#define DEFAULT_LOGIN "admin" +#define DEFAULT_LOGIN_PASS "pass" + +#define DEFAULT_SERVER "svrX.supla.org" +#define DEFAULT_EMAIL "email@address.com" + +#define MAX_GUID SUPLA_GUID_SIZE +#define MAX_AUTHKEY SUPLA_GUID_SIZE +#define MAX_SSID 32 +#define MAX_PASSWORD 64 +#define MIN_PASSWORD 8 +#define MAX_MLOGIN 32 +#define MAX_MPASSWORD 64 +#define MAX_HOSTNAME 32 +#define MAX_SUPLA_SERVER SUPLA_SERVER_NAME_MAXSIZE +#define MAX_EMAIL SUPLA_EMAIL_MAXSIZE +#define MAX_DS18B20_ADDRESS_HEX 16 +#define MAX_DS18B20_ADDRESS 8 +#define MAX_DS18B20_NAME 8 +#define MAX_TYPE_BUTTON 4 +#define MAX_MONOSTABLE_TRIGGER 1 +#define MAX_FUNCTION 1 + +#define MAX_DS18B20 20 +#define MAX_GPIO 17 + +enum _key +{ + KEY_SUPLA_GUID, + KEY_SUPLA_AUTHKEY, + KEY_WIFI_SSID, + KEY_WIFI_PASS, + KEY_LOGIN, + KEY_LOGIN_PASS, + KEY_HOST_NAME, + KEY_SUPLA_SERVER, + KEY_SUPLA_EMAIL, + KEY_MAX_RELAY, + KEY_MAX_BUTTON, + KEY_MAX_LIMIT_SWITCH, + KEY_MAX_DHT22, + KEY_MAX_DHT11, + KEY_MULTI_MAX_DS18B20, + KEY_MAX_ROLLERSHUTTER, + KEY_ALTITUDE_BME280, + KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, + KEY_MAX_IMPULSE_COUNTER, + KEY_ACTIVE_SENSOR, + KEY_BOARD, + KEY_CFG_MODE, + KEY_ADDR_DS18B20, + KEY_NAME_SENSOR, + KEY_GPIO, + KEY_LEVEL_LED = KEY_GPIO + MAX_GPIO + 1, + KEY_OLED_ANIMATION, + KEY_OLED_BACK_LIGHT_TIME + // KEY_DS = KEY_GPIO + MAX_GPIO + MAX_DS18B20, + // KEY_DS_NAME = KEY_DS + MAX_DS18B20 +}; + +//#define GPIO "GPIO" +#define SEPARATOR ',' + +enum _settings +{ + NR, + FUNCTION, + LEVEL, + MEMORY, + CFG, + ACTION, + MCP23017_NR_1, + MCP23017_FUNCTION_1, + MCP23017_NR_2, + MCP23017_FUNCTION_2, + MCP23017_NR_3, + MCP23017_FUNCTION_3, + MCP23017_NR_4, + MCP23017_FUNCTION_4, + SETTINGSCOUNT +}; + +enum _function +{ + FUNCTION_OFF, + FUNCTION_RELAY, + FUNCTION_BUTTON, + FUNCTION_LIMIT_SWITCH, + FUNCTION_CFG_LED, + FUNCTION_CFG_BUTTON, + FUNCTION_DS18B20, + FUNCTION_DHT11, + FUNCTION_DHT22, + FUNCTION_SDA, + FUNCTION_SCL, + FUNCTION_TRIG, + FUNCTION_ECHO, + FUNCTION_SI7021_SONOFF, + FUNCTION_CLK, + FUNCTION_CS, + FUNCTION_D0, + FUNCTION_IMPULSE_COUNTER, + FUNCTION_CF, + FUNCTION_CF1, + FUNCTION_SEL, + FUNCTION_LED +}; + +enum _e_onfig +{ + E_CONFIG_OK, + E_CONFIG_FS_ACCESS, + E_CONFIG_FILE_NOT_FOUND, + E_CONFIG_FILE_OPEN, + E_CONFIG_PARSE_ERROR, + E_CONFIG_MAX +}; + +#define CONFIG_MAX_OPTIONS 88 + +class ConfigOption { + public: + ConfigOption(uint8_t key, const char *value, int maxLength); + uint8_t getKey(); + const char *getValue(); + int getValueInt(); + const char *getValueHex(size_t size); + int getValueElement(int element); + + int getLength(); + void setValue(const char *value); + const String getElement(int index); + uint8_t getElement(int index, size_t size); + const String replaceElement(int index, int value); + const String replaceElement(int index, const char *newvalue); + + private: + uint8_t _key; + char *_value; + int _maxLength; +}; + +class SuplaConfigManager { + public: + SuplaConfigManager(); + uint8_t addKey(uint8_t key, int maxLength); + uint8_t addKey(uint8_t key, const char *value, int maxLength); + uint8_t addKeyAndRead(uint8_t key, const char *value, int maxLength); + uint8_t deleteKey(uint8_t key); + uint8_t load(); + uint8_t loadItem(uint8_t key); + uint8_t save(); + void showAllValue(); + + ConfigOption *get(uint8_t key); + bool set(uint8_t key, const char *value); + bool setElement(uint8_t key, int index, int newvalue); + bool setElement(uint8_t key, int index, const char *newvalue); + + bool isDeviceConfigured(); + void setGUIDandAUTHKEY(); + + private: + int _optionCount; + ConfigOption *_options[CONFIG_MAX_OPTIONS]; +}; +#endif diff --git a/SuplaDeviceGUI.cpp b/src/SuplaDeviceGUI.cpp similarity index 61% rename from SuplaDeviceGUI.cpp rename to src/SuplaDeviceGUI.cpp index 8526a4f6..c73f7fc8 100644 --- a/SuplaDeviceGUI.cpp +++ b/src/SuplaDeviceGUI.cpp @@ -1,163 +1,231 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#include "SuplaDeviceGUI.h" -#include "SuplaConfigManager.h" -#include "SuplaGuiWiFi.h" - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -#define TIME_SAVE_PERIOD_SEK 30 // the time is given in seconds -#define STORAGE_OFFSET 0 -#include -Supla::Eeprom eeprom(STORAGE_OFFSET); -#endif - -namespace Supla { -namespace GUI { -void begin() { -#ifdef DEBUG_MODE - new Supla::Sensor::EspFreeHeap(); -#endif - - Supla::GUIESPWifi *wifi = new Supla::GUIESPWifi(ConfigManager->get(KEY_WIFI_SSID)->getValue(), ConfigManager->get(KEY_WIFI_PASS)->getValue()); - - wifi->enableBuffer(true); - wifi->enableSSL(false); - - String supla_hostname = ConfigManager->get(KEY_HOST_NAME)->getValue(); - supla_hostname.replace(" ", "_"); - wifi->setHostName(supla_hostname.c_str()); - - SuplaDevice.setName((char *)ConfigManager->get(KEY_HOST_NAME)->getValue()); - - SuplaDevice.begin((char *)ConfigManager->get(KEY_SUPLA_GUID)->getValue(), // Global Unique Identifier - (char *)ConfigManager->get(KEY_SUPLA_SERVER)->getValue(), // SUPLA server address - (char *)ConfigManager->get(KEY_SUPLA_EMAIL)->getValue(), // Email address used to login to Supla Cloud - (char *)ConfigManager->get(KEY_SUPLA_AUTHKEY)->getValue()); // Authorization key - - ConfigManager->showAllValue(); - - WebServer->begin(); -} - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -void addRelayButton(int pinRelay, int pinButton, bool highIsOn) { - if (pinRelay != OFF_GPIO) { - relay.push_back(new Supla::Control::Relay(pinRelay, highIsOn)); - - int size = relay.size() - 1; - - switch (ConfigESP->getMemoryRelay(size + 1)) { - case MEMORY_RELAY_OFF: - relay[size]->setDefaultStateOff(); - break; - case MEMORY_RELAY_ON: - relay[size]->setDefaultStateOn(); - break; - case MEMORY_RELAY_RESTORE: - relay[size]->setDefaultStateRestore(); - break; - } - - relay[size]->keepTurnOnDuration(); - eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); - - button.push_back(new Supla::Control::Button(pinButton, true)); - if (pinButton != OFF_GPIO) { - button[size]->addAction(Supla::TOGGLE, *relay[size], ConfigESP->getLevel(size + 1, FUNCTION_BUTTON)); - button[size]->setSwNoiseFilterDelay(50); - } - } -} -#endif - -#ifdef SUPLA_DS18B20 -void addDS18B20MultiThermometer(int pinNumber) { - if (ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt() > 1) { - for (int i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); ++i) { - String ds_key = KEY_DS; - ds_key += i; - sensorDS.push_back(new DS18B20(pinNumber, ConfigManager->get(ds_key.c_str())->getValueBin(MAX_DS18B20_ADDRESS))); - supla_log(LOG_DEBUG, "Index %d - address %s", i, ConfigManager->get(ds_key.c_str())->getValue()); - } - } - else { - sensorDS.push_back(new DS18B20(ConfigESP->getGpio(FUNCTION_DS18B20))); - } -} -#endif - -#ifdef SUPLA_CONFIG -void addConfigESP(int pinNumberConfig, int pinLedConfig, int modeConfigButton, bool highIsOn) { - ConfigESP->addConfigESP(pinNumberConfig, pinLedConfig, modeConfigButton, highIsOn); -} -#endif - -#ifdef SUPLA_ROLLERSHUTTER -void addRolleShutter(int pinRelayUp, int pinRelayDown, int pinButtonUp, int pinButtonDown, bool highIsOn) { - RollerShutterRelay.push_back(new Supla::Control::RollerShutter(pinRelayUp, pinRelayDown, highIsOn)); - if (pinButtonUp != OFF_GPIO) - RollerShutterButtonOpen.push_back(new Supla::Control::Button(pinButtonUp, true, true)); - if (pinButtonDown != OFF_GPIO) - RollerShutterButtonClose.push_back(new Supla::Control::Button(pinButtonDown, true, true)); - int size = RollerShutterRelay.size() - 1; - if (pinButtonUp != OFF_GPIO && pinButtonDown != OFF_GPIO) { - RollerShutterButtonOpen[size]->addAction(Supla::OPEN_OR_STOP, *RollerShutterRelay[size], Supla::ON_PRESS); - RollerShutterButtonClose[size]->addAction(Supla::CLOSE_OR_STOP, *RollerShutterRelay[size], Supla::ON_PRESS); - } - else if ((pinButtonUp == OFF_GPIO && pinButtonDown != OFF_GPIO) || (pinButtonUp != OFF_GPIO && pinButtonDown == OFF_GPIO)) { - RollerShutterButtonOpen[size]->addAction(Supla::STEP_BY_STEP, *RollerShutterRelay[size], Supla::ON_PRESS); - } - eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); -} - -void addRolleShutterMomentary(int pinRelayUp, int pinRelayDown, int pinButtonUp, int pinButtonDown, bool highIsOn) { - RollerShutterRelay.push_back(new Supla::Control::RollerShutter(pinRelayUp, pinRelayDown, highIsOn)); - if (pinButtonUp != OFF_GPIO) - RollerShutterButtonOpen.push_back(new Supla::Control::Button(pinButtonUp, true, true)); - if (pinButtonDown != OFF_GPIO) - RollerShutterButtonClose.push_back(new Supla::Control::Button(pinButtonDown, true, true)); - int size = RollerShutterRelay.size() - 1; - if (pinButtonUp != OFF_GPIO && pinButtonDown != OFF_GPIO) { - RollerShutterButtonOpen[size]->addAction(Supla::OPEN, *RollerShutterRelay[size], Supla::ON_PRESS); - RollerShutterButtonOpen[size]->addAction(Supla::STOP, *RollerShutterRelay[size], Supla::ON_RELEASE); - - RollerShutterButtonClose[size]->addAction(Supla::CLOSE, *RollerShutterRelay[size], Supla::ON_PRESS); - RollerShutterButtonClose[size]->addAction(Supla::STOP, *RollerShutterRelay[size], Supla::ON_RELEASE); - } - eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); -} -#endif - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -std::vector relay; -std::vector button; -#endif - -#ifdef SUPLA_DS18B20 -std::vector sensorDS; -#endif - -#ifdef SUPLA_ROLLERSHUTTER -std::vector RollerShutterRelay; -std::vector RollerShutterButtonOpen; -std::vector RollerShutterButtonClose; -#endif -} // namespace GUI -} // namespace Supla - -SuplaConfigManager *ConfigManager = new SuplaConfigManager(); -SuplaConfigESP *ConfigESP = new SuplaConfigESP(); -SuplaWebServer *WebServer = new SuplaWebServer(); +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SuplaDeviceGUI.h" +#include "SuplaConfigManager.h" +#include "SuplaGuiWiFi.h" + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) +#define TIME_SAVE_PERIOD_SEK 30 // the time is given in seconds +#define TIME_SAVE_PERIOD_IMPULSE_COUNTER_SEK 600 // 10min +#define STORAGE_OFFSET 0 +#include +Supla::Eeprom eeprom(STORAGE_OFFSET); +#endif +namespace Supla { +namespace GUI { +void begin() { +#ifdef DEBUG_MODE + new Supla::Sensor::EspFreeHeap(); +#endif + + Supla::GUIESPWifi *wifi = new Supla::GUIESPWifi(ConfigManager->get(KEY_WIFI_SSID)->getValue(), ConfigManager->get(KEY_WIFI_PASS)->getValue()); + + wifi->enableBuffer(true); + wifi->enableSSL(false); + + String supla_hostname = ConfigManager->get(KEY_HOST_NAME)->getValue(); + supla_hostname.replace(" ", "_"); + wifi->setHostName(supla_hostname.c_str()); + + SuplaDevice.setName((char *)ConfigManager->get(KEY_HOST_NAME)->getValue()); + + SuplaDevice.setSwVersion(BUILD_VERSION); + + SuplaDevice.begin((char *)ConfigManager->get(KEY_SUPLA_GUID)->getValue(), // Global Unique Identifier + (char *)ConfigManager->get(KEY_SUPLA_SERVER)->getValue(), // SUPLA server address + (char *)ConfigManager->get(KEY_SUPLA_EMAIL)->getValue(), // Email address used to login to Supla Cloud + (char *)ConfigManager->get(KEY_SUPLA_AUTHKEY)->getValue()); // Authorization key + + ConfigManager->showAllValue(); + WebServer->begin(); +} + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) +void addRelayButton(uint8_t nr) { + uint8_t pinRelay, pinButton, pinLED; + bool highIsOn, levelLed; + + pinRelay = ConfigESP->getGpio(nr, FUNCTION_RELAY); + pinButton = ConfigESP->getGpio(nr, FUNCTION_BUTTON); + pinLED = ConfigESP->getGpio(nr, FUNCTION_LED); + highIsOn = ConfigESP->getLevel(nr, FUNCTION_RELAY); + levelLed = ConfigManager->get(KEY_LEVEL_LED)->getValueInt(); + + if (pinRelay != OFF_GPIO) { + relay.push_back(new Supla::Control::Relay(pinRelay, highIsOn)); + + int size = relay.size() - 1; + + switch (ConfigESP->getMemory(size + 1, FUNCTION_RELAY)) { + case MEMORY_RELAY_OFF: + relay[size]->setDefaultStateOff(); + break; + case MEMORY_RELAY_ON: + relay[size]->setDefaultStateOn(); + break; + case MEMORY_RELAY_RESTORE: + relay[size]->setDefaultStateRestore(); + break; + } + + relay[size]->keepTurnOnDuration(); + eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); + + if (pinButton != OFF_GPIO) { + button.push_back(new Supla::Control::Button(pinButton, true)); + button[size]->addAction(ConfigESP->getAction(size + 1, FUNCTION_BUTTON), *relay[size], ConfigESP->getLevel(size + 1, FUNCTION_BUTTON)); + button[size]->setSwNoiseFilterDelay(50); + } + + if (pinLED != OFF_GPIO) { + new Supla::Control::PinStatusLed(pinRelay, pinLED, !levelLed); + } + } +} +#endif + +#ifdef SUPLA_DS18B20 +void addDS18B20MultiThermometer(int pinNumber) { + if (ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt() > 1) { + for (int i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); ++i) { + sensorDS.push_back(new DS18B20(pinNumber, HexToBytes(ConfigManager->get(KEY_ADDR_DS18B20)->getElement(i)))); + supla_log(LOG_DEBUG, "Index %d - address %s", i, ConfigManager->get(KEY_ADDR_DS18B20)->getElement(i).c_str()); + } + } + else { + sensorDS.push_back(new DS18B20(ConfigESP->getGpio(FUNCTION_DS18B20))); + } +} +#endif + +#ifdef SUPLA_CONFIG +void addConfigESP(int pinNumberConfig, int pinLedConfig, int modeConfigButton, bool highIsOn) { + ConfigESP->addConfigESP(pinNumberConfig, pinLedConfig, modeConfigButton, highIsOn); +} +#endif + +#ifdef SUPLA_ROLLERSHUTTER +std::vector RollerShutterRelay; +std::vector RollerShutterButtonOpen; +std::vector RollerShutterButtonClose; + +void addRolleShutter(uint8_t nr) { + int pinRelayUp, pinRelayDown, pinButtonUp, pinButtonDown, pinLedUP, pinLedDown; + bool highIsOn, levelLed; + + pinRelayUp = ConfigESP->getGpio(nr, FUNCTION_RELAY); + pinRelayDown = ConfigESP->getGpio(nr + 1, FUNCTION_RELAY); + pinButtonUp = ConfigESP->getGpio(nr, FUNCTION_BUTTON); + pinButtonDown = ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON); + pinLedUP = ConfigESP->getGpio(nr, FUNCTION_LED); + pinLedDown = ConfigESP->getGpio(nr + 1, FUNCTION_LED); + + highIsOn = ConfigESP->getLevel(nr, FUNCTION_RELAY); + levelLed = ConfigManager->get(KEY_LEVEL_LED)->getValueInt(); + + RollerShutterRelay.push_back(new Supla::Control::RollerShutter(pinRelayUp, pinRelayDown, highIsOn)); + if (pinButtonUp != OFF_GPIO) + RollerShutterButtonOpen.push_back(new Supla::Control::Button(pinButtonUp, true, true)); + if (pinButtonDown != OFF_GPIO) + RollerShutterButtonClose.push_back(new Supla::Control::Button(pinButtonDown, true, true)); + int size = RollerShutterRelay.size() - 1; + if (pinButtonUp != OFF_GPIO && pinButtonDown != OFF_GPIO) { + RollerShutterButtonOpen[size]->addAction(Supla::OPEN_OR_STOP, *RollerShutterRelay[size], Supla::ON_PRESS); + RollerShutterButtonClose[size]->addAction(Supla::CLOSE_OR_STOP, *RollerShutterRelay[size], Supla::ON_PRESS); + } + else if ((pinButtonUp == OFF_GPIO && pinButtonDown != OFF_GPIO) || (pinButtonUp != OFF_GPIO && pinButtonDown == OFF_GPIO)) { + RollerShutterButtonOpen[size]->addAction(Supla::STEP_BY_STEP, *RollerShutterRelay[size], Supla::ON_PRESS); + } + eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); + + if (pinLedUP != OFF_GPIO) { + new Supla::Control::PinStatusLed(pinRelayUp, pinLedUP, !levelLed); + } + if (pinLedDown != OFF_GPIO) { + new Supla::Control::PinStatusLed(pinRelayDown, pinLedDown, !levelLed); + } +} + +void addRolleShutterMomentary(uint8_t nr) { + int pinRelayUp, pinRelayDown, pinButtonUp, pinButtonDown, pinLedUP, pinLedDown; + bool highIsOn, levelLed; + + pinRelayUp = ConfigESP->getGpio(nr, FUNCTION_RELAY); + pinRelayDown = ConfigESP->getGpio(nr + 1, FUNCTION_RELAY); + pinButtonUp = ConfigESP->getGpio(nr, FUNCTION_BUTTON); + pinButtonDown = ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON); + pinLedUP = ConfigESP->getGpio(nr, FUNCTION_LED); + pinLedDown = ConfigESP->getGpio(nr + 1, FUNCTION_LED); + + highIsOn = ConfigESP->getLevel(nr, FUNCTION_RELAY); + levelLed = ConfigManager->get(KEY_LEVEL_LED)->getValueInt(); + + RollerShutterRelay.push_back(new Supla::Control::RollerShutter(pinRelayUp, pinRelayDown, highIsOn)); + if (pinButtonUp != OFF_GPIO) + RollerShutterButtonOpen.push_back(new Supla::Control::Button(pinButtonUp, true, true)); + if (pinButtonDown != OFF_GPIO) + RollerShutterButtonClose.push_back(new Supla::Control::Button(pinButtonDown, true, true)); + int size = RollerShutterRelay.size() - 1; + if (pinButtonUp != OFF_GPIO && pinButtonDown != OFF_GPIO) { + RollerShutterButtonOpen[size]->addAction(Supla::OPEN, *RollerShutterRelay[size], Supla::ON_PRESS); + RollerShutterButtonOpen[size]->addAction(Supla::STOP, *RollerShutterRelay[size], Supla::ON_RELEASE); + + RollerShutterButtonClose[size]->addAction(Supla::CLOSE, *RollerShutterRelay[size], Supla::ON_PRESS); + RollerShutterButtonClose[size]->addAction(Supla::STOP, *RollerShutterRelay[size], Supla::ON_RELEASE); + } + eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_SEK * 1000); + + if (pinLedUP != OFF_GPIO) { + new Supla::Control::PinStatusLed(pinRelayUp, pinLedUP, !levelLed); + } + if (pinLedDown != OFF_GPIO) { + new Supla::Control::PinStatusLed(pinRelayDown, pinLedDown, !levelLed); + } +} +#endif + +#ifdef SUPLA_IMPULSE_COUNTER +std::vector impulseCounter; + +void addImpulseCounter(int pin, bool lowToHigh, bool inputPullup, unsigned int debounceDelay) { + impulseCounter.push_back(new Supla::Sensor::ImpulseCounter(pin, lowToHigh, inputPullup, debounceDelay)); + eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_IMPULSE_COUNTER_SEK * 1000); +} +#endif + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) +std::vector relay; +std::vector button; +#endif + +#ifdef SUPLA_DS18B20 +std::vector sensorDS; +#endif + +#ifdef SUPLA_HLW8012 +Supla::Sensor::HJ101 *counterHLW8012; + +void addHLW8012(int8_t pinCF, int8_t pinCF1, int8_t pinSEL) { + counterHLW8012 = new Supla::Sensor::HJ101(pinCF, pinCF1, pinSEL); + eeprom.setStateSavePeriod(TIME_SAVE_PERIOD_IMPULSE_COUNTER_SEK * 1000); +} +#endif +} // namespace GUI +} // namespace Supla + +SuplaConfigManager *ConfigManager = new SuplaConfigManager(); +SuplaConfigESP *ConfigESP = new SuplaConfigESP(); +SuplaWebServer *WebServer = new SuplaWebServer(); diff --git a/SuplaDeviceGUI.h b/src/SuplaDeviceGUI.h similarity index 58% rename from SuplaDeviceGUI.h rename to src/SuplaDeviceGUI.h index 2bc43268..0c33001d 100644 --- a/SuplaDeviceGUI.h +++ b/src/SuplaDeviceGUI.h @@ -1,81 +1,130 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef SuplaDeviceGUI_h -#define SuplaDeviceGUI_h - -#include "GUI-Generic_Config.h" - -#include - -#include -#include -#include -#include "SuplaSensorDS18B20.h" - -#ifdef DEBUG_MODE -#include -#endif - -#include "SuplaConfigESP.h" -#include "SuplaConfigManager.h" -#include "SuplaWebServer.h" -#include "SuplaWebPageRelay.h" - -#include - -enum ConfigMode { CONFIG_MODE_10_ON_PRESSES, CONFIG_MODE_5SEK_HOLD }; - -namespace Supla { -namespace GUI { - -void begin(); - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) -void addRelayButton(int pinRelay, int pinButton, bool highIsOn = true); - -extern std::vector relay; -extern std::vector button; -#endif - -#ifdef SUPLA_DS18B20 -void addDS18B20MultiThermometer(int pinNumber); - -extern std::vector sensorDS; -#endif - -#ifdef SUPLA_CONFIG -void addConfigESP(int pinNumberConfig, int pinLedConfig, int modeConfigButton, bool highIsOn); -#endif - -#ifdef SUPLA_ROLLERSHUTTER -void addRolleShutter( - int pinRelayUp, int pinRelayDown, int pinButtonUp, int pinButtonDown, bool highIsOn = true); -void addRolleShutterMomentary( - int pinRelayUp, int pinRelayDown, int pinButtonUp, int pinButtonDown, bool highIsOn = true); - -extern std::vector RollerShutterRelay; -extern std::vector RollerShutterButtonOpen; -extern std::vector RollerShutterButtonClose; -#endif -}; -}; - -extern SuplaConfigManager *ConfigManager; -extern SuplaConfigESP *ConfigESP; -extern SuplaWebServer *WebServer; - -#endif // SuplaDeviceGUI_h +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SuplaDeviceGUI_h +#define SuplaDeviceGUI_h + +#include "GUI-Generic_Config.h" + +#include +#include + +#include + +#include "SuplaConfigESP.h" +#include "SuplaConfigManager.h" +#include "SuplaWebServer.h" +#include "SuplaWebPageRelay.h" +#include "SuplaWebPageSensor.h" + +#include "SuplaWebPageDownload.h" +#include "SuplaWebPageUpload.h" +#include "SuplaWebPageTools.h" +#include "GUIGenericCommon.h" +#include "SuplaCommonPROGMEM.h" +#include "Markup.h" +#include "SuplaOled.h" + +#include + +#include +#include +#include + +#include "SuplaSensorDS18B20.h" +#include +#include +#include + +#ifdef SUPLA_BME280 +#include +#endif +#ifdef SUPLA_SI7021_SONOFF +#include +#endif +#ifdef SUPLA_BME280 +#include +#endif +#ifdef SUPLA_SHT3x +#include +#endif +#ifdef SUPLA_SI7021 +#include +#endif +#ifdef SUPLA_MAX6675 +#include +#endif +#ifdef SUPLA_IMPULSE_COUNTER +#include +#endif +#ifdef DEBUG_MODE +#include +#endif + +#include +#include + +#include + +namespace Supla { +namespace GUI { + +void begin(); + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) +void addRelayButton(uint8_t nr); + +extern std::vector relay; +extern std::vector button; +#endif + +#ifdef SUPLA_DS18B20 +void addDS18B20MultiThermometer(int pinNumber); + +extern std::vector sensorDS; +#endif + +#ifdef SUPLA_CONFIG +void addConfigESP(int pinNumberConfig, int pinLedConfig, int modeConfigButton, bool highIsOn); +#endif + +#ifdef SUPLA_ROLLERSHUTTER +void addRolleShutter(uint8_t nr); +void addRolleShutterMomentary(uint8_t nr); + +extern std::vector RollerShutterRelay; +extern std::vector RollerShutterButtonOpen; +extern std::vector RollerShutterButtonClose; +#endif + +#ifdef SUPLA_IMPULSE_COUNTER +extern std::vector impulseCounter; +void addImpulseCounter(int pin, bool lowToHigh, bool inputPullup, unsigned int debounceDelay); +#endif + +#ifdef SUPLA_HLW8012 +extern Supla::Sensor::HJ101 *counterHLW8012; +void addHLW8012(int8_t pinCF, int8_t pinCF1, int8_t pinSEL); +#endif + +}; // namespace GUI +}; // namespace Supla + +extern SuplaConfigManager *ConfigManager; +extern SuplaConfigESP *ConfigESP; +extern SuplaWebServer *WebServer; + +#endif // SuplaDeviceGUI_h diff --git a/SuplaGuiWiFi.h b/src/SuplaGuiWiFi.h similarity index 99% rename from SuplaGuiWiFi.h rename to src/SuplaGuiWiFi.h index 7194892c..f8c322e8 100644 --- a/SuplaGuiWiFi.h +++ b/src/SuplaGuiWiFi.h @@ -99,8 +99,8 @@ class GUIESPWifi : public Supla::ESPWifi { } WiFi.reconnect(); } - yield(); } + yield(); } void enableBuffer(bool value) { diff --git a/src/SuplaHTTPUpdateServer.cpp b/src/SuplaHTTPUpdateServer.cpp new file mode 100644 index 00000000..63923abf --- /dev/null +++ b/src/SuplaHTTPUpdateServer.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include "StreamString.h" +#include "SuplaHTTPUpdateServer.h" + +#include "SuplaWebServer.h" +#include "SuplaDeviceGUI.h" +#include "GUIGenericCommon.h" + +static const char serverIndex[] PROGMEM = + R"( + + + + + + +
Flash Size: {f}kB
+
Sketch Max Size: {s}kB
+
+ +
+ +
+ + )"; +static const char successResponse[] PROGMEM = "Update Success! Rebooting..."; +static const char twoStepResponse[] PROGMEM = + "WARNING only use 2-step OTA update. Use GUI-GenericUpdater.bin"; + +ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug) { + _serial_output = serial_debug; + _server = NULL; + _username = emptyString; + _password = emptyString; + _authenticated = false; +} + +void ESP8266HTTPUpdateServer::setup(ESP8266WebServer* server, const String& path, const String& username, const String& password) { + _server = server; + _username = username; + _password = password; + + _server->on(getURL(PATH_UPDATE_HENDLE), std::bind(&ESP8266HTTPUpdateServer::handleFirmwareUp, this)); + // handler for the /update form page + _server->on(path.c_str(), HTTP_GET, [&]() { + if (_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str())) + return _server->requestAuthentication(); + + String index = FPSTR(serverIndex); + index.replace("{f}", String(ESP.getFlashChipRealSize() / 1024)); + index.replace("{s}", String(ESP.getFreeSketchSpace() / 1024)); + _server->send(200, PSTR("text/html"), index); + }); + + // handler for the /update form POST (once file upload finishes) + _server->on( + path.c_str(), HTTP_POST, + [&]() { + if (!_authenticated) + return _server->requestAuthentication(); + if (Update.hasError()) { + _server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError); + } + else { + _server->client().setNoDelay(true); + _server->send_P(200, PSTR("text/html"), successResponse); + delay(100); + _server->client().stop(); + ESP.restart(); + } + }, + [&]() { + // handler for the file upload, get's the sketch bytes, and writes + // them through the Update object + HTTPUpload& upload = _server->upload(); + + if (upload.status == UPLOAD_FILE_START) { + _updaterError = String(); + if (_serial_output) + Serial.setDebugOutput(true); + + _authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str())); + if (!_authenticated) { + if (_serial_output) + Serial.printf("Unauthenticated Update\n"); + return; + } + + WiFiUDP::stopAll(); + if (_serial_output) + Serial.printf("Update: %s\n", upload.filename.c_str()); + if (upload.name == "filesystem") { + size_t fsSize = ((size_t)&_FS_end - (size_t)&_FS_start); + close_all_fs(); + if (!Update.begin(fsSize, U_FS)) { // start with max available size + if (_serial_output) + Update.printError(Serial); + } + } + else { + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace, U_FLASH)) { // start with max available size + _setUpdaterError(); + } + } + } + else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) { + if (_serial_output) + Serial.printf("."); + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + _server->send_P(200, PSTR("text/html"), twoStepResponse); + + _setUpdaterError(); + } + } + else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) { + if (Update.end(true)) { // true to set the size to the current progress + if (_serial_output) + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } + else { + _setUpdaterError(); + } + if (_serial_output) + Serial.setDebugOutput(false); + } + else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) { + Update.end(); + if (_serial_output) + Serial.println(F("Update was aborted")); + } + delay(0); + }); +} + +void ESP8266HTTPUpdateServer::handleFirmwareUp() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + suplaWebPageUpddate(); +} + +void ESP8266HTTPUpdateServer::suplaWebPageUpddate() { + webContentBuffer += SuplaJavaScript(); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_SOFTWARE_UPDATE; + webContentBuffer += F("

"); + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F(""); + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + WebServer->sendContent(); +} + +void ESP8266HTTPUpdateServer::_setUpdaterError() { + if (_serial_output) + Update.printError(Serial); + StreamString str; + Update.printError(str); + Serial.println(str.c_str()); + _updaterError = str.c_str(); +} diff --git a/src/SuplaHTTPUpdateServer.h b/src/SuplaHTTPUpdateServer.h new file mode 100644 index 00000000..0466f93e --- /dev/null +++ b/src/SuplaHTTPUpdateServer.h @@ -0,0 +1,52 @@ +#ifndef __HTTP_UPDATE_SERVER_H +#define __HTTP_UPDATE_SERVER_H + +#include + +#define PATH_UPDATE_HENDLE "update" +#define PATH_UPDATE "/updateOTA" + +class ESP8266HTTPUpdateServer +{ + public: + ESP8266HTTPUpdateServer(bool serial_debug=false); + + void setup(ESP8266WebServer *server) + { + setup(server, emptyString, emptyString); + } + + void setup(ESP8266WebServer *server, const String& path) + { + setup(server, path, emptyString, emptyString); + } + + void setup(ESP8266WebServer *server, const String& username, const String& password) + { + setup(server, PATH_UPDATE, username, password); + } + + void setup(ESP8266WebServer *server, const String& path, const String& username, const String& password); + + void updateCredentials(const String& username, const String& password) + { + _username = username; + _password = password; + } + + protected: + void _setUpdaterError(); + + private: + bool _serial_output; + ESP8266WebServer *_server; + String _username; + String _password; + bool _authenticated; + String _updaterError; + + void handleFirmwareUp(); + void suplaWebPageUpddate(); +}; + +#endif diff --git a/src/SuplaOled.cpp b/src/SuplaOled.cpp new file mode 100644 index 00000000..c3f68126 --- /dev/null +++ b/src/SuplaOled.cpp @@ -0,0 +1,419 @@ +#include "SuplaOled.h" +#include "SuplaDeviceGUI.h" + +#ifdef SUPLA_OLED + +uint8_t* chanelSensor; + +String getTempString(double temperature) { + if (temperature == -275) { + return F("error"); + } + else { + return String(temperature, 1); + } +} + +String getHumidityString(double humidity) { + if (humidity == -1) { + return F("error"); + } + else { + return String(humidity, 1); + } +} + +String getPressureString(double pressure) { + if (pressure == -1) { + return F("error"); + } + else { + return String(pressure, 0); + } +} + +int getQuality() { + if (WiFi.status() != WL_CONNECTED) + return -1; + int dBm = WiFi.RSSI(); + if (dBm <= -100) + return 0; + if (dBm >= -50) + return 100; + return 2 * (dBm + 100); +} + +void displayUiSignal(OLEDDisplay* display) { + int x = display->getWidth() - 17; + int y = 0; + int value = getQuality(); + + display->setColor(BLACK); + display->fillRect(x, y, x + 46, 16); + display->setColor(WHITE); + if (value == -1) { + display->setFont(ArialMT_Plain_10); + display->drawString(x + 1, y, "x"); + } + else { + if (value > 0) + display->fillRect(x, y + 6, 3, 4); + + if (value >= 25) + display->fillRect(x + 4, y + 4, 3, 6); + + if (value >= 50) + display->fillRect(x + 8, y + 2, 3, 8); + + if (value >= 75) + display->fillRect(x + 12, y, 3, 10); + } +} + +void displayUiRelayState(OLEDDisplay* display) { + int y = 0; + int x = 0; + + display->setFont(ArialMT_Plain_10); + display->setTextAlignment(TEXT_ALIGN_LEFT); + for (int i = 0; i < Supla::GUI::relay.size(); i++) { + if (Supla::GUI::relay[i]->isOn()) { + display->setColor(WHITE); + display->fillRect(x, y + 1, 10, 10); + display->setColor(BLACK); + display->drawString(x + 2, y, String(i + 1)); + } + else { + display->setColor(WHITE); + display->drawString(x + 2, y, String(i + 1)); + } + x += 15; + } + display->setColor(WHITE); + display->drawHorizontalLine(0, 14, display->getWidth()); +} + +void msOverlay(OLEDDisplay* display, OLEDDisplayUiState* state) { + displayUiSignal(display); + if (Supla::GUI::relay.size()) { + displayUiRelayState(display); + } +} + +void displayUiSuplaStatus(OLEDDisplay* display) { + int x = 0; + int y = display->getHeight() / 3; + display->clear(); + + displayUiSignal(display); + + display->setFont(ArialMT_Plain_10); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setColor(WHITE); + display->drawStringMaxWidth(x, y, display->getWidth(), ConfigESP->supla_status.msg); + display->display(); +} + +void displayConfigMode(OLEDDisplay* display) { + display->clear(); + display->setFont(ArialMT_Plain_10); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setColor(WHITE); + display->drawString(0, 15, F("Tryb konfiguracyjny")); + display->drawString(0, 28, F("AP name:")); + display->drawString(0, 41, ConfigESP->getConfigNameAP()); + display->drawString(0, 54, F("IP: 192.168.4.1")); + display->display(); +} + +void displayUiBlank(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // display->drawXbm(10, 17, supla_logo_width, supla_logo_height, supla_logo_bits); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_16); + display->drawString(10, display->getHeight() / 2, F("SUPLA")); +} + +void displayUiTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double temp, const String& name) { + uint8_t temp_width, temp_height; + + int drawHeightIcon = display->getHeight() / 2 - 10; + int drawStringIcon = display->getHeight() / 2 - 6; + + display->setColor(WHITE); + display->setTextAlignment(TEXT_ALIGN_LEFT); + + if (display->getWidth() <= 64 || display->getHeight() <= 48) { + temp_width = 0; + temp_height = 0; + } + else { + display->drawXbm(x + 0, y + drawHeightIcon, TEMP_WIDTH, TEMP_HEIGHT, temp_bits); + + temp_width = TEMP_WIDTH + 10; + temp_height = TEMP_HEIGHT; + } + + if (name != NULL) { + display->setFont(ArialMT_Plain_10); + display->drawString(x + TEMP_WIDTH + 20, y + display->getHeight() / 2 - 15, name); + } + + display->setFont(ArialMT_Plain_24); + display->drawString(x + temp_width, y + drawStringIcon, getTempString(temp)); + display->setFont(ArialMT_Plain_16); + display->drawString(x + temp_width + (getTempString(temp).length() * 12), y + drawStringIcon, "ºC"); +} + +void displaUiHumidity(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double humidity, const String& name) { + uint8_t humidity_width, humidity_height; + + int drawHeightIcon = display->getHeight() / 2 - 10; + int drawStringIcon = display->getHeight() / 2 - 6; + + display->setColor(WHITE); + display->setTextAlignment(TEXT_ALIGN_LEFT); + + if (display->getWidth() <= 64 || display->getHeight() <= 48) { + humidity_width = 0; + humidity_height = 0; + } + else { + display->drawXbm(x + 0, y + drawHeightIcon, HUMIDITY_WIDTH, HUMIDITY_HEIGHT, humidity_bits); + humidity_width = HUMIDITY_WIDTH + 20; + humidity_height = HUMIDITY_HEIGHT; + } + + if (name != NULL) { + display->setFont(ArialMT_Plain_10); + display->drawString(x + TEMP_WIDTH + 20, y + display->getHeight() / 2 - 15, name); + } + + display->setFont(ArialMT_Plain_24); + display->drawString(x + humidity_width, y + drawStringIcon, getHumidityString(humidity)); + display->setFont(ArialMT_Plain_16); + display->drawString(x + humidity_width + (getHumidityString(humidity).length() * 12), y + drawStringIcon, "%"); +} + +void displayUiPressure(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double pressure, const String& name) { + uint8_t pressure_width, pressure_height; + + int drawHeightIcon = display->getHeight() / 2 - 10; + int drawStringIcon = display->getHeight() / 2 - 6; + + display->setColor(WHITE); + display->setTextAlignment(TEXT_ALIGN_LEFT); + + if (display->getWidth() <= 64 || display->getHeight() <= 48) { + pressure_width = 0; + pressure_height = 0; + } + else { + display->drawXbm(x + 0, y + drawHeightIcon, PRESSURE_WIDTH, PRESSURE_HEIGHT, pressure_bits); + pressure_width = PRESSURE_WIDTH + 10; + pressure_height = PRESSURE_HEIGHT; + } + + if (name != NULL) { + display->setFont(ArialMT_Plain_10); + display->drawString(x + TEMP_WIDTH + 20, y + display->getHeight() / 2 - 15, name); + } + + display->setFont(ArialMT_Plain_24); + display->drawString(x + pressure_width, y + drawStringIcon, getPressureString(pressure)); + display->setFont(ArialMT_Plain_16); + display->drawString(x + pressure_width + (getPressureString(pressure).length() * 14), y + drawStringIcon, "hPa"); +} + +void displayTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + if (channel->getChannelNumber() == chanelSensor[state->currentFrame]) { + double lastTemperature = channel->getValueDouble(); + String name = ConfigManager->get(KEY_NAME_SENSOR)->getElement(state->currentFrame); + displayUiTemperature(display, state, x, y, lastTemperature, name); + } + } + } +} + +void displayDoubleTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + if (channel->getChannelNumber() == chanelSensor[state->currentFrame]) { + double lastTemperature = channel->getValueDoubleFirst(); + String name = ConfigManager->get(KEY_NAME_SENSOR)->getElement(state->currentFrame); + displayUiTemperature(display, state, x, y, lastTemperature, name); + } + } + } +} + +void displayDoubleHumidity(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + if (channel->getChannelNumber() == chanelSensor[state->currentFrame]) { + double lastHumidit = channel->getValueDoubleSecond(); + String name = ConfigManager->get(KEY_NAME_SENSOR)->getElement(state->currentFrame); + displaUiHumidity(display, state, x, y, lastHumidit, name); + } + } + } +} + +void displayPressure(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getSecondaryChannel()) { + auto channel = element->getSecondaryChannel(); + if (channel->getChannelNumber() == chanelSensor[state->currentFrame]) { + double lastPressure = channel->getValueDouble(); + String name = ConfigManager->get(KEY_NAME_SENSOR)->getElement(state->currentFrame); + displayUiPressure(display, state, x, y, lastPressure, name); + } + } + } +} + +SuplaOled::SuplaOled() { + if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { + switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_OLED).toInt()) { + case OLED_SSD1306_0_96: + display = new SSD1306Wire(0x3c, ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL)); + break; + case OLED_SH1106_1_3: + display = new SH1106Wire(0x3c, ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL)); + break; + case OLED_SSD1306_0_66: + display = new SSD1306Wire(0x3c, ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL), GEOMETRY_64_48); + break; + } + + ui = new OLEDDisplayUi(display); + + overlays[0] = {msOverlay}; + int maxFrame = getCountSensorChannels(); + + if (maxFrame == 0) + maxFrame = 1; + + frames = new FrameCallback[maxFrame]; + chanelSensor = new uint8_t[maxFrame]; + + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_THERMOMETER) { + frames[frameCount] = {displayTemperature}; + chanelSensor[frameCount] = channel->getChannelNumber(); + frameCount += 1; + } + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR) { + frames[frameCount] = {displayDoubleTemperature}; + chanelSensor[frameCount] = channel->getChannelNumber(); + frameCount += 1; + frames[frameCount] = {displayDoubleHumidity}; + chanelSensor[frameCount] = channel->getChannelNumber(); + frameCount += 1; + } + } + if (element->getSecondaryChannel()) { + auto channel = element->getSecondaryChannel(); + if (channel->getChannelType() == SUPLA_CHANNELTYPE_PRESSURESENSOR) { + frames[frameCount] = {displayPressure}; + chanelSensor[frameCount] = channel->getChannelNumber(); + frameCount += 1; + } + } + } + + if (frameCount == 0) { + frames[frameCount] = {displayUiBlank}; + frameCount += 1; + } + + if (frameCount == 1) { + ui->disableAllIndicators(); + ui->disableAutoTransition(); + } + else { + switch (ConfigManager->get(KEY_OLED_ANIMATION)->getValueInt()) { + case OLED_CONTROLL_NORMAL: + ui->setTimePerFrame(5000); + break; + case OLED_CONTROLL_SLOW: + ui->setTimePerFrame(10000); + break; + case OLED_CONTROLL_MANUAL: + ui->disableAutoTransition(); + ui->setTimePerTransition(250); + break; + } + /*ui->setTargetFPS(30); + ui->setIndicatorPosition(BOTTOM); + ui->setIndicatorDirection(LEFT_RIGHT); + ui->setFrameAnimation(SLIDE_LEFT);*/ + } + + ui->setFrames(frames, frameCount); + ui->setOverlays(overlays, overlaysCount); + ui->init(); + display->flipScreenVertically(); + } +} + +void SuplaOled::iterateAlways() { + if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { + if (ConfigESP->supla_status.status != STATUS_REGISTERED_AND_READY) { + displayUiSuplaStatus(display); + return; + } + + if (millis() - timeLastChangeOled > (ConfigManager->get(KEY_OLED_BACK_LIGHT_TIME)->getValueInt() * 1000) && oledON && + ConfigManager->get(KEY_OLED_BACK_LIGHT_TIME)->getValueInt() != 0) { + display->setBrightness(50); + oledON = false; + // display.displayOff(); + } + + if (ConfigESP->configModeESP == NORMAL_MODE) { + int remainingTimeBudget = ui->update(); + + if (remainingTimeBudget > 0) + delay(remainingTimeBudget); + } + else { + displayConfigMode(display); + } + } +} + +void SuplaOled::addButtonOled(int pin) { + if (pin != OFF_GPIO) { + Supla::Control::Button* button = new Supla::Control::Button(pin, true, true); + + if (ConfigManager->get(KEY_OLED_BACK_LIGHT_TIME)->getValueInt() != 0) { + button->addAction(TURN_ON_OLED, this, Supla::ON_PRESS); + } + + if (ConfigManager->get(KEY_OLED_ANIMATION)->getValueInt() == OLED_CONTROLL_MANUAL) { + button->addAction(NEXT_FRAME, this, Supla::ON_PRESS); + } + } +} + +void SuplaOled::handleAction(int event, int action) { + if (action == NEXT_FRAME) { + ui->nextFrame(); + } + + if (action == TURN_ON_OLED) { + display->setBrightness(255); + timeLastChangeOled = millis(); + oledON = true; + } +} +#endif \ No newline at end of file diff --git a/src/SuplaOled.h b/src/SuplaOled.h new file mode 100644 index 00000000..da33d3e6 --- /dev/null +++ b/src/SuplaOled.h @@ -0,0 +1,109 @@ +#ifndef SuplaOled_H +#define SuplaOled_H + +#include "GUI-Generic_Config.h" + +#ifdef SUPLA_OLED +#include +#include +#include +#include // Only needed for Arduino 1.6.5 and earlier +#include //OLED 0,96" or 0.66" WEMOS OLED shield +#include //OLED 1.3" +#include + +enum customActions +{ + TURN_ON_OLED, + NEXT_FRAME +}; + +enum _OLED +{ + OLED_SSD1306_0_96 = 1, + OLED_SH1106_1_3, + OLED_SSD1306_0_66 +}; + +enum +{ + OLED_CONTROLL_NORMAL, + OLED_CONTROLL_SLOW, + OLED_CONTROLL_MANUAL +}; + +String getTempString(double temperature); +String getHumidityString(double humidity); +String getPressureString(double pressure); +int32_t getQuality(); + +void msOverlay(OLEDDisplay* display, OLEDDisplayUiState* state); + +void displayUiSignal(OLEDDisplay* display); +void displayUiRelayState(OLEDDisplay* display); +void displayUiSuplaStatus(OLEDDisplay* display); +void displayUiConfigMode(OLEDDisplay* display); +void displayUiBlank(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void displayUiTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double temp, const String& name = "\n"); +void displaUiHumidity(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double humidity, const String& name = "\n"); +void displayUiPressure(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y, double pressure, const String& name = "\n"); + +void displayTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void displayDoubleTemperature(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void displayDoubleHumidity(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void displayPressure(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y); + +class SuplaOled : public Supla::ActionHandler, public Supla::Element { + public: + SuplaOled(); + void addButtonOled(int pin); + + private: + void iterateAlways(); + void handleAction(int event, int action); + + OLEDDisplay* display; + OLEDDisplayUi* ui; + + FrameCallback* frames; + int frameCount = 0; + OverlayCallback overlays[1]; + int overlaysCount = 1; + + unsigned long timeLastChangeOled = millis(); + bool oledON = true; +}; + +// https://www.online-utility.org/image/convert/to/XBM +#define TEMP_WIDTH 32 +#define TEMP_HEIGHT 32 +const uint8_t temp_bits[] PROGMEM = {0x00, 0x3C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x81, 0xF8, 0x03, 0x00, 0x99, 0x00, + 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0xF8, 0x03, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, + 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0xF8, 0x03, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, + 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x80, 0x99, 0x01, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x3C, 0x06, + 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x60, 0x3C, 0x06, 0x00, 0xC0, 0x18, + 0x03, 0x00, 0x80, 0x81, 0x01, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00}; + +#define HUMIDITY_WIDTH 32 +#define HUMIDITY_HEIGHT 32 +const uint8_t humidity_bits[] PROGMEM = { + 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x38, 0x1C, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x01, 0x80, 0x00, + 0x80, 0x01, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x30, 0x08, 0x06, 0x30, 0x48, 0x0C, 0x0C, 0x30, 0x48, + 0x06, 0x0C, 0x30, 0x30, 0x03, 0x0C, 0x30, 0x80, 0x01, 0x0C, 0x30, 0xC0, 0x00, 0x0C, 0x30, 0x60, 0x06, 0x0C, 0x30, 0x30, 0x09, 0x0C, + 0x30, 0x10, 0x09, 0x0C, 0x30, 0x00, 0x06, 0x0C, 0x20, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x01, + 0x80, 0x01, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x0E, 0x70, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF0, 0x0F, 0x00}; + +#define PRESSURE_WIDTH 32 +#define PRESSURE_HEIGHT 32 +const uint8_t pressure_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x0F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x80, 0x81, 0x81, 0x01, 0xC0, 0x80, 0x01, 0x03, + 0x60, 0x80, 0x01, 0x06, 0x30, 0x00, 0xC0, 0x0C, 0x30, 0x03, 0xE0, 0x18, 0x18, 0x07, 0x40, 0x10, 0x0C, 0x82, 0x01, 0x30, 0x0C, 0x80, + 0x01, 0x30, 0x0C, 0x80, 0x01, 0x30, 0x0C, 0x80, 0x01, 0x30, 0x0C, 0x80, 0x01, 0x30, 0x0C, 0x81, 0x81, 0x30, 0x8C, 0x83, 0xC1, 0x31, + 0x8C, 0x81, 0x81, 0x31, 0x08, 0xC0, 0x03, 0x10, 0x08, 0xE0, 0x07, 0x10, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#endif +#endif // SuplaOled_H \ No newline at end of file diff --git a/SuplaSensorDS18B20.cpp b/src/SuplaSensorDS18B20.cpp similarity index 96% rename from SuplaSensorDS18B20.cpp rename to src/SuplaSensorDS18B20.cpp index 874f6a7b..a5f0a3a1 100644 --- a/SuplaSensorDS18B20.cpp +++ b/src/SuplaSensorDS18B20.cpp @@ -1,169 +1,169 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "SuplaSensorDS18B20.h" - -OneWireBus::OneWireBus(uint8_t pinNumberConfig) - : oneWire(pinNumberConfig), pin(pinNumberConfig), nextBus(nullptr), lastReadTime(0) { - supla_log(LOG_DEBUG, "Initializing OneWire bus at pin %d", pinNumberConfig); - sensors.setOneWire(&oneWire); - sensors.begin(); - if (sensors.isParasitePowerMode()) { - supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is ON", pinNumberConfig); - } else { - supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is OFF", pinNumberConfig); - } - - supla_log(LOG_DEBUG, "OneWire(pin %d) Found %d devices:", pinNumberConfig, - sensors.getDeviceCount()); - - // report parasite power requirements - - DeviceAddress address; - char strAddr[64]; - for (int i = 0; i < sensors.getDeviceCount(); i++) { - if (!sensors.getAddress(address, i)) { - supla_log(LOG_DEBUG, "Unable to find address for Device %d", i); - } else { - /*sprintf( - strAddr, - "{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}", - address[0], - address[1], - address[2], - address[3], - address[4], - address[5], - address[6], - address[7]); - supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr);*/ - sensors.setResolution(address, 12); - } - delay(0); - } - sensors.setWaitForConversion(true); - sensors.requestTemperatures(); - sensors.setWaitForConversion(false); -} - -int8_t OneWireBus::getIndex(uint8_t *deviceAddress) { - DeviceAddress address; - for (int i = 0; i < sensors.getDeviceCount(); i++) { - if (sensors.getAddress(address, i)) { - bool found = true; - for (int j = 0; j < 8; j++) { - if (deviceAddress[j] != address[j]) { - found = false; - } - } - if (found) { - return i; - } - } - } - return -1; -} - -DS18B20::DS18B20(uint8_t pin, uint8_t *deviceAddress) { - OneWireBus *bus = oneWireBus; - OneWireBus *prevBus = nullptr; - address[0] = 0; - lastValidValue = TEMPERATURE_NOT_AVAILABLE; - retryCounter = 0; - - if (bus) { - while (bus) { - if (bus->pin == pin) { - myBus = bus; - break; - } - prevBus = bus; - bus = bus->nextBus; - } - } - - // There is no OneWire bus created yet for this pin - if (!bus) { - supla_log(LOG_DEBUG, "Creating OneWire bus for pin: %d", pin); - myBus = new OneWireBus(pin); - if (prevBus) { - prevBus->nextBus = myBus; - } else { - oneWireBus = myBus; - } - } - if (deviceAddress == nullptr) { - supla_log(LOG_DEBUG, "Device address not provided. Using device from index 0"); - } else { - memcpy(address, deviceAddress, 8); - } -} - -void DS18B20::iterateAlways() { - if (myBus->lastReadTime + 10000 < millis()) { - myBus->sensors.requestTemperatures(); - myBus->lastReadTime = millis(); - } - if (myBus->lastReadTime + 5000 < millis() && (lastReadTime != myBus->lastReadTime)) { - channel.setNewValue(getValue()); - lastReadTime = myBus->lastReadTime; - } -} - -double DS18B20::getValue() { - double value = TEMPERATURE_NOT_AVAILABLE; - if (address[0] == 0) { - value = myBus->sensors.getTempCByIndex(0); - } else { - value = myBus->sensors.getTempC(address); - } - - if (value == DEVICE_DISCONNECTED_C || value == 85.0) { - value = TEMPERATURE_NOT_AVAILABLE; - } - - if (value == TEMPERATURE_NOT_AVAILABLE) { - retryCounter++; - if (retryCounter > 3) { - retryCounter = 0; - } else { - value = lastValidValue; - } - } else { - retryCounter = 0; - } - lastValidValue = value; - - return value; -} - -void DS18B20::onInit() { - channel.setNewValue(getValue()); -} - -uint8_t DS18B20::getPin() { - return myBus->pin; -} - -void DS18B20::setDeviceAddress(uint8_t *deviceAddress) { - if (deviceAddress == nullptr) { - supla_log(LOG_DEBUG, "Device address not provided. Using device from index 0"); - } else { - memcpy(address, deviceAddress, 8); - } -} - -OneWireBus *DS18B20::oneWireBus = nullptr; +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "SuplaSensorDS18B20.h" + +OneWireBus::OneWireBus(uint8_t pinNumberConfig) + : oneWire(pinNumberConfig), pin(pinNumberConfig), nextBus(nullptr), lastReadTime(0) { + supla_log(LOG_DEBUG, "Initializing OneWire bus at pin %d", pinNumberConfig); + sensors.setOneWire(&oneWire); + sensors.begin(); + if (sensors.isParasitePowerMode()) { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is ON", pinNumberConfig); + } else { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is OFF", pinNumberConfig); + } + + supla_log(LOG_DEBUG, "OneWire(pin %d) Found %d devices:", pinNumberConfig, + sensors.getDeviceCount()); + + // report parasite power requirements + + DeviceAddress address; + char strAddr[64]; + for (int i = 0; i < sensors.getDeviceCount(); i++) { + if (!sensors.getAddress(address, i)) { + supla_log(LOG_DEBUG, "Unable to find address for Device %d", i); + } else { + /*sprintf( + strAddr, + "{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}", + address[0], + address[1], + address[2], + address[3], + address[4], + address[5], + address[6], + address[7]); + supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr);*/ + sensors.setResolution(address, 12); + } + delay(0); + } + sensors.setWaitForConversion(true); + sensors.requestTemperatures(); + sensors.setWaitForConversion(false); +} + +int8_t OneWireBus::getIndex(uint8_t *deviceAddress) { + DeviceAddress address; + for (int i = 0; i < sensors.getDeviceCount(); i++) { + if (sensors.getAddress(address, i)) { + bool found = true; + for (int j = 0; j < 8; j++) { + if (deviceAddress[j] != address[j]) { + found = false; + } + } + if (found) { + return i; + } + } + } + return -1; +} + +DS18B20::DS18B20(uint8_t pin, uint8_t *deviceAddress) { + OneWireBus *bus = oneWireBus; + OneWireBus *prevBus = nullptr; + address[0] = 0; + lastValidValue = TEMPERATURE_NOT_AVAILABLE; + retryCounter = 0; + + if (bus) { + while (bus) { + if (bus->pin == pin) { + myBus = bus; + break; + } + prevBus = bus; + bus = bus->nextBus; + } + } + + // There is no OneWire bus created yet for this pin + if (!bus) { + supla_log(LOG_DEBUG, "Creating OneWire bus for pin: %d", pin); + myBus = new OneWireBus(pin); + if (prevBus) { + prevBus->nextBus = myBus; + } else { + oneWireBus = myBus; + } + } + if (deviceAddress == nullptr) { + supla_log(LOG_DEBUG, "Device address not provided. Using device from index 0"); + } else { + memcpy(address, deviceAddress, 8); + } +} + +void DS18B20::iterateAlways() { + if (myBus->lastReadTime + 10000 < millis()) { + myBus->sensors.requestTemperatures(); + myBus->lastReadTime = millis(); + } + if (myBus->lastReadTime + 5000 < millis() && (lastReadTime != myBus->lastReadTime)) { + channel.setNewValue(getValue()); + lastReadTime = myBus->lastReadTime; + } +} + +double DS18B20::getValue() { + double value = TEMPERATURE_NOT_AVAILABLE; + if (address[0] == 0) { + value = myBus->sensors.getTempCByIndex(0); + } else { + value = myBus->sensors.getTempC(address); + } + + if (value == DEVICE_DISCONNECTED_C || value == 85.0) { + value = TEMPERATURE_NOT_AVAILABLE; + } + + if (value == TEMPERATURE_NOT_AVAILABLE) { + retryCounter++; + if (retryCounter > 3) { + retryCounter = 0; + } else { + value = lastValidValue; + } + } else { + retryCounter = 0; + } + lastValidValue = value; + + return value; +} + +void DS18B20::onInit() { + channel.setNewValue(getValue()); +} + +uint8_t DS18B20::getPin() { + return myBus->pin; +} + +void DS18B20::setDeviceAddress(uint8_t *deviceAddress) { + if (deviceAddress == nullptr) { + supla_log(LOG_DEBUG, "Device address not provided. Using device from index 0"); + } else { + memcpy(address, deviceAddress, 8); + } +} + +OneWireBus *DS18B20::oneWireBus = nullptr; diff --git a/SuplaSensorDS18B20.h b/src/SuplaSensorDS18B20.h similarity index 96% rename from SuplaSensorDS18B20.h rename to src/SuplaSensorDS18B20.h index 5bbf3696..b80b2e48 100644 --- a/SuplaSensorDS18B20.h +++ b/src/SuplaSensorDS18B20.h @@ -1,59 +1,59 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef SuplaSensorDS18B20_h -#define SuplaSensorDS18B20_h - -#include -#include -#include - -#include -#include - - -class OneWireBus { - public: - OneWireBus(uint8_t pinNumberConfig); - int8_t getIndex(uint8_t *deviceAddress); - - uint8_t pin; - OneWireBus *nextBus; - unsigned long lastReadTime; - DallasTemperature sensors; - - protected: - OneWire oneWire; -}; - -class DS18B20 : public Supla::Sensor::Thermometer { - public: - DS18B20(uint8_t pin, uint8_t *deviceAddress = nullptr); - void iterateAlways(); - double getValue(); - void onInit(); - uint8_t getPin(); - void setDeviceAddress(uint8_t *deviceAddress = nullptr); - - protected: - static OneWireBus *oneWireBus; - OneWireBus *myBus; - DeviceAddress address; - int8_t retryCounter; - double lastValidValue; -}; - -#endif // SuplaSensorDS18B20_h +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SuplaSensorDS18B20_h +#define SuplaSensorDS18B20_h + +#include +#include +#include + +#include +#include + + +class OneWireBus { + public: + OneWireBus(uint8_t pinNumberConfig); + int8_t getIndex(uint8_t *deviceAddress); + + uint8_t pin; + OneWireBus *nextBus; + unsigned long lastReadTime; + DallasTemperature sensors; + + protected: + OneWire oneWire; +}; + +class DS18B20 : public Supla::Sensor::Thermometer { + public: + DS18B20(uint8_t pin, uint8_t *deviceAddress = nullptr); + void iterateAlways(); + double getValue(); + void onInit(); + uint8_t getPin(); + void setDeviceAddress(uint8_t *deviceAddress = nullptr); + + protected: + static OneWireBus *oneWireBus; + OneWireBus *myBus; + DeviceAddress address; + int8_t retryCounter; + double lastValidValue; +}; + +#endif // SuplaSensorDS18B20_h diff --git a/src/SuplaTemplateBoard.cpp b/src/SuplaTemplateBoard.cpp new file mode 100644 index 00000000..88aa8ace --- /dev/null +++ b/src/SuplaTemplateBoard.cpp @@ -0,0 +1,212 @@ +#include "SuplaTemplateBoard.h" +#include "SuplaWebPageRelay.h" +#include + +void addButton(uint8_t gpio, uint8_t event) { + uint8_t nr = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); + String test; + nr = nr + 1; + test = nr; + ConfigManager->set(KEY_MAX_BUTTON, test.c_str()); + ConfigESP->setGpio(gpio, nr, FUNCTION_BUTTON, event); +} + +void addRelay(uint8_t gpio, uint8_t level) { + uint8_t nr = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); + String test; + nr = nr + 1; + test = nr; + ConfigManager->set(KEY_MAX_RELAY, test.c_str()); + ConfigESP->setGpio(gpio, nr, FUNCTION_RELAY, level, MEMORY_RELAY_RESTORE); +} + +void addLimitSwitch(uint8_t gpio) { + uint8_t nr = ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); + String test; + nr = nr + 1; + test = nr; + ConfigManager->set(KEY_MAX_LIMIT_SWITCH, test.c_str()); + ConfigESP->setGpio(gpio, nr, FUNCTION_LIMIT_SWITCH, 0); + ConfigESP->setGpio(4, 1, FUNCTION_LIMIT_SWITCH, 0); +} + +void addLedCFG(uint8_t gpio, uint8_t level) { + ConfigESP->setGpio(gpio, FUNCTION_CFG_LED, level); +} + +void addLed(uint8_t gpio) { + ConfigESP->setGpio(gpio, FUNCTION_LED); +} + +void addButtonCFG(uint8_t gpio) { + ConfigESP->setGpio(gpio, FUNCTION_CFG_BUTTON); +} + +#ifdef SUPLA_HLW8012 +void addHLW8012(int8_t pinCF, int8_t pinCF1, int8_t pinSEL) { + ConfigESP->setGpio(pinCF, FUNCTION_CF); + ConfigESP->setGpio(pinCF1, FUNCTION_CF1); + ConfigESP->setGpio(pinSEL, FUNCTION_SEL); + Supla::GUI::addHLW8012(ConfigESP->getGpio(FUNCTION_CF), ConfigESP->getGpio(FUNCTION_CF1), ConfigESP->getGpio(FUNCTION_SEL)); +} +#endif + +void chooseTemplateBoard(uint8_t board) { + ConfigManager->set(KEY_MAX_BUTTON, "0"); + ConfigManager->set(KEY_MAX_RELAY, "0"); + ConfigManager->set(KEY_MAX_LIMIT_SWITCH, "0"); + + switch (board) { + case BOARD_ELECTRODRAGON: + addLedCFG(16); + addButtonCFG(0); + addButton(0); + addButton(2); + addRelay(12); + addRelay(13); + break; + case BOARD_INCAN3: + addLedCFG(2, LOW); + addButtonCFG(0); + addButton(14, Supla::ON_CHANGE); + addButton(12, Supla::ON_CHANGE); + addRelay(5); + addRelay(13); + addLimitSwitch(4); + addLimitSwitch(16); + break; + case BOARD_INCAN4: + addLedCFG(12); + addButtonCFG(0); + addButton(2, Supla::ON_CHANGE); + addButton(10, Supla::ON_CHANGE); + addRelay(4); + addRelay(14); + addLimitSwitch(4); + addLimitSwitch(16); + break; + case BOARD_MELINK: + addLedCFG(12); + addButtonCFG(5); + addButton(5); + addRelay(4); + break; + case BOARD_NEO_COOLCAM: + addLedCFG(4); + addButtonCFG(13); + addButton(13); + addRelay(12); + break; + case BOARD_SHELLY1: + addButtonCFG(5); + addButton(5); + addRelay(4); + break; + case BOARD_SHELLY2: + addLedCFG(16); + addButtonCFG(12); + addButton(12); + addButton(14); + addRelay(4); + addRelay(5); + break; + case BOARD_SONOFF_BASIC: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addRelay(12); + break; + case BOARD_SONOFF_DUAL_R2: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addButton(9); + addRelay(12); + addRelay(5); + break; + case BOARD_SONOFF_S2X: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addRelay(12); + ConfigESP->setGpio(14, FUNCTION_SI7021_SONOFF); + break; + case BOARD_SONOFF_SV: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addRelay(12); + break; + case BOARD_SONOFF_TH: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addRelay(12); + ConfigESP->setGpio(14, FUNCTION_SI7021_SONOFF); + break; + case BOARD_SONOFF_TOUCH: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addRelay(12); + break; + case BOARD_SONOFF_TOUCH_2CH: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addButton(9); + addRelay(12); + addRelay(5); + break; + case BOARD_SONOFF_TOUCH_3CH: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addButton(9); + addButton(10); + addRelay(12); + addRelay(5); + addRelay(4); + break; + case BOARD_SONOFF_4CH: + addLedCFG(13); + addButtonCFG(0); + addButton(0); + addButton(9); + addButton(10); + addButton(14); + addRelay(12); + addRelay(5); + addRelay(4); + addRelay(15); + break; + case BOARD_YUNSHAN: + addLedCFG(2, LOW); + addButtonCFG(0); + addButton(3, Supla::ON_CHANGE); + addRelay(4); + break; + + case BOARD_YUNTONG_SMART: + addLedCFG(15); + addButtonCFG(12); + addButton(12, Supla::ON_CHANGE); + addRelay(4); + break; + + case BOARD_GOSUND_SP111: + addLedCFG(2, LOW); + addButtonCFG(13); + addButton(13, Supla::ON_RELEASE); + addRelay(15); + addLed(0); +#ifdef SUPLA_HLW8012 + addHLW8012(5, 4, 12); + Supla::GUI::counterHLW8012->setCurrentMultiplier(18388); + Supla::GUI::counterHLW8012->setVoltageMultiplier(247704); + Supla::GUI::counterHLW8012->setPowerMultiplier(2586583); + Supla::GUI::counterHLW8012->saveState(); +#endif + break; + } +} diff --git a/SuplaTemplateBoard.h b/src/SuplaTemplateBoard.h similarity index 58% rename from SuplaTemplateBoard.h rename to src/SuplaTemplateBoard.h index b44f9409..8e6b6ceb 100644 --- a/SuplaTemplateBoard.h +++ b/src/SuplaTemplateBoard.h @@ -2,12 +2,22 @@ #define SuplaTemplateBoard_h #include +#include "SuplaWebPageRelay.h" +#include "GUIGenericCommon.h" +void addButton(uint8_t gpio, uint8_t event = 0); +void addRelay(uint8_t gpio, uint8_t level = HIGH); +void addLimitSwitch(uint8_t gpio); +void addLedCFG(uint8_t gpio, uint8_t level = HIGH); +void addLed(uint8_t gpio); +void addButtonCFG(uint8_t gpio); +void addHLW8012(int8_t pinCF, int8_t pinCF1, int8_t pinSEL); enum _board { BOARD_ELECTRODRAGON = 1, BOARD_INCAN3, + BOARD_INCAN4, BOARD_MELINK, BOARD_NEO_COOLCAM, BOARD_SHELLY1, @@ -15,6 +25,7 @@ enum _board BOARD_SONOFF_BASIC, BOARD_SONOFF_DUAL_R2, BOARD_SONOFF_S2X, + BOARD_SONOFF_SV, BOARD_SONOFF_TH, BOARD_SONOFF_TOUCH, BOARD_SONOFF_TOUCH_2CH, @@ -22,12 +33,14 @@ enum _board BOARD_SONOFF_4CH, BOARD_YUNSHAN, BOARD_YUNTONG_SMART, + BOARD_GOSUND_SP111, MAX_MODULE }; -const char BOARD_NULL[] PROGMEM = "BRAK"; +const char BOARD_NULL[] PROGMEM = S_ABSENT; const char ELECTRODRAGON[] PROGMEM = "ElectroDragon"; const char INCAN3[] PROGMEM = "inCan3"; +const char INCAN4[] PROGMEM = "inCan4"; const char MELINK[] PROGMEM = "Melink"; const char NEO_COOLCAM[] PROGMEM = "Neo Coolcam"; const char SHELLY1[] PROGMEM = "Shelly 1"; @@ -35,6 +48,7 @@ const char SHELLY2[] PROGMEM = "Shelly 2"; const char SONOFF_BASIC[] PROGMEM = "SONOFF BASIC"; const char SONOFF_DUAL_R2[] PROGMEM = "SONOFF DUAL R2"; const char SONOFF_S2X[] PROGMEM = "SONOFF S2X"; +const char SONOFF_SV[] PROGMEM = "SONOFF SV"; const char SONOFF_TH[] PROGMEM = "SONOFF TH"; const char SONOFF_TOUCH[] PROGMEM = "SONOFF TOUCH"; const char SONOFF_TOUCH_2CH[] PROGMEM = "SONOFF TOUCH DUAL"; @@ -42,10 +56,11 @@ const char SONOFF_TOUCH_3CH[] PROGMEM = "SONOFF TOUCH TRIPLE"; const char SONOFF_4CH[] PROGMEM = "SONOFF 4CH"; const char YUNSHAN[] PROGMEM = "Yunshan"; const char YUNTONG_SMART[] PROGMEM = "YUNTONG Smart"; +const char GOSUNG_SP111[] PROGMEM = "Gosund SP111"; -const char* const BOARD_P[MAX_MODULE] PROGMEM = {BOARD_NULL, ELECTRODRAGON, INCAN3, MELINK, NEO_COOLCAM, SHELLY1, - SHELLY2, SONOFF_BASIC, SONOFF_DUAL_R2, SONOFF_S2X, SONOFF_TH, SONOFF_TOUCH, - SONOFF_TOUCH_2CH, SONOFF_TOUCH_3CH, SONOFF_4CH, YUNSHAN, YUNTONG_SMART}; +const char* const BOARD_P[MAX_MODULE] PROGMEM = { + BOARD_NULL, ELECTRODRAGON, INCAN3, INCAN4, MELINK, NEO_COOLCAM, SHELLY1, SHELLY2, SONOFF_BASIC, SONOFF_DUAL_R2, + SONOFF_S2X, SONOFF_SV, SONOFF_TH, SONOFF_TOUCH, SONOFF_TOUCH_2CH, SONOFF_TOUCH_3CH, SONOFF_4CH, YUNSHAN, YUNTONG_SMART, GOSUNG_SP111}; void chooseTemplateBoard(uint8_t board); diff --git a/src/SuplaWebPageConfig.cpp b/src/SuplaWebPageConfig.cpp new file mode 100644 index 00000000..def78b14 --- /dev/null +++ b/src/SuplaWebPageConfig.cpp @@ -0,0 +1,90 @@ +#include "SuplaWebPageConfig.h" +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "SuplaCommonPROGMEM.h" +#include "GUIGenericCommon.h" +#include "Markup.h" + +SuplaWebPageConfig *WebPageConfig = new SuplaWebPageConfig(); + +SuplaWebPageConfig::SuplaWebPageConfig() { +} + +void SuplaWebPageConfig::createWebPageConfig() { + String path; + path = PATH_START; + path += PATH_CONFIG; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageConfig::handleConfig, this)); + path = PATH_START; + path += PATH_SAVE_CONFIG; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageConfig::handleConfigSave, this)); +} + +void SuplaWebPageConfig::handleConfig() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_config(0); +} + +void SuplaWebPageConfig::handleConfigSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + if (!WebServer->saveGPIO(INPUT_CFG_LED_GPIO, FUNCTION_CFG_LED)) { + supla_webpage_config(6); + return; + } + + uint8_t key = KEY_GPIO + ConfigESP->getGpio(FUNCTION_CFG_LED); + String input = INPUT_CFG_LED_LEVEL; + ConfigManager->setElement(key, LEVEL, WebServer->httpServer.arg(input).toInt()); + + if (!WebServer->saveGPIO(INPUT_CFG_BTN_GPIO, FUNCTION_CFG_BUTTON)) { + supla_webpage_config(6); + return; + } + + if (strcmp(WebServer->httpServer.arg(INPUT_CFG_MODE).c_str(), "") != 0) { + ConfigManager->set(KEY_CFG_MODE, WebServer->httpServer.arg(INPUT_CFG_MODE).c_str()); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + // Serial.println(F("E_CONFIG_OK: Config save")); + supla_webpage_config(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_config(2); + break; + } +} + +void SuplaWebPageConfig::supla_webpage_config(int save) { + uint8_t selected, suported; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_CONFIG); + + addForm(webContentBuffer, F("post"), PATH_SAVE_CONFIG); + addFormHeader(webContentBuffer, S_GPIO_SETTINGS_FOR_CONFIG); + addListGPIOBox(webContentBuffer, INPUT_CFG_LED_GPIO, F("LED"), FUNCTION_CFG_LED); + + selected = ConfigESP->getLevel(FUNCTION_CFG_LED); + addListBox(webContentBuffer, INPUT_CFG_LED_LEVEL, S_STATE_CONTROL, LEVEL_P, 2, selected); + addListGPIOBox(webContentBuffer, INPUT_CFG_BTN_GPIO, S_BUTTON, FUNCTION_CFG_BUTTON); + + selected = ConfigManager->get(KEY_CFG_MODE)->getValueInt(); + addListBox(webContentBuffer, INPUT_CFG_MODE, S_CFG_MODE, CFG_MODE_P, 2, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendContent(); +} diff --git a/SuplaWebPageConfig.h b/src/SuplaWebPageConfig.h similarity index 87% rename from SuplaWebPageConfig.h rename to src/SuplaWebPageConfig.h index 66a93a18..c2d55089 100644 --- a/SuplaWebPageConfig.h +++ b/src/SuplaWebPageConfig.h @@ -1,25 +1,26 @@ -#ifndef SuplaWebPageConfig_h -#define SuplaWebPageConfig_h - -#include "SuplaWebServer.h" -#include "SuplaDeviceGUI.h" - -#define PATH_CONFIG "config" -#define PATH_SAVE_CONFIG "saveconfig" -#define INPUT_CFG_LED_GPIO "cfgl" -#define INPUT_CFG_BTN_GPIO "cfgb" -#define INPUT_CFG_LED_LEVEL "icll" - -class SuplaWebPageConfig { - public: - SuplaWebPageConfig(); - void createWebPageConfig(); - void handleConfig(); - void handleConfigSave(); - - private: - String supla_webpage_config(int save); -}; - -extern SuplaWebPageConfig *WebPageConfig; -#endif // SuplaWebPageConfig_h +#ifndef SuplaWebPageConfig_h +#define SuplaWebPageConfig_h + +#include "SuplaWebServer.h" +#include "SuplaDeviceGUI.h" + +#define PATH_CONFIG "config" +#define PATH_SAVE_CONFIG "saveconfig" +#define INPUT_CFG_LED_GPIO "cfgl" +#define INPUT_CFG_BTN_GPIO "cfgb" +#define INPUT_CFG_LED_LEVEL "icll" +#define INPUT_CFG_MODE "cfgm" + +class SuplaWebPageConfig { + public: + SuplaWebPageConfig(); + void createWebPageConfig(); + void handleConfig(); + void handleConfigSave(); + + private: + void supla_webpage_config(int save); +}; + +extern SuplaWebPageConfig *WebPageConfig; +#endif // SuplaWebPageConfig_h diff --git a/src/SuplaWebPageControl.cpp b/src/SuplaWebPageControl.cpp new file mode 100644 index 00000000..996b70bb --- /dev/null +++ b/src/SuplaWebPageControl.cpp @@ -0,0 +1,374 @@ +#include "SuplaWebPageControl.h" +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "GUIGenericCommon.h" +#include "Markup.h" + +SuplaWebPageControl *WebPageControl = new SuplaWebPageControl(); + +void SuplaWebPageControl::createWebPageControl() { + String path; + path += PATH_START; + path += PATH_CONTROL; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControl, this)); + path = PATH_START; + path += PATH_SAVE_CONTROL; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControlSave, this)); + +#ifdef SUPLA_BUTTON + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + path = PATH_START; + path += PATH_BUTTON_SET; + WebServer->httpServer.on(path, HTTP_GET, std::bind(&SuplaWebPageControl::handleButtonSetMCP23017, this)); + + path = PATH_START; + path += PATH_SAVE_BUTTON_SET; + WebServer->httpServer.on(path, HTTP_POST, std::bind(&SuplaWebPageControl::handleButtonSaveSetMCP23017, this)); + } + else { + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); i++) { + path = PATH_START; + path += PATH_BUTTON_SET; + path += i; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSet, this)); + + path = PATH_START; + path += PATH_SAVE_BUTTON_SET; + path += i; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSaveSet, this)); + } + } +#endif + +#ifdef SUPLA_LIMIT_SWITCH + path = PATH_START; + path += PATH_SWITCH; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleLimitSwitch, this)); + path = PATH_START; + path += PATH_SAVE_SWITCH; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleLimitSwitchSave, this)); +#endif +} + +void SuplaWebPageControl::handleControl() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_control(0); +} + +void SuplaWebPageControl::handleControlSave() { + // Serial.println(F("HTTP_POST - metoda handleControlSave")); + + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + uint8_t nr, last_value; +#ifdef SUPLA_BUTTON + last_value = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + if (!WebServer->saveGpioMCP23017(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr, INPUT_MAX_BUTTON)) { + supla_webpage_control(6); + return; + } + } + else { + if (!WebServer->saveGPIO(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr, INPUT_MAX_BUTTON)) { + supla_webpage_control(6); + return; + } + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_BUTTON, WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str()); + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_control(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_control(2); + break; + } +} + +void SuplaWebPageControl::supla_webpage_control(int save) { + uint8_t nr, countFreeGpio; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_CONTROL); + addForm(webContentBuffer, F("post"), PATH_SAVE_CONTROL); + +#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_BUTTON) && defined(SUPLA_ROLLERSHUTTER)) + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR_BUTTONS)); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 16; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_BUTTON); + } + + addNumberBox(webContentBuffer, INPUT_MAX_BUTTON, S_QUANTITY, KEY_MAX_BUTTON, countFreeGpio); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOLinkBox(webContentBuffer, INPUT_BUTTON_GPIO, S_BUTTON, FUNCTION_BUTTON, PATH_BUTTON_SET, nr); + } + else { + addListGPIOLinkBox(webContentBuffer, INPUT_BUTTON_GPIO, S_BUTTON, FUNCTION_BUTTON, PATH_BUTTON_SET, nr); + } + } + addFormHeaderEnd(webContentBuffer); +#endif + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendContent(); +} + +#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_BUTTON) && defined(SUPLA_ROLLERSHUTTER)) +void SuplaWebPageControl::handleButtonSet() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_button_set(0); +} + +void SuplaWebPageControl::handleButtonSaveSet() { + // Serial.println(F("HTTP_POST - metoda handleRelaySaveSet")); + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String readUrl, nr_button, input, path; + uint8_t place, key, gpio; + + input.reserve(5); + readUrl.reserve(16); + nr_button.reserve(2); + path.reserve(16); + + path = PATH_START; + path += PATH_SAVE_BUTTON_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); + + gpio = ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON); + key = KEY_GPIO + gpio; + + input = INPUT_BUTTON_LEVEL; + input += nr_button; + ConfigManager->setElement(key, LEVEL, WebServer->httpServer.arg(input).toInt()); + + input = INPUT_BUTTON_ACTION; + input += nr_button; + ConfigManager->setElement(key, ACTION, WebServer->httpServer.arg(input).toInt()); + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_control(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_control(2); + break; + } +} + +void SuplaWebPageControl::supla_webpage_button_set(int save) { + String path, readUrl, nr_button; + uint8_t place, selected; + + path.reserve(10); + readUrl.reserve(11); + nr_button.reserve(2); + + path = PATH_START; + path += PATH_BUTTON_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_CONTROL); + + addForm(webContentBuffer, F("post"), PATH_SAVE_BUTTON_SET + nr_button); + addFormHeader(webContentBuffer, S_BUTTON_NR_SETTINGS + nr_button); + + selected = ConfigESP->getLevel(nr_button.toInt(), FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_LEVEL + nr_button, S_REACTION_TO, TRIGGER_P, 3, selected); + + selected = ConfigESP->getAction(nr_button.toInt(), FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_ACTION + nr_button, S_ACTION, ACTION_P, 3, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_CONTROL); + + WebServer->sendContent(); +} +#endif + +#ifdef SUPLA_LIMIT_SWITCH +void SuplaWebPageControl::handleLimitSwitch() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + suplaWebpageLimitSwitch(0); +} + +void SuplaWebPageControl::handleLimitSwitchSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + uint8_t nr, last_value; + + last_value = ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + if (!WebServer->saveGpioMCP23017(INPUT_LIMIT_SWITCH_GPIO, FUNCTION_LIMIT_SWITCH, nr, INPUT_MAX_LIMIT_SWITCH)) { + suplaWebpageLimitSwitch(6); + return; + } + } + else { + if (!WebServer->saveGPIO(INPUT_LIMIT_SWITCH_GPIO, FUNCTION_LIMIT_SWITCH, nr, INPUT_MAX_LIMIT_SWITCH)) { + suplaWebpageLimitSwitch(6); + return; + } + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_LIMIT_SWITCH, WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str()); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + suplaWebpageLimitSwitch(1); + break; + case E_CONFIG_FILE_OPEN: + suplaWebpageLimitSwitch(2); + break; + } +} + +void SuplaWebPageControl::suplaWebpageLimitSwitch(int save) { + uint8_t nr, countFreeGpio; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_SWITCH); + addForm(webContentBuffer, F("post"), PATH_SAVE_SWITCH); + + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR_LIMIT_SWITCH)); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 16; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_LIMIT_SWITCH); + } + + addNumberBox(webContentBuffer, INPUT_MAX_LIMIT_SWITCH, S_QUANTITY, KEY_MAX_LIMIT_SWITCH, countFreeGpio); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOBox(webContentBuffer, INPUT_LIMIT_SWITCH_GPIO, S_LIMIT_SWITCH, FUNCTION_LIMIT_SWITCH, nr); + } + else { + addListGPIOBox(webContentBuffer, INPUT_LIMIT_SWITCH_GPIO, S_LIMIT_SWITCH, FUNCTION_LIMIT_SWITCH, nr); + } + } + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendContent(); +} +#endif + +void SuplaWebPageControl::handleButtonSetMCP23017() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_button_set_MCP23017(0); +} + +void SuplaWebPageControl::supla_webpage_button_set_MCP23017(int save) { + uint8_t selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_CONTROL); + + addForm(webContentBuffer, F("post"), PATH_SAVE_BUTTON_SET); + addFormHeader(webContentBuffer, F("Ustawienia dla przycisków")); + + selected = ConfigESP->getLevel(1, FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_LEVEL, S_REACTION_TO, TRIGGER_P, 3, selected); + + selected = ConfigESP->getAction(1, FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_ACTION, S_ACTION, ACTION_P, 3, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_CONTROL); + + WebServer->sendContent(); +} + +void SuplaWebPageControl::handleButtonSaveSetMCP23017() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String input; + uint8_t key, gpio, level, action; + + input.reserve(10); + + input = INPUT_BUTTON_LEVEL; + level = WebServer->httpServer.arg(input).toInt(); + + input = INPUT_BUTTON_ACTION; + action = WebServer->httpServer.arg(input).toInt(); + + for (uint8_t i = 1; i <= OFF_GPIO; i++) { + gpio = ConfigESP->getGpioMCP23017(i, FUNCTION_BUTTON); + if (gpio != OFF_GPIO) { + key = KEY_GPIO + gpio; + ConfigManager->setElement(key, LEVEL, level); + ConfigManager->setElement(key, ACTION, action); + } + } + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_control(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_control(2); + break; + } +} diff --git a/src/SuplaWebPageControl.h b/src/SuplaWebPageControl.h new file mode 100644 index 00000000..400989a3 --- /dev/null +++ b/src/SuplaWebPageControl.h @@ -0,0 +1,59 @@ +#ifndef SuplaWebPageControl_h +#define SuplaWebPageControl_h + +#include "SuplaWebServer.h" +#include "SuplaDeviceGUI.h" + +#define PATH_CONTROL "control" +#define PATH_SWITCH "switch" +#define PATH_SAVE_SWITCH "saveswitch" +#define PATH_SAVE_CONTROL "savecontrol" +#define PATH_BUTTON_SET "setbutton" +#define PATH_SAVE_BUTTON_SET "savesetbutton" +#define INPUT_TRIGGER "trs" +#define INPUT_BUTTON_SET "bts" +#define INPUT_BUTTON_GPIO "btg" +#define INPUT_BUTTON_LEVEL "icl" +#define INPUT_BUTTON_ACTION "bta" +#define INPUT_LIMIT_SWITCH_GPIO "lsg" +#define INPUT_MAX_BUTTON "mbt" +#define INPUT_MAX_LIMIT_SWITCH "mls" + +/*enum _trigger_button { + TRIGGER_PRESS, + TRIGGER_RELEASE, + TRIGGER_CHANGE +};*/ + +class SuplaWebPageControl { + public: + void createWebPageControl(); + void handleControl(); + void handleControlSave(); + +#ifdef SUPLA_LIMIT_SWITCH + void handleLimitSwitch(); + void handleLimitSwitchSave(); + void suplaWebpageLimitSwitch(int save); +#endif + +#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_RSUPLA_BUTTONELAY) || defined(SUPLA_ROLLERSHUTTER)) + void handleButtonSet(); + void handleButtonSaveSet(); + + private: + void supla_webpage_control(int save); +#endif + +#ifdef SUPLA_BUTTON + void supla_webpage_button_set(int save); +#endif + +void handleButtonSetMCP23017(); +void handleButtonSaveSetMCP23017(); +void supla_webpage_button_set_MCP23017(int save); + +}; + +extern SuplaWebPageControl* WebPageControl; +#endif // SuplaWebPageControl_h diff --git a/src/SuplaWebPageDownload.cpp b/src/SuplaWebPageDownload.cpp new file mode 100644 index 00000000..b4e08d5b --- /dev/null +++ b/src/SuplaWebPageDownload.cpp @@ -0,0 +1,29 @@ +#include "SuplaWebPageDownload.h" +#include "SuplaDeviceGUI.h" + +void createWebDownload() { + WebServer->httpServer.on(getURL(PATH_DOWNLOAD), handleDownload); +} + +void handleDownload() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + File dataFile = SPIFFS.open(F(CONFIG_FILE_PATH), "r"); + + if (!dataFile) { + return; + } + + String str = F("attachment; filename=config_"); + str += ConfigManager->get(KEY_HOST_NAME)->getValue(); + str += Supla::Channel::reg_dev.SoftVer; + str += '_'; + str += F(".dat"); + + WebServer->httpServer.sendHeader(F("Content-Disposition"), str); + WebServer->httpServer.streamFile(dataFile, F("application/octet-stream")); + dataFile.close(); +} \ No newline at end of file diff --git a/src/SuplaWebPageDownload.h b/src/SuplaWebPageDownload.h new file mode 100644 index 00000000..947be49e --- /dev/null +++ b/src/SuplaWebPageDownload.h @@ -0,0 +1,10 @@ + +#ifndef SuplaWebPageDownload_h +#define SuplaWebPageDownload_h + +#define PATH_DOWNLOAD "download" + +void createWebDownload(); +void handleDownload(); + +#endif // ifndef SuplaWebPageDownload_h diff --git a/src/SuplaWebPageRelay.cpp b/src/SuplaWebPageRelay.cpp new file mode 100644 index 00000000..09f4bb08 --- /dev/null +++ b/src/SuplaWebPageRelay.cpp @@ -0,0 +1,282 @@ +#include "SuplaWebPageRelay.h" +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "SuplaCommonPROGMEM.h" +#include "GUIGenericCommon.h" +#include "Markup.h" +#include "SuplaWebPageSensor.h" + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) +SuplaWebPageRelay *WebPageRelay = new SuplaWebPageRelay(); + +SuplaWebPageRelay::SuplaWebPageRelay() { +} + +void SuplaWebPageRelay::createWebPageRelay() { + String path; + path.reserve(11); + + path += PATH_START; + path += PATH_RELAY; + WebServer->httpServer.on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelay, this)); + path = PATH_START; + path += PATH_SAVE_RELAY; + WebServer->httpServer.on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySave, this)); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + path = PATH_START; + path += PATH_RELAY_SET; + WebServer->httpServer.on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelaySetMCP23017, this)); + + path = PATH_START; + path += PATH_SAVE_RELAY_SET; + WebServer->httpServer.on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySaveSetMCP23017, this)); + } + else { + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); i++) { + path = PATH_START; + path += PATH_RELAY_SET; + path += i; + WebServer->httpServer.on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelaySet, this)); + + path = PATH_START; + path += PATH_SAVE_RELAY_SET; + path += i; + WebServer->httpServer.on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySaveSet, this)); + } + } +} + +void SuplaWebPageRelay::handleRelay() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_relay(0); +} + +void SuplaWebPageRelay::handleRelaySave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + uint8_t nr, last_value; + + last_value = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + if (!WebServer->saveGpioMCP23017(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr, INPUT_MAX_RELAY)) { + supla_webpage_relay(6); + return; + } + } + else { + if (!WebServer->saveGPIO(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr, INPUT_MAX_RELAY)) { + supla_webpage_relay(6); + return; + } + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_RELAY, WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str()); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_relay(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_relay(2); + break; + } +} + +void SuplaWebPageRelay::supla_webpage_relay(int save) { + uint8_t nr, countFreeGpio; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_RELAY); + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY); + addFormHeader(webContentBuffer, S_GPIO_SETTINGS_FOR_RELAYS); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 16; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_RELAY); + } + + addNumberBox(webContentBuffer, INPUT_MAX_RELAY, S_QUANTITY, KEY_MAX_RELAY, countFreeGpio); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOLinkBox(webContentBuffer, INPUT_RELAY_GPIO, S_RELAY, FUNCTION_RELAY, PATH_RELAY_SET, nr); + } + else { + addListGPIOLinkBox(webContentBuffer, INPUT_RELAY_GPIO, S_RELAY, FUNCTION_RELAY, PATH_RELAY_SET, nr); + } + } + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendContent(); +} + +void SuplaWebPageRelay::handleRelaySet() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_relay_set(0); +} + +void SuplaWebPageRelay::handleRelaySaveSet() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String readUrl, nr_relay, input, path; + uint8_t place, key, gpio; + + input.reserve(6); + readUrl.reserve(11); + nr_relay.reserve(2); + path.reserve(15); + + path = PATH_START; + path += PATH_SAVE_RELAY_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); + + gpio = ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY); + key = KEY_GPIO + gpio; + + input = INPUT_RELAY_MEMORY; + input += nr_relay; + ConfigManager->setElement(key, MEMORY, WebServer->httpServer.arg(input).toInt()); + + input = INPUT_RELAY_LEVEL; + input += nr_relay; + ConfigManager->setElement(key, LEVEL, WebServer->httpServer.arg(input).toInt()); + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_relay(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_relay(2); + break; + } +} + +void SuplaWebPageRelay::supla_webpage_relay_set(int save) { + String path, readUrl, nr_relay; + uint8_t place, selected; + + path.reserve(10); + readUrl.reserve(11); + nr_relay.reserve(2); + + path = PATH_START; + path += PATH_RELAY_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_RELAY); + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY_SET + nr_relay); + addFormHeader(webContentBuffer, S_RELAY_NR_SETTINGS + nr_relay); + + selected = ConfigESP->getLevel(nr_relay.toInt(), FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_LEVEL + nr_relay, S_STATE_CONTROL, LEVEL_P, 2, selected); + + selected = ConfigESP->getMemory(nr_relay.toInt(), FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_MEMORY + nr_relay, S_REACTION_AFTER_RESET, MEMORY_P, 3, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_RELAY); + + WebServer->sendContent(); +} + +void SuplaWebPageRelay::handleRelaySetMCP23017() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_relay_set_MCP23017(0); +} + +void SuplaWebPageRelay::supla_webpage_relay_set_MCP23017(int save) { + uint8_t selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_RELAY); + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY_SET); + addFormHeader(webContentBuffer, F("Ustawienia dla przekaźników")); + + selected = ConfigESP->getLevel(1, FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_LEVEL, S_STATE_CONTROL, LEVEL_P, 2, selected); + + selected = ConfigESP->getMemory(1, FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_MEMORY, S_REACTION_AFTER_RESET, MEMORY_P, 3, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_RELAY); + + WebServer->sendContent(); +} + +void SuplaWebPageRelay::handleRelaySaveSetMCP23017() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String input; + uint8_t key, gpio, memory, level; + + input.reserve(9); + + input = INPUT_RELAY_MEMORY; + memory = WebServer->httpServer.arg(input).toInt(); + + input = INPUT_RELAY_LEVEL; + level = WebServer->httpServer.arg(input).toInt(); + + for (uint8_t i = 1; i <= OFF_GPIO; i++) { + gpio = ConfigESP->getGpioMCP23017(i, FUNCTION_RELAY); + if (gpio != OFF_GPIO) { + key = KEY_GPIO + gpio; + ConfigManager->setElement(key, MEMORY, memory); + ConfigManager->setElement(key, LEVEL, level); + } + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_relay(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_relay(2); + break; + } +} +#endif diff --git a/src/SuplaWebPageRelay.h b/src/SuplaWebPageRelay.h new file mode 100644 index 00000000..19a12401 --- /dev/null +++ b/src/SuplaWebPageRelay.h @@ -0,0 +1,48 @@ +#ifndef SuplaWebPageRelay_h +#define SuplaWebPageRelay_h + +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" + +#define PATH_RELAY "relay" +#define PATH_SAVE_RELAY "saverelay" +#define PATH_RELAY_SET "setrelay" +#define PATH_SAVE_RELAY_SET "savesetrelay" +#define INPUT_MAX_RELAY "mrl" +#define INPUT_RELAY_GPIO "rlg" +#define INPUT_ADRESS_MCP23017 "iam" +#define INPUT_RELAY_LEVEL "irl" +#define INPUT_RELAY_MEMORY "irm" +#define INPUT_RELAY_DURATION "ird" +#define INPUT_ROLLERSHUTTER "irsr" + +enum _memory_relay +{ + MEMORY_RELAY_OFF, + MEMORY_RELAY_ON, + MEMORY_RELAY_RESTORE +}; + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + +class SuplaWebPageRelay { + public: + SuplaWebPageRelay(); + void createWebPageRelay(); + void handleRelay(); + void handleRelaySave(); + void handleRelaySet(); + void handleRelaySaveSet(); + void handleRelaySaveSetMCP23017(); + void handleRelaySetMCP23017(); + + + private: + void supla_webpage_relay_set(int save); + void supla_webpage_relay(int save); + void supla_webpage_relay_set_MCP23017(int save); +}; + +extern SuplaWebPageRelay* WebPageRelay; +#endif +#endif // SuplaWebPageRelay_h diff --git a/src/SuplaWebPageSensor.cpp b/src/SuplaWebPageSensor.cpp new file mode 100644 index 00000000..e7e5dbcb --- /dev/null +++ b/src/SuplaWebPageSensor.cpp @@ -0,0 +1,978 @@ +#include "SuplaWebPageSensor.h" + +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "SuplaCommonPROGMEM.h" +#include "GUIGenericCommon.h" +#include "Markup.h" + +SuplaWebPageSensor *WebPageSensor = new SuplaWebPageSensor(); + +SuplaWebPageSensor::SuplaWebPageSensor() { +} + +void SuplaWebPageSensor::createWebPageSensor() { + String path; + +#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) + path = PATH_START; + path += PATH_1WIRE; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1Wire, this)); + path = PATH_START; + path += PATH_SAVE_1WIRE; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1WireSave, this)); +#ifdef SUPLA_DS18B20 + path = PATH_START; + path += PATH_MULTI_DS; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSearchDS, this)); + path = PATH_START; + path += PATH_SAVE_MULTI_DS; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleDSSave, this)); +#endif +#endif + +#if defined(SUPLA_BME280) || defined(SUPLA_SHT3x) || defined(SUPLA_SI7021) || defined(SUPLA_MCP23017) + path = PATH_START; + path += PATH_I2C; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2c, this)); + path = PATH_START; + path += PATH_SAVE_I2C; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2cSave, this)); +#endif + +#if defined(SUPLA_MAX6675) + path = PATH_START; + path += PATH_SPI; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpi, this)); + path = PATH_START; + path += PATH_SAVE_SPI; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpiSave, this)); +#endif + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) + path = PATH_START; + path += PATH_OTHER; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleOther, this)); + path = PATH_START; + path += PATH_SAVE_OTHER; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleOtherSave, this)); + +#if defined(SUPLA_IMPULSE_COUNTER) + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); i++) { + path = PATH_START; + path += PATH_IMPULSE_COUNTER_SET; + path += i; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleImpulseCounterSet, this)); + + path = PATH_START; + path += PATH_SAVE_IMPULSE_COUNTER_SET; + path += i; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleImpulseCounterSaveSet, this)); + } +#endif + +#if defined(SUPLA_HLW8012) + path = PATH_START; + path += PATH_HLW8012_CALIBRATE; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleHLW8012Calibrate, this)); + + path = PATH_START; + path += PATH_SAVE_HLW8012_CALIBRATE; + WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleHLW8012CalibrateSave, this)); +#endif +#endif +} + +#ifdef SUPLA_DS18B20 +void SuplaWebPageSensor::handleSearchDS() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_search(0); +} + +void SuplaWebPageSensor::handleDSSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { + String dsAddr = INPUT_DS18B20_ADDR; + String dsName = INPUT_DS18B20_NAME; + dsAddr += i; + dsName += i; + + ConfigManager->setElement(KEY_ADDR_DS18B20, i, WebServer->httpServer.arg(dsAddr).c_str()); + + if (strcmp(WebServer->httpServer.arg(dsName).c_str(), "") != 0) { + ConfigManager->setElement(KEY_NAME_SENSOR, i, WebServer->httpServer.arg(dsName).c_str()); + } + + Supla::GUI::sensorDS[i]->setDeviceAddress(HexToBytes(ConfigManager->get(KEY_ADDR_DS18B20)->getElement(i))); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + // Serial.println(F("E_CONFIG_OK: Config save")); + supla_webpage_search(1); + // WebServer->rebootESP(); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_search(2); + break; + } +} + +void SuplaWebPageSensor::supla_webpage_search(int save) { + uint8_t count = 0; + uint8_t pin = ConfigESP->getGpio(FUNCTION_DS18B20); + + OneWire ow(pin); + DallasTemperature sensors; + DeviceAddress address; + char strAddr[64]; + uint8_t i; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_MULTI_DS); + webContentBuffer += F("
"); + if (ConfigESP->getGpio(FUNCTION_DS18B20) < OFF_GPIO) { + webContentBuffer += F("
"); + this->showDS18B20(); + webContentBuffer += F("
"); + webContentBuffer += F("
"); + } + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_FOUND; + webContentBuffer += F(" DS18b20

"); + sensors.setOneWire(&ow); + sensors.begin(); + if (sensors.isParasitePowerMode()) { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is ON", pin); + } + else { + supla_log(LOG_DEBUG, "OneWire(pin %d) Parasite power is OFF", pin); + } + // report parasite power requirements + for (i = 0; i < sensors.getDeviceCount(); i++) { + if (!sensors.getAddress(address, i)) { + supla_log(LOG_DEBUG, "Unable to find address for Device %d", i); + } + else { + sprintf(strAddr, "%02X%02X%02X%02X%02X%02X%02X%02X", address[0], address[1], address[2], address[3], address[4], address[5], address[6], + address[7]); + supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr); + + webContentBuffer += F(""); + + count++; + } + delay(0); + } + + if (count == 0) { + webContentBuffer += F(""); + } + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F(""); + webContentBuffer += F("

"); + webContentBuffer += F("

"); + WebServer->sendContent(); +} + +void SuplaWebPageSensor::showDS18B20(bool readonly) { + if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_TEMPERATURE; + webContentBuffer += F("

"); + for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { + double temp = Supla::GUI::sensorDS[i]->getValue(); + webContentBuffer += F(""); + webContentBuffer += F(""); + delay(0); + } + webContentBuffer += F("
"); + } +} +#endif + +#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) +void SuplaWebPageSensor::handle1Wire() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_1wire(0); +} + +void SuplaWebPageSensor::handle1WireSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + uint8_t nr, last_value; + +#ifdef SUPLA_DHT11 + last_value = ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (!WebServer->saveGPIO(INPUT_DHT11_GPIO, FUNCTION_DHT11, nr, INPUT_MAX_DHT11)) { + supla_webpage_1wire(6); + return; + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_DHT11, WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str()); + } +#endif + +#ifdef SUPLA_DHT22 + last_value = ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (!WebServer->saveGPIO(INPUT_DHT22_GPIO, FUNCTION_DHT22, nr, INPUT_MAX_DHT22)) { + supla_webpage_1wire(6); + return; + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_DHT22, WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str()); + } +#endif + +#ifdef SUPLA_DS18B20 + if (!WebServer->saveGPIO(INPUT_MULTI_DS_GPIO, FUNCTION_DS18B20)) { + supla_webpage_1wire(6); + return; + } + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str(), "") > 0) { + ConfigManager->set(KEY_MULTI_MAX_DS18B20, WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str()); + } +#endif + +#ifdef SUPLA_SI7021_SONOFF + if (!WebServer->saveGPIO(INPUT_SI7021_SONOFF, FUNCTION_SI7021_SONOFF)) { + supla_webpage_1wire(6); + return; + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_1wire(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_1wire(2); + break; + } +} + +void SuplaWebPageSensor::supla_webpage_1wire(int save) { + uint8_t nr, max; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_1WIRE); + webContentBuffer += F("
"); +#ifdef SUPLA_DHT11 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" DHT11")); + addNumberBox(webContentBuffer, INPUT_MAX_DHT11, S_QUANTITY, KEY_MAX_DHT11, ConfigESP->countFreeGpio(FUNCTION_DHT11)); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { + addListGPIOBox(webContentBuffer, INPUT_DHT11_GPIO, F("DHT11"), FUNCTION_DHT11, nr); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_DHT22 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" DHT22")); + addNumberBox(webContentBuffer, INPUT_MAX_DHT22, S_QUANTITY, KEY_MAX_DHT22, ConfigESP->countFreeGpio(FUNCTION_DHT22)); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { + addListGPIOBox(webContentBuffer, INPUT_DHT22_GPIO, F("DHT22"), FUNCTION_DHT22, nr); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_SI7021_SONOFF + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" Si7021 Sonoff")); + addListGPIOBox(webContentBuffer, INPUT_SI7021_SONOFF, F("Si7021 Sonoff"), FUNCTION_SI7021_SONOFF); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_DS18B20 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" Multi DS18B20")); + addNumberBox(webContentBuffer, INPUT_MAX_DS18B20, S_QUANTITY, KEY_MULTI_MAX_DS18B20, MAX_DS18B20); + if (ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt() > 1) { + addListGPIOLinkBox(webContentBuffer, INPUT_MULTI_DS_GPIO, F("MULTI DS18B20"), FUNCTION_DS18B20, PATH_MULTI_DS); + } + else { + addListGPIOBox(webContentBuffer, INPUT_MULTI_DS_GPIO, F("MULTI DS18B20"), FUNCTION_DS18B20); + } + addFormHeaderEnd(webContentBuffer); +#endif + + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + + WebServer->sendContent(); +} +#endif + +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) +void SuplaWebPageSensor::handlei2c() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_i2c(0); +} + +void SuplaWebPageSensor::handlei2cSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String input; + uint8_t key; + + if (!WebServer->saveGPIO(INPUT_SDA_GPIO, FUNCTION_SDA)) { + supla_webpage_i2c(6); + return; + } + if (!WebServer->saveGPIO(INPUT_SCL_GPIO, FUNCTION_SCL)) { + supla_webpage_i2c(6); + return; + } + +#ifdef SUPLA_BME280 + key = KEY_ACTIVE_SENSOR; + input = INPUT_BME280; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_BME280, WebServer->httpServer.arg(input).toInt()); + } + + key = KEY_ALTITUDE_BME280; + input = INPUT_ALTITUDE_BME280; + if (strcmp(WebServer->httpServer.arg(INPUT_ALTITUDE_BME280).c_str(), "") != 0) { + ConfigManager->set(key, WebServer->httpServer.arg(input).c_str()); + } +#endif + +#ifdef SUPLA_SHT3x + key = KEY_ACTIVE_SENSOR; + input = INPUT_SHT3x; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SHT3x, WebServer->httpServer.arg(input).toInt()); + } +#endif + +#ifdef SUPLA_SI7021 + key = KEY_ACTIVE_SENSOR; + input = INPUT_SI7021; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SI7021, WebServer->httpServer.arg(input).toInt()); + } +#endif + +#ifdef SUPLA_OLED + key = KEY_ACTIVE_SENSOR; + input = INPUT_OLED; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_OLED, WebServer->httpServer.arg(input).toInt()); + } + + if (!WebServer->saveGPIO(INPUT_BUTTON_GPIO, FUNCTION_CFG_BUTTON)) { + supla_webpage_i2c(6); + return; + } + + input = INPUT_OLED_ANIMATION; + ConfigManager->set(KEY_OLED_ANIMATION, WebServer->httpServer.arg(input).c_str()); + input = INPUT_OLED_BRIGHTNESS; + ConfigManager->set(KEY_OLED_BACK_LIGHT_TIME, WebServer->httpServer.arg(input).c_str()); + + for (uint8_t i = 0; i < getCountSensorChannels(); i++) { + input = INPUT_DS18B20_NAME; + input += i; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_NAME_SENSOR, i, WebServer->httpServer.arg(input).c_str()); + } + } +#endif + +#ifdef SUPLA_MCP23017 + key = KEY_ACTIVE_SENSOR; + input = INPUT_MCP23017; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_MCP23017, WebServer->httpServer.arg(input).toInt()); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt()) { + ConfigESP->clearFunctionGpio(FUNCTION_RELAY); + ConfigESP->clearFunctionGpio(FUNCTION_BUTTON); + } + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_i2c(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_i2c(2); + break; + } +} + +void SuplaWebPageSensor::supla_webpage_i2c(int save) { + uint8_t selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_I2C); + + addForm(webContentBuffer, F("post"), PATH_SAVE_I2C); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" i2c")); + addListGPIOBox(webContentBuffer, INPUT_SDA_GPIO, F("SDA"), FUNCTION_SDA); + addListGPIOBox(webContentBuffer, INPUT_SCL_GPIO, F("SCL"), FUNCTION_SCL); + addFormHeaderEnd(webContentBuffer); + + if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { +#ifdef SUPLA_BME280 + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_BME280).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_BME280, F("BME280 adres"), BME280_P, 4, selected); + addNumberBox(webContentBuffer, INPUT_ALTITUDE_BME280, S_ALTITUDE_ABOVE_SEA_LEVEL, KEY_ALTITUDE_BME280, 1500); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_SHT3x + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SHT3x).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_SHT3x, F("SHT3x"), SHT3x_P, 4, selected); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_SI7021 + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SI7021).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_SI7021, F("SI7021"), STATE_P, 2, selected); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_OLED + addFormHeader(webContentBuffer); + + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_OLED).toInt(); + addListBox(webContentBuffer, INPUT_OLED, F("OLED"), OLED_P, 4, selected); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_OLED).toInt()) { + String name, sensorName, input; + + addListGPIOBox(webContentBuffer, INPUT_BUTTON_GPIO, F("PRZYCISK OLED"), FUNCTION_CFG_BUTTON); + selected = ConfigManager->get(KEY_OLED_ANIMATION)->getValueInt(); + addListBox(webContentBuffer, INPUT_OLED_ANIMATION, F("STEROWANIE"), OLED_CONTROLL_P, 3, selected); + addNumberBox(webContentBuffer, INPUT_OLED_BRIGHTNESS, F("PODŚWIETLENIE[s]"), KEY_OLED_BACK_LIGHT_TIME, 99); + + for (uint8_t i = 0; i < getCountSensorChannels(); i++) { + sensorName = String(ConfigManager->get(KEY_NAME_SENSOR)->getElement(i)); + input = INPUT_DS18B20_NAME; + input += i; + name = F("EKRAN "); + name += i + 1; + addTextBox(webContentBuffer, input, name, sensorName, 0, MAX_DS18B20_NAME, false); + } + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_MCP23017 + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MCP23017).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_MCP23017, F("MCP23017"), STATE_P, 2, selected); + addFormHeaderEnd(webContentBuffer); +#endif + } + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendContent(); +} +#endif + +#if defined(SUPLA_MAX6675) +void SuplaWebPageSensor::handleSpi() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_spi(0); +} + +void SuplaWebPageSensor::handleSpiSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String input; + uint8_t key; + +#if defined(SUPLA_MAX6675) + if (!WebServer->saveGPIO(INPUT_CLK_GPIO, FUNCTION_CLK)) { + supla_webpage_spi(6); + return; + } + if (!WebServer->saveGPIO(INPUT_CS_GPIO, FUNCTION_CS)) { + supla_webpage_spi(6); + return; + } + if (!WebServer->saveGPIO(INPUT_D0_GPIO, FUNCTION_D0)) { + supla_webpage_spi(6); + return; + } +#endif + +#ifdef SUPLA_MAX6675 + key = KEY_ACTIVE_SENSOR; + input = INPUT_MAX6675; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_MAX6675, WebServer->httpServer.arg(input).toInt()); + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_spi(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_spi(2); + break; + } +} + +void SuplaWebPageSensor::supla_webpage_spi(int save) { + uint8_t nr, suported, selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_SPI); + webContentBuffer += F("
"); + +#if defined(SUPLA_MAX6675) + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" SPI")); + addListGPIOBox(webContentBuffer, INPUT_CLK_GPIO, F("CLK"), FUNCTION_CLK); + addListGPIOBox(webContentBuffer, INPUT_CS_GPIO, F("CS"), FUNCTION_CS); + addListGPIOBox(webContentBuffer, INPUT_D0_GPIO, F("D0"), FUNCTION_D0); + + if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MAX6675).toInt(); + addListBox(webContentBuffer, INPUT_MAX6675, F("MAX6675"), STATE_P, 2, selected); + } + addFormHeaderEnd(webContentBuffer); +#endif + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + WebServer->sendContent(); +} +#endif + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) +void SuplaWebPageSensor::handleOther() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_webpage_other(0); +} + +void SuplaWebPageSensor::handleOtherSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + uint8_t nr, current_value, last_value; + +#ifdef SUPLA_HC_SR04 + if (!WebServer->saveGPIO(INPUT_TRIG_GPIO, FUNCTION_TRIG)) { + supla_webpage_other(6); + return; + } + if (!WebServer->saveGPIO(INPUT_ECHO_GPIO, FUNCTION_ECHO)) { + supla_webpage_other(6); + return; + } +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + // Supla::GUI::impulseCounter[0]->setCounter((unsigned long long)WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); + + last_value = ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (!WebServer->saveGPIO(INPUT_IMPULSE_COUNTER_GPIO, FUNCTION_IMPULSE_COUNTER, nr, INPUT_MAX_IMPULSE_COUNTER)) { + supla_webpage_other(6); + return; + } + } + + if (strcmp(WebServer->httpServer.arg(INPUT_MAX_IMPULSE_COUNTER).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_IMPULSE_COUNTER, WebServer->httpServer.arg(INPUT_MAX_IMPULSE_COUNTER).c_str()); + } +#endif + +#ifdef SUPLA_HLW8012 + if (!WebServer->saveGPIO(INPUT_CF, FUNCTION_CF)) { + supla_webpage_other(6); + return; + } + if (!WebServer->saveGPIO(INPUT_CF1, FUNCTION_CF1)) { + supla_webpage_other(6); + return; + } + if (!WebServer->saveGPIO(INPUT_SEL, FUNCTION_SEL)) { + supla_webpage_other(6); + return; + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_other(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_other(2); + break; + } +} + +void SuplaWebPageSensor::supla_webpage_other(int save) { + uint8_t nr, suported, selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_OTHER); + + addForm(webContentBuffer, F("post"), PATH_SAVE_OTHER); +#ifdef SUPLA_HC_SR04 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" HC-SR04")); + addListGPIOBox(webContentBuffer, INPUT_TRIG_GPIO, F("TRIG"), FUNCTION_TRIG); + addListGPIOBox(webContentBuffer, INPUT_ECHO_GPIO, F("ECHO"), FUNCTION_ECHO); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + " " + S_IMPULSE_COUNTER); + addNumberBox(webContentBuffer, INPUT_MAX_IMPULSE_COUNTER, S_QUANTITY, KEY_MAX_IMPULSE_COUNTER, ConfigESP->countFreeGpio(FUNCTION_IMPULSE_COUNTER)); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { + addListGPIOLinkBox(webContentBuffer, INPUT_IMPULSE_COUNTER_GPIO, F("IC GPIO"), FUNCTION_IMPULSE_COUNTER, PATH_IMPULSE_COUNTER_SET, nr); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_HLW8012 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" HLW8012")); + addListGPIOBox(webContentBuffer, INPUT_CF, F("CF"), FUNCTION_CF); + addListGPIOBox(webContentBuffer, INPUT_CF1, F("CF1"), FUNCTION_CF1); + addListGPIOBox(webContentBuffer, INPUT_SEL, F("SELi"), FUNCTION_SEL); + if (ConfigESP->getGpio(FUNCTION_CF) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CF1) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SEL) != OFF_GPIO) { + addLinkBox(webContentBuffer, F("Kalibracja"), PATH_HLW8012_CALIBRATE); + } + addFormHeaderEnd(webContentBuffer); +#endif + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendContent(); +} +#endif + +#ifdef SUPLA_IMPULSE_COUNTER +void SuplaWebPageSensor::handleImpulseCounterSet() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + supla_impulse_counter_set(0); +} + +void SuplaWebPageSensor::handleImpulseCounterSaveSet() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + String readUrl, nr, input; + uint8_t place; + + String path = PATH_START; + path += PATH_SAVE_IMPULSE_COUNTER_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr = readUrl.substring(place + path.length(), place + path.length() + 3); + uint8_t key = KEY_GPIO + ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER); + + input = INPUT_IMPULSE_COUNTER_PULL_UP; + input += nr; + ConfigManager->setElement(key, MEMORY, WebServer->httpServer.arg(input).toInt()); + + input = INPUT_IMPULSE_COUNTER_RAISING_EDGE; + input += nr; + ConfigManager->setElement(key, LEVEL, WebServer->httpServer.arg(input).toInt()); + + ConfigManager->set(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT).c_str()); + Supla::GUI::impulseCounter[nr.toInt() - 1]->setCounter((unsigned long long)WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + // Serial.println(F("E_CONFIG_OK: Dane zapisane")); + supla_webpage_other(1); + break; + + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_other(2); + break; + } +} + +void SuplaWebPageSensor::supla_impulse_counter_set(int save) { + String readUrl, nr; + uint8_t place, selected, suported; + + String path = PATH_START; + path += PATH_IMPULSE_COUNTER_SET; + readUrl = WebServer->httpServer.uri(); + + place = readUrl.indexOf(path); + nr = readUrl.substring(place + path.length(), place + path.length() + 3); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_OTHER); + uint8_t relays = ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); + if (nr.toInt() <= relays && ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { + webContentBuffer += F("

"); + webContentBuffer += S_IMPULSE_COUNTER_SETTINGS_NR; + webContentBuffer += F(" "); + webContentBuffer += nr; + webContentBuffer += F("

"); + webContentBuffer += F(""); + webContentBuffer += F(""); + addNumberBox(webContentBuffer, INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, + 99999999); + webContentBuffer += F(""); + + webContentBuffer += F("
"); + } + else { + webContentBuffer += F("

"); + webContentBuffer += S_NO_IMPULSE_COUNTER_NR; + webContentBuffer += F(" "); + webContentBuffer += nr; + webContentBuffer += F("

"); + } + webContentBuffer += F("
"); + webContentBuffer += F("

"); + WebServer->sendContent(); +} +#endif + +#if defined(SUPLA_HLW8012) +void SuplaWebPageSensor::handleHLW8012Calibrate() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + suplaWebpageHLW8012Calibrate(0); +} + +void SuplaWebPageSensor::handleHLW8012CalibrateSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + double calibPower, calibVoltage = 0; + String input = INPUT_CALIB_POWER; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + calibPower = WebServer->httpServer.arg(input).toDouble(); + } + + input = INPUT_CALIB_VOLTAGE; + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + calibVoltage = WebServer->httpServer.arg(input).toDouble(); + } + + if (calibPower && calibVoltage) { + Supla::GUI::counterHLW8012->calibrate(calibPower, calibVoltage); + suplaWebpageHLW8012Calibrate(1); + } + else { + suplaWebpageHLW8012Calibrate(6); + } +} + +void SuplaWebPageSensor::suplaWebpageHLW8012Calibrate(uint8_t save) { + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_HLW8012_CALIBRATE); + + addFormHeader(webContentBuffer); + webContentBuffer += F("

Current Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getCurrentMultiplier(); + webContentBuffer += F("
Voltage Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getVoltageMultiplier(); + webContentBuffer += F("
Power Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getPowerMultiplier(); + webContentBuffer += F("

"); + addFormHeaderEnd(webContentBuffer); + + addForm(webContentBuffer, F("post"), PATH_SAVE_HLW8012_CALIBRATE); + addFormHeader(webContentBuffer, F("Ustawienia kalibracji")); + addNumberBox(webContentBuffer, INPUT_CALIB_POWER, F("Moc żarówki [W]"), F("25"), true); + addNumberBox(webContentBuffer, INPUT_CALIB_VOLTAGE, F("Napięcie [V]"), F("230"), true); + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, F("Kalibracja")); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_OTHER); + WebServer->sendContent(); +} +#endif \ No newline at end of file diff --git a/src/SuplaWebPageSensor.h b/src/SuplaWebPageSensor.h new file mode 100644 index 00000000..c539868f --- /dev/null +++ b/src/SuplaWebPageSensor.h @@ -0,0 +1,151 @@ +#ifndef SuplaWebPageSensor_h +#define SuplaWebPageSensor_h + +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "SuplaWebPageControl.h" + +#define PATH_MULTI_DS "multids" +#define PATH_SAVE_MULTI_DS "savemultids" +#define PATH_1WIRE "1wire" +#define PATH_SAVE_1WIRE "save1wire" +#define PATH_I2C "i2c" +#define PATH_SAVE_I2C "savei2c" +#define PATH_SPI "spi" +#define PATH_SAVE_SPI "savespi" +#define PATH_OTHER "other" +#define PATH_SAVE_OTHER "saveother" +#define PATH_IMPULSE_COUNTER_SET "setimpulsecounter" +#define PATH_SAVE_IMPULSE_COUNTER_SET "savesetimpulsecounter" + +#define INPUT_MULTI_DS_GPIO "mdsg" +#define INPUT_DHT11_GPIO "dht11" +#define INPUT_DHT22_GPIO "dht22" +#define INPUT_SDA_GPIO "sdag" +#define INPUT_SCL_GPIO "sclg" +#define INPUT_BME280 "bme280" +#define INPUT_ALTITUDE_BME280 "abme280" +#define INPUT_SHT3x "sht30" +#define INPUT_SI7021 "si7021" +#define INPUT_OLED "oled" +#define INPUT_OLED_ANIMATION "oleda" +#define INPUT_OLED_BRIGHTNESS "oledb" +#define INPUT_MCP23017 "mcp" +#define INPUT_SI7021_SONOFF "si7021sonoff" +#define INPUT_TRIG_GPIO "trig" +#define INPUT_ECHO_GPIO "echo" +#define INPUT_MAX_DHT11 "mdht11" +#define INPUT_MAX_DHT22 "mdht22" +#define INPUT_MAX_DS18B20 "maxds" +#define INPUT_CLK_GPIO "clk" +#define INPUT_CS_GPIO "cs" +#define INPUT_D0_GPIO "d0" +#define INPUT_MAX6675 "max6675" +#define INPUT_IMPULSE_COUNTER_GPIO "ic" +#define INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "icdt" +#define INPUT_IMPULSE_COUNTER_PULL_UP "icpu" +#define INPUT_IMPULSE_COUNTER_RAISING_EDGE "icre" +#define INPUT_IMPULSE_COUNTER_CHANGE_VALUE "iccv" +#define INPUT_MAX_IMPULSE_COUNTER "imic" + +#if defined(SUPLA_BME280) || defined(SUPLA_SHT3x) || defined(SUPLA_SI7021) || defined(SUPLA_MAX6675) || defined(SUPLA_MCP23017) +enum _sensor +{ + SENSOR_BME280, + SENSOR_SHT3x, + SENSOR_SI7021, + SENSOR_MAX6675, + SENSOR_OLED, + SENSOR_MCP23017 +}; +#endif + +#ifdef SUPLA_BME280 +enum _bmeAdress +{ + BME280_ADDRESS_0X76 = 1, + BME280_ADDRESS_0X77, + BME280_ADDRESS_0X76_AND_0X77 +}; +#endif + +#ifdef SUPLA_SHT3x +enum _shtAdress +{ + SHT3x_ADDRESS_0X44 = 1, + SHT3x_ADDRESS_0X45, + SHT3x_ADDRESS_0X44_AND_0X45 +}; +#endif + +class SuplaWebPageSensor { + public: + SuplaWebPageSensor(); + void createWebPageSensor(); + +#ifdef SUPLA_DS18B20 +#define INPUT_DS18B20_ADDR "dsaddr" +#define INPUT_DS18B20_NAME "dsname" + + void handleSearchDS(); + void handleDSSave(); + void showDS18B20(bool readonly = false); +#endif + +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) + void handle1Wire(); + void handle1WireSave(); + void handlei2c(); + void handlei2cSave(); + void supla_webpage_i2c(int save); +#endif + +#if defined(SUPLA_MAX6675) + void handleSpi(); + void handleSpiSave(); +#endif + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) + void handleOther(); + void handleOtherSave(); + + void handleImpulseCounterSet(); + void handleImpulseCounterSaveSet(); + void supla_impulse_counter_set(int save); +#endif + + private: +#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) + void supla_webpage_1wire(int save); +#ifdef SUPLA_DS18B20 + void supla_webpage_search(int save); +#endif +#endif + +#if defined(SUPLA_MAX6675) + void supla_webpage_spi(int save); +#endif + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) + void supla_webpage_other(int save); +#endif + +#if defined(SUPLA_HLW8012) +#define INPUT_CF "cf" +#define INPUT_CF1 "cf1" +#define INPUT_SEL "sel" + +#define PATH_HLW8012_CALIBRATE "calibrate" +#define PATH_SAVE_HLW8012_CALIBRATE "savecalibrate" + +#define INPUT_CALIB_POWER "power" +#define INPUT_CALIB_VOLTAGE "voltage" + + void handleHLW8012Calibrate(); + void handleHLW8012CalibrateSave(); + void suplaWebpageHLW8012Calibrate(uint8_t save); +#endif +}; + +extern SuplaWebPageSensor* WebPageSensor; +#endif // SuplaWebPageSensor_h diff --git a/src/SuplaWebPageStatusLed.cpp b/src/SuplaWebPageStatusLed.cpp new file mode 100644 index 00000000..b6d110eb --- /dev/null +++ b/src/SuplaWebPageStatusLed.cpp @@ -0,0 +1,71 @@ +#include "SuplaWebPageStatusLed.h" +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" +#include "GUIGenericCommon.h" +#include "Markup.h" + +void createWebStatusLed() { + WebServer->httpServer.on(getURL(PATH_LED), handleStatusLed); + WebServer->httpServer.on(getURL(PATH_SAVE_LED), handleStatusLedSave); +} + +void handleStatusLed() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + webStatusLed(0); +} + +void handleStatusLedSave() { + uint8_t nr; + + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + if (strcmp(WebServer->httpServer.arg(INPUT_LEVEL_LED).c_str(), "") != 0) { + ConfigManager->set(KEY_LEVEL_LED, WebServer->httpServer.arg(INPUT_LEVEL_LED).c_str()); + } + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + if (!WebServer->saveGPIO(INPUT_LED, FUNCTION_LED, nr)) { + webStatusLed(6); + return; + } + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + webStatusLed(1); + break; + case E_CONFIG_FILE_OPEN: + webStatusLed(2); + break; + } +} + +void webStatusLed(int save) { + uint8_t nr, selected; + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_LED); + addForm(webContentBuffer, F("post"), PATH_SAVE_LED); + + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + F(" LED")); + selected = ConfigManager->get(KEY_LEVEL_LED)->getValueInt(); + addListBox(webContentBuffer, INPUT_LEVEL_LED, S_STATE_CONTROL, LEVEL_P, 2, selected); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + addListGPIOBox(webContentBuffer, INPUT_LED, F("LED"), FUNCTION_LED, nr); + } + + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendContent(); +} \ No newline at end of file diff --git a/src/SuplaWebPageStatusLed.h b/src/SuplaWebPageStatusLed.h new file mode 100644 index 00000000..3da59441 --- /dev/null +++ b/src/SuplaWebPageStatusLed.h @@ -0,0 +1,14 @@ +#ifndef SuplaWebPageStatusLed_h +#define SuplaWebPageStatusLed_h + +#define PATH_LED "led" +#define PATH_SAVE_LED "saveled" +#define INPUT_LED "led" +#define INPUT_LEVEL_LED "ill" + +void createWebStatusLed(); +void handleStatusLed(); +void handleStatusLedSave(); +void webStatusLed(int save); + +#endif // SuplaWebPageStatusLed_h diff --git a/src/SuplaWebPageTools.cpp b/src/SuplaWebPageTools.cpp new file mode 100644 index 00000000..4cd9173a --- /dev/null +++ b/src/SuplaWebPageTools.cpp @@ -0,0 +1,40 @@ +#include "SuplaWebPageTools.h" +#include "SuplaDeviceGUI.h" + +void createWebTools() { + WebServer->httpServer.on(getURL(PATH_TOOLS), handleTools); + + WebServer->httpServer.on(getURL(PATH_FACTORY_RESET), [&]() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + WebServer->httpServer.sendHeader("Location", "/"); + WebServer->httpServer.send(303); + WebServer->supla_webpage_start(0); + ConfigESP->factoryReset(true); + }); +} + +void handleTools() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + addFormHeader(webContentBuffer, F("Tools")); + //#ifdef SUPLA_BUTTON + addButton(webContentBuffer, F("Save config"), PATH_DOWNLOAD); + //#endif + //#ifdef SUPLA_BUTTON + addButton(webContentBuffer, F("Load config"), PATH_UPLOAD); + //#endif +#ifdef SUPLA_OTA + addButton(webContentBuffer, S_UPDATE, PATH_UPDATE_HENDLE); +#endif + addButton(webContentBuffer, F("Factory reset"), PATH_FACTORY_RESET); + addFormHeaderEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, ""); + + WebServer->sendContent(); +} \ No newline at end of file diff --git a/src/SuplaWebPageTools.h b/src/SuplaWebPageTools.h new file mode 100644 index 00000000..ab4f4029 --- /dev/null +++ b/src/SuplaWebPageTools.h @@ -0,0 +1,11 @@ + +#ifndef SuplaWebPageTools_h +#define SuplaWebPageTools_h + +#define PATH_TOOLS "tools" +#define PATH_FACTORY_RESET "factoryreset" + +void createWebTools(); +void handleTools(); + +#endif // ifndef SuplaWebPageTools_h diff --git a/src/SuplaWebPageUpload.cpp b/src/SuplaWebPageUpload.cpp new file mode 100644 index 00000000..c05e6f74 --- /dev/null +++ b/src/SuplaWebPageUpload.cpp @@ -0,0 +1,76 @@ +#include "SuplaWebPageUpload.h" +#include "SuplaDeviceGUI.h" + +static const char uploadIndex[] PROGMEM = + R"(
+ +
+ +
)"; + +File dataFile; + +void createWebUpload() { + // WebServer->httpServer.on(F("/upload"), HTTP_GET, handleUpload); + WebServer->httpServer.on(getURL(PATH_UPLOAD), HTTP_GET, []() { handleUpload(); }); + // WebServer->httpServer.on(F("/upload"), HTTP_POST, handleFileUpload); + WebServer->httpServer.on( + getURL(PATH_UPLOAD), HTTP_POST, []() { WebServer->httpServer.send(200); }, handleFileUpload); +} + +void handleUpload(int save) { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += F("Wgraj konfiguracje"); + webContentBuffer += F("

"); + webContentBuffer += F("
"); + webContentBuffer += FPSTR(uploadIndex); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + + WebServer->sendContent(); + // WebServer->httpServer.send(200, PSTR("text/html"), FPSTR(uploadIndex)); +} + +void handleFileUpload() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) + return WebServer->httpServer.requestAuthentication(); + } + + if (SPIFFS.begin()) { + HTTPUpload& upload = WebServer->httpServer.upload(); + + if (upload.status == UPLOAD_FILE_START) { + dataFile = SPIFFS.open(CONFIG_FILE_PATH, "w"); + } + else if (upload.status == UPLOAD_FILE_WRITE) { + if (dataFile) + dataFile.write(upload.buf, upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) { + if (dataFile) { + dataFile.close(); + // WebServer->httpServer.sendHeader("Location", "/upload"); + // WebServer->httpServer.send(303); + ConfigManager->load(); + handleUpload(1); + } + else { + handleUpload(6); + // WebServer->httpServer.send(500, "text/plain", "500: couldn't create file"); + } + } + } +} \ No newline at end of file diff --git a/src/SuplaWebPageUpload.h b/src/SuplaWebPageUpload.h new file mode 100644 index 00000000..b7635fe6 --- /dev/null +++ b/src/SuplaWebPageUpload.h @@ -0,0 +1,11 @@ + +#ifndef SuplaWebPageUpload_h +#define SuplaWebPageUpload_h + +#define PATH_UPLOAD "upload" + +void createWebUpload(); +void handleUpload(int save = 0); +void handleFileUpload(); + +#endif // ifndef SuplaWebPageUpload_h diff --git a/src/SuplaWebServer.cpp b/src/SuplaWebServer.cpp new file mode 100644 index 00000000..c20e6b4a --- /dev/null +++ b/src/SuplaWebServer.cpp @@ -0,0 +1,427 @@ +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "SuplaWebServer.h" +#include "SuplaDeviceGUI.h" +#include "SuplaWebPageConfig.h" +#include "SuplaWebPageControl.h" +#include "SuplaWebPageRelay.h" +#include "SuplaWebPageSensor.h" +#include "SuplaWebPageStatusLed.h" +#include "SuplaCommonPROGMEM.h" +#include "SuplaTemplateBoard.h" +#include "Markup.h" + +String webContentBuffer; + +SuplaWebServer::SuplaWebServer() { +} + +void SuplaWebServer::begin() { + this->createWebServer(); + + strcpy(this->www_username, ConfigManager->get(KEY_LOGIN)->getValue()); + strcpy(this->www_password, ConfigManager->get(KEY_LOGIN_PASS)->getValue()); + + httpServer.onNotFound(std::bind(&SuplaWebServer::handleNotFound, this)); + httpServer.begin(80); +} + +void SuplaWebServer::iterateAlways() { + httpServer.handleClient(); +} + +void SuplaWebServer::createWebServer() { + String path = PATH_START; + httpServer.on(path, HTTP_GET, std::bind(&SuplaWebServer::handle, this)); + path = PATH_START; + httpServer.on(path, std::bind(&SuplaWebServer::handleSave, this)); + path = PATH_START; + path += PATH_REBOT; + httpServer.on(path, std::bind(&SuplaWebServer::supla_webpage_reboot, this)); + path = PATH_START; + path += PATH_DEVICE_SETTINGS; + httpServer.on(path, std::bind(&SuplaWebServer::handleDeviceSettings, this)); + path = PATH_START; + path += PATH_SAVE_BOARD; + httpServer.on(path, std::bind(&SuplaWebServer::handleBoardSave, this)); + +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + WebPageRelay->createWebPageRelay(); +#endif +#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) + WebPageControl->createWebPageControl(); +#endif + WebPageSensor->createWebPageSensor(); +#ifdef SUPLA_CONFIG + WebPageConfig->createWebPageConfig(); +#endif +#ifdef SUPLA_OTA + httpUpdater.setup(&httpServer, this->www_username, this->www_password); +#endif + + createWebDownload(); + createWebUpload(); + createWebTools(); + createWebStatusLed(); +} + +void SuplaWebServer::handle() { + // Serial.println(F("HTTP_GET - metoda handle")); + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer.authenticate(this->www_username, this->www_password)) + return httpServer.requestAuthentication(); + } + supla_webpage_start(0); +} + +void SuplaWebServer::handleSave() { + // Serial.println(F("HTTP_POST - metoda handleSave")); + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer.authenticate(this->www_username, this->www_password)) + return httpServer.requestAuthentication(); + } + + if (strcmp(httpServer.arg(PATH_REBOT).c_str(), "1") == 0) { + ConfigESP->rebootESP(); + return; + } + + ConfigManager->set(KEY_WIFI_SSID, httpServer.arg(INPUT_WIFI_SSID).c_str()); + ConfigManager->set(KEY_WIFI_PASS, httpServer.arg(INPUT_WIFI_PASS).c_str()); + ConfigManager->set(KEY_SUPLA_SERVER, httpServer.arg(INPUT_SERVER).c_str()); + ConfigManager->set(KEY_SUPLA_EMAIL, httpServer.arg(INPUT_EMAIL).c_str()); + ConfigManager->set(KEY_HOST_NAME, httpServer.arg(INPUT_HOSTNAME).c_str()); + ConfigManager->set(KEY_LOGIN, httpServer.arg(INPUT_MODUL_LOGIN).c_str()); + ConfigManager->set(KEY_LOGIN_PASS, httpServer.arg(INPUT_MODUL_PASS).c_str()); + +#ifdef SUPLA_ROLLERSHUTTER + if (strcmp(WebServer->httpServer.arg(INPUT_ROLLERSHUTTER).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_ROLLERSHUTTER, httpServer.arg(INPUT_ROLLERSHUTTER).c_str()); + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + // Serial.println(F("E_CONFIG_OK: Dane zapisane")); + if (ConfigESP->configModeESP == NORMAL_MODE) { + supla_webpage_start(1); + ConfigESP->rebootESP(); + } + else { + supla_webpage_start(7); + } + break; + + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + supla_webpage_start(4); + break; + } +} + +void SuplaWebServer::handleDeviceSettings() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer.authenticate(www_username, www_password)) + return httpServer.requestAuthentication(); + } + deviceSettings(0); +} + +void SuplaWebServer::supla_webpage_start(int save) { + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(); + + addForm(webContentBuffer, F("post")); + addFormHeader(webContentBuffer, S_SETTING_WIFI_SSID); + addTextBox(webContentBuffer, INPUT_WIFI_SSID, S_WIFI_SSID, KEY_WIFI_SSID, 0, MAX_SSID, true); + addTextBoxPassword(webContentBuffer, INPUT_WIFI_PASS, S_WIFI_PASS, KEY_WIFI_PASS, MIN_PASSWORD, MAX_PASSWORD, true); + addTextBox(webContentBuffer, INPUT_HOSTNAME, S_HOST_NAME, KEY_HOST_NAME, 0, MAX_HOSTNAME, true); + addFormHeaderEnd(webContentBuffer); + + addFormHeader(webContentBuffer, S_SETTING_SUPLA); + addTextBox(webContentBuffer, INPUT_SERVER, S_SUPLA_SERVER, KEY_SUPLA_SERVER, DEFAULT_SERVER, 0, MAX_SUPLA_SERVER, true); + addTextBox(webContentBuffer, INPUT_EMAIL, S_SUPLA_EMAIL, KEY_SUPLA_EMAIL, DEFAULT_EMAIL, 0, MAX_EMAIL, true); + addFormHeaderEnd(webContentBuffer); + + addFormHeader(webContentBuffer, S_SETTING_ADMIN); + addTextBox(webContentBuffer, INPUT_MODUL_LOGIN, S_LOGIN, KEY_LOGIN, 0, MAX_MLOGIN, true); + addTextBoxPassword(webContentBuffer, INPUT_MODUL_PASS, S_LOGIN_PASS, KEY_LOGIN_PASS, MIN_PASSWORD, MAX_MPASSWORD, true); + addFormHeaderEnd(webContentBuffer); + +#ifdef SUPLA_ROLLERSHUTTER + uint8_t maxrollershutter = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); + if (maxrollershutter >= 2) { + addFormHeader(webContentBuffer, S_ROLLERSHUTTERS); + addNumberBox(webContentBuffer, INPUT_ROLLERSHUTTER, S_QUANTITY, KEY_MAX_ROLLERSHUTTER, (maxrollershutter / 2)); + addFormHeaderEnd(webContentBuffer); + } +#endif + +#ifdef SUPLA_DS18B20 + WebPageSensor->showDS18B20(true); +#endif + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_DEVICE_SETTINGS, PATH_DEVICE_SETTINGS); + addButton(webContentBuffer, F("Tools"), PATH_TOOLS); + + WebServer->sendContent(); +} + +void SuplaWebServer::supla_webpage_reboot() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer.authenticate(www_username, www_password)) + return httpServer.requestAuthentication(); + } + supla_webpage_start(2); + ConfigESP->rebootESP(); +} + +void SuplaWebServer::deviceSettings(int save) { + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_DEVICE_SETTINGS); + + webContentBuffer += F("
"); + + addFormHeader(webContentBuffer, S_TEMPLATE_BOARD); + uint8_t selected = ConfigManager->get(KEY_BOARD)->getValueInt(); + addListBox(webContentBuffer, INPUT_BOARD, S_TYPE, BOARD_P, MAX_MODULE, selected); + addFormHeaderEnd(webContentBuffer); + + webContentBuffer += F("


"); + + addFormHeader(webContentBuffer, S_DEVICE_SETTINGS); +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + addButton(webContentBuffer, S_RELAYS, PATH_RELAY); +#endif + +#ifdef SUPLA_BUTTON + addButton(webContentBuffer, S_BUTTONS, PATH_CONTROL); +#endif + +#ifdef SUPLA_LIMIT_SWITCH + addButton(webContentBuffer, F("KONTAKTRON"), PATH_SWITCH); +#endif + + addButton(webContentBuffer, F("LED"), PATH_LED); + +#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) + addButton(webContentBuffer, S_SENSORS_1WIRE, PATH_1WIRE); +#endif + +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) + addButton(webContentBuffer, S_SENSORS_I2C, PATH_I2C); +#endif + +#if defined(SUPLA_MAX6675) + addButton(webContentBuffer, S_SENSORS_SPI, PATH_SPI); +#endif + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) + addButton(webContentBuffer, S_SENSORS_OTHER, PATH_OTHER); +#endif + +#ifdef SUPLA_CONFIG + addButton(webContentBuffer, S_LED_BUTTON_CFG, PATH_CONFIG); +#endif + addFormHeaderEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, ""); + + WebServer->sendContent(); +} + +void SuplaWebServer::handleBoardSave() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer.authenticate(this->www_username, this->www_password)) + return httpServer.requestAuthentication(); + } + String input = INPUT_BOARD; + + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { + ConfigManager->set(KEY_BOARD, httpServer.arg(input).c_str()); + + int nr; + uint8_t key; + for (nr = 0; nr <= 17; nr++) { + key = KEY_GPIO + nr; + ConfigManager->set(key, ""); + } + + chooseTemplateBoard(WebServer->httpServer.arg(input).toInt()); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + deviceSettings(1); + break; + case E_CONFIG_FILE_OPEN: + deviceSettings(2); + break; + } +} + +void SuplaWebServer::sendContent() { + // httpServer.send(200, "text/html", ""); + // const int bufferSize = 1000; + // String _buffer; + //_buffer.reserve(bufferSize); + // int bufferCounter = 0; + + httpServer.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + httpServer.sendHeader("Pragma", "no-cache"); + httpServer.sendHeader("Expires", "-1"); + httpServer.setContentLength(CONTENT_LENGTH_UNKNOWN); + httpServer.chunkedResponseModeStart(200, "text/html"); + + httpServer.sendContent_P(HTTP_META); + httpServer.sendContent_P(HTTP_FAVICON); + httpServer.sendContent_P(HTTP_STYLE); + httpServer.sendContent_P(HTTP_LOGO); + + String summary = FPSTR(HTTP_SUMMARY); + + summary.replace("{h}", ConfigManager->get(KEY_HOST_NAME)->getValue()); + summary.replace("{s}", ConfigESP->getLastStatusSupla()); + summary.replace("{v}", Supla::Channel::reg_dev.SoftVer); + summary.replace("{g}", ConfigManager->get(KEY_SUPLA_GUID)->getValueHex(SUPLA_GUID_SIZE)); + summary.replace("{m}", ConfigESP->getMacAddress(true)); + summary.replace("{f}", String(ESP.getFreeHeap() / 1024.0)); + + httpServer.sendContent(summary); + httpServer.sendContent_P(HTTP_COPYRIGHT); + + // httpServer.send(200, "text/html", ""); + /*for (int i = 0; i < fileSize; i++) { + _buffer += content[i]; + bufferCounter++; + + if (bufferCounter >= bufferSize) { + httpServer.sendContent(_buffer); + yield(); + bufferCounter = 0; + _buffer = ""; + } + } + if (bufferCounter > 0) { + httpServer.sendContent(_buffer); + yield(); + bufferCounter = 0; + _buffer = ""; + }*/ + + httpServer.sendContent(webContentBuffer); + httpServer.sendContent_P(HTTP_RBT); + httpServer.chunkedResponseFinalize(); + +#ifdef DEBUG_MODE + Serial.printf_P(PSTR("Content size=%d\n"), webContentBuffer.length()); + Serial.printf_P(PSTR("Sent INDEX...Free mem=%d\n"), ESP.getFreeHeap()); + checkRAM(); +#endif + webContentBuffer = ""; +} + +void SuplaWebServer::handleNotFound() { + httpServer.sendHeader("Location", "/", true); + httpServer.send(302, "text/plane", ""); +} + +bool SuplaWebServer::saveGPIO(const String& _input, uint8_t function, uint8_t nr, const String& input_max) { + uint8_t current_value, key; + String input; + input = _input; + + if (nr != 0) { + input += nr; + } + else { + nr = 1; + } + + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") == 0) { + return true; + } + + key = KEY_GPIO + WebServer->httpServer.arg(input).toInt(); + + if (ConfigESP->getGpio(nr, function) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { + ConfigESP->clearGpio(ConfigESP->getGpio(nr, function), function); + } + + if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { + if (ConfigManager->get(key)->getElement(FUNCTION).toInt() == FUNCTION_OFF) { + ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, function, 1); + } + else if (ConfigESP->getGpio(nr, function) == WebServer->httpServer.arg(input).toInt() && + ConfigManager->get(key)->getElement(FUNCTION).toInt() == function) { + ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, function, ConfigESP->getLevel(nr, function)); + } + else if (function == FUNCTION_CFG_BUTTON) { + ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), FUNCTION_CFG_BUTTON); + } + else { + return false; + } + } + + if (input_max != "\n") { + current_value = WebServer->httpServer.arg(input_max).toInt(); + if (ConfigManager->get(key)->getElement(NR).toInt() > current_value) { + ConfigESP->clearGpio(ConfigESP->getGpio(nr, function), function); + } + } + return true; +} + +bool SuplaWebServer::saveGpioMCP23017(const String& _input, uint8_t function, uint8_t nr, const String& input_max) { + uint8_t key, address, addressInput, gpio, gpioInput, functionElementInput; + String input = _input + nr; + + if (strcmp(WebServer->httpServer.arg(input).c_str(), "") == 0) { + return true; + } + + addressInput = WebServer->httpServer.arg(INPUT_ADRESS_MCP23017).toInt(); + + gpioInput = WebServer->httpServer.arg(input).toInt(); + key = KEY_GPIO + gpioInput; + functionElementInput = ConfigManager->get(key)->getElement(ConfigESP->getFunctionMCP23017(addressInput)).toInt(); + gpio = ConfigESP->getGpioMCP23017(nr, function); + if (addressInput == OFF_MCP23017) { + ConfigESP->clearGpioMCP23017(gpio, nr, function); + return true; + } + + if (gpioInput != OFF_GPIO) { + if (functionElementInput == FUNCTION_OFF) { + ConfigESP->setGpioMCP23017(gpioInput, addressInput, nr, function, 1, 0); + } + else if (gpio == gpioInput && functionElementInput == function) { + ConfigESP->setGpioMCP23017(gpioInput, addressInput, nr, function, ConfigESP->getLevel(nr, function), ConfigESP->getMemory(nr, function)); + } + else { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/SuplaWebServer.h b/src/SuplaWebServer.h similarity index 53% rename from SuplaWebServer.h rename to src/SuplaWebServer.h index 7d1b73b6..ce5e8d77 100644 --- a/SuplaWebServer.h +++ b/src/SuplaWebServer.h @@ -1,97 +1,110 @@ -/* - Copyright (C) krycha88 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#ifndef SuplaWebServer_h -#define SuplaWebServer_h - -#include -#include -#include - -#include "SuplaConfigManager.h" - -#define GUI_BLUE "#005c96" -#define GUI_GREEN "#00D151" - -#define DEFAULT_LOGIN "admin" -#define DEFAULT_PASSWORD "password" - -#define MAX_GPIO 13 -#define OFF_GPIO 17 - -#define PATH_START "/" -#define PATH_SAVE_LOGIN "savelogin" -#define UPDATE_PATH "/firmware" -#define PATH_UPDATE "update" -#define PATH_REBOT "rbt" -#define PATH_DEVICE_SETTINGS "devicesettings" -#define PATH_DEFAULT_SETTINGS "defaultsettings" -#define PATH_LOGIN_SETTINGS "loginsettings" -#define PATH_SAVE_BOARD "saveboard" - -#define INPUT_WIFI_SSID "sid" -#define INPUT_WIFI_PASS "wpw" -#define INPUT_EMAIL "eml" -#define INPUT_SERVER "svr" -#define INPUT_HOSTNAME "shn" -#define INPUT_MODUL_LOGIN "mlg" -#define INPUT_MODUL_PASS "mps" -#define INPUT_ROLLERSHUTTER "irsr" -#define INPUT_BOARD "board" - -class SuplaWebServer : public Supla::Element { - public: - String selectGPIO(const char* input, uint8_t function, uint8_t nr = 0); - SuplaWebServer(); - void begin(); - - char www_username[MAX_MLOGIN]; - char www_password[MAX_MPASSWORD]; - char* update_path = (char*)UPDATE_PATH; - - const String SuplaFavicon(); - const String SuplaIconEdit(); - const String SuplaJavaScript(String java_return = PATH_START); - const String SuplaSaveResult(int save); - - void sendContent(const String content); - void rebootESP(); - - ESP8266WebServer httpServer = {80}; - ESP8266HTTPUpdateServer httpUpdater; - - private: - void iterateAlways(); - void handle(); - void handleSave(); - void handleWizardSave(); - void handleFirmwareUp(); - void handleDeviceSettings(); - void handleBoardSave(); - void handleDefaultSettings(); - void handleLoginSettings(); - void createWebServer(); - - String supla_webpage_start(int save); - String supla_webpage_upddate(); - void supla_webpage_reboot(); - String deviceSettings(int save); - String loginSettings(); - - void redirectToIndex(); -}; - -#endif // SuplaWebServer_h +/* + Copyright (C) krycha88 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SuplaWebServer_h +#define SuplaWebServer_h +#include "GUI-Generic_Config.h" + +#ifdef SUPLA_OTA +#include "SuplaHTTPUpdateServer.h" +#endif +#include +#include + +#include "SuplaConfigManager.h" + +#define GUI_BLUE "#005c96" +#define GUI_GREEN "#00D151" + +#define DEFAULT_LOGIN "admin" +#define DEFAULT_PASSWORD "password" + +#define PATH_START "/" +#define PATH_SAVE_LOGIN "savelogin" +#define PATH_REBOT "rbt" +#define PATH_DEVICE_SETTINGS "devicesettings" +#define PATH_DEFAULT_SETTINGS "defaultsettings" +#define PATH_LOGIN_SETTINGS "loginsettings" +#define PATH_SAVE_BOARD "saveboard" + +#define INPUT_WIFI_SSID "sid" +#define INPUT_WIFI_PASS "wpw" +#define INPUT_EMAIL "eml" +#define INPUT_SERVER "svr" +#define INPUT_HOSTNAME "shn" +#define INPUT_MODUL_LOGIN "mlg" +#define INPUT_MODUL_PASS "mps" +#define INPUT_ROLLERSHUTTER "irsr" +#define INPUT_BOARD "board" + + +extern String webContentBuffer; + +//https://www.esp8266.com/viewtopic.php?p=84249#p84249 +class MyWebServer : public ESP8266WebServer { + public: + virtual ~MyWebServer() { + if (this->_currentArgs) { + delete[] this->_currentArgs; + this->_currentArgs = nullptr; + } + + if (this->_postArgs) { + delete[] this->_postArgs; + this->_postArgs = nullptr; + } + } +}; + +class SuplaWebServer : public Supla::Element { + public: + SuplaWebServer(); + void begin(); + + char www_username[MAX_MLOGIN]; + char www_password[MAX_MPASSWORD]; + + void supla_webpage_start(int save); + + //void sendContent(const String& content); + void sendContent(); + + MyWebServer httpServer; + +#ifdef SUPLA_OTA + ESP8266HTTPUpdateServer httpUpdater; +#endif + + bool saveGPIO(const String& _input, uint8_t function, uint8_t nr = 0, const String& input_max = "\n"); + bool saveGpioMCP23017(const String& _input, uint8_t function, uint8_t nr = 0, const String& input_max = "\n"); + + private: + void iterateAlways(); + void handle(); + void handleSave(); + void handleWizardSave(); + void handleDeviceSettings(); + void handleBoardSave(); + void handleDefaultSettings(); + void handleLoginSettings(); + void createWebServer(); + + void supla_webpage_reboot(); + void deviceSettings(int save); + + void handleNotFound(); +}; +#endif // SuplaWebServer_h diff --git a/src/language/de.h b/src/language/de.h new file mode 100644 index 00000000..7384c7d4 --- /dev/null +++ b/src/language/de.h @@ -0,0 +1,107 @@ +#ifndef _LANGUAGE_DE_S_H_ +#define _LANGUAGE_DE_S_H_ + +#define S_SETTING_WIFI_SSID "WIFI Einstellung" +#define S_WIFI_SSID "WIFI Name" +#define S_WIFI_PASS "Passwort" +#define S_HOST_NAME "Modulname" +#define S_SETTING_SUPLA "SUPLA Einstellung" +#define S_SUPLA_SERVER "Serveradresse" +#define S_SUPLA_EMAIL "Email" +#define S_SETTING_ADMIN "Administratordaten" +#define S_LOGIN "Login" +#define S_LOGIN_PASS "Passwort" +#define S_ROLLERSHUTTERS "Schalousien" +#define S_SAVE "Speichern" +#define S_DEVICE_SETTINGS "Gerät Einstellung" +#define S_UPDATE "Aktualisierung" +#define S_RESTART "Neustart" +#define S_RETURN "Zurück" +#define S_TEMPLATE_BOARD "Modul Vorlage" +#define S_TYPE "Typ" +#define S_RELAYS "RELAIS" +#define S_BUTTONS "TASTEN" +#define S_SENSORS_1WIRE "1Wire SENSOREN" +#define S_SENSORS_I2C "i2c SENSOREN" +#define S_SENSORS_SPI "SPI SENSOREN" +#define S_SENSORS_OTHER "ANDERE SENSOREN" +#define S_LED_BUTTON_CFG "LED, TASTENKONFIGURATION" +#define S_CFG_MODE "Modus" +#define S_QUANTITY "MENGE" +#define S_GPIO_SETTINGS_FOR_RELAYS "GPIO-Parameter für Relais" +#define S_RELAY "RELAIS" +#define S_RELAY_NR_SETTINGS "Konfiguration des Relais Nr." +#define S_NO_RELAY_NR "Kein Relais Nr." +#define S_STATE_CONTROL "Status Kontrolle" +#define S_REACTION_AFTER_RESET "Reaktion nach Neustart" +#define S_GPIO_SETTINGS_FOR_BUTTONS "GPIO-Parameter für Tasten" +#define S_BUTTON "TASTE" +#define S_BUTTON_NR_SETTINGS "Parameter für Tasten Nr." +#define S_NO_BUTTON_NR "Kein Tasten Nr." +#define S_REACTION_TO "Reaktion auf" +#define S_ACTION "Action" +#define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "GPIO-Einstellungen für Endschalter" +#define S_LIMIT_SWITCH "ENDSCHALTER" +#define S_GPIO_SETTINGS_FOR "GPIO-Parameter für" +#define S_FOUND "Gefunden" +#define S_NO_SENSORS_CONNECTED "Fehlende angeschlossene Sensoren" +#define S_SAVE_FOUND "Gefunden Speichern" +#define S_TEMPERATURE "Temperatur" +#define S_NAME "Name" +#define S_ALTITUDE_ABOVE_SEA_LEVEL "Höhe über dem Meeresspiegel" +#define S_GPIO_SETTINGS_FOR_CONFIG "GPIO-Parameter für CONFIG" +#define S_SOFTWARE_UPDATE "Software-Update" +#define S_DATA_SAVED "Daten Gespeichert" +#define S_RESTART_MODULE "Modul Neustart" +#define S_DATA_ERASED_RESTART_DEVICE "Daten Gelöscht: Modul Neu Starten" +#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Schreibfehler: Datei kann nicht gelesen werden. FS-Partition fehlt." +#define S_DATA_SAVED_RESTART_MODULE "Daten Gespeichert: Modul Neustart." +#define S_WRITE_ERROR_BAD_DATA "Schreibfehler - falsche Daten." + +//#### SuplaConfigESP.cpp #### +#define S_ALREADY_INITIATED "Bereits gestartet" +#define S_NOT_ASSIGNED_CB "CB nicht zugewiesen" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "Falsche GUID-ID oder INAKTIVE Geräteregistrierung" +#define S_UNKNOWN_SEVER_ADDRESS "Unbekannte Serveradresse" +#define S_UNKNOWN_ID "Unbekannte ID" +#define S_INITIATED "Gestartet" +#define S_CHANNEL_LIMIT_EXCEEDED "Kanallimit überschritten" +#define S_DISCONNECTED "off-line" +#define S_REGISTRATION_IS_PENDING "Ausstehende Registrierung" +#define S_VARIABLE_ERROR "Fehler in Variable" +#define S_PROTOCOL_VERSION_ERROR "Protokollversionsfehler" +#define S_BAD_CREDENTIALS "falsche Anmeldeinformationen" +#define S_TEMPORARILY_UNAVAILABLE "Vorübergehend nicht erreichbar" +#define S_LOCATION_CONFLICT "Lokalisierungskonflikt" +#define S_CHANNEL_CONFLICT "Kanalkonflikt" +#define S_REGISTERED_AND_READY "Registriert und bereit" +#define S_DEVICE_IS_DISCONNECTED "Gerät ist abgeschalten" +#define S_LOCATION_IS_DISABLED "Die Lokalisierung ist deaktiviert" +#define S_DEVICE_LIMIT_EXCEEDED "Gerätelimit überschritten" + +//#### SuplaCommonPROGMEM.h #### +#define S_OFF "AUS" +#define S_ON "EINGESCHALTET" +#define S_TOGGLE "TOGGLE" +#define S_LOW "NIEDRIG" +#define S_HIGH "HOCH" +#define S_POSITION_MEMORY "LETZTEN STAND ZURÜCKSETZEN" +#define S_REACTION_ON_PRESS "WENN GEDRÜCKT" +#define S_REACTION_ON_RELEASE "WENN LOSGELASSEN" +#define S_REACTION_ON_CHANGE "STATUS ÄNDERUNG" +#define S_CFG_10_PRESSES "10 MAL DRÜCKEN" +#define S_5SEK_HOLD "5 SEKUNDEN GEDRÜCKT HALTEN" + +//#### SuplaTemplateBoard.h #### +#define S_ABSENT "ABWESEND" + +//#### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Impulszähler" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Zeitlimit" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Steigende Flanke" +#define S_IMPULSE_COUNTER_PULL_UP "Pull-up" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Wertänderung" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Einstellung Impulszähler Nr." +#define S_NO_IMPULSE_COUNTER_NR "Fehlender Impulszähler Nr." + +#endif // _LANGUAGE_DE_S_H_ diff --git a/language/en.h b/src/language/en.h similarity index 61% rename from language/en.h rename to src/language/en.h index ab971024..7cf2c9ad 100644 --- a/language/en.h +++ b/src/language/en.h @@ -1,4 +1,3 @@ - #ifndef _LANGUAGE_EN_S_H_ #define _LANGUAGE_EN_S_H_ @@ -25,7 +24,9 @@ #define S_SENSORS_1WIRE "SENSORS 1Wire" #define S_SENSORS_I2C "SENSORS i2c" #define S_SENSORS_SPI "SENSORS SPI" +#define S_SENSORS_OTHER "SENSORY OTHER" #define S_LED_BUTTON_CFG "LED, BUTTON CONFIG" +#define S_CFG_MODE "CFG mode" #define S_QUANTITY "QUANTITY" #define S_GPIO_SETTINGS_FOR_RELAYS "GPIO settings for relays" #define S_RELAY "RELAYS" @@ -38,6 +39,7 @@ #define S_BUTTON_NR_SETTINGS "Setting button nr." #define S_NO_BUTTON_NR "No button nr." #define S_REACTION_TO "Reaction to" +#define S_ACTION "Action" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "GPIO settings for limit switch" #define S_LIMIT_SWITCH "LIMIT SWITCH" #define S_GPIO_SETTINGS_FOR "GPIO settigs for" @@ -56,4 +58,50 @@ #define S_DATA_SAVED_RESTART_MODULE "Data saved - restart module" #define S_WRITE_ERROR_BAD_DATA "Write error - bad data" +//#### SuplaConfigESP.cpp #### +#define S_ALREADY_INITIATED "Already initiated" +#define S_NOT_ASSIGNED_CB "Not assigned CB" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "Invalid GUID or device registration INACTIVE" +#define S_UNKNOWN_SEVER_ADDRESS "Unknown server address" +#define S_UNKNOWN_ID "Unknown ID" +#define S_INITIATED "Initiated" +#define S_CHANNEL_LIMIT_EXCEEDED "Channel limit exceeded" +#define S_DISCONNECTED "Disconnected" +#define S_REGISTRATION_IS_PENDING "Registration is pending" +#define S_VARIABLE_ERROR "Variable error" +#define S_PROTOCOL_VERSION_ERROR "Protocol version error" +#define S_BAD_CREDENTIALS "Bad credentials" +#define S_TEMPORARILY_UNAVAILABLE "Temporarily unavailable" +#define S_LOCATION_CONFLICT "Location conflict" +#define S_CHANNEL_CONFLICT "Channel conflict" +#define S_REGISTERED_AND_READY "Registered and ready" +#define S_DEVICE_IS_DISCONNECTED "The device is disconnected" +#define S_LOCATION_IS_DISABLED "The location is disabled" +#define S_DEVICE_LIMIT_EXCEEDED "Device limit exceeded" + +//#### SuplaCommonPROGMEM.h #### +#define S_OFF "OFF" +#define S_ON "ON" +#define S_TOGGLE "TOGGLE" +#define S_LOW "LOW" +#define S_HIGH "HIGH" +#define S_POSITION_MEMORY "POSITION MEMORY" +#define S_REACTION_ON_PRESS "ON PRESS" +#define S_REACTION_ON_RELEASE "ON RELEASE" +#define S_REACTION_ON_CHANGE "ON CHANGE" +#define S_CFG_10_PRESSES "10 ON PRESSES" +#define S_5SEK_HOLD "5 SEK HOLD" + +//#### SuplaTemplateBoard.h #### +#define S_ABSENT "ABSENT" + +//#### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Impulse counter" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Debounce timeout" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Raising edge" +#define S_IMPULSE_COUNTER_PULL_UP "Pull up" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Change value" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Settings IC nr." +#define S_NO_IMPULSE_COUNTER_NR "No IC nr." + #endif // _LANGUAGE_EN_S_H_ diff --git a/src/language/es.h b/src/language/es.h new file mode 100644 index 00000000..436af679 --- /dev/null +++ b/src/language/es.h @@ -0,0 +1,110 @@ +#ifndef _LANGUAGE_ES_S_H_ +#define _LANGUAGE_ES_S_H_ + +// Translated by EñE Partycja Gajek-Nowrot +// Checked by elmaya + +#define S_SETTING_WIFI_SSID "Configuración de WIFI" +#define S_WIFI_SSID "Nombre WIFI" +#define S_WIFI_PASS "Contraseña" +#define S_HOST_NAME "Nombre de módulo" +#define S_SETTING_SUPLA "Configuración SUPLA" +#define S_SUPLA_SERVER "Dirección de servidor" +#define S_SUPLA_EMAIL "Correo electrónico" +#define S_SETTING_ADMIN "Datos de administrador" +#define S_LOGIN "Login" +#define S_LOGIN_PASS "Contraseña" +#define S_ROLLERSHUTTERS "Persianas" +#define S_SAVE "Guardar" +#define S_DEVICE_SETTINGS "Configuración de dispositivo" +#define S_UPDATE "Actualización" +#define S_RESTART "Reiniciar" +#define S_RETURN "Volver" +#define S_TEMPLATE_BOARD "Modelos de placas" +#define S_TYPE "Tipo" +#define S_RELAYS "RELÉS" +#define S_BUTTONS "BOTONES" +#define S_SENSORS_1WIRE "SENSORES 1Wire" +#define S_SENSORS_I2C "SENSORES i2c" +#define S_SENSORS_SPI "SENSORES SPI" +#define S_SENSORS_OTHER "OTROS SENSORES" +#define S_LED_BUTTON_CFG "LED, CONFIGURACIÓN DE BOTÓN" +#define S_CFG_MODE "Modo" +#define S_QUANTITY "CANTIDAD" +#define S_GPIO_SETTINGS_FOR_RELAYS "Parámetros GPIO para los relés" +#define S_RELAY "RELÉ" +#define S_RELAY_NR_SETTINGS "Configuración del relé no." +#define S_NO_RELAY_NR "Falta del relé no." +#define S_STATE_CONTROL "Control de estado" +#define S_REACTION_AFTER_RESET "Reacción después de reinicio" +#define S_GPIO_SETTINGS_FOR_BUTTONS "Parámetros GPIO para los botones" +#define S_BUTTON "BOTÓN" +#define S_BUTTON_NR_SETTINGS "Parámetros del botón no." +#define S_NO_BUTTON_NR "Falta del botón no." +#define S_REACTION_TO "Reacción a" +#define S_ACTION "Action" +#define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Parámetros GPIO para el sensor de apertura" +#define S_LIMIT_SWITCH "INTERRUPTOR DE LÍMITE" +#define S_GPIO_SETTINGS_FOR "Parámetros GPIO para" +#define S_FOUND "Encontrado" +#define S_NO_SENSORS_CONNECTED "Falta de sensores conectados" +#define S_SAVE_FOUND "Guardar encontrado" +#define S_TEMPERATURE "Temperatura" +#define S_NAME "Nombre" +#define S_ALTITUDE_ABOVE_SEA_LEVEL "Altitud sobre el nivel del mar" +#define S_GPIO_SETTINGS_FOR_CONFIG "Parámetros GPIO para CONFIG" +#define S_SOFTWARE_UPDATE "Actualización de software" +#define S_DATA_SAVED "Datos guardados" +#define S_RESTART_MODULE "Reinicio de módulo" +#define S_DATA_ERASED_RESTART_DEVICE "Datos borrados: reinicia el dispositivo" +#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Error de escritura: no se puede leer el archivo. Falta partición FS." +#define S_DATA_SAVED_RESTART_MODULE "Datos guardados: reinicio de módulo." +#define S_WRITE_ERROR_BAD_DATA "Error de escritura: datos incorrectos." + +//#### SuplaConfigESP.cpp #### +#define S_ALEREADY_INITIATED "Ya iniciado" +#define S_NOT_ASSIGNED_CB "CB no asignado" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "Identificador GUID incorrecto o registro de dispositivos INACTIVO" +#define S_UNKNOWN_SEVER_ADDRESS "Dirección de servidor desconocida" +#define S_UNKNOWN_ID "ID desconocido" +#define S_INITIATED "Iniciado" +#define S_CHANNEL_LIMIT_EXCEEDED "Límite de canal superado" +#define S_DISCONNECTED "Desconectado" +#define S_REGISRATION_IS_PENDING "Registro pendiente" +#define S_VARIABLE_ERROR "Error de variable" +#define S_PROTOCOL_VERSION_ERROR "Error de versión de protocolo" +#define S_BAD_CREDENTIALS "Credenciales incorrectas" +#define S_TEMPORARILY_UNAVAILABLE "Temporalmente no disponible" +#define S_LOCATION_CONFLICT "Conflicto de localización" +#define S_CHANNEL_CONFLICT "Conflicto de canales" +#define S_REGISTERED_AND_READY "Registrado y listo" +#define S_DEVICE_IS_DISCONNECTED "Dispositivo desconectado" +#define S_LOCATION_IS_DISABLED "Localización está desactivada" +#define S_DEVICE_LIMIT_EXCEEDED "Superado límite de dispositivos" + +//#### SuplaCommonPROGMEM.h #### +#define S_OFF "APAGADO" +#define S_ON "ENCENDIDO" +#define S_TOGGLE "TOGGLE" +#define S_LOW "BAJO" +#define S_HIGH "ALTO" +#define S_POSITION_MEMORY "RECORDAR ESTADO" +#define S_REACTION_ON_PRESS "AL PRESIONAR" +#define S_REACTION_ON_RELEASE "AL SOLTAR" +#define S_REACTION_ON_CHANGE "AL CAMBIAR DE ESTADO" +#define S_CFG_10_PRESSES "AL PRESIONAR 10 VECES" +#define S_5SEK_HOLD "AL MANTENER PRESIONADO 5 SEGUNDOS" + +//#### SuplaTemplateBoard.h #### +#define S_ABSENT "BRAK" + +//#### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Contador de impulsos" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Límite de tiempo" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Flanco ascendente" +#define S_IMPULSE_COUNTER_PULL_UP "Pull-up" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Cambia el valor" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Configuración contador de impulsos no." +#define S_NO_IMPULSE_COUNTER_NR "Falta de contador de impulsos no." + +#endif // _LANGUAGE_ES_S_H_ diff --git a/src/language/fr.h b/src/language/fr.h new file mode 100644 index 00000000..f444bd0f --- /dev/null +++ b/src/language/fr.h @@ -0,0 +1,108 @@ +#ifndef _LANGUAGE_FR_S_H_ +#define _LANGUAGE_FR_S_H_ + +// translated by Fryga +#define S_SETTING_WIFI_SSID "Mettre à jour du WIFI" +#define S_WIFI_SSID "Nom du WIFI" +#define S_WIFI_PASS "Mot de passe" +#define S_HOST_NAME "Nom du module" +#define S_SETTING_SUPLA "Paramètres SUPLA" +#define S_SUPLA_SERVER "Adresse du serveur" +#define S_SUPLA_EMAIL "E-mail" +#define S_SETTING_ADMIN "Paramètres administrateur" +#define S_LOGIN "Connexion" +#define S_LOGIN_PASS "Mot de passe" +#define S_ROLLERSHUTTERS "Volet roulant" +#define S_SAVE "Enregistrer" +#define S_DEVICE_SETTINGS "Paramètres du module" +#define S_UPDATE "Mettre à jour" +#define S_RESTART "Réinitialisation" +#define S_RETURN "Retour" +#define S_TEMPLATE_BOARD "Modèle de la planches" +#define S_TYPE "Genre" +#define S_RELAYS "LES RELAIS" +#define S_BUTTONS "LES BOUTONS" +#define S_SENSORS_1WIRE "LES SENSORS du 1Wire" +#define S_SENSORS_I2C "LES SENSORS du i2c" +#define S_SENSORS_SPI "LES SENSORS du SPI" +#define S_SENSORS_OTHER "LES SENSORS" +#define S_LED_BUTTON_CFG "LED, BOUTON metre à jour" +#define S_CFG_MODE "CFG mode" +#define S_QUANTITY "Quantité" +#define S_GPIO_SETTINGS_FOR_RELAYS "Paramètres du GPIO pour les relais" +#define S_RELAY "LE RELAIS" +#define S_RELAY_NR_SETTINGS "Paramètres de la relais No." +#define S_NO_RELAY_NR "Manquant numéro de relais" +#define S_STATE_CONTROL "Contrôle d'état" +#define S_REACTION_AFTER_RESET "Réaction après réinitialisation" +#define S_GPIO_SETTINGS_FOR_BUTTONS "Paramètres GPIO pour les boutons" +#define S_BUTTON "BOUTON" +#define S_BUTTON_NR_SETTINGS "Bouton mettre à jour" +#define S_NO_BUTTON_NR "Manquant numéro de bouton" +#define S_REACTION_TO "Réaction à" +#define S_ACTION "Action" +#define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Paramètres GPIO pour capteur de limite" +#define S_LIMIT_SWITCH "Capteur de limite" +#define S_GPIO_SETTINGS_FOR "GPIO mettre à jour pour" +#define S_FOUND "Trouvé" +#define S_NO_SENSORS_CONNECTED "Aucun capteur connecté" +#define S_SAVE_FOUND "Enregistrer trouvé" +#define S_TEMPERATURE "Température" +#define S_NAME "Nom" +#define S_ALTITUDE_ABOVE_SEA_LEVEL "Altitude" +#define S_GPIO_SETTINGS_FOR_CONFIG "Paramètres GPIO pour la à jour" +#define S_SOFTWARE_UPDATE "Mise à jour du logiciel" +#define S_DATA_SAVED "Données enregistrées" +#define S_RESTART_MODULE "Redémarrer le module" +#define S_DATA_ERASED_RESTART_DEVICE "Données effacées - redémarrer le module" +#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Erreur d'écriture - impossible de lire à partir du fichier FS. Partition manquante" +#define S_DATA_SAVED_RESTART_MODULE "Données enregistrées - redémarrer le module" +#define S_WRITE_ERROR_BAD_DATA "Erreur d'écriture - données incorrectes" + +//#### SuplaConfigESP.cpp #### +#define S_ALREADY_INITIATED "Déjà lancé" +#define S_NOT_ASSIGNED_CB "CB non attribué" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "GUID non valide ou enregistrement de l'appareil INACTIF" +#define S_UNKNOWN_SEVER_ADDRESS "Adresse de serveur inconnue" +#define S_UNKNOWN_ID "ID inconnu" +#define S_INITIATED "Initié" +#define S_CHANNEL_LIMIT_EXCEEDED "Limite de canal dépassée" +#define S_DISCONNECTED "Déconnecté" +#define S_REGISTRATION_IS_PENDING "L'enregistrement est en attente" +#define S_VARIABLE_ERROR "Erreur de variable" +#define S_PROTOCOL_VERSION_ERROR "Erreur de version de protocole" +#define S_BAD_CREDENTIALS "Informations d'identification incorrectes" +#define S_TEMPORARILY_UNAVAILABLE "Temporairement indisponible" +#define S_LOCATION_CONFLICT "Conflit d'emplacement" +#define S_CHANNEL_CONFLICT "Conflit de canal" +#define S_REGISTERED_AND_READY "Enregistré et prêt" +#define S_DEVICE_IS_DISCONNECTED "L'appareil est déconnecté" +#define S_LOCATION_IS_DISABLED "L'emplacement est désactivé" +#define S_DEVICE_LIMIT_EXCEEDED "Limite d'appareils dépassée" + +// #### SuplaCommonPROGMEM.h #### +#define S_OFF "ÉTEINDRE" +#define S_ON "ALLUMER" +#define S_TOGGLE "TOGGLE" +#define S_LOW "BASSE" +#define S_HIGH "HAUT" +#define S_POSITION_MEMORY "MEMOIRE DE POSITION" +#define S_REACTION_ON_PRESS "ON PRESSE" +#define S_REACTION_ON_RELEASE "EN LIBÉRATION" +#define S_REACTION_ON_CHANGE "SUR LE CHANGEMENT" +#define S_CFG_10_PRESSES "10 FOIS SUR PRESSE" +#define S_5SEK_HOLD "5 SEC TENIR" + +// #### SuplaTemplateBoard.h #### +#define S_ABSENT "ABSENT" + +// #### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Compteur d'impulsions" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Délai anti-rebond" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Bord montant" +#define S_IMPULSE_COUNTER_PULL_UP "Remonter" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Modifier la valeur" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Paramètres Compteur d'impulsions No." +#define S_NO_IMPULSE_COUNTER_NR "Aucun Compteur d'impulsions No." + +#endif // _LANGUAGE_FR_S_H_ \ No newline at end of file diff --git a/language/pl.h b/src/language/pl.h similarity index 60% rename from language/pl.h rename to src/language/pl.h index efecb24f..62eecf04 100644 --- a/language/pl.h +++ b/src/language/pl.h @@ -22,12 +22,15 @@ #define S_TYPE "Rodzaj" #define S_RELAYS "PRZEKAŹNIKI" #define S_BUTTONS "PRZYCISKI" -#define S_SENSORS_1WIRE "SENSORY 1Wire" -#define S_SENSORS_I2C "SENSORY i2c" -#define S_SENSORS_SPI "SENSORY SPI" -#define S_LED_BUTTON_CFG "LED, BUTTON CONFIG" +#define S_SENSORS_1WIRE "1WIRE" +#define S_SENSORS_I2C "I2C" +#define S_SENSORS_SPI "SPI" +#define S_SENSORS_OTHER "INNE" +#define S_LED_BUTTON_CFG "KONFIGURACJA" +#define S_CFG_MODE "Tryb" #define S_QUANTITY "ILOŚĆ" #define S_GPIO_SETTINGS_FOR_RELAYS "Ustawienie GPIO dla przekaźników" +#define S_GPIO_SETTINGS_FOR "Ustawienie GPIO dla" #define S_RELAY "PRZEKAŹNIK" #define S_RELAY_NR_SETTINGS "Ustawienia przekaźnika nr." #define S_NO_RELAY_NR "Brak przekaźnika nr." @@ -38,6 +41,7 @@ #define S_BUTTON_NR_SETTINGS "Ustawienia przycisku nr." #define S_NO_BUTTON_NR "Brak przycisku nr." #define S_REACTION_TO "Reakcja na" +#define S_ACTION "Akcja" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Ustawienie GPIO dla cz. otwarcia" #define S_LIMIT_SWITCH "KRAŃCÓWKA" #define S_GPIO_SETTINGS_FOR "Ustawienie GPIO dla" @@ -56,4 +60,50 @@ #define S_DATA_SAVED_RESTART_MODULE "Dane zapisane - restart modułu." #define S_WRITE_ERROR_BAD_DATA "Błąd zapisu - złe dane." +//#### SuplaConfigESP.cpp #### +#define S_ALREADY_INITIATED "Już zainicjalizowane" +#define S_NOT_ASSIGNED_CB "Nie przypisane CB" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "Nieprawidłowy identyfikator GUID lub rejestracja urządzeń NIEAKTYWNA" +#define S_UNKNOWN_SEVER_ADDRESS "Nieznany adres serwera" +#define S_UNKNOWN_ID "Nieznany identyfikator ID" +#define S_INITIATED "Zainicjowany" +#define S_CHANNEL_LIMIT_EXCEEDED "Przekroczono limit kanału" +#define S_DISCONNECTED "Rozłączony" +#define S_REGISTRATION_IS_PENDING "Rejestracja w toku" +#define S_VARIABLE_ERROR "Błąd zmiennej" +#define S_PROTOCOL_VERSION_ERROR "Błąd wersji protokołu" +#define S_BAD_CREDENTIALS "Złe poświadczenia" +#define S_TEMPORARILY_UNAVAILABLE "Tymczasowo niedostępne" +#define S_LOCATION_CONFLICT "Konflikt lokalizacji" +#define S_CHANNEL_CONFLICT "Konflikt kanałów" +#define S_REGISTERED_AND_READY "Zarejestrowany i gotowy" +#define S_DEVICE_IS_DISCONNECTED "Urządzenie jest rozłączone" +#define S_LOCATION_IS_DISABLED "Lokalizacja jest wyłączona" +#define S_DEVICE_LIMIT_EXCEEDED "Przekroczono limit urządzeń" + +//#### SuplaCommonPROGMEM.h #### +#define S_OFF "WYŁĄCZ" +#define S_ON "ZAŁĄCZ" +#define S_TOGGLE "PRZEŁĄCZ" +#define S_LOW "ODWRÓCONE" +#define S_HIGH "NORMALNE" +#define S_POSITION_MEMORY "PAMIĘTAJ STAN" +#define S_REACTION_ON_PRESS "WCIŚNIĘCIE" +#define S_REACTION_ON_RELEASE "ZWOLNIENIE" +#define S_REACTION_ON_CHANGE "ZMIANA STANU" +#define S_CFG_10_PRESSES "10 WCIŚNIĘĆ" +#define S_5SEK_HOLD "5 SEK" + +//#### SuplaTemplateBoard.h #### +#define S_ABSENT "BRAK" + +//#### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Licznik impulsów" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Limit czasu" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Zbocze rosnące" +#define S_IMPULSE_COUNTER_PULL_UP "Podciąganie do VCC" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Zmień wartość" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Ustawienia IC nr." +#define S_NO_IMPULSE_COUNTER_NR "Brak IC nr." + #endif // _LANGUAGE_PL_S_H_ diff --git a/tools/copy_files.py b/tools/copy_files.py new file mode 100644 index 00000000..7a3b6c3d --- /dev/null +++ b/tools/copy_files.py @@ -0,0 +1,51 @@ +# Inspired by: https://github.com/arendst/Tasmota/blob/development/pio/name-firmware.py +# Thanks Theo & Jason2866 :) + +Import('env') +import os +import shutil + +OUTPUT_DIR = "build_output{}".format(os.path.sep) + +def copy_to_build_output(sourcedir, variant, file_suffix): + in_file = "{}{}".format(variant, file_suffix) + full_in_file = os.path.join(sourcedir, in_file) + firmware_name = in_file = "{}{}".format(str(sourcedir).split(os.path.sep)[2], file_suffix) + + if os.path.isfile(full_in_file): + if ".bin" in file_suffix: + out_file = "{}bin{}{}".format(OUTPUT_DIR, os.path.sep, firmware_name) + else: + out_file = "{}debug{}{}".format(OUTPUT_DIR, os.path.sep, firmware_name) + + if os.path.isfile(out_file): + os.remove(out_file) + + print("\u001b[33m in file : \u001b[0m {}".format(full_in_file)) + + print("\u001b[33m copy to: \u001b[0m {}".format(out_file)) + shutil.copy(full_in_file, out_file) + + +def bin_elf_copy(source, target, env): + variant = env['PROGNAME'] + split_path = str(source[0]).rsplit(os.path.sep, 1) + + # Create a dump of the used build environment + with open('{}{}{}.env.txt'.format(split_path[0], os.path.sep, variant), 'w') as outfile: + outfile.write(env.Dump()) + outfile.close() + + # check if output directories exist and create if necessary + if not os.path.isdir(OUTPUT_DIR): + os.mkdir(OUTPUT_DIR) + + for d in ['bin', 'debug']: + if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)): + os.mkdir("{}{}".format(OUTPUT_DIR, d)) + + for suff in [".elf", ".bin", ".bin.gz", "-factory.bin", ".env.txt"]: + copy_to_build_output(split_path[0], variant, suff) + + +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_elf_copy])