diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index c4ec4b00b..c823d8f89 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -19,6 +19,7 @@ UpdaterClass::UpdaterClass() , _size(0) , _startAddress(0) , _currentAddress(0) +, _command(U_FLASH) { } @@ -30,9 +31,10 @@ void UpdaterClass::_reset() { _startAddress = 0; _currentAddress = 0; _size = 0; + _command = U_FLASH; } -bool UpdaterClass::begin(size_t size){ +bool UpdaterClass::begin(size_t size, int command) { if(_size > 0){ #ifdef DEBUG_UPDATER DEBUG_UPDATER.println("already running"); @@ -40,7 +42,13 @@ bool UpdaterClass::begin(size_t size){ return false; } - if(size == 0){ +#ifdef DEBUG_UPDATER + if (command == U_SPIFFS) { + DEBUG_UPDATER.println("Update SPIFFS."); + } +#endif + + if(size == 0) { _error = UPDATE_ERROR_SIZE; #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); @@ -51,20 +59,33 @@ bool UpdaterClass::begin(size_t size){ _reset(); _error = 0; - //size of current sketch rounded to a sector - uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - //address of the end of the space available for sketch and update - uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000; - //size of the update rounded to a sector - uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - //address where we will start writing the update - uint32_t updateStartAddress = updateEndAddress - roundedSize; + uint32_t updateStartAddress = 0; + if (command == U_FLASH) { + //size of current sketch rounded to a sector + uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + //address of the end of the space available for sketch and update + uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000; + //size of the update rounded to a sector + uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + //address where we will start writing the update + updateStartAddress = updateEndAddress - roundedSize; - //make sure that the size of both sketches is less than the total space (updateEndAddress) - if(updateStartAddress < currentSketchSize){ - _error = UPDATE_ERROR_SPACE; + //make sure that the size of both sketches is less than the total space (updateEndAddress) + if(updateStartAddress < currentSketchSize) { + _error = UPDATE_ERROR_SPACE; #ifdef DEBUG_UPDATER - printError(DEBUG_UPDATER); + printError(DEBUG_UPDATER); +#endif + return false; + } + } + else if (command == U_SPIFFS) { + updateStartAddress = (uint32_t)&_SPIFFS_start - 0x40200000; + } + else { + // unknown command +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.println("Unknown update command."); #endif return false; } @@ -74,6 +95,7 @@ bool UpdaterClass::begin(size_t size){ _currentAddress = _startAddress; _size = size; _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + _command = command; return true; } @@ -95,23 +117,28 @@ bool UpdaterClass::end(bool evenIfRemaining){ return false; } - if(evenIfRemaining){ - if(_bufferLen > 0){ + if(evenIfRemaining) { + if(_bufferLen > 0) { _writeBuffer(); } _size = progress(); } - eboot_command ebcmd; - ebcmd.action = ACTION_COPY_RAW; - ebcmd.args[0] = _startAddress; - ebcmd.args[1] = 0x00000; - ebcmd.args[2] = _size; - eboot_command_write(&ebcmd); + if (_command == U_FLASH) { + eboot_command ebcmd; + ebcmd.action = ACTION_COPY_RAW; + ebcmd.args[0] = _startAddress; + ebcmd.args[1] = 0x00000; + ebcmd.args[2] = _size; + eboot_command_write(&ebcmd); #ifdef DEBUG_UPDATER DEBUG_UPDATER.printf("Staged: address:0x%08X, size:0x%08X\n", _startAddress, _size); + } + else if (_command == U_SPIFFS) { + DEBUG_UPDATER.printf("SPIFFS: address:0x%08X, size:0x%08X\n", _startAddress, _size); #endif + } _reset(); return true; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index c8b4e81f1..670b0e7e9 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -11,6 +11,9 @@ #define UPDATE_ERROR_SIZE 4 #define UPDATE_ERROR_STREAM 5 +#define U_FLASH 0 +#define U_SPIFFS 100 + //#define DEBUG_UPDATER Serial1 class UpdaterClass { @@ -20,7 +23,7 @@ class UpdaterClass { Call this to check the space needed for the update Will return false if there is not enough space */ - bool begin(size_t size); + bool begin(size_t size, int = U_FLASH); /* Writes a buffer to the flash and increments the address @@ -116,6 +119,7 @@ class UpdaterClass { size_t _size; uint32_t _startAddress; uint32_t _currentAddress; + uint32_t _command; }; extern UpdaterClass Update; diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index 131c6f64b..b41331b2b 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -41,6 +41,7 @@ localIP KEYWORD2 subnetMask KEYWORD2 gatewayIP KEYWORD2 SSID KEYWORD2 +psk KEYWORD2 BSSID KEYWORD2 RSSI KEYWORD2 encryptionType KEYWORD2 diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 50358eb55..ebb7151bd 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -145,6 +145,17 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch return status(); } +int ESP8266WiFiClass::begin() +{ + ETS_UART_INTR_DISABLE(); + wifi_station_connect(); + ETS_UART_INTR_ENABLE(); + + if(!_useStaticIp) + wifi_station_dhcpc_start(); + return status(); +} + uint8_t ESP8266WiFiClass::waitForConnectResult(){ if ((wifi_get_opmode() & 1) == 0)//1 and 3 have STA enabled return WL_DISCONNECTED; @@ -366,6 +377,13 @@ char* ESP8266WiFiClass::SSID() return reinterpret_cast(conf.ssid); } +const char* ESP8266WiFiClass::psk() +{ + static struct station_config conf; + wifi_station_get_config(&conf); + return reinterpret_cast(conf.password); +} + uint8_t* ESP8266WiFiClass::BSSID(void) { static struct station_config conf; diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 2e5337ed4..8198c2805 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -58,6 +58,9 @@ public: 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); + // Use sdk config to connect. + int begin(); + /* Wait for Wifi connection to reach a result * returns the status reached or disconnect if STA is off @@ -172,6 +175,13 @@ public: */ char* SSID(); + /* + * Return the current pre shared key associated with the network + * + * return: psk string + */ + const char* psk(); + /* * Return the current bssid / mac associated with the network if configured * diff --git a/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/OTA-mDNS-SPIFFS.ino b/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/OTA-mDNS-SPIFFS.ino new file mode 100644 index 000000000..332112b74 --- /dev/null +++ b/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/OTA-mDNS-SPIFFS.ino @@ -0,0 +1,317 @@ +/** + * @file OTA-mDNS-SPIFFS.ino + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + * @data 2015-09-18 + * + */ + + +#include +#include +#include +#include + + +/** + * @brief mDNS and OTA Constants + * @{ + */ +#define HOSTNAME "ESP8266-ota" ///< Hostename +#define APORT 8266 ///< Port for OTA update +/// @} + +/** + * @brief Default WiFi connection information. + * @{ + */ +const char* ap_default_ssid = "esp8266"; ///< Default SSID. +const char* ap_default_psk = "esp8266esp8266"; ///< Default PSK. +/// @} + +/// OTA Update UDP server handle. +WiFiUDP OTA; + + +/** + * @brief Read WiFi connection information from file system. + * @param ssid String pointer for storing SSID. + * @param pass String pointer for storing PSK. + * @return True or False. + * + * The config file have to containt the WiFi SSID in the first line + * and the WiFi PSK in the second line. + * Line seperator have to be \r\n (CR LF). + */ +bool loadConfig(String *ssid, String *pass) +{ + // open file for reading. + File configFile = SPIFFS.open("/cl_conf.txt", "r"); + if (!configFile) + { + Serial.println("Failed to open cl_conf.txt."); + + return false; + } + + // Read content from config file. + String content = configFile.readString(); + configFile.close(); + + content.trim(); + + // Check if ther is a second line available. + uint8_t pos = content.indexOf("\r\n"); + if (pos == 0) + { + Serial.println("Infvalid content."); + Serial.println(content); + + return false; + } + + // Store SSID and PSK into string vars. + *ssid = content.substring(0, pos); + *pass = content.substring(pos + 2); + + return true; +} // loadConfig + + +/** + * @brief Save WiFi SSID and PSK to configuration file. + * @param ssid SSID as string pointer. + * @param pass PSK as string pointer, + * @return True or False. + */ +bool saveConfig(String *ssid, String *pass) +{ + // Open config file for writing. + File configFile = SPIFFS.open("/cl_conf.txt", "w"); + if (!configFile) + { + Serial.println("Failed to open cl_conf.txt for writing"); + + return false; + } + + // Save SSID and PSK. + configFile.println(*ssid); + configFile.println(*pass); + + configFile.close(); + + return true; +} // saveConfig + + +/** + * @brief Handle OTA update stuff. + * + * This function comes from ESP8266 Arduino example: + * https://github.com/esp8266/Arduino/blob/esp8266/hardware/esp8266com/esp8266/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino + * + * Modification for uploading SPIFFS images from Pascal Gollor. + * + */ +static inline void ota_handle(void) +{ + bool spiffs = false; + + if (! OTA.parsePacket()) + { + return; + } + + // Get remote IP + IPAddress remote = OTA.remoteIP(); + + // Get command + int cmd = OTA.parseInt(); + Serial.print("command: "); + Serial.println(cmd); + if (cmd == U_SPIFFS) + { + spiffs = true; + Serial.println("Get SPIFFS image."); + } + + // Get remote port + int port = OTA.parseInt(); + + // Get sketch size. + int sketch_size = OTA.parseInt(); + + // Output stuff + Serial.print("Update Start: ip:"); + Serial.print(remote); + Serial.printf(", port:%d, size:%d\r\n", port, sketch_size); + + // Stop all UDP connections. + WiFiUDP::stopAll(); + + // OTA start Time + uint32_t startTime = millis(); + + // Start Updateing. + if(!Update.begin(sketch_size, cmd)) + { + Serial.println("Update Begin Error"); + return; + } + + WiFiClient client; + if (client.connect(remote, port)) + { + uint32_t written; + while(!Update.isFinished()) + { + written = Update.write(client); + if(written > 0) client.print(written, DEC); + } + Serial.setDebugOutput(false); + + if(Update.end()) + { + client.println("OK"); + Serial.printf("Update Success: %u\nRebooting...\n", (unsigned int)(millis() - startTime)); + ESP.restart(); + } + else + { + Update.printError(client); + Update.printError(Serial); + } + } + else + { + Serial.printf("Connect Failed: %u\n", (unsigned int)(millis() - startTime)); + } +} // ota_handle + + +/** + * @brief Arduino setup function. + */ +void setup() +{ + String station_ssid = ""; + String station_psk = ""; + + Serial.begin(115200); + + delay(100); + + Serial.println("\r\n"); + Serial.print("Chip ID: 0x"); + Serial.println(ESP.getChipId(), HEX); + + // Set Hostname. + WiFi.hostname(HOSTNAME); + Serial.print("hostname: "); + Serial.println(WiFi.hostname()); + + + // Initialize file system. + if (!SPIFFS.begin()) + { + Serial.println("Failed to mount file system"); + return; + } + + // Load wifi connection information. + if (! loadConfig(&station_ssid, &station_psk)) + { + station_ssid = ""; + station_psk = ""; + + Serial.println("No WiFi connection information available."); + } + + // Check WiFi connection + // ... check mode + if (WiFi.getMode() != WIFI_STA) + { + WiFi.mode(WIFI_STA); + delay(10); + } + + // ... Load sdk config. + String ssid(WiFi.SSID()); + String psk(WiFi.psk()); + + // ... Compare fiel config with sdk config. + if (ssid != station_ssid || psk != station_psk) + { + Serial.println("WiFi config changed."); + + // ... Try to connect to WiFi station. + WiFi.begin(station_ssid.c_str(), station_psk.c_str()); + + // ... Pritn new SSID + Serial.print("new SSID: "); + Serial.println(WiFi.SSID()); + + // ... Uncomment this for debugging output. + //WiFi.printDiag(Serial); + } + else + { + // ... Begin with sdk config. + WiFi.begin(); + } + + Serial.println("Wait for WiFi connection."); + + // ... Give ESP 10 seconds to connect to station. + unsigned long startTime = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) + { + Serial.write('.'); + //Serial.print(WiFi.status()); + delay(500); + } + Serial.println(); + + // Check connection + if(WiFi.status() == WL_CONNECTED) + { + // ... print IP Address + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } + else + { + Serial.println("Can not connect to WiFi station. Go into AP mode."); + + // Go into software AP mode. + WiFi.mode(WIFI_AP); + + delay(10); + + WiFi.softAP(ap_default_ssid, ap_default_psk); + + Serial.print("IP address: "); + Serial.println(WiFi.softAPIP()); + } + + // Initialize mDNS service. + MDNS.begin(HOSTNAME); + + // ... Add OTA service. + MDNS.addService("arduino", "tcp", APORT); + + // Open OTA Server. + OTA.begin(APORT); +} + + +/** + * @brief Arduino loop function. + */ +void loop() +{ + // Handle OTA update. + ota_handle(); +} + diff --git a/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/data/cl_conf.txt b/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/data/cl_conf.txt new file mode 100644 index 000000000..d2f1ff4ae --- /dev/null +++ b/libraries/ESP8266mDNS/examples/OTA-mDNS-SPIFFS/data/cl_conf.txt @@ -0,0 +1,2 @@ +YOUR_SSID +YOUR_PSK diff --git a/platform.txt b/platform.txt index dd1c5a6df..5931916a0 100644 --- a/platform.txt +++ b/platform.txt @@ -93,7 +93,7 @@ tools.esptool.upload.protocol=esp tools.esptool.upload.params.verbose=-vv tools.esptool.upload.params.quiet= tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" -tools.esptool.network.pattern=python "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin" +tools.esptool.network.pattern=python "{path}/espota.py" -i "{serial.port}" -p "{network.port}" -f "{build.path}/{build.project_name}.bin" tools.espota.cmd=python tools.espota.cmd.windows=python.exe @@ -102,4 +102,4 @@ tools.espota.path={runtime.platform.path}/tools tools.espota.upload.protocol=espota tools.espota.upload.params.verbose= tools.espota.upload.params.quiet= -tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" 8266 "{build.path}/{build.project_name}.bin" +tools.espota.upload.pattern="{cmd}" "{path}/espota.py" -i "{serial.port}" -p 8266 -f "{build.path}/{build.project_name}.bin" diff --git a/tools/espota.py b/tools/espota.py index 01d79d558..7e26b5cfb 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -1,45 +1,63 @@ #!/usr/bin/env python # -# this script will push an OTA update to the ESP -# use it like: python espota.py +# Original espoty.py comes from ...? +# +# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor) +# +# This script will push an OTA update to the ESP +# use it like: python espota.py -i -p -f +# +# Changes +# 2015-09-18: +# - Add option parser. +# - Add logging. +# - Send command to controller to differ between flashing and transmitting SPIFFS image. +# from __future__ import print_function import socket import sys import os +import optparse +import logging + +# Commands +FLASH = 0 +SPIFFS = 100 + -def serve(remoteAddr, remotePort, filename): +def serve(remoteAddr, remotePort, filename, command = FLASH): # Create a TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverPort = 48266 server_address = ('0.0.0.0', serverPort) - print('Starting on %s:%s' % server_address, file=sys.stderr) + logging.info('Starting on %s:%s', str(server_address[0]), str(server_address[1])) try: sock.bind(server_address) sock.listen(1) except: - print('Listen Failed', file=sys.stderr) + logging.error("Listen Failed") return 1 content_size = os.path.getsize(filename) - print('Upload size: %d' % content_size, file=sys.stderr) - message = '%d %d %d\n' % (0, serverPort, content_size) + logging.info('Upload size: %d', content_size) + message = '%d %d %d\n' % (command, serverPort, content_size) # Wait for a connection - print('Sending invitation to:', remoteAddr, file=sys.stderr) + logging.info('Sending invitation to: %s', remoteAddr) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) remote_address = (remoteAddr, int(remotePort)) sent = sock2.sendto(message, remote_address) sock2.close() - print('Waiting for device...\n', file=sys.stderr) + logging.info('Waiting for device...\n') try: sock.settimeout(10) connection, client_address = sock.accept() sock.settimeout(None) connection.settimeout(None) except: - print('No response from device', file=sys.stderr) + logging.error('No response from device') sock.close() return 1 @@ -57,23 +75,23 @@ def serve(remoteAddr, remotePort, filename): connection.sendall(chunk) res = connection.recv(4) except: - print('\nError Uploading', file=sys.stderr) + logging.error('\nError Uploading') connection.close() f.close() sock.close() return 1 - print('\nWaiting for result...\n', file=sys.stderr) + logging.info('\nWaiting for result...\n') try: connection.settimeout(60) data = connection.recv(32) - print('Result: %s' % data, file=sys.stderr) + logging.info('Result: %s' ,data) connection.close() f.close() sock.close() return 0 except: - print('Result: No Answer!', file=sys.stderr) + logging.error('Result: No Answer!') connection.close() f.close() sock.close() @@ -85,12 +103,94 @@ def serve(remoteAddr, remotePort, filename): sock.close() return 1 - +# end serve + + +def parser(): + parser = optparse.OptionParser( + usage = "%prog [options]", + description = "Transmit image over the air to the esp8266 module with OTA support." + ) + + # destination ip and port + group = optparse.OptionGroup(parser, "Destination") + group.add_option("-i", "--ip", + dest = "esp_ip", + action = "store", + help = "ESP8266 IP Address.", + default = False + ) + group.add_option("-p", "--port", + dest = "esp_port", + type = "int", + help = "ESP8266 ota Port.", + default = 8266 + ) + parser.add_option_group(group) + + # image + group = optparse.OptionGroup(parser, "Image") + group.add_option("-f", "--file", + dest = "image", + help = "Image file.", + metavar="FILE", + default = None + ) + group.add_option("-s", "--spiffs", + dest = "spiffs", + action = "store_true", + help = "Use this option to transmit a SPIFFS image and do not flash the module.", + default = False + ) + parser.add_option_group(group) + + # output group + group = optparse.OptionGroup(parser, "Output") + group.add_option("-d", "--debug", + dest = "debug", + help = "Show debug output. And override loglevel with debug.", + action = "store_true", + default = False + ) + parser.add_option_group(group) + + (options, args) = parser.parse_args() + + return options +# end parser + + def main(args): - return serve(args[1], args[2], args[3]) - - - + # get options + options = parser() + + # adapt log level + loglevel = logging.WARNING + if (options.debug): + loglevel = logging.DEBUG + # end if + + # logging + logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S') + + logging.debug("Options: %s", str(options)) + + # check options + if (not options.esp_ip or not options.image): + logging.critical("Not enough arguments.") + + return 1 + # end if + + command = FLASH + if (options.spiffs): + command = SPIFFS + # end if + + return serve(options.esp_ip, options.esp_port, options.image, command) +# end main + + if __name__ == '__main__': - sys.exit(main(sys.argv)) - + sys.exit(main(sys.argv)) +# end if