/* update_service.cpp - update services functions class Copyright (c) 2014 Luc Lebosse. All rights reserved. This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This code 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with This code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../include/esp3d_config.h" #ifdef SD_UPDATE_FEATURE #include "update_service.h" #include "../../core/settings_esp3d.h" #include "../../core/esp3doutput.h" #include "../../core/commands.h" #include "esp_config_file.h" #include "../filesystem/esp_sd.h" #include "../filesystem/esp_filesystem.h" #if defined (ARDUINO_ARCH_ESP32) #include #define U_FS U_SPIFFS #endif //ARDUINO_ARCH_ESP32 #if defined (ARDUINO_ARCH_ESP8266) #endif //ARDUINO_ARCH_ESP8266 UpdateService update_service; #define CONFIG_FILE "/esp3dcnf.ini" #define FW_FILE "/esp3dfw.bin" #define FS_FILE "/esp3dfs.bin" const char * NetstringKeysVal[] = {"hostname", "STA_SSID", "STA_Password", "AP_SSID", "AP_Password" } ; const uint16_t NetstringKeysPos[] = {ESP_HOSTNAME, ESP_STA_SSID, ESP_STA_PASSWORD, ESP_AP_SSID, ESP_AP_PASSWORD } ; const char * ServstringKeysVal[] = { "Time_server1", "Time_server2", "Time_server3", "ADMIN_PASSWORD", "USER_PASSWORD", "NOTIF_TOKEN1", "NOTIF_TOKEN2", "NOTIF_TOKEN_Settings" } ; const uint16_t ServstringKeysPos[] = { ESP_TIME_SERVER1, ESP_TIME_SERVER2, ESP_TIME_SERVER3, ESP_ADMIN_PWD, ESP_USER_PWD, ESP_NOTIFICATION_TOKEN1, ESP_NOTIFICATION_TOKEN2, ESP_NOTIFICATION_SETTINGS } ; const char * IPKeysVal[] = {"STA_IP", "STA_GW", "STA_MSK", "STA_DNS", "AP_IP" } ; const uint16_t IPKeysPos[] = {ESP_STA_IP_VALUE, ESP_STA_GATEWAY_VALUE, ESP_STA_MASK_VALUE, ESP_STA_DNS_VALUE, ESP_AP_IP_VALUE } ; const char * ServintKeysVal[] = { "HTTP_Port", "TELNET_Port", "SENSOR_INTERVAL", "WebSocket_Port", "WebDav_Port", "FTP_Control_Port", "FTP_Active_Port ", "FTP_Passive_Port" } ; const uint16_t ServintKeysPos[] = { ESP_HTTP_PORT, ESP_TELNET_PORT, ESP_SENSOR_INTERVAL, ESP_WEBSOCKET_PORT, ESP_WEBDAV_PORT, ESP_FTP_CTRL_PORT, ESP_FTP_DATA_ACTIVE_PORT, ESP_FTP_DATA_PASSIVE_PORT } ; const char * SysintKeysVal[] = {"Baud_rate", "Boot_delay" } ; const uint16_t SysintKeysPos[] = {ESP_BAUD_RATE, ESP_BOOT_DELAY } ; const char * ServboolKeysVal[] = {"HTTP_active", "TELNET_active", "WebSocket_active", "WebDav_active", "Time_DST", "CHECK_FOR_UPDATE", "Active_buzzer", "Active_Internet_time" } ; const uint16_t ServboolKeysPos[] = {ESP_HTTP_ON, ESP_TELNET_ON, ESP_WEBSOCKET_ON, ESP_WEBDAV_ON, ESP_TIME_IS_DST, ESP_SD_CHECK_UPDATE_AT_BOOT, ESP_BUZZER, ESP_INTERNET_TIME } ; const char * SysboolKeysVal[] = {"Active_Printer_LCD", "Active_ESP3D_LCD", "Active_Serial ", "Active_WebSocket", "Active_Telnet", "Active_BT", "Boot_verbose", "Secure_serial" } ; const uint16_t SysboolKeysPos[] = {ESP_PRINTER_LCD_FLAG, ESP_LCD_FLAG, ESP_SERIAL_FLAG, ESP_WEBSOCKET_FLAG, ESP_TELNET_FLAG, ESP_BT_FLAG, ESP_VERBOSE_BOOT, ESP_SECURE_SERIAL } ; const char * NetbyteKeysVal[] = { "AP_channel" } ; const uint16_t NetbyteKeysPos[] = { ESP_AP_CHANNEL } ; const char * ServbyteKeysVal[] = {"Time_zone", "Sesion_timeout", "SD_SPEED", "Time_DST" } ; const uint16_t ServbyteKeysPos[] = {ESP_TIMEZONE, ESP_SESSION_TIMEOUT, ESP_SD_SPEED_DIV } ; bool processString(const char** keysval, const uint16_t * keypos, const size_t size, const char * key, const char * value, char & T, int & P ) { for(uint i=0; i< size ; i++) { if (strcasecmp(keysval[i],key)==0) { //if it is a previouly saved scrambled password ignore it if (strcasecmp(value,"********")!=0) { T='S'; P=keypos[i]; return true; } } } return false; } bool processInt(const char** keysval, const uint16_t * keypos, const size_t size, const char * key, const char * value, char & T, int & P, uint32_t & v) { for(uint i=0; i< size ; i++) { if (strcasecmp(keysval[i],key)==0) { T='I'; P=keypos[i]; v=String(value).toInt(); return true; } } return false; } bool processBool(const char** keysval, const uint16_t * keypos, const size_t size, const char * key, const char * value, char & T, int & P, byte & b) { for(uint i=0; i< size ; i++) { if (strcasecmp(keysval[i],key)==0) { T='B'; P=keypos[i]; if ((strcasecmp("yes",value)==0)||(strcasecmp("on", value)==0)||(strcasecmp("true", value)==0)||(strcasecmp("1", value)==0) ) { b = 1; } else if ((strcasecmp("no", value)==0)||(strcasecmp("off", value)==0)||(strcasecmp("false", value)==0)||(strcasecmp("0", value)==0) ) { b = 0; } else { P=-1; } return true; } } return false; } //Parsing all entries of file once is faster that checking all possible parameters for each line of file bool processingFileFunction (const char * section, const char * key, const char * value) { bool res = true; char T = '\0'; int P = -1; uint32_t v = 0; byte b = 0; bool done=false; log_esp3d("[%s]%s=%s",section, key,value); //network / services / system sections if (strcasecmp("network",section)==0) { if (!done) { done = processString(NetstringKeysVal,NetstringKeysPos,sizeof(NetstringKeysVal)/sizeof(char*), key, value, T, P ); } if (!done) { done = processString(IPKeysVal,IPKeysPos,sizeof(IPKeysVal)/sizeof(char*), key, value, T, P ); if(done) { T='A'; } } if (!done) { done = processInt(NetbyteKeysVal,NetbyteKeysPos,sizeof(NetbyteKeysVal)/sizeof(char*), key, value, T, P, v); if(done) { T='B'; b=v; } } //Radio mode BT, WIFI-STA, WIFI-AP, ETH-STA, OFF if (!done) { if (strcasecmp("radio_mode",key)==0) { T='B'; P=ESP_RADIO_MODE; done = true; if (strcasecmp("BT",value)==0) { b=ESP_BT; } else if (strcasecmp("WIFI-STA",value)==0) { b=ESP_WIFI_STA; } else if (strcasecmp("WIFI-AP",value)==0) { b=ESP_WIFI_AP; } else if (strcasecmp("ETH-STA",value)==0) { b=ESP_ETH_STA; } else if (strcasecmp("OFF",value)==0) { b=ESP_RADIO_OFF; } else { P=-1; //invalide value } } } //STA IP Mode DHCP / STATIC if (!done) { if (strcasecmp("STA_IP_mode",key)==0) { T='B'; P=ESP_STA_IP_MODE; done = true; if (strcasecmp("DHCP",value)==0) { b=DHCP_MODE; } else if (strcasecmp("STATIC",key)==0) { b=STATIC_IP_MODE; } else { P=-1; //invalide value } } } } else if (strcasecmp("services",section)==0) { if (!done) { done = processString(ServstringKeysVal,ServstringKeysPos,sizeof(ServstringKeysVal)/sizeof(char*), key, value, T, P ); } if (!done) { done = processInt(ServintKeysVal,ServintKeysPos,sizeof(ServintKeysVal)/sizeof(char*), key, value, T, P, v); } if (!done) { done = processBool(ServboolKeysVal,ServboolKeysPos,sizeof(ServboolKeysVal)/sizeof(char*), key, value, T, P, b); } if (!done) { done = processInt(ServbyteKeysVal,ServbyteKeysPos,sizeof(ServbyteKeysVal)/sizeof(char*), key, value, T, P, v); if(done) { T='B'; b=v; } } //Notification type None / PushOver / Line / Email / Telegram if (!done) { if (strcasecmp("NOTIF_TYPE",key)==0) { T='B'; P=ESP_NOTIFICATION_TYPE; done = true; if (strcasecmp("None",value)==0) { b=ESP_NO_NOTIFICATION; } else if (strcasecmp("PushOver",value)==0) { b=ESP_PUSHOVER_NOTIFICATION; } else if (strcasecmp("Line",value)==0) { b=ESP_LINE_NOTIFICATION; } else if (strcasecmp("Email",value)==0) { b=ESP_EMAIL_NOTIFICATION; } else if (strcasecmp("Telegram",value)==0) { b=ESP_TELEGRAM_NOTIFICATION; } else { P=-1; //invalide value } } } //Sensor type if enabled None / DHT11 / DHT22 / ANALOG / BMP280 / BME280 if (!done) { if (strcasecmp("SENSOR_TYPE",key)==0) { T='B'; P=ESP_SENSOR_TYPE; done = true; if (strcasecmp("None",value)==0) { b=NO_SENSOR_DEVICE; } else if (strcasecmp("DHT11",key)==0) { b=DHT11_DEVICE; } else if (strcasecmp("DHT22",key)==0) { b=DHT22_DEVICE; } else if (strcasecmp("ANALOG",key)==0) { b=ANALOG_DEVICE; } else if (strcasecmp("BMP280",key)==0) { b=BMP280_DEVICE; } else if (strcasecmp("BME280",key)==0) { b=BME280_DEVICE; } else { P=-1; //invalide value } } } } else if (strcasecmp("system",section)==0) { if (!done) { done = processInt(SysintKeysVal,SysintKeysPos,sizeof(SysintKeysVal)/sizeof(char*), key, value, T, P, v); } if (!done) { done = processBool(SysboolKeysVal,SysboolKeysPos,sizeof(SysboolKeysVal)/sizeof(char*), key, value, T, P, b); } //Target Firmware None / Marlin / Repetier / MarlinKimbra / Smoothieware / GRBL if (!done) { if (strcasecmp("TargetFW",key)==0) { T='B'; P=ESP_TARGET_FW; done = true; if (strcasecmp("None",value)==0) { b=UNKNOWN_FW; } else if (strcasecmp("MARLIN",value)==0) { b=MARLIN; } else if (strcasecmp("MARLINKIMBRA",value)==0) { b=MARLINKIMBRA; } else if (strcasecmp("GRBL",value)==0) { b=GRBL; } else if (strcasecmp("REPETIER",value)==0) { b=REPETIER; } else if (strcasecmp("SMOOTHIEWARE",value)==0) { b=SMOOTHIEWARE; } else { P=-1; //invalide value } } } } //now we save -handle saving status //if setting is not recognized it is not a problem //but if save is fail - that is a problem - so report it if(P!=-1) { switch(T) { case 'S': log_esp3d("Saving setting to ESP3D"); res = Settings_ESP3D::write_string (P, value); break; case 'B': case 'F': res = Settings_ESP3D::write_byte (P, b); break; case 'I': res = Settings_ESP3D::write_uint32 (P, v); break; case 'A': res = Settings_ESP3D::write_IP_String (P, value); break; default: log_esp3d("Unknown flag"); } } return res; } UpdateService::UpdateService() {} UpdateService::~UpdateService() {} bool UpdateService::flash(const char * filename, int type) { bool res = false; if (ESP_SD::exists (filename)) { log_esp3d("Update found"); bool issucess = false; ESP_SDFile sdfile; String finalName = filename; sdfile = ESP_SD::open(filename); if(sdfile) { size_t s = sdfile.size(); size_t rs = 0; uint8_t v[1] ; if(Update.begin(s, type)) { log_esp3d("Update started"); while (sdfile.available() && (rs <= (s+1))) { rs++; v[0]=sdfile.read(); Update.write(v,1); Hal::wait(0); } if (rs==s) { log_esp3d("Update done"); if(Update.end(true)) { log_esp3d("Update success"); issucess = true; } } else { Update.end(); log_esp3d("Wrong size"); } } sdfile.close(); } else { log_esp3d("Cannot open file"); } if(issucess) { res = true; finalName.replace(".bin", ".ok"); } else { finalName.replace(".bin", ".bad"); } if (ESP_SD::exists (finalName.c_str())) { String name = filename; uint8_t n = 1; log_esp3d("Final name already exists, backup existing"); name.replace("bin", String(n).c_str()); while(ESP_SD::exists (name.c_str())) { n++; name.replace("bin", String(n).c_str()); } ESP_SD::rename(finalName.c_str(),name.c_str()); } ESP_SD::rename(filename, finalName.c_str()); } return res; } bool UpdateService::begin() { bool res = false; if(Settings_ESP3D::read_byte(ESP_SD_CHECK_UPDATE_AT_BOOT)!=0) { bool isactive = ESP_SD::accessSD(); log_esp3d("Update SD for update requestest"); if(ESP_SD::getState(true) == ESP_SDCARD_IDLE) { ESP_ConfigFile updateConfig(CONFIG_FILE, processingFileFunction); if (updateConfig.processFile()) { log_esp3d("Processing ini file done"); if(updateConfig.revokeFile()) { log_esp3d("Revoking ini file done"); res = true; } else { log_esp3d("Revoking ini file failed"); } } else { log_esp3d("Processing ini file failed"); } if (flash(FW_FILE,U_FLASH)) { res = true; } else { if (flash(FS_FILE,U_FS)) { res = true; } } } if (!isactive) { ESP_SD::releaseSD(); } } else { log_esp3d("No need to check for update"); } return res; } void UpdateService::end() { } void UpdateService::handle() {} #endif //SD_UPDATE_FEATURE