diff --git a/cores/esp8266/abi.cpp b/cores/esp8266/abi.cpp index 91e999a0c..b38f16453 100644 --- a/cores/esp8266/abi.cpp +++ b/cores/esp8266/abi.cpp @@ -17,14 +17,16 @@ */ #include +#include +#include extern "C" { #include "ets_sys.h" #include "os_type.h" #include "osapi.h" #include "mem.h" -#include "user_interface.h" } + void *operator new(size_t size) { size = ((size + 3) & ~((size_t)0x3)); return os_malloc(size); @@ -47,27 +49,26 @@ extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); void __cxa_pure_virtual(void) { - abort(); + panic(); } void __cxa_deleted_virtual(void) { - abort(); + panic(); } namespace std { void __throw_bad_function_call() { - abort(); + panic(); } void __throw_length_error(char const*) { - abort(); + panic(); } void __throw_bad_alloc() { - abort(); + panic(); } } // TODO: rebuild windows toolchain to make this unnecessary: void* __dso_handle; - diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 5ea7a1be7..92c48312d 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -66,13 +66,6 @@ static os_event_t g_loop_queue[LOOP_QUEUE_SIZE]; static uint32_t g_micros_at_task_start; - -extern "C" void abort() { - do { - *((int*)0) = 0; - } while(true); -} - extern "C" void esp_yield() { if (cont_can_yield(&g_cont)) { cont_yield(&g_cont); @@ -89,7 +82,7 @@ extern "C" void __yield() { esp_yield(); } else { - abort(); + panic(); } } @@ -117,9 +110,8 @@ static void loop_wrapper() { static void loop_task(os_event_t *events) { g_micros_at_task_start = system_get_time(); cont_run(&g_cont, &loop_wrapper); - if(cont_check(&g_cont) != 0) { - ets_printf("\r\nsketch stack overflow detected\r\n"); - abort(); + if (cont_check(&g_cont) != 0) { + panic(); } } diff --git a/cores/esp8266/core_esp8266_postmortem.c b/cores/esp8266/core_esp8266_postmortem.c index 1fd929e85..1af8f57ca 100644 --- a/cores/esp8266/core_esp8266_postmortem.c +++ b/cores/esp8266/core_esp8266_postmortem.c @@ -22,6 +22,7 @@ #include #include #include +#include "debug.h" #include "ets_sys.h" #include "user_interface.h" #include "esp8266_peri.h" @@ -30,6 +31,11 @@ extern void __real_system_restart_local(); extern cont_t g_cont; +static const char* s_panic_file = 0; +static int s_panic_line = 0; +static const char* s_panic_func = 0; + +static bool s_abort_called = false; void uart_write_char_d(char c); static void uart0_write_char_d(char c); @@ -56,7 +62,13 @@ void __wrap_system_restart_local() { ets_install_putc1(&uart_write_char_d); - if (rst_info.reason == REASON_EXCEPTION_RST) { + if (s_panic_line) { + ets_printf("\nPanic %s:%d %s\n", s_panic_file, s_panic_line, s_panic_func); + } + else if (s_abort_called) { + ets_printf("Abort called\n"); + } + else if (rst_info.reason == REASON_EXCEPTION_RST) { ets_printf("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n", rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); } @@ -158,3 +170,25 @@ static void uart1_write_char_d(char c) { } USF(1) = c; } +void abort() __attribute__((noreturn)); + +void abort(){ + // cause exception + s_abort_called = true; + do { + *((int*)0) = 0; + } while(true); +} + +void __assert_func(const char *file, int line, const char *func, const char *what) { + s_panic_file = file; + s_panic_line = line; + s_panic_func = func; +} + +void __panic_func(const char* file, int line, const char* func) { + s_panic_file = file; + s_panic_line = line; + s_panic_func = func; + abort(); +} diff --git a/cores/esp8266/debug.h b/cores/esp8266/debug.h index fe389bb2d..9cf5980ef 100644 --- a/cores/esp8266/debug.h +++ b/cores/esp8266/debug.h @@ -2,7 +2,9 @@ #define ARD_DEBUG_H #include -//#define DEBUGV(...) ets_printf(__VA_ARGS__) +#include + +#define DEBUGV(...) ets_printf(__VA_ARGS__) #ifndef DEBUGV #define DEBUGV(...) @@ -14,4 +16,16 @@ void hexdump(uint8_t *mem, uint32_t len, uint8_t cols = 16); void hexdump(uint8_t *mem, uint32_t len, uint8_t cols); #endif +#ifdef __cplusplus +extern "C" { +#endif + +void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn)); +#define panic() __panic_func(__FILE__, __LINE__, __func__) + +#ifdef __cplusplus +} +#endif + + #endif//ARD_DEBUG_H diff --git a/cores/esp8266/time.c b/cores/esp8266/time.c index 7410b4f16..e75c04915 100644 --- a/cores/esp8266/time.c +++ b/cores/esp8266/time.c @@ -124,4 +124,5 @@ int gettimeofday(struct timeval *tp, void *tzp) tp->tv_sec = (s_bootTime + millis()) / 1000; tp->tv_usec = micros() * 1000; } + return 0; } diff --git a/doc/ota_updates/ota-web-browser-form-ok.png b/doc/ota_updates/ota-web-browser-form-ok.png new file mode 100644 index 000000000..ed44e9cbb Binary files /dev/null and b/doc/ota_updates/ota-web-browser-form-ok.png differ diff --git a/doc/ota_updates/ota-web-browser-form.png b/doc/ota_updates/ota-web-browser-form.png new file mode 100644 index 000000000..87872393f Binary files /dev/null and b/doc/ota_updates/ota-web-browser-form.png differ diff --git a/doc/ota_updates/ota-web-path-to-binary.png b/doc/ota_updates/ota-web-path-to-binary.png new file mode 100644 index 000000000..9237d8780 Binary files /dev/null and b/doc/ota_updates/ota-web-path-to-binary.png differ diff --git a/doc/ota_updates/ota-web-serial-monitor-ready.png b/doc/ota_updates/ota-web-serial-monitor-ready.png new file mode 100644 index 000000000..5ce011170 Binary files /dev/null and b/doc/ota_updates/ota-web-serial-monitor-ready.png differ diff --git a/doc/ota_updates/ota-web-serial-monitor-reboot.png b/doc/ota_updates/ota-web-serial-monitor-reboot.png new file mode 100644 index 000000000..0fd469e11 Binary files /dev/null and b/doc/ota_updates/ota-web-serial-monitor-reboot.png differ diff --git a/doc/ota_updates/ota-web-show-verbose-compilation.png b/doc/ota_updates/ota-web-show-verbose-compilation.png new file mode 100644 index 000000000..00ac3871f Binary files /dev/null and b/doc/ota_updates/ota-web-show-verbose-compilation.png differ diff --git a/doc/ota_updates/ota_updates.md b/doc/ota_updates/ota_updates.md index 09916e27f..bac75e9ec 100644 --- a/doc/ota_updates/ota_updates.md +++ b/doc/ota_updates/ota_updates.md @@ -3,26 +3,43 @@ title: OTA Update --- ## Table of Contents - * [Basic Requirements](#basic-requirements) - * [Arduino IDE](#arduino-ide) - * [HTTP Server](#http-server) - * [Stream Interface](#stream-interface) +* [Introduction](#introduction) + * [Security](#security) + * [Safety](#safety) + * [Basic Requirements](#basic-requirements) +* [Arduino IDE](#arduino-ide) + * [Requirements](#requirements) + * [Application Example](#application-example) + * [Classic OTA](#classic-ota) + * [ArduinoOTA](#arduinoota) +* [Web Browser](#web-browser) + * [Requirements](#requirements-1) + * [Implementation Overview](#implementation-overview) + * [Application Example](#application-example-1) +* [HTTP Server](#http-server) +* [Stream Interface](#stream-interface) +* [Updater class](#updater-class) + ## Introduction -OTA (Over the Air) update is the process of loading the firmware to ESP module using WiFi connection rather that a serial port. Such functionality became extremely useful in case of limited or no physical access to the module. +OTA (Over the Air) update is the process of loading the firmware to ESP module using Wi-Fi connection rather that a serial port. Such functionality became extremely useful in case of limited or no physical access to the module. -OTA may be done from: - - [Arduino IDE](#arduino-ide) - - [HTTP server](#http-server) +OTA may be done using: +* [Arduino IDE](#arduino-ide) +* [Web Browser](#web-browser) +* [HTTP Server](#http-server) -In any case first firmware upload have to be done over a serial port. If OTA routines are correctly implemented in sketch, then all subsequent uploads may be done over the air. +Arduino IDE option is intended primarily for software development phase. The two other options would be more useful after deployment, to provide module with application updates manually with a web browser or automatically using a http server. + +In any case first firmware upload have to be done over a serial port. If OTA routines are correctly implemented in a sketch, then all subsequent uploads may be done over the air. + +There is no imposed security on OTA process from being hacked. It is up to developer to ensure that updates are allowed only from legitimate / trusted source. Once update is complete, module restarts and new code is executed. Developer should ensure that application running on module is shut down and restarted in a safe manner. Chapters below provide additional information regarding security and safety of OTA process. -There is no imposed security on OTA process from being hacked. It is up to developer to ensure that updates are allowed only from legitimate / trusted source. Once update is complete module restarts and new code is executed. Developer should ensure that application running on module is shut down and restarted in safe manner. Chapters below provide additinal information regarding security and safety of OTA process. ### Security -Module has to be exposed wirelessly to get it updated with a new code. That poses chances of module being violently hacked and loaded with some other firmware. To reduce likelihood of being hacked consider protecting your uploads with a password, selecting certain OTA port, etc. +Module has to be exposed wirelessly to get it updated with a new sketch. That poses chances of module being violently hacked and loaded with some other code. To reduce likelihood of being hacked consider protecting your uploads with a password, selecting certain OTA port, etc. Check functionality provided with [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library that may improve security: ```cpp @@ -30,7 +47,10 @@ void setPort(uint16_t port); void setHostname(const char *hostname); void setPassword(const char *password); ``` -If possible implement other means of protection from being hacked, e.g. exposing module for uploads only according to specific schedule, trigger OTA only be user pressing dedicated “Update” button, etc. +Certain protection functionality is already built in and do not require any additional coding by developer. [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) and espota.py use [Digest-MD5](https://en.wikipedia.org/wiki/Digest_access_authentication) to authenticate upload. Integrity of transferred data is verified on ESP side using [MD5](https://en.wikipedia.org/wiki/MD5) checksum. + +Make your own risk analysis and depending on application decide what library functions to implement. If required consider implementation of other means of protection from being hacked, e.g. exposing module for uploads only according to specific schedule, trigger OTA only be user pressing dedicated “Update” button, etc. + ### Safety @@ -46,14 +66,14 @@ void onProgress(OTA_CALLBACK_PROGRESS(fn)); void onError(OTA_CALLBACK_ERROR (fn)); ``` -The following chapters provide more details and specific methods of doing OTA. - - -## Basic Requirements +### Basic Requirements - Flash chip size is 2x the size of the sketch. +The following chapters provide more details and specific methods of doing OTA. + + ## Arduino IDE Uploading modules wirelessly from Arduino IDE is intended for the following typical scenarios: @@ -61,75 +81,175 @@ Uploading modules wirelessly from Arduino IDE is intended for the following typi - for updating small quantity of modules - only if modules are available on the same network as the computer with Arduino IDE -#### Requirements + +### Requirements - The ESP and the computer must be connected to the same network. -#### Let's Do It + +### Application Example Currently there are two software configurations that support OTA updates -- [Classic OTA](#classic-ota-configuration): Arduino IDE 1.6.5 and [stable](https://github.com/esp8266/Arduino#staging-version-) (July 23, 2015) or [staging](https://github.com/esp8266/Arduino#staging-version-) (Sep 30, 2015) platform package that provides first OTA implementation, yet without support for [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This particular configuration is intended for less experienced users. It soon will be depreciated once implementation below is fully released. +- [Classic OTA](#classic-ota-configuration): Arduino IDE 1.6.5 and 1.6.5-947-g39819f0 (of July 23, 2015) or 1.6.5-1160-gef26c5f (of Sep 30, 2015) version of platform package that provides first OTA implementation, yet without support for [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This particular configuration is easier to configure in Arduino IDE and therefore suggested for less experienced users. It soon will be depreciated once implementation below is fully released. - [ArduinoOTA](#arduinoota-configuration): Arduino-PR-4107-BUILD-421 and latest git version of platform package that includes [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This configuration features preliminary build of Arduino IDE and is intended for more experienced users. Please mid your step. -Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-configuration) and [ArduinoOTA](#arduinoota-configuration) using NodeMCU 1.0 board with ESP-12E. +Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-configuration) and [ArduinoOTA](#arduinoota-configuration) using NodeMCU 1.0 (ESP-12E Module) board. -##### Classic OTA Configuration + +#### Classic OTA 1. Before you begin, please make sure that you have the following installed: - - Arduino IDE and ESP8266 board support as described under https://github.com/esp8266/Arduino#installing-with-boards-manager - - [Python](https://www.python.org/) 2.7.10 (do not install Python 3.5.0 that is not supported): + - Arduino IDE and ESP8266 board support as described under https://github.com/esp8266/Arduino#installing-with-boards-manager + - [Python](https://www.python.org/) 2.7 (do not install Python 3.5 that is not supported): - **Note:** Windows users should select “Add python.exe to Path” (see below – this option is not selected by default) + **Note:** Windows users should select “Add python.exe to Path” (see below – this option is not selected by default). ![Python installation set up](ota-ide-python-configuration.png) 2. Now prepare the sketch and configuration for the upload over a serial port. - - Start Arduino IDE and load sketch DNS_SD_Arduino_OTA.ino available under File > Examples > ESP8266mDNS + - Start Arduino IDE and load sketch DNS_SD_Arduino_OTA.ino available under File > Examples > ESP8266mDNS ![OTA sketch selection](ota-ide-sketch-selection.png) - **Note:** This sketch is available only for stable (July 23, 2015) and staging (Sep 30, 2015) releases installed in Arduino IDE using https://github.com/esp8266/Arduino#installing-with-boards-manager. It was removed in [#980](https://github.com/esp8266/Arduino/pull/980) from Github repository. + **Note:** This sketch is available only for 1.6.5-947-g39819f0 (of July 23, 2015) and 1.6.5-1160-gef26c5f (of Sep 30, 2015) versions of platform packages installed in Arduino IDE using https://github.com/esp8266/Arduino#installing-with-boards-manager. It was removed in [#980](https://github.com/esp8266/Arduino/pull/980) from GitHub repository. - - Update ssid and pass in the sketch so the module can join your WiFi network + - Update ssid and pass in the sketch so the module can join your Wi-Fi network ![ssid and pass entry](ota-ide-ssid-pass-entry.png) - - Configure upload parameters as below (you may need to adjust configuration if you are using a different module): + - Configure upload parameters as below (you may need to adjust configuration if you are using a different module): ![configuration of serial upload](ota-ide-serial-upload-configuration.png) -3. Upload the sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if the module has joined your WiFi network. +3. Upload the sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if module has joined your Wi-Fi network. - ![check if module joined network](ota-ide-module-joined-wifi.png) + ![check if module joined network](ota-ide-module-joined-wifi.png) 4. Only if module is connected to network, after a couple of seconds, the esp8266-ota port will show up in Arduino IDE: - ![selection og OTA port](ota-ide-ota-port-selection.png) + ![selection og OTA port](ota-ide-ota-port-selection.png) 5. Now get ready for your first OTA upload by changing configuration settings as follows: - ![configuration of OTA upload](ota-ide-ota-upload-configuration.png) + ![configuration of OTA upload](ota-ide-ota-upload-configuration.png) - **Note:** If you do not see “Upload Using: OTA” option available for “NodeMCU 1.0 (ESP-12E Module)” board, please upload the latest [boards.txt](https://github.com/esp8266/Arduino/blob/master/boards.txt) file from Github repository, replace existing file and restart Arduino IDE. + **Note:** If you do not see “Upload Using: OTA” option available for “NodeMCU 1.0 (ESP-12E Module)” board, please upload the latest [boards.txt](https://github.com/esp8266/Arduino/blob/master/boards.txt) file from GitHub repository, replace existing file and restart Arduino IDE. 6. If you have successfully completed all the above steps, you can upload (Ctrl+U) the same (or any other) sketch over OTA: - ![OTA upload complete](ota-ide-ota-upload-complete.png) + ![OTA upload complete](ota-ide-ota-upload-complete.png) **Note** To be able to upload your sketch over and over again using OTA, you need to embed OTA routines inside. Please use DNS_SD_Arduino_OTA.ino as an example. -##### ArduinoOTA Configuration -1. Get the following software: - - Arduino-PR-4107-BUILD-421 - https://github.com/esp8266/Arduino/pull/984#issuecomment-155905800 - - Latest git version of pacakge - https://github.com/esp8266/Arduino#using-git-version- - - Python 2.7.10 +#### ArduinoOTA -2. Proceed to step 2 under [Classic OTA Configuration](#classic-ota-configuration) using BasicOTA.ino or OTALeds.ino skech instead. +1. Upload and install the following software: + - Arduino-PR-4107-BUILD-421 - https://github.com/esp8266/Arduino/pull/984#issuecomment-155905800 + - Latest git version of platform package - https://github.com/esp8266/Arduino#using-git-version- + - Python 2.7 + +2. Proceed to step 2 under [Classic OTA Configuration](#classic-ota-configuration) using BasicOTA.ino or OTALeds.ino sketch instead. 3. Carry on with remaining steps. +## Web Browser + +Updates described in this chapter are done with a web browser that can be useful in the following typical scenarios: +- after application deployment if loading directly from Arduino IDE is inconvenient or not possible +- after deployment if user is unable to expose module for OTA from external update server +- to provide updates after deployment to small quantity of modules when setting an update server is not practicable + + +### Requirements + +- The ESP and the computer must be connected to the same network. + + +### Implementation Overview + +Updates with a web browswer are implemented using ```ESP8266HTTPUpdateServer``` class together with ```ESP8266WebServer``` and ```ESP8266mDNS``` classes. The following code is required to get it work: + +setup() + +```cpp + MDNS.begin(host); + + httpUpdater.setup(&httpServer); + httpServer.begin(); + + MDNS.addService("http", "tcp", 80); +``` + +loop() + +```cpp + httpServer.handleClient(); +``` + + +### Application Example + +The sample implementation provided below has been done using: + +- example sketch WebUpdater.ino available in ESP8266HTTPUpdateServer library +- NodeMCU 1.0 (ESP-12E Module) + +You can use another module if it meets “Flash chip size is 2x the size of the sketch” requirement. + + +1. Before you begin, please make sure that you have the following software installed: + + - Arduino IDE and 2.0.0-rc1 (of Nov 17, 2015) version of platform package as described under https://github.com/esp8266/Arduino#installing-with-boards-manager + - Host software depending on O/S you use: + 1. Avahi http://avahi.org/ for Linux + 2. Bonjour http://www.apple.com/support/bonjour/ for Windows + 3. Mac OSX and iOS - support is already built in / no any extra s/w is required + +2. Prepare the sketch and configuration for initial upload with a serial port. + - Start Arduino IDE and load sketch WebUpdater.ino available under File > Examples > ESP8266HTTPUpdateServer. + - Update ssid and pass in the sketch so the module can join your Wi-Fi network. + - Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option. + + ![Preferences - enablig verbose output during compilation](ota-web-show-verbose-compilation.png) + + **Note:** This setting will be required in step 5 below. You can uncheck this setting it afterwards. + +3. Upload sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if you see the following message displayed, that contains url for OTA update. + + ![Serial Monitor - after first load using serial](ota-web-serial-monitor-ready.png) + + **Note:** Such message will be shown only after module successfully joins network and is ready for an OTA upload: + +4. Now open web browser and enter the url provided on Serial Monitor, i.e. http://esp8266-webupdate.local/update. Once entered, browser should display a form like below that has been served by your module. The form invites you to choose a file for update. + + ![OTA update form in web browser](ota-web-browser-form.png) + + **Note:** If entering “http://esp8266-webupdate.local/update” does not work, try replacing “esp8266-webupdate” with module’s IP address. For example, if your module IP is “192.168.1.100” then url should be “http://192.168.1.100/update”. This workaround is useful in case the host software installed in step 2 does not work. If still nothing works and there are no clues on Serial Monitor, try to diagnose issue by opening provided url in Google Chrome, pressing F12 and checking contents of “Console” and “Network” tabs. Chrome provides some advanced logging on these tabs. + + +5. To obtain the file navigate to directory used by Arduino IDE to store results of compilation. You can check the path to this file in compilation log shown in IDE debug window as marked below. + + ![Compilation complete - path to binary file](ota-web-path-to-binary.png) + +6. Now press “Choose File” in web browser, go to directory identified in step 5 above, find the file “WebUpdater.cpp.bin” and upload it. If upload is successful you will see “OK” on web browser like below. + + ![OTA update complete](ota-web-browser-form-ok.png) + + Module will reboot that should be visible on Serial Monitor: + + ![Serial Monitor - after OTA update](ota-web-serial-monitor-reboot.png) + +Just after reboot you should see exactly the same message “HTTPUpdateServer ready! Open http:// esp8266-webupdate.local /update in your browser” like in step 3. This is because module has been loaded again with the same code – first using serial port, and then using OTA. + +Once you are comfortable with this procedure go ahead and modify WebUpdater.ino sketch to print some additional messages, compile it, locate new binary file and upload it using web browser to see entered changes on a Serial Monitor. + +You can also add OTA routines to your own sketch following guidelines in [Implementation Overview](#implementation-overview) above. If this is done correctly you should be always able to upload new sketch over the previous one using a web browser. + +In case OTA update fails dead after entering modifications in your sketch, you can always recover module by loading it over a serial port. Then diagnose the issue with sketch using Serial Monitor. Once the issue is fixed try OTA again. + + ## HTTP Server ```ESPhttpUpdate``` class can check for updates and download a binary file from HTTP web server. @@ -261,6 +381,16 @@ header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500); ``` +## Stream Interface + +TODO describe Stream Interface + +The Stream Interface is the base for all other update modes like OTA, http Server / client. + + ## Updater class TODO describe Updater class + +Updater is in the Core and deals with writing the firmware to the flash, checking its integrity and telling the bootloader to load the new firmware on the next boot. + diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 665ef46ea..59b82e290 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -36,6 +36,7 @@ extern "C" { #include "WiFiClient.h" #include "WiFiUdp.h" +#include "debug.h" extern "C" void esp_schedule(); extern "C" void esp_yield(); @@ -44,6 +45,7 @@ ESP8266WiFiClass::ESP8266WiFiClass() : _smartConfigStarted(false) , _smartConfigDone(false) , _useStaticIp(false) +, _persistent(true) { uint8 m = wifi_get_opmode(); _useClientMode = (m & WIFI_STA); @@ -51,6 +53,12 @@ ESP8266WiFiClass::ESP8266WiFiClass() wifi_set_event_handler_cb((wifi_event_handler_cb_t)&ESP8266WiFiClass::_eventCallback); } +void ESP8266WiFiClass::persistent(bool persistent) +{ + _persistent = persistent; +} + + void ESP8266WiFiClass::mode(WiFiMode m) { if(wifi_get_opmode() == (uint8)m) { @@ -69,9 +77,7 @@ void ESP8266WiFiClass::mode(WiFiMode m) _useClientMode = false; } - ETS_UART_INTR_DISABLE(); - wifi_set_opmode(m); - ETS_UART_INTR_ENABLE(); + _mode(m); } WiFiMode ESP8266WiFiClass::getMode() @@ -86,15 +92,44 @@ void ESP8266WiFiClass::_mode(WiFiMode m) } ETS_UART_INTR_DISABLE(); - wifi_set_opmode(m); + if (_persistent) + wifi_set_opmode(m); + else + wifi_set_opmode_current(m); ETS_UART_INTR_ENABLE(); + } -int ESP8266WiFiClass::begin(char* ssid, char *passphrase, int32_t channel, uint8_t bssid[6]){ +static bool sta_config_equal(const station_config& lhs, const station_config& rhs) +{ + if (strcmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid)) != 0) + return false; + + if (strcmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password)) != 0) + return false; + + if (lhs.bssid_set) { + if (!rhs.bssid_set) + return false; + + if (memcmp(lhs.bssid, rhs.bssid, 6) != 0) + return false; + } + else { + if (rhs.bssid_set) + return false; + } + + return true; +} + +int ESP8266WiFiClass::begin(char* ssid, char *passphrase, int32_t channel, const uint8_t* bssid) +{ return begin((const char*) ssid, (const char*) passphrase, channel, bssid); } -int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t channel, uint8_t bssid[6]){ +int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid) +{ _useClientMode = true; if(_useApMode) { @@ -106,12 +141,12 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch } if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { - // fail SSID to long or missing! + // fail SSID too long or missing! return WL_CONNECT_FAILED; } if(passphrase && strlen(passphrase) > 63) { - // fail passphrase to long! + // fail passphrase too long! return WL_CONNECT_FAILED; } @@ -131,8 +166,18 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch conf.bssid_set = 0; } + struct station_config current_conf; + wifi_station_get_config(¤t_conf); + if (sta_config_equal(current_conf, conf)) { + DEBUGV("sta config unchanged"); + return status(); + } + ETS_UART_INTR_DISABLE(); - wifi_station_set_config(&conf); + if (_persistent) + wifi_station_set_config(&conf); + else + wifi_station_set_config_current(&conf); wifi_station_connect(); ETS_UART_INTR_ENABLE(); @@ -203,8 +248,10 @@ int ESP8266WiFiClass::softAPdisconnect(bool wifioff) *conf.ssid = 0; *conf.password = 0; ETS_UART_INTR_DISABLE(); - wifi_softap_set_config(&conf); - wifi_station_disconnect(); + if (_persistent) + wifi_softap_set_config(&conf); + else + wifi_softap_set_config_current(&conf); ETS_UART_INTR_ENABLE(); if(wifioff) { @@ -228,7 +275,10 @@ int ESP8266WiFiClass::disconnect(bool wifioff) *conf.ssid = 0; *conf.password = 0; ETS_UART_INTR_DISABLE(); - wifi_station_set_config(&conf); + if (_persistent) + wifi_station_set_config(&conf); + else + wifi_station_set_config_current(&conf); wifi_station_disconnect(); ETS_UART_INTR_ENABLE(); @@ -247,6 +297,20 @@ int ESP8266WiFiClass::disconnect(bool wifioff) return 0; } +static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) +{ + if (strcmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid)) != 0) + return false; + if (strcmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password)) != 0) + return false; + if (lhs.channel != rhs.channel) + return false; + if (lhs.ssid_hidden != rhs.ssid_hidden) + return false; + return true; +} + + void ESP8266WiFiClass::softAP(const char* ssid) { softAP(ssid, 0); @@ -264,8 +328,8 @@ void ESP8266WiFiClass::softAP(const char* ssid, const char* passphrase, int chan _mode(WIFI_AP); } - if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { - // fail SSID to long or missing! + if(!ssid || *ssid == 0 || strlen(ssid) > 31) { + // fail SSID too long or missing! return; } @@ -294,8 +358,19 @@ void ESP8266WiFiClass::softAP(const char* ssid, const char* passphrase, int chan strcpy(reinterpret_cast(conf.password), passphrase); } + struct softap_config conf_current; + wifi_softap_get_config(&conf_current); + if (!softap_config_equal(conf, conf_current)) + { + DEBUGV("softap config unchanged"); + return; + } + ETS_UART_INTR_DISABLE(); - wifi_softap_set_config(&conf); + if (_persistent) + wifi_softap_set_config(&conf); + else + wifi_softap_set_config_current(&conf); ETS_UART_INTR_ENABLE(); } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 4c1330b2e..0d732ab4c 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -44,6 +44,8 @@ public: ESP8266WiFiClass(); + void persistent(bool persistent); + void mode(WiFiMode); WiFiMode getMode(); @@ -56,8 +58,8 @@ public: * @param channel Optional. Channel of AP * @return */ - int begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL); - int begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL); + int begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL); + int begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL); // Use sdk config to connect. int begin(); @@ -385,6 +387,7 @@ protected: bool _useApMode; bool _useClientMode; bool _useStaticIp; + bool _persistent; static bool _scanAsync; static bool _scanStarted; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp index 3369c3448..ef737c6d4 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp @@ -273,7 +273,7 @@ int WiFiClientSecure::available() { uint8_t WiFiClientSecure::connected() { if (!_client) return 0; - + if (_client->state() == ESTABLISHED) return 1; @@ -384,8 +384,7 @@ extern "C" void* ax_port_malloc(size_t size, const char* file, int line) { if (result == nullptr) { DEBUG_TLS_MEM_PRINT("%s:%d malloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap()); - - while(true){} + panic(); } if (size >= 1024) DEBUG_TLS_MEM_PRINT("%s:%d malloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap()); @@ -402,7 +401,7 @@ extern "C" void* ax_port_realloc(void* ptr, size_t size, const char* file, int l void* result = realloc(ptr, size); if (result == nullptr) { DEBUG_TLS_MEM_PRINT("%s:%d realloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap()); - while(true){} + panic(); } if (size >= 1024) DEBUG_TLS_MEM_PRINT("%s:%d realloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap()); diff --git a/package/build_boards_manager_package.sh b/package/build_boards_manager_package.sh index 3e969cdbc..261dd2afd 100755 --- a/package/build_boards_manager_package.sh +++ b/package/build_boards_manager_package.sh @@ -1,6 +1,12 @@ #!/bin/bash # +# Figure out how will the package be called +ver=`git describe --tags --always` +package_name=esp8266-$ver +echo "Version: $ver" +echo "Package name: $package_name" + # Set REMOTE_URL environment variable to the address where the package will be # available for download. This gets written into package json file. if [ -z "$REMOTE_URL" ]; then @@ -8,13 +14,17 @@ if [ -z "$REMOTE_URL" ]; then echo "REMOTE_URL not defined, using default" fi echo "Remote: $REMOTE_URL" -pushd .. -# Figure out how will the package be called -ver=`git describe --tags --always` -package_name=esp8266-$ver -echo "Version: $ver" -echo "Package name: $package_name" +if [ -z "$PKG_URL" ]; then + PKG_URL="$REMOTE_URL/versions/$ver/$package_name.zip" +fi +echo "Package: $PKG_URL" + +if [ -z "$DOC_URL" ]; then + DOC_URL="$REMOTE_URL/versions/$ver/doc/reference.html" +fi +echo "Docs: $DOC_URL" +pushd .. # Create directory for the package outdir=package/versions/$ver/$package_name srcdir=$PWD @@ -72,11 +82,11 @@ echo SHA-256: $sha echo "Making package_esp8266com_index.json" cat $srcdir/package/package_esp8266com_index.template.json | \ jq ".packages[0].platforms[0].version = \"$ver\" | \ - .packages[0].platforms[0].url = \"$REMOTE_URL/versions/$ver/$package_name.zip\" |\ + .packages[0].platforms[0].url = \"$PKG_URL\" |\ .packages[0].platforms[0].archiveFileName = \"$package_name.zip\" |\ .packages[0].platforms[0].checksum = \"SHA-256:$sha\" |\ .packages[0].platforms[0].size = \"$size\" |\ - .packages[0].platforms[0].help.online = \"$REMOTE_URL/versions/$ver/doc/reference.html\"" \ + .packages[0].platforms[0].help.online = \"$DOC_URL\"" \ > package_esp8266com_index.json popd diff --git a/package/merge_packages.py b/package/merge_packages.py old mode 100644 new mode 100755 index 350522bac..dba8bb9d6 --- a/package/merge_packages.py +++ b/package/merge_packages.py @@ -10,8 +10,8 @@ import sys def load_package(filename): pkg = json.load(open(filename))['packages'][0] - print("Loaded package {0} from {1}".format(pkg['name'], filename)) - print("{0} platform(s), {1} tools".format(len(pkg['platforms']), len(pkg['tools']))) + print("Loaded package {0} from {1}".format(pkg['name'], filename), file=sys.stderr) + print("{0} platform(s), {1} tools".format(len(pkg['platforms']), len(pkg['tools'])), file=sys.stderr) return pkg def merge_objects(versions, obj): @@ -19,17 +19,17 @@ def merge_objects(versions, obj): name = o['name'].encode('ascii') ver = o['version'].encode('ascii') if not name in versions: - print("found new object, {0}".format(name)) + print("found new object, {0}".format(name), file=sys.stderr) versions[name] = {} if not ver in versions[name]: - print("found new version {0} for object {1}".format(ver, name)) + print("found new version {0} for object {1}".format(ver, name), file=sys.stderr) versions[name][ver] = o return versions def main(args): if len(args) < 3: - print("Usage: {0} ".format(args[0])) + print("Usage: {0} ".format(args[0]), file=sys.stderr) return 1 tools = {} @@ -46,12 +46,12 @@ def main(args): for name in tools: for version in tools[name]: - print("Adding tool {0}-{1}".format(name, version)) + print("Adding tool {0}-{1}".format(name, version), file=sys.stderr) pkg1['tools'].append(tools[name][version]) for name in platforms: for version in platforms[name]: - print("Adding platform {0}-{1}".format(name, version)) + print("Adding platform {0}-{1}".format(name, version), file=sys.stderr) pkg1['platforms'].append(platforms[name][version]) json.dump({'packages':[pkg1]}, sys.stdout, indent=2)