main.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <regex>
  5. //#include "freertos/FreeRTOS.h"
  6. //#include "freertos/task.h"
  7. //#include "freertos/event_groups.h"
  8. //#include "driver/gpio.h"
  9. //#include "sdkconfig.h"
  10. //#include "esp_psram.h" // Comming in IDF 5.0, see https://docs.espressif.com/projects/esp-idf/en/v5.0-beta1/esp32/migration-guides/release-5.x/system.html?highlight=esp_psram_get_size
  11. //#include "spiram.h"
  12. #include "esp32/spiram.h"
  13. // SD-Card ////////////////////
  14. //#include "nvs_flash.h"
  15. #include "esp_vfs_fat.h"
  16. //#include "sdmmc_cmd.h"
  17. #include "driver/sdmmc_host.h"
  18. //#include "driver/sdmmc_defs.h"
  19. ///////////////////////////////
  20. #include "ClassLogFile.h"
  21. #include "connect_wlan.h"
  22. #include "read_wlanini.h"
  23. #include "server_main.h"
  24. #include "server_tflite.h"
  25. #include "server_file.h"
  26. #include "server_ota.h"
  27. #include "time_sntp.h"
  28. #include "configFile.h"
  29. //#include "ClassControllCamera.h"
  30. #include "server_main.h"
  31. #include "server_camera.h"
  32. #ifdef ENABLE_MQTT
  33. #include "server_mqtt.h"
  34. #endif //ENABLE_MQTT
  35. //#include "Helper.h"
  36. #include "../../include/defines.h"
  37. //#include "server_GPIO.h"
  38. #ifdef ENABLE_SOFTAP
  39. #include "softAP.h"
  40. #endif //ENABLE_SOFTAP
  41. #ifdef DISABLE_BROWNOUT_DETECTOR
  42. #include "soc/soc.h"
  43. #include "soc/rtc_cntl_reg.h"
  44. #endif
  45. #ifdef DEBUG_ENABLE_SYSINFO
  46. #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
  47. #include "esp_sys.h"
  48. #endif
  49. #endif //DEBUG_ENABLE_SYSINFO
  50. #ifdef USE_HIMEM_IF_AVAILABLE
  51. #include "esp32/himem.h"
  52. #ifdef DEBUG_HIMEM_MEMORY_CHECK
  53. #include "himem_memory_check.h"
  54. #endif
  55. #endif
  56. //#ifdef CONFIG_HEAP_TRACING_STANDALONE
  57. #if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START
  58. #include <esp_heap_trace.h>
  59. #define NUM_RECORDS 300
  60. static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
  61. #endif
  62. extern const char* GIT_TAG;
  63. extern const char* GIT_REV;
  64. extern const char* GIT_BRANCH;
  65. extern const char* BUILD_TIME;
  66. extern std::string getFwVersion(void);
  67. extern std::string getHTMLversion(void);
  68. extern std::string getHTMLcommit(void);
  69. std::vector<std::string> splitString(const std::string& str);
  70. bool replace(std::string& s, std::string const& toReplace, std::string const& replaceWith);
  71. bool replace(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt);
  72. //bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith);
  73. bool isInString(std::string& s, std::string const& toFind);
  74. void migrateConfiguration(void);
  75. static const char *TAG = "MAIN";
  76. bool Init_NVS_SDCard()
  77. {
  78. esp_err_t ret = nvs_flash_init();
  79. if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
  80. ESP_ERROR_CHECK(nvs_flash_erase());
  81. ret = nvs_flash_init();
  82. }
  83. ////////////////////////////////////////////////
  84. ESP_LOGI(TAG, "Using SDMMC peripheral");
  85. sdmmc_host_t host = SDMMC_HOST_DEFAULT();
  86. // This initializes the slot without card detect (CD) and write protect (WP) signals.
  87. // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
  88. sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
  89. // To use 1-line SD mode, uncomment the following line:
  90. #ifdef __SD_USE_ONE_LINE_MODE__
  91. slot_config.width = 1;
  92. #endif
  93. // GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
  94. // Internal pull-ups are not sufficient. However, enabling internal pull-ups
  95. // does make a difference some boards, so we do that here.
  96. gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
  97. gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
  98. #ifndef __SD_USE_ONE_LINE_MODE__
  99. gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
  100. gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
  101. #endif
  102. gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
  103. // Options for mounting the filesystem.
  104. // If format_if_mount_failed is set to true, SD card will be partitioned and
  105. // formatted in case when mounting fails.
  106. esp_vfs_fat_sdmmc_mount_config_t mount_config = {
  107. .format_if_mount_failed = false,
  108. .max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7
  109. .allocation_unit_size = 16 * 1024
  110. };
  111. // Use settings defined above to initialize SD card and mount FAT filesystem.
  112. // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
  113. // Please check its source code and implement error recovery when developing
  114. // production applications.
  115. sdmmc_card_t* card;
  116. ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
  117. if (ret != ESP_OK) {
  118. if (ret == ESP_FAIL) {
  119. ESP_LOGE(TAG, "Failed to mount filesystem. "
  120. "If you want the card to be formatted, set format_if_mount_failed = true.");
  121. } else {
  122. ESP_LOGE(TAG, "Failed to initialize the card (%s). "
  123. "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
  124. }
  125. return false;
  126. }
  127. sdmmc_card_print_info(stdout, card);
  128. SaveSDCardInfo(card);
  129. return true;
  130. }
  131. void task_MainInitError_blink(void *pvParameter)
  132. {
  133. gpio_pad_select_gpio(BLINK_GPIO);
  134. gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
  135. TickType_t xDelay;
  136. xDelay = 100 / portTICK_PERIOD_MS;
  137. ESP_LOGD(TAG, "SD-Card could not be inialized - STOP THE PROGRAMM HERE");
  138. while (1)
  139. {
  140. gpio_set_level(BLINK_GPIO, 1);
  141. vTaskDelay( xDelay );
  142. gpio_set_level(BLINK_GPIO, 0);
  143. vTaskDelay( xDelay );
  144. }
  145. vTaskDelete(NULL); //Delete this task if it exits from the loop above
  146. }
  147. extern "C" void app_main(void)
  148. {
  149. //#ifdef CONFIG_HEAP_TRACING_STANDALONE
  150. #if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START
  151. //register a buffer to record the memory trace
  152. ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
  153. #endif
  154. TickType_t xDelay;
  155. #ifdef DISABLE_BROWNOUT_DETECTOR
  156. WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  157. #endif
  158. ESP_LOGI(TAG, "\n\n\n\n\n"); // Add mark on log to see when it restarted
  159. PowerResetCamera();
  160. esp_err_t camStatus = Camera.InitCam();
  161. Camera.LightOnOff(false);
  162. xDelay = 2000 / portTICK_PERIOD_MS;
  163. ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay);
  164. vTaskDelay( xDelay );
  165. if (!Init_NVS_SDCard())
  166. {
  167. xTaskCreate(&task_MainInitError_blink, "task_MainInitError_blink", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, NULL);
  168. return; // No way to continue without SD-Card!
  169. }
  170. migrateConfiguration();
  171. setupTime();
  172. string versionFormated = getFwVersion() + ", Date/Time: " + std::string(BUILD_TIME) + \
  173. ", Web UI: " + getHTMLversion();
  174. if (std::string(GIT_TAG) != "") { // We are on a tag, add it as prefix
  175. string versionFormated = "Tag: '" + std::string(GIT_TAG) + "', " + versionFormated;
  176. }
  177. LogFile.CreateLogDirectories();
  178. MakeDir("/sdcard/demo"); // needed for demo mode
  179. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
  180. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Startup ====================");
  181. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
  182. LogFile.WriteToFile(ESP_LOG_INFO, TAG, versionFormated);
  183. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reset reason: " + getResetReason());
  184. #ifdef DEBUG_ENABLE_SYSINFO
  185. #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
  186. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device Info : " + get_device_info() );
  187. ESP_LOGD(TAG, "Device infos %s", get_device_info().c_str());
  188. #endif
  189. #endif //DEBUG_ENABLE_SYSINFO
  190. #ifdef USE_HIMEM_IF_AVAILABLE
  191. #ifdef DEBUG_HIMEM_MEMORY_CHECK
  192. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Himem mem check : " + himem_memory_check() );
  193. ESP_LOGD(TAG, "Himem mem check %s", himem_memory_check().c_str());
  194. #endif
  195. #endif
  196. CheckIsPlannedReboot();
  197. CheckOTAUpdate();
  198. CheckUpdate();
  199. #ifdef ENABLE_SOFTAP
  200. CheckStartAPMode(); // if no wlan.ini and/or config.ini --> AP ist startet and this function does not exit anymore until reboot
  201. #endif
  202. #ifdef HEAP_TRACING_MAIN_WIFI
  203. ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
  204. #endif
  205. char *ssid = NULL, *passwd = NULL, *hostname = NULL, *ip = NULL, *gateway = NULL, *netmask = NULL, *dns = NULL; int rssithreshold = 0;
  206. LoadWlanFromFile(WLAN_CONFIG_FILE, ssid, passwd, hostname, ip, gateway, netmask, dns, rssithreshold);
  207. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WLAN-Settings - RSSI-Threshold: " + to_string(rssithreshold));
  208. if (ssid != NULL && passwd != NULL)
  209. #ifdef __HIDE_PASSWORD
  210. ESP_LOGD(TAG, "WLan: %s, XXXXXX", ssid);
  211. #else
  212. ESP_LOGD(TAG, "WLan: %s, %s", ssid, passwd);
  213. #endif
  214. else
  215. ESP_LOGD(TAG, "No SSID and PASSWORD set!!!");
  216. if (hostname != NULL)
  217. ESP_LOGD(TAG, "Hostname: %s", hostname);
  218. else
  219. ESP_LOGD(TAG, "Hostname not set");
  220. if (ip != NULL && gateway != NULL && netmask != NULL)
  221. ESP_LOGD(TAG, "Fixed IP: %s, Gateway %s, Netmask %s", ip, gateway, netmask);
  222. if (dns != NULL)
  223. ESP_LOGD(TAG, "DNS IP: %s", dns);
  224. wifi_init_sta(ssid, passwd, hostname, ip, gateway, netmask, dns, rssithreshold);
  225. xDelay = 2000 / portTICK_PERIOD_MS;
  226. ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay);
  227. vTaskDelay( xDelay );
  228. #ifdef HEAP_TRACING_MAIN_WIFI
  229. ESP_ERROR_CHECK( heap_trace_stop() );
  230. heap_trace_dump();
  231. #endif
  232. #ifdef HEAP_TRACING_MAIN_START
  233. ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
  234. #endif
  235. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
  236. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "================== Main Started =================");
  237. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
  238. if (getHTMLcommit().substr(0, 7) != std::string(GIT_REV).substr(0, 7)) { // Compare the first 7 characters of both hashes
  239. LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Web UI version (") + getHTMLcommit() + ") does not match firmware version (" + std::string(GIT_REV) + ") !");
  240. LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Please make sure to setup the SD-Card properly (check the documentation) or re-install using the AI-on-the-edge-device__update__*.zip!");
  241. }
  242. std::string zw = getCurrentTimeString("%Y%m%d-%H%M%S");
  243. ESP_LOGD(TAG, "time %s", zw.c_str());
  244. #ifdef HEAP_TRACING_MAIN_START
  245. ESP_ERROR_CHECK( heap_trace_stop() );
  246. heap_trace_dump();
  247. #endif
  248. /* Check if PSRAM can be initalized */
  249. esp_err_t ret;
  250. ret = esp_spiram_init();
  251. if (ret == ESP_FAIL) { // Failed to init PSRAM, most likely not available or broken
  252. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize PSRAM (" + std::to_string(ret) + ")!");
  253. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Either your device misses the PSRAM chip or it is broken!");
  254. setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
  255. }
  256. else { // PSRAM init ok
  257. /* Check if PSRAM provides at least 4 MB */
  258. size_t psram_size = esp_spiram_get_size();
  259. // size_t psram_size = esp_psram_get_size(); // comming in IDF 5.0
  260. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "The device has " + std::to_string(psram_size/1024/1024) + " MBytes of PSRAM");
  261. if (psram_size < (4*1024*1024)) { // PSRAM is below 4 MBytes
  262. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "At least 4 MBytes are required!");
  263. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Does the device really have a 4 Mbytes PSRAM?");
  264. setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
  265. }
  266. }
  267. /* Check available Heap memory */
  268. size_t _hsize = getESPHeapSize();
  269. if (_hsize < 4000000) { // Check available Heap memory for a bit less than 4 MB (a test on a good device showed 4187558 bytes to be available)
  270. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Not enough Heap memory available. Expected around 4 MBytes, but only " + std::to_string(_hsize) + " Bytes are available! That is not enough for this firmware!");
  271. setSystemStatusFlag(SYSTEM_STATUS_HEAP_TOO_SMALL);
  272. } else { // Heap memory is ok
  273. if (camStatus != ESP_OK) {
  274. LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Failed to initialize camera module, retrying...");
  275. PowerResetCamera();
  276. esp_err_t camStatus = Camera.InitCam();
  277. Camera.LightOnOff(false);
  278. xDelay = 2000 / portTICK_PERIOD_MS;
  279. ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay);
  280. vTaskDelay( xDelay );
  281. if (camStatus != ESP_OK) {
  282. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize camera module!");
  283. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Check that your camera module is working and connected properly!");
  284. setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
  285. }
  286. } else { // Test Camera
  287. if (!Camera.testCamera()) {
  288. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera Framebuffer cannot be initialized!");
  289. /* Easiest would be to simply restart here and try again,
  290. how ever there seem to be systems where it fails at startup but still work corectly later.
  291. Therefore we treat it still as successed! */
  292. setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
  293. }
  294. else {
  295. Camera.LightOnOff(false);
  296. }
  297. }
  298. }
  299. xDelay = 2000 / portTICK_PERIOD_MS;
  300. ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay*10);
  301. vTaskDelay( xDelay );
  302. ESP_LOGD(TAG, "starting servers");
  303. server = start_webserver();
  304. register_server_camera_uri(server);
  305. register_server_tflite_uri(server);
  306. register_server_file_uri(server, "/sdcard");
  307. register_server_ota_sdcard_uri(server);
  308. #ifdef ENABLE_MQTT
  309. register_server_mqtt_uri(server);
  310. #endif //ENABLE_MQTT
  311. gpio_handler_create(server);
  312. ESP_LOGD(TAG, "Before reg server main");
  313. register_server_main_uri(server, "/sdcard");
  314. /* Testing */
  315. //setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
  316. //setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
  317. /* Main Init has successed or only an error which allows to continue operation */
  318. if (getSystemStatus() == 0) { // No error flag is set
  319. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Initialization completed successfully!");
  320. ESP_LOGD(TAG, "Before do autostart");
  321. TFliteDoAutoStart();
  322. }
  323. else if (isSetSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD) || // Non critical errors occured, we try to continue...
  324. isSetSystemStatusFlag(SYSTEM_STATUS_NTP_BAD)) {
  325. LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Initialization completed with errors, but trying to continue...");
  326. ESP_LOGD(TAG, "Before do autostart");
  327. TFliteDoAutoStart();
  328. }
  329. else { // Any other error is critical and makes running the flow impossible.
  330. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Not starting flows!");
  331. }
  332. }
  333. void migrateConfiguration(void) {
  334. bool migrated = false;
  335. if (!FileExists(CONFIG_FILE)) {
  336. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Config file seems to be missing!");
  337. return;
  338. }
  339. std::string section = "";
  340. std::ifstream ifs(CONFIG_FILE);
  341. std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
  342. /* Split config file it array of lines */
  343. std::vector<std::string> configLines = splitString(content);
  344. /* Process each line */
  345. for (int i = 0; i < configLines.size(); i++) {
  346. //ESP_LOGI(TAG, "Line %d: %s", i, configLines[i].c_str());
  347. if (configLines[i].find("[") != std::string::npos) { // Start of new section
  348. section = configLines[i];
  349. replace(section, ";", "", false); // Remove possible semicolon (just for the string comparison)
  350. //ESP_LOGI(TAG, "New section: %s", section.c_str());
  351. }
  352. /* Migrate parameters as needed
  353. * For the boolean parameters, we make them enabled all the time now:
  354. * 1. If they where disabled, set them to their default value
  355. * 2. Enable them
  356. * Notes:
  357. * The migration has some simplifications:
  358. * - Case Sensitiveness must be like in the initial config.ini
  359. * - No Whitespace after a semicollon
  360. * - Only one whitespace before/after the equal sign
  361. */
  362. if (section == "[MakeImage]") {
  363. migrated = migrated | replace(configLines[i], "[MakeImage]", "[TakeImage]"); // Rename the section itself
  364. }
  365. if (section == "[MakeImage]" || section == "[TakeImage]") {
  366. migrated = migrated | replace(configLines[i], "LogImageLocation", "RawImagesLocation");
  367. migrated = migrated | replace(configLines[i], "LogfileRetentionInDays", "RawImagesRetention");
  368. migrated = migrated | replace(configLines[i], ";Demo = true", ";Demo = false"); // Set it to its default value
  369. migrated = migrated | replace(configLines[i], ";Demo", "Demo"); // Enable it
  370. migrated = migrated | replace(configLines[i], ";FixedExposure = true", ";FixedExposure = false"); // Set it to its default value
  371. migrated = migrated | replace(configLines[i], ";FixedExposure", "FixedExposure"); // Enable it
  372. }
  373. if (section == "[Alignment]") {
  374. migrated = migrated | replace(configLines[i], ";InitialMirror = true", ";InitialMirror = false"); // Set it to its default value
  375. migrated = migrated | replace(configLines[i], ";InitialMirror", "InitialMirror"); // Enable it
  376. migrated = migrated | replace(configLines[i], ";FlipImageSize = true", ";FlipImageSize = false"); // Set it to its default value
  377. migrated = migrated | replace(configLines[i], ";FlipImageSize", "FlipImageSize"); // Enable it
  378. }
  379. if (section == "[Digits]") {
  380. migrated = migrated | replace(configLines[i], "LogImageLocation", "ROIImagesLocation");
  381. migrated = migrated | replace(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention");
  382. }
  383. if (section == "[Analog]") {
  384. migrated = migrated | replace(configLines[i], "LogImageLocation", "ROIImagesLocation");
  385. migrated = migrated | replace(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention");
  386. migrated = migrated | replace(configLines[i], "ExtendedResolution", ";UNUSED_PARAMETER"); // This parameter is no longer used
  387. }
  388. if (section == "[PostProcessing]") {
  389. migrated = migrated | replace(configLines[i], ";PreValueUse = true", ";PreValueUse = false"); // Set it to its default value
  390. migrated = migrated | replace(configLines[i], ";PreValueUse", "PreValueUse"); // Enable it
  391. /* AllowNegativeRates has a <NUMBER> as prefix! */
  392. if (isInString(configLines[i], "AllowNegativeRates") && isInString(configLines[i], ";")) { // It is the parameter "AllowNegativeRates" and it is commented out
  393. migrated = migrated | replace(configLines[i], "true", "false"); // Set it to its default value
  394. migrated = migrated | replace(configLines[i], ";", ""); // Enable it
  395. }
  396. /* IgnoreLeadingNaN has a <NUMBER> as prefix! */
  397. if (isInString(configLines[i], "IgnoreLeadingNaN") && isInString(configLines[i], ";")) { // It is the parameter "IgnoreLeadingNaN" and it is commented out
  398. migrated = migrated | replace(configLines[i], "true", "false"); // Set it to its default value
  399. migrated = migrated | replace(configLines[i], ";", ""); // Enable it
  400. }
  401. /* ExtendedResolution has a <NUMBER> as prefix! */
  402. if (isInString(configLines[i], "ExtendedResolution") && isInString(configLines[i], ";")) { // It is the parameter "ExtendedResolution" and it is commented out
  403. migrated = migrated | replace(configLines[i], "true", "false"); // Set it to its default value
  404. migrated = migrated | replace(configLines[i], ";", ""); // Enable it
  405. }
  406. migrated = migrated | replace(configLines[i], ";ErrorMessage = true", ";ErrorMessage = false"); // Set it to its default value
  407. migrated = migrated | replace(configLines[i], ";ErrorMessage", "ErrorMessage"); // Enable it
  408. migrated = migrated | replace(configLines[i], ";CheckDigitIncreaseConsistency = true", ";CheckDigitIncreaseConsistency = false"); // Set it to its default value
  409. migrated = migrated | replace(configLines[i], ";CheckDigitIncreaseConsistency", "CheckDigitIncreaseConsistency"); // Enable it
  410. }
  411. if (section == "[MQTT]") {
  412. migrated = migrated | replace(configLines[i], "SetRetainFlag", "RetainMessages"); // First rename it, enable it with its default value
  413. migrated = migrated | replace(configLines[i], ";RetainMessages = true", ";RetainMessages = false"); // Set it to its default value
  414. migrated = migrated | replace(configLines[i], ";RetainMessages", "RetainMessages"); // Enable it
  415. migrated = migrated | replace(configLines[i], ";HomeassistantDiscovery = true", ";HomeassistantDiscovery = false"); // Set it to its default value
  416. migrated = migrated | replace(configLines[i], ";HomeassistantDiscovery", "HomeassistantDiscovery"); // Enable it
  417. if (configLines[i].rfind("Topic", 0) != std::string::npos) // only if string starts with "Topic" (Was the naming in very old version)
  418. {
  419. migrated = migrated | replace(configLines[i], "Topic", "MainTopic");
  420. }
  421. }
  422. if (section == "[InfluxDB]") {
  423. }
  424. if (section == "[GPIO]") {
  425. }
  426. if (section == "[DataLogging]") {
  427. migrated = migrated | replace(configLines[i], "DataLogRetentionInDays", "DataFilesRetention");
  428. /* DataLogActive is true by default! */
  429. migrated = migrated | replace(configLines[i], ";DataLogActive = true", ";DataLogActive = true"); // Set it to its default value
  430. migrated = migrated | replace(configLines[i], ";DataLogActive", "DataLogActive"); // Enable it
  431. }
  432. if (section == "[AutoTimer]") {
  433. migrated = migrated | replace(configLines[i], "Intervall", "Interval");
  434. migrated = migrated | replace(configLines[i], ";AutoStart = true", ";AutoStart = false"); // Set it to its default value
  435. migrated = migrated | replace(configLines[i], ";AutoStart", "AutoStart"); // Enable it
  436. }
  437. if (section == "[Debug]") {
  438. migrated = migrated | replace(configLines[i], "Logfile ", "LogLevel "); // Whitespace needed so it does not match `LogfileRetentionInDays`
  439. /* LogLevel (resp. LogFile) was originally a boolean, but we switched it to an int
  440. * For both cases (true/false), we set it to level 2 (WARNING) */
  441. migrated = migrated | replace(configLines[i], "LogLevel = true", "LogLevel = 2");
  442. migrated = migrated | replace(configLines[i], "LogLevel = false", "LogLevel = 2");
  443. migrated = migrated | replace(configLines[i], "LogfileRetentionInDays", "LogfilesRetention");
  444. }
  445. if (section == "[System]") {
  446. migrated = migrated | replace(configLines[i], "RSSIThreashold", "RSSIThreshold");
  447. migrated = migrated | replace(configLines[i], "AutoAdjustSummertime", ";UNUSED_PARAMETER"); // This parameter is no longer used
  448. migrated = migrated | replace(configLines[i], ";SetupMode = true", ";SetupMode = false"); // Set it to its default value
  449. migrated = migrated | replace(configLines[i], ";SetupMode", "SetupMode"); // Enable it
  450. }
  451. }
  452. if (migrated) { // At least one replacement happened
  453. if (! RenameFile(CONFIG_FILE, CONFIG_FILE_BACKUP)) {
  454. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create backup of Config file!");
  455. return;
  456. }
  457. FILE* pfile = fopen(CONFIG_FILE, "w");
  458. for (int i = 0; i < configLines.size(); i++) {
  459. fwrite(configLines[i].c_str() , configLines[i].length(), 1, pfile);
  460. fwrite("\n" , 1, 1, pfile);
  461. }
  462. fclose(pfile);
  463. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Config file migrated. Saved backup to " + string(CONFIG_FILE_BACKUP));
  464. }
  465. }
  466. std::vector<std::string> splitString(const std::string& str) {
  467. std::vector<std::string> tokens;
  468. std::stringstream ss(str);
  469. std::string token;
  470. while (std::getline(ss, token, '\n')) {
  471. tokens.push_back(token);
  472. }
  473. return tokens;
  474. }
  475. /*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
  476. std::string buf;
  477. std::size_t pos = 0;
  478. std::size_t prevPos;
  479. bool found = false;
  480. // Reserves rough estimate of final size of string.
  481. buf.reserve(s.size());
  482. while (true) {
  483. prevPos = pos;
  484. pos = s.find(toReplace, pos);
  485. if (pos == std::string::npos) {
  486. break;
  487. }
  488. found = true;
  489. buf.append(s, prevPos, pos - prevPos);
  490. buf += replaceWith;
  491. pos += toReplace.size();
  492. }
  493. buf.append(s, prevPos, s.size() - prevPos);
  494. s.swap(buf);
  495. return found;
  496. }*/
  497. bool replace(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
  498. return replace(s, toReplace, replaceWith, true);
  499. }
  500. bool replace(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt) {
  501. std::size_t pos = s.find(toReplace);
  502. if (pos == std::string::npos) { // Not found
  503. return false;
  504. }
  505. std::string old = s;
  506. s.replace(pos, toReplace.length(), replaceWith);
  507. if (logIt) {
  508. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line '" + old + "' to '" + s + "'");
  509. }
  510. return true;
  511. }
  512. bool isInString(std::string& s, std::string const& toFind) {
  513. std::size_t pos = s.find(toFind);
  514. if (pos == std::string::npos) { // Not found
  515. return false;
  516. }
  517. return true;
  518. }