diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d9277e..7fa43d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,7 @@ set(SOURCE_FILES user/cgi_sockets.h user/ansi_parser_callbacks.c user/ansi_parser_callbacks.h - user/user_main.h user/wifi_manager.c user/wifi_manager.h) + user/user_main.h user/wifimgr.c user/wifimgr.h user/persist.c user/persist.h) include_directories(include) include_directories(user) diff --git a/user/persist.c b/user/persist.c new file mode 100644 index 0000000..381b266 --- /dev/null +++ b/user/persist.c @@ -0,0 +1,192 @@ +// +// Created by MightyPork on 2017/07/09. +// + +#include "persist.h" +#include +#include "wifimgr.h" +#include "screen.h" + +FullPersistBlock persist; + +#define PERSIST_SECTOR_ID 0x3D + +//region Persist and restore individual modules + +/** + * Load persistent settings to live config structs + */ +static void ICACHE_FLASH_ATTR +load_settings_to_live(void) +{ + dbg("[Persist] Loading current settings to modules..."); + memcpy(&wificonf, &persist.current.wificonf, sizeof(WiFiConfigBlock)); + memcpy(&termconf, &persist.current.termconf, sizeof(TerminalConfigBlock)); + // ... +} + +static void ICACHE_FLASH_ATTR +store_settings_from_live(void) +{ + dbg("[Persist] Collecting live settings to persist block..."); + memcpy(&persist.current.wificonf, &wificonf, sizeof(wificonf)); + memcpy(&persist.current.termconf, &termconf, sizeof(termconf)); + // ... +} + +static void ICACHE_FLASH_ATTR +apply_live_settings(void) +{ + dbg("[Persist] Applying live settings..."); + terminal_apply_settings(); + wifimgr_apply_settings(); + // ... +} + +static void ICACHE_FLASH_ATTR +restore_live_settings_to_hard_defaults(void) +{ + wifimgr_restore_defaults(); + terminal_restore_defaults(); + // ... +} + +//endregion + +/** + * Compute CRC32. Adapted from https://github.com/esp8266/Arduino + * @param data + * @param length + * @return crc32 + */ +static uint32_t ICACHE_FLASH_ATTR +calculateCRC32(const uint8_t *data, size_t length) +{ + uint32_t crc = 0xffffffff; + while (length--) { + uint8_t c = *data++; + for (uint32_t i = 0x80; i > 0; i >>= 1) { + bool bit = (bool) (crc & 0x80000000UL); + if (c & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x04c11db7UL; + } + } + } + return crc; +} + +/** + * Compute a persist bundle checksum + * + * @param bundle + * @return + */ +static uint32_t ICACHE_FLASH_ATTR +compute_checksum(PersistBundle *bundle) +{ + return calculateCRC32((uint8_t *) bundle, sizeof(PersistBundle) - 4); +} + +/** + * Load, verify and apply persistent config + */ +void ICACHE_FLASH_ATTR +persist_load(void) +{ + info("[Persist] Loading stored settings from FLASH..."); + + bool hard_reset = false; + + // Try to load + hard_reset |= !system_param_load(PERSIST_SECTOR_ID, 0, &persist, sizeof(persist)); + + // Verify checksums + if (hard_reset || + (compute_checksum(&persist.defaults) != persist.defaults.checksum) || + (compute_checksum(&persist.current) != persist.current.checksum)) { + error("[Persist] Config block failed to load, restoring to hard defaults."); + hard_reset = true; + } + + if (hard_reset) { + persist_restore_hard_default(); + // this also stores them to flash and applies to modues + } else { + load_settings_to_live(); + apply_live_settings(); + } + + info("[Persist] All settings loaded and applied."); +} + +void ICACHE_FLASH_ATTR +persist_store(void) +{ + info("[Persist] Storing all settings to FLASH..."); + store_settings_from_live(); + + // Update checksums before write + persist.current.checksum = compute_checksum(&persist.current); + persist.defaults.checksum = compute_checksum(&persist.defaults); + + if (!system_param_save_with_protect(PERSIST_SECTOR_ID, &persist, sizeof(persist))) { + error("[Persist] Store to flash failed!"); + } + info("[Persist] All settings persisted."); +} + +/** + * Restore to built-in defaults + */ +void ICACHE_FLASH_ATTR +persist_restore_hard_default(void) +{ + info("[Persist] Restoring all settings to hard defaults..."); + + // Set live config to default values + restore_live_settings_to_hard_defaults(); + + // Store live -> current + store_settings_from_live(); + + // Store current -> default + memcpy(&persist.defaults, &persist.current, sizeof(persist.current)); + persist_store(); + + info("[Persist] All settings restored to hard defaults."); + + apply_live_settings(); // apply +} + +/** + * Restore default settings & apply + */ +void ICACHE_FLASH_ATTR +persist_restore_default(void) +{ + info("[Persist] Restoring live settings to stored defaults..."); + memcpy(&persist.current, &persist.defaults, sizeof(persist.defaults)); + load_settings_to_live(); + apply_live_settings(); + info("[Persist] Settings restored to stored defaults."); +} + +/** + * Store current settings as defaults & write to flash + */ +void ICACHE_FLASH_ATTR +persist_set_as_default(void) +{ + info("[Persist] Storing live settings as defaults.."); + + store_settings_from_live(); + memcpy(&persist.defaults, &persist.current, sizeof(persist.current)); + + persist_store(); + + info("[Persist] Default settings updated."); +} diff --git a/user/persist.h b/user/persist.h new file mode 100644 index 0000000..197eaa6 --- /dev/null +++ b/user/persist.h @@ -0,0 +1,39 @@ +// +// Created by MightyPork on 2017/07/09. +// +// There are 4 sets of settings. +// - hard defaults - hardcoded in firmware, used for init defaults after flash or if stored data are corrupt +// - defaults - persisted by privileged user +// - current - persistent current config state, can be restored to defaults any time +// - live - non-persistent settings valid only for the current runtime + +#ifndef ESP_VT100_FIRMWARE_PERSIST_H +#define ESP_VT100_FIRMWARE_PERSIST_H + +#include "wifimgr.h" +#include "screen.h" + +typedef struct { + WiFiConfigBlock wificonf; + TerminalConfigBlock termconf; + // ... + // other settings here + // ... + uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults. +} PersistBundle; + +typedef struct { + PersistBundle defaults; // defaults are stored here + PersistBundle current; // settings persisted by user +} FullPersistBlock; + +// Persist holds the data currently loaded from the flash +extern FullPersistBlock persist; + +void persist_load(void); +void persist_restore_hard_default(void); +void persist_restore_default(void); +void persist_set_as_default(void); +void persist_store(void); + +#endif //ESP_VT100_FIRMWARE_PERSIST_H diff --git a/user/screen.c b/user/screen.c index 54fd004..90f3fbc 100644 --- a/user/screen.c +++ b/user/screen.c @@ -4,6 +4,36 @@ //region Data structures +TerminalConfigBlock termconf; + +/** + * Restore hard defaults + */ +void terminal_restore_defaults(void) +{ + termconf.default_bg = 0; + termconf.default_fg = 7; + termconf.width = 26; + termconf.height = 10; + sprintf(termconf.title, "ESP8266 Wireless Terminal"); + sprintf(termconf.btn1, "1"); + sprintf(termconf.btn2, "2"); + sprintf(termconf.btn3, "3"); + sprintf(termconf.btn4, "4"); + sprintf(termconf.btn5, "5"); +} + +/** + * Apply settings after eg. restore from defaults + */ +void terminal_apply_settings(void) +{ + screen_init(); +} + +#define W termconf.width +#define H termconf.height + /** * Highest permissible value of the color attribute */ @@ -50,16 +80,6 @@ static struct { Color bg; } cursor_sav; -/** - * Active screen width - */ -static int W = SCREEN_DEF_W; - -/** - * Active screen height - */ -static int H = SCREEN_DEF_H; - // XXX volatile is probably not needed static volatile int notifyLock = 0; @@ -99,8 +119,8 @@ cursor_reset(void) { cursor.x = 0; cursor.y = 0; - cursor.fg = SCREEN_DEF_FG; - cursor.bg = SCREEN_DEF_BG; + cursor.fg = termconf.default_fg; + cursor.bg = termconf.default_bg; cursor.visible = 1; cursor.inverse = 0; cursor.autowrap = 1; @@ -363,8 +383,8 @@ screen_cursor_save(bool withAttrs) cursor_sav.bg = cursor.bg; cursor_sav.inverse = cursor.inverse; } else { - cursor_sav.fg = SCREEN_DEF_FG; - cursor_sav.bg = SCREEN_DEF_BG; + cursor_sav.fg = termconf.default_fg; + cursor_sav.bg = termconf.default_bg; cursor_sav.inverse = 0; } } diff --git a/user/screen.h b/user/screen.h index 701e759..36ab637 100644 --- a/user/screen.h +++ b/user/screen.h @@ -34,6 +34,25 @@ * */ +typedef struct { + u32 width; + u32 height; + u8 default_bg; + u8 default_fg; + char title[64]; + char btn1[10]; + char btn2[10]; + char btn3[10]; + char btn4[10]; + char btn5[10]; +} TerminalConfigBlock; + +// Live config +extern TerminalConfigBlock termconf; + +void terminal_restore_defaults(void); +void terminal_apply_settings(void); + /** * Maximum screen size (determines size of the static data array) * @@ -42,20 +61,13 @@ */ #define MAX_SCREEN_SIZE (80*25) -#define SCREEN_DEF_W 26 //!< Default screen width -#define SCREEN_DEF_H 10 //!< Default screen height - -#define SCREEN_DEF_BG 0 //!< Default screen background -#define SCREEN_DEF_FG 7 //!< Default screen foreground - typedef enum { CLEAR_TO_CURSOR=0, CLEAR_FROM_CURSOR=1, CLEAR_ALL=2 } ClearMode; typedef uint8_t Color; -httpd_cgi_state ICACHE_FLASH_ATTR -screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); +httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); /** Init the screen */ void screen_init(void); diff --git a/user/user_main.c b/user/user_main.c index 0990b41..67c7762 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -26,7 +26,8 @@ #include "user_main.h" #include "uart_driver.h" #include "ansi_parser_callbacks.h" -#include "wifi_manager.h" +#include "wifimgr.h" +#include "persist.h" #ifdef ESPFS_POS CgiUploadFlashDef uploadParams={ @@ -48,9 +49,6 @@ CgiUploadFlashDef uploadParams={ #define INCLUDE_FLASH_FNS #endif -static ETSTimer prHeapTimer; -static ETSTimer userStartTimer; - /** Periodically show heap usage */ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { @@ -79,42 +77,15 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) cnt++; } -static void user_start(void *unused) -{ - // TODO load persistent data, init wificonf - - // Change AP name if AI-THINKER found (means un-initialized device) - struct softap_config apconf; - wifi_softap_get_config(&apconf); - if (strstarts((char*)apconf.ssid, "AI-THINKER")) { - warn("Un-initialized device, performing factory reset."); - apars_handle_OSC_FactoryReset(); - return; - } - - // Set up WiFi & connect - wifimgr_restore_defaults(); - wifimgr_apply_settings(); - - // Captive portal - captdnsInit(); - - // Server - httpdInit(routes, 80); - - // The terminal screen - screen_init(); - - // Print the CANCEL character to indicate the module has restarted - // Critically important for client application if any kind of screen persistence / content re-use is needed - UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN - - info("Listening on UART0, 115200-8-N-1!"); -} +// Deferred init +static void user_start(void *unused); //Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. void ICACHE_FLASH_ATTR user_init(void) { + static ETSTimer userStartTimer; + static ETSTimer prHeapTimer; + serialInit(); // Prevent WiFi starting and connecting by default @@ -147,12 +118,40 @@ void ICACHE_FLASH_ATTR user_init(void) os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); os_timer_arm(&prHeapTimer, 1000, 1); - // do later (some functions do not yet work if called from user_init) + // do later (some functions do not work if called from user_init) os_timer_disarm(&userStartTimer); os_timer_setfn(&userStartTimer, user_start, NULL); os_timer_arm(&userStartTimer, 10, 0); } +static void user_start(void *unused) +{ + // Change AP name if AI-THINKER found (means un-initialized device) +// struct softap_config apconf; +// wifi_softap_get_config(&apconf); +// if (strstarts((char *) apconf.ssid, "AI-THINKER")) { +// warn("Un-initialized device, performing factory reset."); +// apars_handle_OSC_FactoryReset(); +// return; +// } + + // Load and apply stored settings, or defaults if stored settings are invalid + persist_load(); + // Captive portal (DNS redirector) + captdnsInit(); + // Server + httpdInit(routes, 80); + + // The terminal screen + screen_init(); + + // Print the CANCEL character to indicate the module has restarted + // Critically important for client application if any kind of screen persistence / content re-use is needed + UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN + + info("Listening on UART0, 115200-8-N-1!"); +} + // ---- unused funcs removed from sdk to save space --- // вызывается из phy_chip_v6.o diff --git a/user/wifi_manager.c b/user/wifimgr.c similarity index 73% rename from user/wifi_manager.c rename to user/wifimgr.c index d19a6dc..04ec363 100644 --- a/user/wifi_manager.c +++ b/user/wifimgr.c @@ -2,37 +2,40 @@ // Created by MightyPork on 2017/07/08. // -#include "wifi_manager.h" +#include "wifimgr.h" -WiFiSettingsBlock wificonf; +WiFiConfigBlock wificonf; /** * Restore defaults in the WiFi config block. * This is to be called if the WiFi config is corrupted on startup, * before applying the config. */ -void wifimgr_restore_defaults(void) +void ICACHE_FLASH_ATTR +wifimgr_restore_defaults(void) { u8 mac[6]; wifi_get_macaddr(SOFTAP_IF, mac); - wificonf.opmode = STATIONAP_MODE; + wificonf.opmode = SOFTAP_MODE; wificonf.tpw = 20; wificonf.ap_channel = 1; sprintf((char *) wificonf.ap_ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]); wificonf.ap_password[0] = 0; // PSK2 always if password is not null. - wificonf.ap_dhcp_lease_time = 120; wificonf.ap_hidden = false; - IP4_ADDR(&wificonf.ap_ip.ip, 192, 168, mac[5], 1); + IP4_ADDR(&wificonf.ap_ip.ip, 192, 168, 4, 60); IP4_ADDR(&wificonf.ap_ip.netmask, 255, 255, 255, 0); - IP4_ADDR(&wificonf.ap_ip.gw, 192, 168, mac[5], 1); + wificonf.ap_ip.gw.addr = wificonf.ap_ip.gw.addr; + + IP4_ADDR(&wificonf.ap_dhcp_range.start_ip, 192, 168, 4, 100); + IP4_ADDR(&wificonf.ap_dhcp_range.end_ip, 192, 168, 4, 200); + wificonf.ap_dhcp_range.enable = 1; + wificonf.ap_dhcp_lease_time = 120; // --- Client config --- wificonf.sta_ssid[0] = 0; wificonf.sta_password[0] = 0; - //sprintf((char *) wificonf.sta_ssid, "Chlivek"); - //sprintf((char *) wificonf.sta_password, "prase chrochta"); strcpy((char *) wificonf.sta_hostname, (char *) wificonf.ap_ssid); // use the same value for sta_hostname as AP name wificonf.sta_dhcp_enable = true; @@ -41,24 +44,8 @@ void wifimgr_restore_defaults(void) IP4_ADDR(&wificonf.sta_ip.gw, 192, 168, 0, 1); } -/** - * Event handler - */ -void wifimgr_event_cb(System_Event_t *event) -{ - switch (event->event) { -// case EVENT_STAMODE_CONNECTED: -// EVENT_STAMODE_DISCONNECTED, -// EVENT_STAMODE_AUTHMODE_CHANGE, -// EVENT_STAMODE_GOT_IP, -// EVENT_STAMODE_DHCP_TIMEOUT, -// EVENT_SOFTAPMODE_STACONNECTED, -// EVENT_SOFTAPMODE_STADISCONNECTED, -// EVENT_SOFTAPMODE_PROBEREQRECVED, - } -} - -static void configure_station(void) +static void ICACHE_FLASH_ATTR +configure_station(void) { info("[WiFi] Configuring Station mode..."); struct station_config conf; @@ -97,7 +84,8 @@ static void configure_station(void) wifi_station_connect(); } -static void configure_ap(void) +static void ICACHE_FLASH_ATTR +configure_ap(void) { bool suc; @@ -108,7 +96,7 @@ static void configure_ap(void) strcpy((char *) conf.ssid, (char *) wificonf.ap_ssid); strcpy((char *) conf.password, (char *) wificonf.ap_password); conf.authmode = (wificonf.ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK); - conf.ssid_len = strlen((char *) conf.ssid); + conf.ssid_len = (uint8_t) strlen((char *) conf.ssid); conf.ssid_hidden = wificonf.ap_hidden; conf.max_connection = 4; // default 4 (max possible) conf.beacon_interval = 100; // default 100 ms @@ -137,19 +125,11 @@ static void configure_ap(void) } info("[WiFi] Configuring SoftAP DHCP server..."); - struct dhcps_lease dhcp_lease; - struct ip_addr ip; - ip.addr = wificonf.ap_ip.ip.addr; - ip.addr = (ip.addr & 0x00FFFFFFUL) | ((((ip.addr >> 24) & 0xFF) + 99UL) << 24); - dhcp_lease.start_ip.addr = ip.addr; - ip.addr = (ip.addr & 0x00FFFFFFUL) | ((((ip.addr >> 24) & 0xFF) + 100UL) << 24); - dhcp_lease.end_ip.addr = ip.addr; - - dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(dhcp_lease.start_ip.addr)); - dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(dhcp_lease.end_ip.addr)); + dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf.ap_dhcp_range.start_ip.addr)); + dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf.ap_dhcp_range.end_ip.addr)); dbg("[WiFi] DHCP.lease = %d minutes", wificonf.ap_dhcp_lease_time); - if (!wifi_softap_set_dhcps_lease(&dhcp_lease)) { + if (!wifi_softap_set_dhcps_lease(&wificonf.ap_dhcp_range)) { error("[WiFi] DHCP address range set fail!"); return; } @@ -172,13 +152,12 @@ static void configure_ap(void) /** * Register the WiFi event listener, cycle WiFi, apply settings */ -void wifimgr_apply_settings(void) +void ICACHE_FLASH_ATTR +wifimgr_apply_settings(void) { - info("[WiFi] Initializing WiFi manager..."); -// wifi_set_event_handler_cb(wifimgr_event_cb); + info("[WiFi] Initializing..."); // Force wifi cycle - dbg("[WiFi] WiFi reset to apply new settings"); wifi_set_opmode(NULL_MODE); wifi_set_opmode(wificonf.opmode); @@ -191,4 +170,6 @@ void wifimgr_apply_settings(void) if (wificonf.opmode == STATIONAP_MODE || wificonf.opmode == SOFTAP_MODE) { configure_ap(); } + + info("[WiFi] WiFi settings applied."); } diff --git a/user/wifi_manager.h b/user/wifimgr.h similarity index 70% rename from user/wifi_manager.h rename to user/wifimgr.h index daf09e3..2bb7f11 100644 --- a/user/wifi_manager.h +++ b/user/wifimgr.h @@ -17,28 +17,29 @@ * This block can be used eg. for WiFi config backup. */ typedef struct { - WIFI_MODE opmode : 32; - u8 sta_hostname[32]; - u32 tpw; + WIFI_MODE opmode : 8; + u8 tpw; // --- AP config --- - u32 ap_channel; // 32 for alignment, needs 8 + u8 ap_channel; u8 ap_ssid[32]; u8 ap_password[32]; - u32 ap_hidden; - u32 ap_dhcp_lease_time; // in minutes + bool ap_hidden; + u16 ap_dhcp_lease_time; // in minutes + struct dhcps_lease ap_dhcp_range; struct ip_info ap_ip; // --- Client config --- u8 sta_ssid[32]; u8 sta_password[64]; - u32 sta_dhcp_enable; + u8 sta_hostname[32]; // hostname set via the API. This does not seem to have much effect. + bool sta_dhcp_enable; struct ip_info sta_ip; -} WiFiSettingsBlock; +} WiFiConfigBlock; -extern WiFiSettingsBlock wificonf; +extern WiFiConfigBlock wificonf; void wifimgr_restore_defaults(void);