From 5465eb1755012cbc7d58fe4bf3ac144faf458274 Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Mon, 3 Aug 2015 17:12:46 +0200 Subject: [PATCH 01/45] add parameter names to Arduino.h --- cores/esp8266/Arduino.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 6b379d5b1..1e655a812 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -179,12 +179,12 @@ void initVariant(void); int atexit(void (*func)()) __attribute__((weak)); -void pinMode(uint8_t, uint8_t); -void digitalWrite(uint8_t, uint8_t); -int digitalRead(uint8_t); -int analogRead(uint8_t); +void pinMode(uint8_t pin, uint8_t mode); +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); +int analogRead(uint8_t pin); void analogReference(uint8_t mode); -void analogWrite(uint8_t, int); +void analogWrite(uint8_t pin, int val); void analogWriteFreq(uint32_t freq); void analogWriteRange(uint32_t range); @@ -198,8 +198,8 @@ unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); -void attachInterrupt(uint8_t, void (*)(void), int mode); -void detachInterrupt(uint8_t); +void attachInterrupt(uint8_t pin, void (*)(void), int mode); +void detachInterrupt(uint8_t pin); void setup(void); void loop(void); From 52390a3f35c31cf996d6d997ce7defb992e9bbfd Mon Sep 17 00:00:00 2001 From: Juha Paananen Date: Mon, 3 Aug 2015 18:21:52 +0300 Subject: [PATCH 02/45] Include WIFI_OFF option for WiFi.mode(m) --- doc/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/reference.md b/doc/reference.md index fd556756a..2ca3d5e9a 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -108,7 +108,7 @@ const char HTTP[] PROGMEM = "http:"; This is mostly similar to WiFi shield library. Differences include: -- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, or `WIFI_AP_STA`. +- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, `WIFI_AP_STA` or `WIFI_OFF`. - call `WiFi.softAP(ssid)` to set up an open network - call `WiFi.softAP(ssid, password)` to set up a WPA2-PSK network (password should be at least 8 characters) - `WiFi.macAddress(mac)` is for STA, `WiFi.softAPmacAddress(mac)` is for AP. From 5ac5d8e008411a1993c7cb966012cb608daad1ac Mon Sep 17 00:00:00 2001 From: Kiril Zyapkov Date: Tue, 4 Aug 2015 00:25:01 +0300 Subject: [PATCH 03/45] ESP8266AVRISP: initial --- .../Arduino_Wifi_AVRISP.ino | 67 +++ libraries/ESP8266AVRISP/library.properties | 9 + libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp | 550 ++++++++++++++++++ libraries/ESP8266AVRISP/src/ESP8266AVRISP.h | 122 ++++ libraries/ESP8266AVRISP/src/command.h | 108 ++++ 5 files changed, 856 insertions(+) create mode 100644 libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino create mode 100644 libraries/ESP8266AVRISP/library.properties create mode 100644 libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp create mode 100644 libraries/ESP8266AVRISP/src/ESP8266AVRISP.h create mode 100644 libraries/ESP8266AVRISP/src/command.h diff --git a/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino new file mode 100644 index 000000000..792d702c9 --- /dev/null +++ b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +const char* host = "esp8266-avrisp"; +const char* ssid = "**********"; +const char* pass = "**********"; +const uint16_t port = 328; +const uint8_t reset_pin = 5; + +ESP8266AVRISP avrprog(port, reset_pin); + +void setup() { + Serial.begin(115200); + Serial.println(""); + Serial.println("Arduino AVR-ISP over TCP"); + avrprog.setReset(false); // let the AVR run + + WiFi.begin(ssid, pass); + while (WiFi.waitForConnectResult() != WL_CONNECTED); + + MDNS.begin(host); + MDNS.addService("avrisp", "tcp", port); + + IPAddress local_ip = WiFi.localIP(); + Serial.print("IP address: "); + Serial.println(local_ip); + Serial.println("Use your avrdude:"); + Serial.print("avrdude -c arduino -p -P net:"); + Serial.print(local_ip); + Serial.print(":"); + Serial.print(port); + Serial.println(" -t # or -U ..."); + + // listen for avrdudes + avrprog.begin(); +} + +void loop() { + static AVRISPState_t last_state = AVRISP_STATE_IDLE; + AVRISPState_t new_state = avrprog.update(); + if (last_state != new_state) { + switch (new_state) { + case AVRISP_STATE_IDLE: { + Serial.printf("[AVRISP] now idle\r\n"); + // Use the SPI bus for other purposes + break; + } + case AVRISP_STATE_PENDING: { + Serial.printf("[AVRISP] connection pending\r\n"); + // Clean up your other purposes and prepare for programming mode + break; + } + case AVRISP_STATE_ACTIVE: { + Serial.printf("[AVRISP] programming mode\r\n"); + // Stand by for completion + break; + } + } + last_state = new_state; + } + // Serve the client + if (last_state != AVRISP_STATE_IDLE) { + avrprog.serve(); + } +} diff --git a/libraries/ESP8266AVRISP/library.properties b/libraries/ESP8266AVRISP/library.properties new file mode 100644 index 000000000..70fa3cf8e --- /dev/null +++ b/libraries/ESP8266AVRISP/library.properties @@ -0,0 +1,9 @@ +name=ESP8266AVRISP +version=1.0 +author=Kiril Zyapkov +maintainer=Kiril Zyapkov +sentence=AVR In-System Programming over WiFi for ESP8266 +paragraph=This library allows programming 8-bit AVR ICSP targets via TCP over WiFi with ESP8266. +category=Communication +url= +architectures=esp8266 diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp new file mode 100644 index 000000000..e93713d27 --- /dev/null +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp @@ -0,0 +1,550 @@ +/* +AVR In-System Programming over WiFi for ESP8266 +Copyright (c) Kiril Zyapkov + +Original version: + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php +*/ + + +#include +#include +#include +#include + +#include "ESP8266AVRISP.h" +#include "command.h" + +extern "C" { + #include "user_interface.h" + #include "mem.h" +} + +#define malloc os_malloc +#define free os_free + +#ifdef AVRISP_ACTIVE_HIGH_RESET +#define AVRISP_RESET_ON HIGH +#define AVRISP_RESET_OFF LOW +#else +#define AVRISP_RESET_ON LOW +#define AVRISP_RESET_OFF HIGH +#endif + + +// #define AVRISP_DEBUG(fmt, ...) os_printf("[AVRP] " fmt "\r\n", ##__VA_ARGS__ ) +#define AVRISP_DEBUG(...) + +#define AVRISP_HWVER 2 +#define AVRISP_SWMAJ 1 +#define AVRISP_SWMIN 18 +#define AVRISP_PTIME 10 + +#define EECHUNK (32) + +#define beget16(addr) (*addr * 256 + *(addr+1)) + +ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq): + _reset_pin(reset_pin), _server(WiFiServer(port)), _state(AVRISP_STATE_IDLE), + _spi_freq(spi_freq) +{ +} + +void ESP8266AVRISP::begin() { + pinMode(_reset_pin, OUTPUT); + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + _server.begin(); +} + +void ESP8266AVRISP::setReset(bool rst) { + if (rst) { + digitalWrite(_reset_pin, AVRISP_RESET_ON); + } else { + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + } +} + +AVRISPState_t ESP8266AVRISP::update() { + switch (_state) { + case AVRISP_STATE_IDLE: { + if (_server.hasClient()) { + _client = _server.available(); + _client.setNoDelay(true); + ip_addr_t lip; + lip.addr = _client.remoteIP(); + AVRISP_DEBUG("client connect %d.%d.%d.%d:%d", IP2STR(&lip), _client.remotePort()); + _client.setTimeout(100); // for getch() + _state = AVRISP_STATE_PENDING; + _reject_incoming(); + } + break; + } + case AVRISP_STATE_PENDING: + case AVRISP_STATE_ACTIVE: { + // handle disconnect + if (!_client.connected()) { + _client.stop(); + AVRISP_DEBUG("client disconnect"); + SPI.end(); + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + _state = AVRISP_STATE_IDLE; + } + break; + } + } + return _state; +} + +AVRISPState_t ESP8266AVRISP::serve() { + switch (update()) { + case AVRISP_STATE_IDLE: + // should not be called when idle, error? + break; + case AVRISP_STATE_PENDING: { + // enter reset, setup SPI + SPI.begin(); + SPI.setFrequency(_spi_freq); + SPI.setHwCs(false); + digitalWrite(_reset_pin, AVRISP_RESET_ON); + _state = AVRISP_STATE_ACTIVE; + // fallthrough + } + case AVRISP_STATE_ACTIVE: { + while (_client.available()) { + avrisp(); + } + return update(); + } + } + return _state; +} + +inline void ESP8266AVRISP::_reject_incoming(void) { + while (_server.hasClient()) _server.available().stop(); +} + +uint8_t ESP8266AVRISP::getch() { + while (!_client.available()) yield(); + uint8_t b = (uint8_t)_client.read(); + // AVRISP_DEBUG("< %02x", b); + return b; +} + +void ESP8266AVRISP::fill(int n) { + // AVRISP_DEBUG("fill(%u)", n); + for (int x = 0; x < n; x++) { + buff[x] = getch(); + } +} + +uint8_t ESP8266AVRISP::spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { + uint8_t n; + SPI.transfer(a); + n = SPI.transfer(b); + n = SPI.transfer(c); + return SPI.transfer(d); +} + +void ESP8266AVRISP::empty_reply() { + if (Sync_CRC_EOP == getch()) { + _client.print((char)Resp_STK_INSYNC); + _client.print((char)Resp_STK_OK); + } else { + error++; + _client.print((char)Resp_STK_NOSYNC); + } +} + +void ESP8266AVRISP::breply(uint8_t b) { + if (Sync_CRC_EOP == getch()) { + uint8_t resp[3]; + resp[0] = Resp_STK_INSYNC; + resp[1] = b; + resp[2] = Resp_STK_OK; + _client.write((const uint8_t *)resp, (size_t)3); + } else { + error++; + _client.print((char)Resp_STK_NOSYNC); + } + +} + +void ESP8266AVRISP::get_parameter(uint8_t c) { + switch (c) { + case 0x80: + breply(AVRISP_HWVER); + break; + case 0x81: + breply(AVRISP_SWMAJ); + break; + case 0x82: + breply(AVRISP_SWMIN); + break; + case 0x93: + breply('S'); // serial programmer + break; + default: + breply(0); + } +} + +void ESP8266AVRISP::set_parameters() { + // call this after reading paramter packet into buff[] + param.devicecode = buff[0]; + param.revision = buff[1]; + param.progtype = buff[2]; + param.parmode = buff[3]; + param.polling = buff[4]; + param.selftimed = buff[5]; + param.lockbytes = buff[6]; + param.fusebytes = buff[7]; + param.flashpoll = buff[8]; + // ignore buff[9] (= buff[8]) + // following are 16 bits (big endian) + param.eeprompoll = beget16(&buff[10]); + param.pagesize = beget16(&buff[12]); + param.eepromsize = beget16(&buff[14]); + + // 32 bits flashsize (big endian) + param.flashsize = buff[16] * 0x01000000 + + buff[17] * 0x00010000 + + buff[18] * 0x00000100 + + buff[19]; + + // AVRISP_DEBUG("devicecode = %d", param.devicecode); + // AVRISP_DEBUG("revision = %d", param.revision); + // AVRISP_DEBUG("progtype = %d", param.progtype); + // AVRISP_DEBUG("parmode = %d", param.parmode); + // AVRISP_DEBUG("polling = %d", param.polling); + // AVRISP_DEBUG("selftimed = %d", param.selftimed); + // AVRISP_DEBUG("lockbytes = %d", param.lockbytes); + // AVRISP_DEBUG("fusebytes = %d", param.fusebytes); + // AVRISP_DEBUG("flashpoll = %d", param.flashpoll); + // AVRISP_DEBUG("eeprompoll = %d", param.eeprompoll); + // AVRISP_DEBUG("pagesize = %d", param.pagesize); + // AVRISP_DEBUG("eepromsize = %d", param.eepromsize); + + // AVRISP_DEBUG("flashsize = %d", param.flashsize); + +} + +void ESP8266AVRISP::start_pmode() { + + // SPI already begun when entering ACTIVE state + //SPI.begin(); + //SPI.setFrequency(AVRISP_SPI_FREQ); + + // following delays may not work on all targets... + SPI.transfer(0x00); + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + delayMicroseconds(50); + digitalWrite(_reset_pin, AVRISP_RESET_ON); + delay(30); + + spi_transaction(0xAC, 0x53, 0x00, 0x00); + pmode = 1; +} + +void ESP8266AVRISP::end_pmode() { + SPI.end(); + digitalWrite(_reset_pin, AVRISP_RESET_OFF); + pmode = 0; +} + +void ESP8266AVRISP::universal() { + int w; + uint8_t ch; + + fill(4); + ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); + breply(ch); +} + +void ESP8266AVRISP::flash(uint8_t hilo, int addr, uint8_t data) { + spi_transaction(0x40 + 8 * hilo, + addr >> 8 & 0xFF, + addr & 0xFF, + data); +} + +void ESP8266AVRISP::commit(int addr) { + spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); + delay(AVRISP_PTIME); +} + +//#define _addr_page(x) (here & 0xFFFFE0) +int ESP8266AVRISP::addr_page(int addr) { + if (param.pagesize == 32) return addr & 0xFFFFFFF0; + if (param.pagesize == 64) return addr & 0xFFFFFFE0; + if (param.pagesize == 128) return addr & 0xFFFFFFC0; + if (param.pagesize == 256) return addr & 0xFFFFFF80; + AVRISP_DEBUG("unknown page size: %d", param.pagesize); + return addr; +} + + +void ESP8266AVRISP::write_flash(int length) { + uint32_t started = millis(); + + fill(length); + + if (Sync_CRC_EOP == getch()) { + _client.print((char) Resp_STK_INSYNC); + _client.print((char) write_flash_pages(length)); + } else { + error++; + _client.print((char) Resp_STK_NOSYNC); + } +} + +uint8_t ESP8266AVRISP::write_flash_pages(int length) { + int x = 0; + int page = addr_page(here); + while (x < length) { + yield(); + if (page != addr_page(here)) { + commit(page); + page = addr_page(here); + } + flash(LOW, here, buff[x++]); + flash(HIGH, here, buff[x++]); + here++; + } + commit(page); + return Resp_STK_OK; +} + +uint8_t ESP8266AVRISP::write_eeprom(int length) { + // here is a word address, get the byte address + int start = here * 2; + int remaining = length; + if (length > param.eepromsize) { + error++; + return Resp_STK_FAILED; + } + while (remaining > EECHUNK) { + write_eeprom_chunk(start, EECHUNK); + start += EECHUNK; + remaining -= EECHUNK; + } + write_eeprom_chunk(start, remaining); + return Resp_STK_OK; +} +// write (length) bytes, (start) is a byte address +uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) { + // this writes byte-by-byte, + // page writing may be faster (4 bytes at a time) + fill(length); + // prog_lamp(LOW); + for (int x = 0; x < length; x++) { + int addr = start + x; + spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]); + delay(45); + } + // prog_lamp(HIGH); + return Resp_STK_OK; +} + +void ESP8266AVRISP::program_page() { + char result = (char) Resp_STK_FAILED; + int length = 256 * getch(); + length += getch(); + char memtype = getch(); + char buf[100]; + // flash memory @here, (length) bytes + if (memtype == 'F') { + write_flash(length); + return; + } + + if (memtype == 'E') { + result = (char)write_eeprom(length); + if (Sync_CRC_EOP == getch()) { + _client.print((char) Resp_STK_INSYNC); + _client.print(result); + } else { + error++; + _client.print((char) Resp_STK_NOSYNC); + } + return; + } + _client.print((char)Resp_STK_FAILED); + return; + +} + +uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) { + return spi_transaction(0x20 + hilo * 8, + (addr >> 8) & 0xFF, + addr & 0xFF, + 0); +} + +void ESP8266AVRISP::flash_read_page(int length) { + uint8_t *data = (uint8_t *) malloc(length + 1); + for (int x = 0; x < length; x += 2) { + *(data + x) = flash_read(LOW, here); + *(data + x + 1) = flash_read(HIGH, here); + here++; + } + *(data + length) = Resp_STK_OK; + _client.write((const uint8_t *)data, (size_t)(length + 1)); + free(data); + return; +} + +void ESP8266AVRISP::eeprom_read_page(int length) { + // here again we have a word address + uint8_t *data = (uint8_t *) malloc(length + 1); + int start = here * 2; + for (int x = 0; x < length; x++) { + int addr = start + x; + uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); + *(data + x) = ee; + } + *(data + length) = Resp_STK_OK; + _client.write((const uint8_t *)data, (size_t)(length + 1)); + free(data); + return; +} + +void ESP8266AVRISP::read_page() { + char result = (char)Resp_STK_FAILED; + int length = 256 * getch(); + length += getch(); + char memtype = getch(); + if (Sync_CRC_EOP != getch()) { + error++; + _client.print((char) Resp_STK_NOSYNC); + return; + } + _client.print((char) Resp_STK_INSYNC); + if (memtype == 'F') flash_read_page(length); + if (memtype == 'E') eeprom_read_page(length); + return; +} + +void ESP8266AVRISP::read_signature() { + if (Sync_CRC_EOP != getch()) { + error++; + _client.print((char) Resp_STK_NOSYNC); + return; + } + _client.print((char) Resp_STK_INSYNC); + + uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); + _client.print((char) high); + uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00); + _client.print((char) middle); + uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00); + _client.print((char) low); + _client.print((char) Resp_STK_OK); +} + +// It seems ArduinoISP is based on the original STK500 (not v2) +// but implements only a subset of the commands. +int ESP8266AVRISP::avrisp() { + uint8_t data, low, high; + uint8_t ch = getch(); + // AVRISP_DEBUG("CMD 0x%02x", ch); + switch (ch) { + case Cmnd_STK_GET_SYNC: + error = 0; + empty_reply(); + break; + + case Cmnd_STK_GET_SIGN_ON: + if (getch() == Sync_CRC_EOP) { + _client.print((char) Resp_STK_INSYNC); + _client.print(F("AVR ISP")); // AVR061 says "AVR STK"? + _client.print((char) Resp_STK_OK); + } + break; + + case Cmnd_STK_GET_PARAMETER: + get_parameter(getch()); + break; + + case Cmnd_STK_SET_DEVICE: + fill(20); + set_parameters(); + empty_reply(); + break; + + case Cmnd_STK_SET_DEVICE_EXT: // ignored + fill(5); + empty_reply(); + break; + + case Cmnd_STK_ENTER_PROGMODE: + start_pmode(); + empty_reply(); + break; + + case Cmnd_STK_LOAD_ADDRESS: + here = getch(); + here += 256 * getch(); + // AVRISP_DEBUG("here=0x%04x", here); + empty_reply(); + break; + + // XXX: not implemented! + case Cmnd_STK_PROG_FLASH: + low = getch(); + high = getch(); + empty_reply(); + break; + + // XXX: not implemented! + case Cmnd_STK_PROG_DATA: + data = getch(); + empty_reply(); + break; + + case Cmnd_STK_PROG_PAGE: + program_page(); + break; + + case Cmnd_STK_READ_PAGE: + read_page(); + break; + + case Cmnd_STK_UNIVERSAL: + universal(); + break; + + case Cmnd_STK_LEAVE_PROGMODE: + error = 0; + end_pmode(); + empty_reply(); + delay(5); + // if (_client && _client.connected()) + _client.stop(); + // AVRISP_DEBUG("left progmode"); + + break; + + case Cmnd_STK_READ_SIGN: + read_signature(); + break; + // expecting a command, not Sync_CRC_EOP + // this is how we can get back in sync + case Sync_CRC_EOP: // 0x20, space + error++; + _client.print((char) Resp_STK_NOSYNC); + break; + + // anything else we will return STK_UNKNOWN + default: + AVRISP_DEBUG("??!?"); + error++; + if (Sync_CRC_EOP == getch()) { + _client.print((char)Resp_STK_UNKNOWN); + } else { + _client.print((char)Resp_STK_NOSYNC); + } + } +} diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h new file mode 100644 index 000000000..2d4728682 --- /dev/null +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -0,0 +1,122 @@ +/* +AVR In-System Programming over WiFi for ESP8266 +Copyright (c) Kiril Zyapkov + +Original version: + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php +*/ + +#ifndef _ESP8266AVRISP_H +#define _ESP8266AVRISP_H + +#include + +// active-high reset if you use an n-mos to level-shift +// comment if hooked directly +#define AVRISP_ACTIVE_HIGH_RESET + +// SPI clock frequency in Hz +#define AVRISP_SPI_FREQ 1e6 + +// programmer states +typedef enum { + AVRISP_STATE_IDLE = 0, // no active TCP session + AVRISP_STATE_PENDING, // TCP connected, pending SPI activation + AVRISP_STATE_ACTIVE // programmer is active and owns the SPI bus +} AVRISPState_t; + +// stk500 parameters +typedef struct { + uint8_t devicecode; + uint8_t revision; + uint8_t progtype; + uint8_t parmode; + uint8_t polling; + uint8_t selftimed; + uint8_t lockbytes; + uint8_t fusebytes; + int flashpoll; + int eeprompoll; + int pagesize; + int eepromsize; + int flashsize; +} AVRISP_parameter_t; + + +class ESP8266AVRISP { +public: + ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ); + + void begin(); + + // control the state of the RESET pin of the target + // see AVRISP_ACTIVE_HIGH_RESET + void setReset(bool); + + // check for pending clients if IDLE, check for disconnect otherwise + // returns the updated state + AVRISPState_t update(); + + // transition to ACTIVE if PENDING + // serve STK500 commands from buffer if ACTIVE + // returns the updated state + AVRISPState_t serve(); + +protected: + + inline void _reject_incoming(void); // reject any incoming tcp connections + + int avrisp(void); // handle incoming STK500 commands + + uint8_t getch(void); // retrieve a character from the remote end + uint8_t spi_transaction(uint8_t, uint8_t, uint8_t, uint8_t); + void empty_reply(void); + void breply(uint8_t); + + void get_parameter(uint8_t); + void set_parameters(void); + int addr_page(int); + void flash(uint8_t, int, uint8_t); + void write_flash(int); + uint8_t write_flash_pages(int length); + uint8_t write_eeprom(int length); + uint8_t write_eeprom_chunk(int start, int length); + void commit(int addr); + void program_page(); + uint8_t flash_read(uint8_t hilo, int addr); + void flash_read_page(int length); + void eeprom_read_page(int length); + void read_page(); + void read_signature(); + + void universal(void); + + void fill(int); // fill the buffer with n bytes + void start_pmode(void); // enter program mode + void end_pmode(void); // exit program mode + + + + uint32_t _spi_freq; + WiFiServer _server; + WiFiClient _client; + AVRISPState_t _state; + uint8_t _reset_pin; + + // programmer settings, set by remote end + AVRISP_parameter_t param; + // page buffer + uint8_t buff[256]; + + int error = 0; + bool pmode = 0; + + // address for reading and writing, set by 'U' command + int here; +}; + + +#endif // _ESP8266AVRISP_H diff --git a/libraries/ESP8266AVRISP/src/command.h b/libraries/ESP8266AVRISP/src/command.h new file mode 100644 index 000000000..2adc22bd3 --- /dev/null +++ b/libraries/ESP8266AVRISP/src/command.h @@ -0,0 +1,108 @@ +//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ +//* +//* Title: AVR061 - STK500 Communication Protocol +//* Filename: command.h +//* Version: 1.0 +//* Last updated: 09.09.2002 +//* +//* Support E-mail: avr@atmel.com +//* +//************************************************************************** + +// *****************[ STK Message constants ]*************************** + +#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON + +// *****************[ STK Response constants ]*************************** + +#define Resp_STK_OK 0x10 // ' ' +#define Resp_STK_FAILED 0x11 // ' ' +#define Resp_STK_UNKNOWN 0x12 // ' ' +#define Resp_STK_NODEVICE 0x13 // ' ' +#define Resp_STK_INSYNC 0x14 // ' ' +#define Resp_STK_NOSYNC 0x15 // ' ' + +#define Resp_ADC_CHANNEL_ERROR 0x16 // ' ' +#define Resp_ADC_MEASURE_OK 0x17 // ' ' +#define Resp_PWM_CHANNEL_ERROR 0x18 // ' ' +#define Resp_PWM_ADJUST_OK 0x19 // ' ' + +// *****************[ STK Special constants ]*************************** + +#define Sync_CRC_EOP 0x20 // 'SPACE' + +// *****************[ STK Command constants ]*************************** + +#define Cmnd_STK_GET_SYNC 0x30 // ' ' +#define Cmnd_STK_GET_SIGN_ON 0x31 // ' ' +#define Cmnd_STK_RESET 0x32 // ' ' +#define Cmnd_STK_SINGLE_CLOCK 0x33 // ' ' +#define Cmnd_STK_STORE_PARAMETERS 0x34 // ' ' + +#define Cmnd_STK_SET_PARAMETER 0x40 // ' ' +#define Cmnd_STK_GET_PARAMETER 0x41 // ' ' +#define Cmnd_STK_SET_DEVICE 0x42 // ' ' +#define Cmnd_STK_GET_DEVICE 0x43 // ' ' +#define Cmnd_STK_GET_STATUS 0x44 // ' ' +#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' ' + +#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' ' +#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' ' +#define Cmnd_STK_CHIP_ERASE 0x52 // ' ' +#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' ' +#define Cmnd_STK_CHECK_DEVICE 0x54 // ' ' +#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' ' +#define Cmnd_STK_UNIVERSAL 0x56 // ' ' + +#define Cmnd_STK_PROG_FLASH 0x60 // ' ' +#define Cmnd_STK_PROG_DATA 0x61 // ' ' +#define Cmnd_STK_PROG_FUSE 0x62 // ' ' +#define Cmnd_STK_PROG_LOCK 0x63 // ' ' +#define Cmnd_STK_PROG_PAGE 0x64 // ' ' +#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' ' + +#define Cmnd_STK_READ_FLASH 0x70 // ' ' +#define Cmnd_STK_READ_DATA 0x71 // ' ' +#define Cmnd_STK_READ_FUSE 0x72 // ' ' +#define Cmnd_STK_READ_LOCK 0x73 // ' ' +#define Cmnd_STK_READ_PAGE 0x74 // ' ' +#define Cmnd_STK_READ_SIGN 0x75 // ' ' +#define Cmnd_STK_READ_OSCCAL 0x76 // ' ' +#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' ' +#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' ' + +// *****************[ STK Parameter constants ]*************************** + +#define Parm_STK_HW_VER 0x80 // ' ' - R +#define Parm_STK_SW_MAJOR 0x81 // ' ' - R +#define Parm_STK_SW_MINOR 0x82 // ' ' - R +#define Parm_STK_LEDS 0x83 // ' ' - R/W +#define Parm_STK_VTARGET 0x84 // ' ' - R/W +#define Parm_STK_VADJUST 0x85 // ' ' - R/W +#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W +#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W +#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W +#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W + +#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255} +#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255} +#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255} +#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S' +#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE +#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE +#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE + + +// *****************[ STK status bit definitions ]*************************** + +#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC +#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE +#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode +#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed +#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed +#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit +#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit +#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink + + +// *****************************[ End Of COMMAND.H ]************************** From 56b70f6aabda2a142414f8a249083c190b25e3e0 Mon Sep 17 00:00:00 2001 From: Kiril Zyapkov Date: Tue, 4 Aug 2015 01:32:55 +0300 Subject: [PATCH 04/45] ESP8266AVRISP: add README.md --- libraries/ESP8266AVRISP/README.md | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 libraries/ESP8266AVRISP/README.md diff --git a/libraries/ESP8266AVRISP/README.md b/libraries/ESP8266AVRISP/README.md new file mode 100644 index 000000000..be691aaa8 --- /dev/null +++ b/libraries/ESP8266AVRISP/README.md @@ -0,0 +1,57 @@ +# AVR In-System Programming over WiFi for ESP8266 + +This library allows an ESP8266 module with the HSPI port available to become +an AVR In-System Programmer. + +## Hardware + +The ESP8266 module connects to the AVR target chip via the standard 6-pin +AVR "Recommended In-System Programming Interface Connector Layout" as seen +in [AVR910](http://www.atmel.com/images/doc0943.pdf) among other places. + +If the AVR target is powered by a different Vcc than what powers your ESP8266 +chip, you **must provide voltage level shifting** or some other form of buffers. +Exposing the pins of ESP8266 to anything larger than 3.6V will damage it. + +Connections are as follows: + +ESP8266 | AVR / SPI +--------|------------ +GPIO12 | MISO +GPIO13 | MOSI +GPIO14 | SCK +any* | RESET + +For RESET use a GPIO other than 0, 2 and 15 (bootselect pins), and apply an +external pullup/down so that the target is normally running. + +## Usage + +See the included example. In short: + +```arduino + +// Create the programmer object +ESP8266AVRISP avrprog(PORT, RESET_PIN) +// ... with custom SPI frequency +ESP8266AVRISP avrprog(PORT, RESET_PIN, 4e6) + +// Check current connection state, but don't perform any actions +AVRISPState_t state = avrprog.update(); + +// Serve the pending connection, execute STK500 commands +AVRISPState_t state = avrprog.serve(); +``` + +### License and Authors + +This library started off from the source of ArduinoISP "sketch" included with +the Arduino IDE: + + ArduinoISP version 04m3 + Copyright (c) 2008-2011 Randall Bohn + If you require a license, see + http://www.opensource.org/licenses/bsd-license.php + + Support for TCP on ESP8266 + Copyright (c) Kiril Zyapkov . From 57642c10b6491a30be932097edd45280500b9b70 Mon Sep 17 00:00:00 2001 From: Makuna Date: Mon, 3 Aug 2015 19:35:17 -0700 Subject: [PATCH 05/45] Interrupt cleanup Fixes issue of reentrant calls to nointerrupts() exposed functional replacements to cli sei and SREG when dealing with interrupts InterruptLock class to auto stop and restore interrupt level Fix user ISR calls to be like Arduino with interrupts disabled fully. --- cores/esp8266/Arduino.h | 49 +++++++++++++++++---- cores/esp8266/core_esp8266_timer.c | 12 ++++- cores/esp8266/core_esp8266_wiring_digital.c | 7 +-- libraries/Servo/src/esp8266/Servo.cpp | 4 -- tools/sdk/include/ets_sys.h | 4 +- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 6b379d5b1..b293bb633 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -137,17 +137,50 @@ void timer0_detachInterrupt(void); void ets_intr_lock(); void ets_intr_unlock(); -// level (0-15), -// level 15 will disable ALL interrupts, -// level 0 will disable most software interrupts +#ifndef __STRINGIFY +#define __STRINGIFY(a) #a +#endif + +// these low level routines provide a replacement for SREG interrupt save that AVR uses +// but are esp8266 specific. A normal use pattern is like // -#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)) -#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") +//{ +// uint32_t savedPS = xt_rsil(1); // this routine will allow level 2 and above +// // do work here +// xt_wsr_ps(savedPS); // restore the state +//} +// +// level (0-15), interrupts of the given level and above will be active +// level 15 will disable ALL interrupts, +// level 0 will enable ALL interrupts, +// +#define xt_rsil(level) (__extension__({uint32_t state; __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)); state;})) +#define xt_wsr_ps(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") -extern uint32_t interruptsState; +#define interrupts() xt_rsil(0) +#define noInterrupts() xt_rsil(15) + +// this auto class wraps up xt_rsil so your code can be simplier, but can only be +// used in an ino or cpp files. A normal use pattern is like +// +//{ +// { +// InterruptLock(1); // this routine will allow level 2 and above +// // do work within interrupt lock here +// } +// do work outside of interrupt lock here outside its scope +//} +// +#define InterruptLock(intrLevel) \ +class _AutoDisableIntr { \ +public: \ + _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ + ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ +private: \ + uint32_t _savedPS; \ + }; \ +_AutoDisableIntr _autoDisableIntr -#define interrupts() xt_enable_interrupts(interruptsState) -#define noInterrupts() __asm__ __volatile__("rsil %0,15" : "=a" (interruptsState)) #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) diff --git a/cores/esp8266/core_esp8266_timer.c b/cores/esp8266/core_esp8266_timer.c index 819c5f990..b130d3c93 100644 --- a/cores/esp8266/core_esp8266_timer.c +++ b/cores/esp8266/core_esp8266_timer.c @@ -32,7 +32,13 @@ static volatile timercallback timer1_user_cb = NULL; void timer1_isr_handler(void *para){ if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable T1I = 0; - if (timer1_user_cb) timer1_user_cb(); + if (timer1_user_cb) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + timer1_user_cb(); + xt_wsr_ps(savedPS); + } } void timer1_isr_init(){ @@ -72,7 +78,11 @@ static volatile timercallback timer0_user_cb = NULL; void timer0_isr_handler(void* para){ if (timer0_user_cb) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts timer0_user_cb(); + xt_wsr_ps(savedPS); } } diff --git a/cores/esp8266/core_esp8266_wiring_digital.c b/cores/esp8266/core_esp8266_wiring_digital.c index 5be065af5..c64961f03 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.c +++ b/cores/esp8266/core_esp8266_wiring_digital.c @@ -123,7 +123,11 @@ void interrupt_handler(void *arg) { if (handler->fn && (handler->mode == CHANGE || (handler->mode & 1) == digitalRead(i))) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts handler->fn(); + xt_wsr_ps(savedPS); } } ETS_GPIO_INTR_ENABLE(); @@ -152,9 +156,6 @@ extern void __detachInterrupt(uint8_t pin) { } } -// stored state for the noInterrupts/interrupts methods -uint32_t interruptsState = 0; - void initPins() { //Disable UART interrupts system_set_os_print(0); diff --git a/libraries/Servo/src/esp8266/Servo.cpp b/libraries/Servo/src/esp8266/Servo.cpp index e9fce77f1..af8bf549e 100644 --- a/libraries/Servo/src/esp8266/Servo.cpp +++ b/libraries/Servo/src/esp8266/Servo.cpp @@ -59,8 +59,6 @@ static uint8_t s_servoCount = 0; // the total number of attached s_se //------------------------------------------------------------------------------ template void Servo_Handler(T* timer) { - noInterrupts(); - uint8_t servoIndex; // clear interrupt @@ -101,8 +99,6 @@ template void Servo_Handler(T* timer) timer->setEndOfCycle(); } - - interrupts(); } static void initISR(ServoTimerSequence timerId) diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 607d601f1..526a24d40 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -65,8 +65,8 @@ inline bool ETS_INTR_WITHINISR() { uint32_t ps; __asm__ __volatile__("rsr %0,ps":"=a" (ps)); - // PS.EXCM bit check - return ((ps & (1 << 4)) != 0); + // PS.INTLEVEL check + return ((ps & 0x0f) != 0); } inline uint32_t ETS_INTR_ENABLED(void) From dfeed84ecb06c442969fe388ae05306e4c30aff2 Mon Sep 17 00:00:00 2001 From: Makuna Date: Mon, 3 Aug 2015 19:55:56 -0700 Subject: [PATCH 06/45] make compatible with existing interrupt lock class Support both the normal auto lock at all levels, and the lock at a specific level requiring different syntax --- cores/esp8266/Arduino.h | 21 --------------------- cores/esp8266/interrupts.h | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index b293bb633..fa65a2d02 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -160,27 +160,6 @@ void ets_intr_unlock(); #define interrupts() xt_rsil(0) #define noInterrupts() xt_rsil(15) -// this auto class wraps up xt_rsil so your code can be simplier, but can only be -// used in an ino or cpp files. A normal use pattern is like -// -//{ -// { -// InterruptLock(1); // this routine will allow level 2 and above -// // do work within interrupt lock here -// } -// do work outside of interrupt lock here outside its scope -//} -// -#define InterruptLock(intrLevel) \ -class _AutoDisableIntr { \ -public: \ - _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ - ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ -private: \ - uint32_t _savedPS; \ - }; \ -_AutoDisableIntr _autoDisableIntr - #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) diff --git a/cores/esp8266/interrupts.h b/cores/esp8266/interrupts.h index d86f89b1f..78982fe45 100644 --- a/cores/esp8266/interrupts.h +++ b/cores/esp8266/interrupts.h @@ -8,23 +8,51 @@ extern "C" { #include "ets_sys.h" } +// these auto classes wrap up xt_rsil so your code can be simplier, but can only be +// used in an ino or cpp files. -#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)) -#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") +// InterruptLock is used when you want to completely disable locks +//{ +// { +// InterruptLock lock; +// // do work within interrupt lock here +// } +// do work outside of interrupt lock here outside its scope +//} +// class InterruptLock { public: InterruptLock() { - xt_disable_interrupts(_state, 15); + _state = = xt_rsil(15); } ~InterruptLock() { - xt_enable_interrupts(_state); + xt_wsr_ps(_state); } protected: uint32_t _state; }; +// AutoInterruptLock is when you need to set a specific level, A normal use pattern is like +// +//{ +// { +// AutoInterruptLock(1); // this routine will allow level 2 and above +// // do work within interrupt lock here +// } +// do work outside of interrupt lock here outside its scope +//} +// +#define AutoInterruptLock(intrLevel) \ +class _AutoDisableIntr { \ +public: \ + _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ + ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ +private: \ + uint32_t _savedPS; \ + }; \ +_AutoDisableIntr _autoDisableIntr #endif //INTERRUPTS_H From a2673f2f4bf387a1a53e400a60bf241e6360a7ee Mon Sep 17 00:00:00 2001 From: Makuna Date: Mon, 3 Aug 2015 19:58:42 -0700 Subject: [PATCH 07/45] copy paste error fi --- cores/esp8266/interrupts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/interrupts.h b/cores/esp8266/interrupts.h index 78982fe45..813997447 100644 --- a/cores/esp8266/interrupts.h +++ b/cores/esp8266/interrupts.h @@ -24,7 +24,7 @@ extern "C" { class InterruptLock { public: InterruptLock() { - _state = = xt_rsil(15); + _state = xt_rsil(15); } ~InterruptLock() { From d5ab22f4aea1f0ad4679cecde7bb35c21e2ed7ac Mon Sep 17 00:00:00 2001 From: Kiril Zyapkov Date: Tue, 4 Aug 2015 13:45:38 +0300 Subject: [PATCH 08/45] ESP8266AVRISP: switch default to active-low reset --- libraries/ESP8266AVRISP/src/ESP8266AVRISP.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h index 2d4728682..4c8ebe161 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -14,9 +14,8 @@ Original version: #include -// active-high reset if you use an n-mos to level-shift -// comment if hooked directly -#define AVRISP_ACTIVE_HIGH_RESET +// uncomment if you use an n-mos to level-shift the reset line +// #define AVRISP_ACTIVE_HIGH_RESET // SPI clock frequency in Hz #define AVRISP_SPI_FREQ 1e6 From 4bf286954e4ce2625f74befc231b51676d0fb5fb Mon Sep 17 00:00:00 2001 From: Kiril Zyapkov Date: Tue, 4 Aug 2015 23:23:46 +0300 Subject: [PATCH 09/45] ESP8266AVRISP: allow setting SPI freq and reset, postpone SPI init --- libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp | 61 ++++++++----------- libraries/ESP8266AVRISP/src/ESP8266AVRISP.h | 8 ++- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp index e93713d27..1cdeb26e8 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp @@ -47,20 +47,28 @@ extern "C" { #define beget16(addr) (*addr * 256 + *(addr+1)) -ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq): - _reset_pin(reset_pin), _server(WiFiServer(port)), _state(AVRISP_STATE_IDLE), - _spi_freq(spi_freq) +ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq, bool reset_state): + _reset_pin(reset_pin), _reset_state(reset_state), _spi_freq(spi_freq), + _server(WiFiServer(port)), _state(AVRISP_STATE_IDLE) { + pinMode(_reset_pin, OUTPUT); + setReset(_reset_state); } void ESP8266AVRISP::begin() { - pinMode(_reset_pin, OUTPUT); - digitalWrite(_reset_pin, AVRISP_RESET_OFF); _server.begin(); } +void ESP8266AVRISP::setSpiFrequency(uint32_t freq) { + _spi_freq = freq; + if (_state == AVRISP_STATE_ACTIVE) { + SPI.setFrequency(freq); + } +} + void ESP8266AVRISP::setReset(bool rst) { - if (rst) { + _reset_state = rst; + if (_reset_state) { digitalWrite(_reset_pin, AVRISP_RESET_ON); } else { digitalWrite(_reset_pin, AVRISP_RESET_OFF); @@ -88,9 +96,14 @@ AVRISPState_t ESP8266AVRISP::update() { if (!_client.connected()) { _client.stop(); AVRISP_DEBUG("client disconnect"); - SPI.end(); - digitalWrite(_reset_pin, AVRISP_RESET_OFF); + if (pmode) { + SPI.end(); + pmode = 0; + } + setReset(_reset_state); _state = AVRISP_STATE_IDLE; + } else { + _reject_incoming(); } break; } @@ -104,11 +117,6 @@ AVRISPState_t ESP8266AVRISP::serve() { // should not be called when idle, error? break; case AVRISP_STATE_PENDING: { - // enter reset, setup SPI - SPI.begin(); - SPI.setFrequency(_spi_freq); - SPI.setHwCs(false); - digitalWrite(_reset_pin, AVRISP_RESET_ON); _state = AVRISP_STATE_ACTIVE; // fallthrough } @@ -213,31 +221,14 @@ void ESP8266AVRISP::set_parameters() { + buff[17] * 0x00010000 + buff[18] * 0x00000100 + buff[19]; - - // AVRISP_DEBUG("devicecode = %d", param.devicecode); - // AVRISP_DEBUG("revision = %d", param.revision); - // AVRISP_DEBUG("progtype = %d", param.progtype); - // AVRISP_DEBUG("parmode = %d", param.parmode); - // AVRISP_DEBUG("polling = %d", param.polling); - // AVRISP_DEBUG("selftimed = %d", param.selftimed); - // AVRISP_DEBUG("lockbytes = %d", param.lockbytes); - // AVRISP_DEBUG("fusebytes = %d", param.fusebytes); - // AVRISP_DEBUG("flashpoll = %d", param.flashpoll); - // AVRISP_DEBUG("eeprompoll = %d", param.eeprompoll); - // AVRISP_DEBUG("pagesize = %d", param.pagesize); - // AVRISP_DEBUG("eepromsize = %d", param.eepromsize); - - // AVRISP_DEBUG("flashsize = %d", param.flashsize); - } void ESP8266AVRISP::start_pmode() { + SPI.begin(); + SPI.setFrequency(_spi_freq); + SPI.setHwCs(false); - // SPI already begun when entering ACTIVE state - //SPI.begin(); - //SPI.setFrequency(AVRISP_SPI_FREQ); - - // following delays may not work on all targets... + // try to sync the bus SPI.transfer(0x00); digitalWrite(_reset_pin, AVRISP_RESET_OFF); delayMicroseconds(50); @@ -250,7 +241,7 @@ void ESP8266AVRISP::start_pmode() { void ESP8266AVRISP::end_pmode() { SPI.end(); - digitalWrite(_reset_pin, AVRISP_RESET_OFF); + setReset(_reset_state); pmode = 0; } diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h index 4c8ebe161..d2bc6f1bc 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -18,7 +18,7 @@ Original version: // #define AVRISP_ACTIVE_HIGH_RESET // SPI clock frequency in Hz -#define AVRISP_SPI_FREQ 1e6 +#define AVRISP_SPI_FREQ 300e3 // programmer states typedef enum { @@ -47,10 +47,13 @@ typedef struct { class ESP8266AVRISP { public: - ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ); + ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false); void begin(); + // set the SPI clock frequency + void setSpiFrequency(uint32_t); + // control the state of the RESET pin of the target // see AVRISP_ACTIVE_HIGH_RESET void setReset(bool); @@ -104,6 +107,7 @@ protected: WiFiClient _client; AVRISPState_t _state; uint8_t _reset_pin; + bool _reset_state; // programmer settings, set by remote end AVRISP_parameter_t param; From b5d9db91aacdd3b26d0f39526b795bf6c060f4b2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 07:41:12 -0400 Subject: [PATCH 10/45] Filesystem: fix File::available, add File::name --- cores/esp8266/FS.cpp | 9 ++++++++- cores/esp8266/FS.h | 1 + cores/esp8266/FSImpl.h | 1 + cores/esp8266/spiffs_api.cpp | 11 +++++++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index 18eda0c86..030d79f0d 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -41,7 +41,7 @@ int File::available() { if (!_p) return false; - return _p->position() < _p->size(); + return _p->size() - _p->position(); } int File::read() { @@ -112,6 +112,13 @@ File::operator bool() const { return !!_p; } +const char* File::name() const { + if (!_p) + return nullptr; + + return _p->name(); +} + File Dir::openFile(const char* mode) { if (!_impl) { return File(); diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index ce6d220ba..c2b29ba09 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -64,6 +64,7 @@ public: size_t size() const; void close(); operator bool() const; + const char* name() const; protected: FileImplPtr _p; diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index 8977e64c0..f11342f12 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -33,6 +33,7 @@ public: virtual size_t position() const = 0; virtual size_t size() const = 0; virtual void close() = 0; + virtual const char* name() const = 0; }; enum OpenMode { diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index d63a1bcd4..480755087 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -259,6 +259,12 @@ public: DEBUGV("SPIFFS_close: fd=%d\r\n", _fd); } + const char* name() const override { + CHECKFD(); + + return (const char*) _stat.name; + } + protected: SPIFFSImpl* _fs; spiffs_file _fd; @@ -283,10 +289,11 @@ public: return FileImplPtr(); } int mode = getSpiffsMode(openMode, accessMode); - spiffs_file fd = SPIFFS_open_by_dirent(_fs->getFs(), &_dirent, mode, 0); + auto fs = _fs->getFs(); + spiffs_file fd = SPIFFS_open_by_dirent(fs, &_dirent, mode, 0); if (fd < 0) { DEBUGV("SPIFFSDirImpl::openFile: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n", - fd, _dirent.name, openMode, accessMode, _fs.err_code); + fd, _dirent.name, openMode, accessMode, fs->err_code); return FileImplPtr(); } return std::make_shared(_fs, fd); From e8b27912d724aa4bc5a78bcb535d08c7bed0a998 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 07:44:00 -0400 Subject: [PATCH 11/45] ESP8266WebServer: serve static files from FS --- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 59 +++++------- .../ESP8266WebServer/src/ESP8266WebServer.h | 18 ++-- .../src/detail/RequestHandler.h | 96 +++++++++++++++++++ 3 files changed, 129 insertions(+), 44 deletions(-) create mode 100644 libraries/ESP8266WebServer/src/detail/RequestHandler.h diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index cf0121388..bc0073774 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.cpp - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -25,25 +25,10 @@ #include "WiFiServer.h" #include "WiFiClient.h" #include "ESP8266WebServer.h" - +#include "detail/RequestHandler.h" // #define DEBUG #define DEBUG_OUTPUT Serial -struct ESP8266WebServer::RequestHandler { - RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) - : fn(fn) - , uri(uri) - , method(method) - , next(NULL) - { - } - - ESP8266WebServer::THandlerFunction fn; - String uri; - HTTPMethod method; - RequestHandler* next; - -}; ESP8266WebServer::ESP8266WebServer(int port) : _server(port) @@ -78,15 +63,22 @@ void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction ha void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { - RequestHandler* handler = new RequestHandler(fn, uri, method); - if (!_lastHandler) { - _firstHandler = handler; - _lastHandler = handler; - } - else { - _lastHandler->next = handler; - _lastHandler = handler; - } + _addRequestHandler(new FunctionRequestHandler(fn, uri, method)); +} + +void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next = handler; + _lastHandler = handler; + } +} + +void ESP8266WebServer::serveStatic(const char* uri, FS fs, const char* path) { + _addRequestHandler(new StaticRequestHandler(fs, uri)); } void ESP8266WebServer::handleClient() @@ -269,16 +261,9 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) { void ESP8266WebServer::_handleRequest() { RequestHandler* handler; - for (handler = _firstHandler; handler; handler = handler->next) - { - if (handler->method != HTTP_ANY && handler->method != _currentMethod) - continue; - - if (handler->uri != _currentUri) - continue; - - handler->fn(); - break; + for (handler = _firstHandler; handler; handler = handler->next) { + if (handler->handle(*this, _currentMethod, _currentUri)) + break; } if (!handler){ diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index bf538c3e0..49c610294 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.h - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -25,6 +25,7 @@ #define ESP8266WEBSERVER_H #include +#include enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; @@ -37,6 +38,8 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) #define CONTENT_LENGTH_NOT_SET ((size_t) -2) +class RequestHandler; + typedef struct { HTTPUploadStatus status; String filename; @@ -59,6 +62,7 @@ public: typedef std::function THandlerFunction; void on(const char* uri, THandlerFunction handler); void on(const char* uri, HTTPMethod method, THandlerFunction fn); + void serveStatic(const char* uri, FS fs, const char* path); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads @@ -66,13 +70,13 @@ public: HTTPMethod method() { return _currentMethod; } WiFiClient client() { return _currentClient; } HTTPUpload& upload() { return _currentUpload; } - + String arg(const char* name); // get request argument value by name String arg(int i); // get request argument value by number String argName(int i); // get request argument name by number int args(); // get arguments count bool hasArg(const char* name); // check if argument exists - + // send response to the client // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" @@ -89,7 +93,7 @@ public: template size_t streamFile(T &file, const String& contentType){ setContentLength(file.size()); - if (String(file.name()).endsWith(".gz") && + if (String(file.name()).endsWith(".gz") && contentType != "application/x-gzip" && contentType != "application/octet-stream"){ sendHeader("Content-Encoding", "gzip"); @@ -97,8 +101,9 @@ template size_t streamFile(T &file, const String& contentType){ send(200, contentType, ""); return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); } - + protected: + void _addRequestHandler(RequestHandler* handler); void _handleRequest(); bool _parseRequest(WiFiClient& client); void _parseArguments(String data); @@ -108,7 +113,6 @@ protected: uint8_t _uploadReadByte(WiFiClient& client); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); - struct RequestHandler; struct RequestArgument { String key; String value; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h new file mode 100644 index 000000000..702e24e0b --- /dev/null +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -0,0 +1,96 @@ +#ifndef REQUESTHANDLER_H +#define REQUESTHANDLER_H + +class RequestHandler { +public: + RequestHandler(const char* uri, HTTPMethod method) + : uri(uri) + , method(method) + , next(NULL) + { + } + + virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) = 0; + + RequestHandler* next; + +protected: + String uri; + HTTPMethod method; +}; + + +class FunctionRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) + : fn(fn) + , base(uri, method) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (method != HTTP_ANY && method != requestMethod) + return false; + + if (requestUri != uri) + return false; + + fn(); + return true; + } + +protected: + ESP8266WebServer::THandlerFunction fn; +}; + +class StaticRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + StaticRequestHandler(FS& fs, const char* uri) + : fs(fs) + , base(uri, HTTP_GET) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (requestMethod != method) + return false; + DEBUGV("StaticRequestHandler::handle: %s\r\n", requestUri.c_str()); + if (!requestUri.startsWith(uri)) + return false; + + auto prefixLength = uri.length() - 1; + String path = requestUri.substring(prefixLength); + DEBUGV("StaticRequestHandler::handle: %d %s\r\n", prefixLength, path.c_str()); + File f = fs.open(path, "r"); + if (!f) + return false; + + server.streamFile(f, getContentType(path)); + return true; + } + + static String getContentType(const String& path) { + if (path.endsWith(".html")) return "text/html"; + else if (path.endsWith(".htm")) return "text/html"; + else if (path.endsWith(".css")) return "text/css"; + else if (path.endsWith(".txt")) return "text/plain"; + else if (path.endsWith(".js")) return "application/javascript"; + else if (path.endsWith(".png")) return "image/png"; + else if (path.endsWith(".gif")) return "image/gif"; + else if (path.endsWith(".jpg")) return "image/jpeg"; + else if (path.endsWith(".ico")) return "image/x-icon"; + else if (path.endsWith(".xml")) return "text/xml"; + else if (path.endsWith(".pdf")) return "application/pdf"; + else if (path.endsWith(".zip")) return "application/zip"; + return "text/plain"; + } + +protected: + FS fs; +}; + +#endif //REQUESTHANDLER_H From 5cbaa57af98a3c3c621b4e3adf75ce2680292c05 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 08:36:43 -0400 Subject: [PATCH 12/45] Resolve naming conflicts between FS and SD library --- cores/esp8266/FS.cpp | 2 ++ cores/esp8266/FS.h | 8 ++++++++ cores/esp8266/FSImpl.h | 3 +++ cores/esp8266/spiffs_api.cpp | 2 ++ libraries/ESP8266WebServer/src/ESP8266WebServer.cpp | 3 ++- libraries/ESP8266WebServer/src/ESP8266WebServer.h | 4 ++-- 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index 030d79f0d..7a13dcede 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -21,6 +21,8 @@ #include "FS.h" #include "FSImpl.h" +using namespace fs; + static bool sflags(const char* mode, OpenMode& om, AccessMode& am); size_t File::write(uint8_t c) { diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index c2b29ba09..3e1b1b4fc 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -24,6 +24,8 @@ #include #include +namespace fs { + class File; class Dir; @@ -106,6 +108,12 @@ protected: }; +} // namespace fs + +using fs::FS; +using fs::File; +using fs::Dir; + extern FS SPIFFS; #endif //FS_H diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index f11342f12..8e48a9fda 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -23,6 +23,8 @@ #include #include +namespace fs { + class FileImpl { public: virtual ~FileImpl() { } @@ -67,5 +69,6 @@ public: }; +} // namespace fs #endif //FSIMPL_H diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index 480755087..157ae86bc 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -32,6 +32,8 @@ extern "C" { #include "spi_flash.h" } +using namespace fs; + extern int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src); extern int32_t spiffs_hal_erase(uint32_t addr, uint32_t size); extern int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index bc0073774..c709dcfdd 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -24,6 +24,7 @@ #include #include "WiFiServer.h" #include "WiFiClient.h" +#include "FS.h" #include "ESP8266WebServer.h" #include "detail/RequestHandler.h" // #define DEBUG @@ -77,7 +78,7 @@ void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { } } -void ESP8266WebServer::serveStatic(const char* uri, FS fs, const char* path) { +void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) { _addRequestHandler(new StaticRequestHandler(fs, uri)); } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 49c610294..74d3ecb45 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -25,7 +25,6 @@ #define ESP8266WEBSERVER_H #include -#include enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; @@ -39,6 +38,7 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; #define CONTENT_LENGTH_NOT_SET ((size_t) -2) class RequestHandler; +class FS; typedef struct { HTTPUploadStatus status; @@ -62,7 +62,7 @@ public: typedef std::function THandlerFunction; void on(const char* uri, THandlerFunction handler); void on(const char* uri, HTTPMethod method, THandlerFunction fn); - void serveStatic(const char* uri, FS fs, const char* path); + void serveStatic(const char* uri, FS& fs, const char* path); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads From e02932fcddc24740852ec0e39e494e57a8906df2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 08:39:34 -0400 Subject: [PATCH 13/45] Only erase RTC RAM if sleep mode doesn't look valid (#619) Source: http://esp8266.ru/forum/threads/mem-check-fail.168/#post-7354 --- cores/esp8266/core_esp8266_phy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cores/esp8266/core_esp8266_phy.c b/cores/esp8266/core_esp8266_phy.c index a4a189384..ea0beefb1 100644 --- a/cores/esp8266/core_esp8266_phy.c +++ b/cores/esp8266/core_esp8266_phy.c @@ -261,7 +261,10 @@ void user_rf_pre_init() { // *((volatile uint32_t*) 0x60000710) = 0; volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; - rtc_reg[30] = 0; + if((rtc_reg[24] >> 16) > 4) { + rtc_reg[24] &= 0xFFFF; + rtc_reg[30] = 0; + } system_set_os_print(0); __run_user_rf_pre_init(); From cc152de90775e730607d69ea6410e2fbf9286235 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 08:42:29 -0400 Subject: [PATCH 14/45] Return to scheduler context from ESP::deepSleep (#609) --- cores/esp8266/Esp.cpp | 24 +++++++++++------------- cores/esp8266/core_esp8266_main.cpp | 4 +++- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index cd3b16f6a..299a97996 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -1,8 +1,8 @@ -/* +/* Esp.cpp - ESP8266-specific APIs Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -32,7 +32,7 @@ extern struct rst_info resetInfo; //#define DEBUG_SERIAL Serial - + /** * User-defined Literals * usage: @@ -92,7 +92,7 @@ void EspClass::wdtEnable(WDTO_t timeout_ms) void EspClass::wdtDisable(void) { - /// Please don’t stop software watchdog too long (less than 6 seconds), + /// Please don't stop software watchdog too long (less than 6 seconds), /// otherwise it will trigger hardware watchdog reset. system_soft_wdt_stop(); } @@ -102,13 +102,15 @@ void EspClass::wdtFeed(void) } +extern "C" void esp_yield(); + void EspClass::deepSleep(uint32_t time_us, WakeMode mode) { - system_deep_sleep_set_option(static_cast(mode)); - system_deep_sleep(time_us); + system_deep_sleep_set_option(static_cast(mode)); + system_deep_sleep(time_us); + esp_yield(); } -extern "C" void esp_yield(); extern "C" void __real_system_restart_local(); void EspClass::reset(void) { @@ -119,9 +121,6 @@ void EspClass::restart(void) { system_restart(); esp_yield(); - // todo: provide an alternative code path if this was called - // from system context, not from continuation - // (implement esp_is_cont_ctx()?) } uint16_t EspClass::getVcc(void) @@ -333,7 +332,7 @@ uint32_t EspClass::getSketchSize() { DEBUG_SERIAL.printf("num_segments=%u\r\n", image_header.num_segments); #endif for (uint32_t section_index = 0; - section_index < image_header.num_segments; + section_index < image_header.num_segments; ++section_index) { section_header_t section_header = {0}; @@ -395,8 +394,7 @@ bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("Update SUCCESS"); -#endif +#endif if(restartOnSuccess) ESP.restart(); return true; } - diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 67c3f3802..a953c8782 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -74,7 +74,9 @@ extern "C" void abort() { } extern "C" void esp_yield() { - cont_yield(&g_cont); + if (cont_can_yield(&g_cont)) { + cont_yield(&g_cont); + } } extern "C" void esp_schedule() { From 661ccb23aed221ff831558356f1a351b5954b4dd Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 08:48:21 -0400 Subject: [PATCH 15/45] Fix compilation error when Esp.h is included first in sketch (#590) --- cores/esp8266/Esp.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 17349f688..c7c44aaf0 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -1,8 +1,8 @@ -/* +/* Esp.h - ESP8266-specific APIs Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -20,6 +20,9 @@ #ifndef ESP_H #define ESP_H + +#include + /** * AVR macros for WDT managment */ From 3a3f25997c84aea2d33a0a40d2677ca4ba5851c3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 08:54:45 -0400 Subject: [PATCH 16/45] Disable interrupts inside Esp.getVcc (#567) --- cores/esp8266/Esp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 299a97996..fc814976c 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -22,6 +22,7 @@ #include "flash_utils.h" #include "eboot_command.h" #include +#include "interrupts.h" extern "C" { #include "user_interface.h" @@ -125,6 +126,7 @@ void EspClass::restart(void) uint16_t EspClass::getVcc(void) { + InterruptLock lock; return system_get_vdd33(); } From afd0ca23a09a2896a73c65a4c0aa014d3602c093 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 09:00:17 -0400 Subject: [PATCH 17/45] EEPROM: round requested size to 4 byte boundary (#659) --- libraries/EEPROM/EEPROM.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/EEPROM/EEPROM.cpp b/libraries/EEPROM/EEPROM.cpp index dfd968a4a..41c484306 100644 --- a/libraries/EEPROM/EEPROM.cpp +++ b/libraries/EEPROM/EEPROM.cpp @@ -1,9 +1,9 @@ -/* +/* EEPROM.cpp - esp8266 EEPROM emulation Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -34,7 +34,7 @@ EEPROMClass::EEPROMClass(uint32_t sector) : _sector(sector) , _data(0) , _size(0) -, _dirty(false) +, _dirty(false) { } @@ -44,6 +44,8 @@ void EEPROMClass::begin(size_t size) { if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE; + size = (size + 3) & (~3); + if (_data) { delete[] _data; } From 2fb1c453ee770bb779b1e70548b4a5668367b546 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 12:27:51 -0400 Subject: [PATCH 18/45] File system API reference --- doc/reference.md | 139 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/doc/reference.md b/doc/reference.md index 2ca3d5e9a..e8c15e034 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -103,6 +103,145 @@ const char HTTP[] PROGMEM = "http:"; } ``` +## File system + +### File system object (SPIFFS) + +#### begin + +```c++ +SPIFFS.begin() +``` + +This method mounts SPIFFS file system. It must be called before any other +FS APIs are used. Returns *true* if file system was mounted successfully, false +otherwise. + +#### open + +```c++ +SPIFFS.open(path, mode) +``` + +Opens a file. `path` should be an absolute path starting with a slash +(e.g. `/dir/filename.txt`). `mode` is a string specifying access mode. It can be +one of "r", "w", "a", "r+", "w+", "a+". Meaning of these modes is the same as +for `fopen` C function. + +Returns *File* object. To check whether the file was opened successfully, use +the boolean operator. + +```c++ +File f = SPIFFS.open("/f.txt", "w"); +if (!f) { + Serial.println("file open failed"); +} +``` + +#### openDir + +```c++ +SPIFFS.openDir(path) +``` + +Opens a directory given its absolute path. Returns a *Dir* object. To check if +directory was opened successfully, use the boolean operator, similar to opening +a file. + +#### remove + +```c++ +SPIFFS.remove(path) +``` + +Deletes the file given its absolute path. Returns *true* if file was deleted successfully. + +#### rename + +```c++ +SPIFFS.rename(pathFrom, pathTo) +``` + +Renames file from `pathFrom` to `pathTo`. Paths must be absolute. Returns *true* +if file was renamed successfully. + +### Directory object (Dir) + +The purpose of *Dir* object is to iterate over files inside a directory. +It provides three methods: `next()`, `fileName()`, and `openFile(mode)`. + +The following example shows how it should be used: + +```c++ +Dir dir = SPIFFS.openDir("/data"); +while (dir.next()) { + Serial.print(dir.fileName()); + File f = dir.openFile("r"); + Serial.println(f.size()); +} +``` + +`dir.next()` returns true while there are files in the directory to iterate over. +It must be called before calling `fileName` and `openFile` functions. + +`openFile` method takes *mode* argument which has the same meaning as for `SPIFFS.open` function. + +### File object + +`SPIFFS.open` and `dir.openFile` functions return a *File* object. This object +supports all the functions of *Stream*, so you can use `readBytes`, `findUntil`, +`parseInt`, `println`, and all other *Stream* methods. + +There are also some functions which are specific to *File* object. + +#### seek + +```c++ +file.seek(offset, mode) +``` + +This function behaves like `fseek` C function. Depending on the value of `mode`, +it moves current position in a file as follows: + +- if `mode` is `SeekSet`, position is set to `offset` bytes from the beginning. +- if `mode` is `SeekCur`, current position is moved by `offset` bytes. +- if `mode` is `SeekEnd`, position is set to `offset` bytes from the end of the +file. + +Returns *true* if position was set successfully. + +#### position + +```c++ +file.position() +``` + +Returns the current position inside the file, in bytes. + +#### size + +```c++ +file.size() +``` + +Returns file size, in bytes. + + +#### name + +```c++ +String name = file.name(); +``` + +Returns file name, as `const char*`. Convert it to *String* for storage. + +#### close + +```c++ +file.close() +``` + +Close the file. No other operations should be performed on *File* object after `close` function was called. ## WiFi(ESP8266WiFi library) From d46285d6f7724954e9f8378782f142a8d23d0b9d Mon Sep 17 00:00:00 2001 From: Lorenzo Cafaro Date: Wed, 5 Aug 2015 21:07:35 +0200 Subject: [PATCH 19/45] Added MOD-WIFI-ESP8266-DEV --- doc/boards.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/boards.md b/doc/boards.md index 99bb7cb8c..df4c8801a 100644 --- a/doc/boards.md +++ b/doc/boards.md @@ -5,6 +5,7 @@ title: Supported Hardware - [Adafruit HUZZAH ESP8266 (ESP-12)](#adafruit-huzzah-esp8266-esp-12) - [NodeMCU 0.9](#nodemcu-0-9) - [NodeMCU 1.0](#nodemcu-1-0) +- [Olimex MOD-WIFI-ESP8266-DEV](#olimex-mod-wifi-esp8266-dev) - [Olimex MOD-WIFI-ESP8266](#olimex-mod-wifi-esp8266) - [SparkFun ESP8266 Thing](#sparkfun-esp8266-thing) - [SweetPea ESP-210](#sweetpea-esp-210) @@ -40,9 +41,23 @@ If you want to use NodeMCU pin 5, use D5 for pin number, and it will be translat *TODO: add notes* +### Olimex MOD-WIFI-ESP8266-DEV + +This board comes with 2 MB of SPI flash and optional accessories (e.g. evaluation board ESP8266-EVB or BAT-BOX for batteries). + +The basic module has three solder jumpers that allow you to switch the operating mode between SDIO, UART and FLASH. + +The board is shipped for FLASH operation mode, with jumpers TD0JP=0, IO0JP=1, IO2JP=1. + +Since jumper IO0JP is tied to GPIO0, which is PIN 21, you'll have to ground it before programming with a USB to serial adapter and reset the board by power cycling it. + +UART pins for programming and serial I/O are GPIO1 (TXD, pin 3) and GPIO3 (RXD, pin 4). + +Get the board schematics [here](https://github.com/OLIMEX/ESP8266/blob/master/HARDWARE/MOD-WIFI-ESP8266-DEV/MOD-WIFI-ESP8266-DEV_schematic.pdf) + ### Olimex MOD-WIFI-ESP8266 -*TODO: add notes* +This is a stripped down version of the above. Behaves identically in terms of jumpers but has less pins readily available for I/O. Still 2 MB of SPI flash. ### SparkFun ESP8266 Thing ### From 80ccbaef0dd744dcad1ce7805292be60cb50c143 Mon Sep 17 00:00:00 2001 From: Martin Ayotte Date: Thu, 6 Aug 2015 13:32:02 -0400 Subject: [PATCH 20/45] fix dtostrf() issue using trackerj/odometer fixes along with my own fix for string null character ending --- cores/esp8266/core_esp8266_noniso.c | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/cores/esp8266/core_esp8266_noniso.c b/cores/esp8266/core_esp8266_noniso.c index 40fe5e6dd..fecdd3362 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -162,46 +162,45 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { strcpy(s, "ovf"); return s; } - char* out = s; - int signInt_Part = 1; - // Handle negative numbers if (number < 0.0) { - signInt_Part = -1; + *out = '-'; + ++out; number = -number; } - // calc left over digits - if (prec > 0) - { - width -= (prec + 1); - } - // Round correctly so that print(1.999, 2) prints as "2.00" - double rounding = 0.5; + // I optimized out most of the divisions + double rounding = 2.0; for (uint8_t i = 0; i < prec; ++i) - rounding /= 10.0; + rounding *= 10.0; + rounding = 1.0 / rounding; number += rounding; // Extract the integer part of the number and print it unsigned long int_part = (unsigned long)number; double remainder = number - (double)int_part; - out += sprintf(out, "%*ld", width, int_part * signInt_Part); + out += sprintf(out, "%d", int_part); // Print the decimal point, but only if there are digits beyond if (prec > 0) { *out = '.'; ++out; - - - for (unsigned char decShift = prec; decShift > 0; decShift--) { - remainder *= 10.0; - } - sprintf(out, "%0*d", prec, (int)remainder); } + // Print the digits after the decimal point + int8_t digit = 0; + while (prec-- > 0) { + remainder *= 10.0; + digit = (int8_t)remainder; + if (digit > 9) digit = 9; // insurance + *out = (char)('0' | digit); + ++out; + remainder -= digit; + } + *out = 0; return s; } From 857592c627a85e3e8415d8be87f9fd26793f4894 Mon Sep 17 00:00:00 2001 From: Lorenzo Cafaro Date: Fri, 7 Aug 2015 00:14:27 +0200 Subject: [PATCH 21/45] Added NodeMCU 1.0 --- doc/boards.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/boards.md b/doc/boards.md index df4c8801a..d8eb03f78 100644 --- a/doc/boards.md +++ b/doc/boards.md @@ -39,7 +39,15 @@ If you want to use NodeMCU pin 5, use D5 for pin number, and it will be translat ### NodeMCU 1.0 -*TODO: add notes* +This module is sold under many names for around $6.50 on AliExpress and it's one of the cheapest, fully integrated ESP8266 solutions. + +It's an open hardware design with an ESP-12E core and 4 MB of SPI flash. + +Acording to the manufacturer, "with a micro USB cable, you can connect NodeMCU devkit to your laptop and flash it without any trouble". This is more or less true: the board comes with a CP2102 onboard USB to serial adapter which just works, well, the majority of the time. Sometimes flashing fails and you have to reset the board by holding down FLASH + RST, then releasing FLASH, then releasing RST. This forces the CP2102 device to power cycle and to be re-numbered by Linux. + +The board also features a NCP1117 voltage regulator, a blue LED on GPIO16 and a 220k/100k Ohm voltage divider on the ADC input pin. + +Full pinout and PDF schematics can be found [here](https://github.com/nodemcu/nodemcu-devkit-v1.0) ### Olimex MOD-WIFI-ESP8266-DEV From aa6965c1722327b665c49e337c69874ee56a4449 Mon Sep 17 00:00:00 2001 From: Martin Ayotte Date: Fri, 7 Aug 2015 11:06:06 -0400 Subject: [PATCH 22/45] fix the missing minimum width requirement --- cores/esp8266/core_esp8266_noniso.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cores/esp8266/core_esp8266_noniso.c b/cores/esp8266/core_esp8266_noniso.c index fecdd3362..7fb4fd66a 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -165,8 +165,7 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { char* out = s; // Handle negative numbers if (number < 0.0) { - *out = '-'; - ++out; + *out++ = '-'; number = -number; } @@ -186,9 +185,12 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { // Print the decimal point, but only if there are digits beyond if (prec > 0) { - *out = '.'; - ++out; + *out++ = '.'; } + // make sure the string is terminated before mesuring it length + *out = 0; + // Reduce minimum width accordingly + width -= strlen(s); // Print the digits after the decimal point int8_t digit = 0; @@ -196,10 +198,13 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { remainder *= 10.0; digit = (int8_t)remainder; if (digit > 9) digit = 9; // insurance - *out = (char)('0' | digit); - ++out; + *out++ = (char)('0' | digit); + width--; remainder -= digit; } + // add '0' to fill minimum width requirement + while (width-- > 0) *out++ = '0'; + // make sure the string is terminated *out = 0; return s; } From 659e467141a9738a883d5cc7e9670a4f1b8934f0 Mon Sep 17 00:00:00 2001 From: Martin Ayotte Date: Fri, 7 Aug 2015 21:55:25 -0400 Subject: [PATCH 23/45] padding should be done with space, not with '0' --- cores/esp8266/core_esp8266_noniso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/core_esp8266_noniso.c b/cores/esp8266/core_esp8266_noniso.c index 7fb4fd66a..0b53583fd 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -203,7 +203,7 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { remainder -= digit; } // add '0' to fill minimum width requirement - while (width-- > 0) *out++ = '0'; + while (width-- > 0) *out++ = ' '; // make sure the string is terminated *out = 0; return s; From 15991fb35a729e5faa470074b31f163f7b7ae529 Mon Sep 17 00:00:00 2001 From: timw1971 Date: Sat, 8 Aug 2015 11:48:18 +0100 Subject: [PATCH 24/45] Improvements to ESP8266WebServer::sendContent Now makes only one call to .c_str() and using pointer tracking, rather that recalculating offset within the sending loop, to manage data window. Moved invariant code out of the sending loop body. --- libraries/ESP8266WebServer/src/ESP8266WebServer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index c709dcfdd..99068661c 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -178,17 +178,18 @@ void ESP8266WebServer::send(int code, const String& content_type, const String& } void ESP8266WebServer::sendContent(const String& content) { + const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; size_t size_to_send = content.length(); - size_t size_sent = 0; - while(size_to_send) { - const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; + const char* send_start = content.c_str(); + + while (size_to_send) { size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; - size_t sent = _currentClient.write(content.c_str() + size_sent, will_send); - size_to_send -= sent; - size_sent += sent; + size_t sent = _currentClient.write(send_start, will_send); if (sent == 0) { break; } + size_to_send -= sent; + send_start += sent; } } From 1cd99391c3b31e23a47955e922ffa7a6eac9f867 Mon Sep 17 00:00:00 2001 From: Martin Ayotte Date: Sat, 8 Aug 2015 15:25:08 -0400 Subject: [PATCH 25/45] integrate new version provided by Odometer --- cores/esp8266/core_esp8266_noniso.c | 72 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/cores/esp8266/core_esp8266_noniso.c b/cores/esp8266/core_esp8266_noniso.c index 0b53583fd..d5b9d82f2 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -148,7 +148,8 @@ char* ultoa(unsigned long value, char* result, int base) { } char * dtostrf(double number, signed char width, unsigned char prec, char *s) { - + bool negative = false; + if (isnan(number)) { strcpy(s, "nan"); return s; @@ -158,14 +159,17 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { return s; } - if (number > 4294967040.0 || number < -4294967040.0) { - strcpy(s, "ovf"); - return s; - } char* out = s; + + int fillme = width; // how many cells to fill for the integer part + if (prec > 0) { + fillme -= (prec+1); + } + // Handle negative numbers if (number < 0.0) { - *out++ = '-'; + negative = true; + fillme--; number = -number; } @@ -173,39 +177,47 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { // I optimized out most of the divisions double rounding = 2.0; for (uint8_t i = 0; i < prec; ++i) - rounding *= 10.0; - rounding = 1.0 / rounding; + rounding *= 10.0; + rounding = 1.0 / rounding; number += rounding; - - // Extract the integer part of the number and print it - unsigned long int_part = (unsigned long)number; - double remainder = number - (double)int_part; - out += sprintf(out, "%d", int_part); - - // Print the decimal point, but only if there are digits beyond - if (prec > 0) { - *out++ = '.'; + + // Figure out how big our number really is + double tenpow = 1.0; + int digitcount = 1; + while (number >= 10.0 * tenpow) { + tenpow *= 10.0; + digitcount++; } - // make sure the string is terminated before mesuring it length - *out = 0; - // Reduce minimum width accordingly - width -= strlen(s); - - // Print the digits after the decimal point + + number /= tenpow; + fillme -= digitcount; + + // Pad unused cells with spaces + while (fillme-- > 0) { + *out++ = ' '; + } + + // Handle negative sign + if (negative) *out++ = '-'; + + // Print the digits, and if necessary, the decimal point + digitcount += prec; int8_t digit = 0; - while (prec-- > 0) { - remainder *= 10.0; - digit = (int8_t)remainder; + while (digitcount-- > 0) { + digit = (int8_t)number; if (digit > 9) digit = 9; // insurance *out++ = (char)('0' | digit); - width--; - remainder -= digit; + if ((digitcount == prec) && (prec > 0)) { + *out++ = '.'; + } + number -= digit; + number *= 10.0; } - // add '0' to fill minimum width requirement - while (width-- > 0) *out++ = ' '; + // make sure the string is terminated *out = 0; return s; } + From b500a1f26a2df8cd06b32d9846c2d526a41eb921 Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 9 Aug 2015 14:10:20 -0500 Subject: [PATCH 26/45] Made changes to allow the compiler to compile without warnings. --- cores/esp8266/Tone.cpp | 2 ++ cores/esp8266/Updater.h | 2 +- cores/esp8266/cbuf.h | 4 ++-- cores/esp8266/core_esp8266_postmortem.c | 4 +++- cores/esp8266/core_esp8266_si2c.c | 3 +++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index f85ed404a..c704c50aa 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -35,10 +35,12 @@ #include "Arduino.h" #include "pins_arduino.h" +/* static int8_t toneBegin(uint8_t _pin) { //TODO implement tone return 0; } +*/ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { //TODO implement tone diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index be1a04dd6..eac84f466 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -108,12 +108,12 @@ class UpdaterClass { void _reset(); bool _writeBuffer(); + uint8_t _error; uint8_t *_buffer; size_t _bufferLen; size_t _size; uint32_t _startAddress; uint32_t _currentAddress; - uint8_t _error; }; extern UpdaterClass Update; diff --git a/cores/esp8266/cbuf.h b/cores/esp8266/cbuf.h index ceb6a7b6d..728547fad 100644 --- a/cores/esp8266/cbuf.h +++ b/cores/esp8266/cbuf.h @@ -70,7 +70,7 @@ class cbuf { size_t bytes_available = getSize(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; - if(_end < _begin && size_to_read > _bufend - _begin) { + if(_end < _begin && size_to_read > (size_t)(_bufend - _begin)) { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); _begin = _buf; @@ -95,7 +95,7 @@ class cbuf { size_t bytes_available = room(); size_t size_to_write = (size < bytes_available) ? size : bytes_available; size_t size_written = size_to_write; - if(_end > _begin && size_to_write > _bufend - _end) { + if(_end > _begin && size_to_write > (size_t)(_bufend - _end)) { size_t top_size = _bufend - _end; memcpy(_end, src, top_size); _end = _buf; diff --git a/cores/esp8266/core_esp8266_postmortem.c b/cores/esp8266/core_esp8266_postmortem.c index f5ede6c76..c34e5918d 100644 --- a/cores/esp8266/core_esp8266_postmortem.c +++ b/cores/esp8266/core_esp8266_postmortem.c @@ -35,7 +35,7 @@ static void uart_write_char_d(char c); static void uart0_write_char_d(char c); static void uart1_write_char_d(char c); static void print_stack(uint32_t start, uint32_t end); -static void print_pcs(uint32_t start, uint32_t end); +//static void print_pcs(uint32_t start, uint32_t end); void __wrap_system_restart_local() { register uint32_t sp asm("a1"); @@ -108,6 +108,7 @@ static void print_stack(uint32_t start, uint32_t end) { ets_printf("<<>>pc>>>\n"); @@ -122,6 +123,7 @@ static void print_pcs(uint32_t start, uint32_t end) { } ets_printf("<< Date: Mon, 10 Aug 2015 10:51:09 +0300 Subject: [PATCH 27/45] Update to SDK 1.3.0 --- libraries/ESP8266WiFi/src/ESP8266WiFi.cpp | 7 +- libraries/ESP8266WiFi/src/ESP8266WiFi.h | 14 +- tools/sdk/changelog.txt | 230 +++++++++++++--------- tools/sdk/include/espconn.h | 24 ++- tools/sdk/include/espnow.h | 10 +- tools/sdk/include/gpio.h | 2 +- tools/sdk/include/json/json.h | 0 tools/sdk/include/json/jsonparse.h | 0 tools/sdk/include/json/jsontree.h | 0 tools/sdk/include/upgrade.h | 5 +- tools/sdk/include/user_interface.h | 16 +- tools/sdk/lib/libat.a | Bin 205586 -> 208568 bytes tools/sdk/lib/libcrypto.a | Bin 55094 -> 55966 bytes tools/sdk/lib/libespnow.a | Bin 26732 -> 30906 bytes tools/sdk/lib/libjson.a | Bin 12934 -> 12934 bytes tools/sdk/lib/liblwip.a | Bin 311536 -> 319040 bytes tools/sdk/lib/liblwip_536.a | Bin 311524 -> 319028 bytes tools/sdk/lib/libmain.a | Bin 146252 -> 151290 bytes tools/sdk/lib/libnet80211.a | Bin 221570 -> 226500 bytes tools/sdk/lib/libphy.a | Bin 147470 -> 148084 bytes tools/sdk/lib/libpp.a | Bin 213572 -> 215552 bytes tools/sdk/lib/libsmartconfig.a | Bin 107454 -> 110388 bytes tools/sdk/lib/libssc.a | Bin 10640 -> 0 bytes tools/sdk/lib/libssl.a | Bin 196932 -> 198726 bytes tools/sdk/lib/libupgrade.a | Bin 17358 -> 23752 bytes tools/sdk/lib/libwpa.a | Bin 127178 -> 128300 bytes tools/sdk/lib/libwps.a | Bin 213960 -> 225684 bytes tools/sdk/version | 2 +- 28 files changed, 195 insertions(+), 115 deletions(-) mode change 100755 => 100644 tools/sdk/include/json/json.h mode change 100755 => 100644 tools/sdk/include/json/jsonparse.h mode change 100755 => 100644 tools/sdk/include/json/jsontree.h mode change 100755 => 100644 tools/sdk/include/upgrade.h delete mode 100755 tools/sdk/lib/libssc.a diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 5400f02d9..161b16f42 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -666,7 +666,7 @@ bool ESP8266WiFiClass::hostname(String aHostname) { //-------------------------------------------------------------- -void wifi_wps_status_cb(WPS_CB_STATUS_t status) +void wifi_wps_status_cb(wps_cb_status status) { DEBUGV("wps cb status: %d\r\n", status); switch (status) { @@ -682,6 +682,9 @@ void wifi_wps_status_cb(WPS_CB_STATUS_t status) case WPS_CB_ST_TIMEOUT: DEBUGV("wps TIMEOUT\n"); break; + case WPS_CB_ST_WEP: + DEBUGV("wps WEP\n"); + break; } // todo user function to get status @@ -715,7 +718,7 @@ bool ESP8266WiFiClass::beginWPSConfig(void) { return false; } - if(!wifi_set_wps_cb(&wifi_wps_status_cb)) { + if(!wifi_set_wps_cb((wps_st_cb_t) &wifi_wps_status_cb)) { DEBUGV("wps cb faild\n"); return false; } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 369892ecf..b8329c4fc 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -44,7 +44,7 @@ public: ESP8266WiFiClass(); void mode(WiFiMode); - + /** * Start Wifi connection * if passphrase is set the most secure supported mode will be automatically selected @@ -95,7 +95,7 @@ public: * param dns: Defined DNS */ void config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns); - + /* Configure access point * * param local_ip: access point IP @@ -336,19 +336,19 @@ public: /* * Start SmartConfig * - */ + */ void beginSmartConfig(); - + /* * Query SmartConfig status, to decide when stop config * - */ + */ bool smartConfigDone(); /* * Stop SmartConfig * - */ + */ void stopSmartConfig(); friend class WiFiClient; @@ -366,7 +366,7 @@ protected: bool _useApMode; bool _useClientMode; bool _useStaticIp; - + static bool _scanAsync; static bool _scanStarted; static bool _scanComplete; diff --git a/tools/sdk/changelog.txt b/tools/sdk/changelog.txt index 05d32ebb5..ad755f7a4 100644 --- a/tools/sdk/changelog.txt +++ b/tools/sdk/changelog.txt @@ -1,7 +1,53 @@ -esp_iot_sdk_v1.2.0_15_07_13_p4 Release Note +esp_iot_sdk_v1.3.0_15_08_08 Release Note +---------------------------------------- + +Resolved Issues(Bugs below are eligible for Bug Bounty Program): +1.Device can't connect to router after it gets ssid and password when using ESPTOUCH with router's ssid hidden. [冯智] +2.Format string of os_random can't be supported by atoi. [æ¨æœä½] +3.Optimized os_printf seems to have an issue on 4 bytes aligned and other valuable suggestions. [Andrey Filimonov] +4.SmartConfig can’t get IP address after connected to router. [孙大明] + +Optimization: +1.Memory optimization to save 12KBytes. +2.Optimize RF calibration to short the booting time,more details in documentation "2A-ESP8266__IOT_SDK_User_Manual" chapter "Appendix". +3.Optimize Wi-Fi function to solve issue that ESP8266 may fail to connect to a special router. +4.Optimize software timer to solve the a connecting problem.Please do not call "os_delay_us" or "while" or "for" to occupy CPU more than 10 ms in timer callback. +5.Optimize system_get_rst_info to obtain more accurate information about the start-up. +6.Optimize function of Wi-Fi scanning to be more reliable. +7.Optimize function of changing Wi-Fi mode to be more reliable. +8.Optimize WPS to improve connectivity.And WPS does not support WEP, it will return status "WPS_CB_ST_WEP". +9.Optimize Wi-Fi function to solve softAP multiple stations DHCP issue. +10.Optimize TCP in LAST_ACK status. +11.Optimize TLS to support SHA256, SHA384, SHA512. +12.Memory optimization during TLS hand-shaking. +13.Optimize OTA funtion to download big chunk of data. +14.Add CRC32 in OTA function.Folder "tools" in esp_iot_sdk has to be updated, otherwise OTA will fail. +15.Optimize mDNS to support both softAP and station interfaces. +16.Optimize ESP-NOW, more details in "Add APIs" +17.Update SmartConfig to version 2.4.7 +18.Remove "-O2" from makefile. +19.Optimize header files to improve compatibility, will not affect compilation. + +Add APIs: +1.system_soft_wdt_feed : feed software watchdog +2.wifi_softap_get_dhcps_lease:get IP range of ESP8266 softAP DHCP server +3.ESP-NOW APIs +esp_now_set_kok: set the secure key to encrypt ESP-NOW communication key +esp_now_register_send_cb: register ESP-NOW send callback +esp_now_unregister_send_cb: unregister ESP-NOW send callback + +AT_v0.40 Release Note: +Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. + +1.Add parameter in command "AT+CWSAP" to set the maximum number of connections allowed. + + + + +esp_iot_sdk_v1.2.0_15_07_13_p4 Release Note ------------------------------------------- -Here is a patch of memory optimization based on SDK_v1.2.0 +Here is a patch of memory optimization based on SDK_v1.2.0 1. It saved about 8KBytes memory. 2. It revised problem that change mode may cause memory leak. 3. Update SmartConfig to version 2.4.3 @@ -13,7 +59,7 @@ Thanks for your interest in Espressif Systems and ESP8266 ! -esp_iot_sdk_v1.2.0_15_07_09_p3 Release Note +esp_iot_sdk_v1.2.0_15_07_09_p3 Release Note ------------------------------------------- Here is a patch based on SDK_v1.2.0 solved problem that if AP’s SSID is hidden,ESPTOUCH may get wrong BSSID of AP and cause connection fail. @@ -24,7 +70,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.2.0_15_07_09_p2 Release Note +esp_iot_sdk_v1.2.0_15_07_09_p2 Release Note ------------------------------------------- Updated libssl again. To support SHA-256 and SHA-512. @@ -34,7 +80,7 @@ Thanks for your interest in Espressif Systems and ESP8266 ! -esp_iot_sdk_v1.2.0_15_07_08_p1 Release Note +esp_iot_sdk_v1.2.0_15_07_08_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.2.0 solved problem that abnormal SSL disconnection may cause reset. @@ -45,7 +91,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.2.0_15_07_03 Release Note +esp_iot_sdk_v1.2.0_15_07_03 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): 1.TLS server disconnect to ESP8266 may cause crash. [孙新虎] @@ -77,10 +123,10 @@ esp_now_add_peer: add an ESP-NOW peer esp_now_del_peer: delete an ESP-NOW peer esp_now_set_self_role: set ESP-NOW role of device itself esp_now_get_self_role: get ESP-NOW role of device itself -esp_now_set_peer_role: set ESP-NOW role about another device -esp_now_get_peer_role: get ESP-NOW role about another device -esp_now_set_peer_key: set ESP-NOW key of a device -esp_now_get_peer_key: get ESP-NOW key of a device +esp_now_set_peer_role: set ESP-NOW role about another device +esp_now_get_peer_role: get ESP-NOW role about another device +esp_now_set_peer_key: set ESP-NOW key of a device +esp_now_get_peer_key: get ESP-NOW key of a device 2. WPS APIs wifi_wps_enable : enable WPS function @@ -92,9 +138,9 @@ wifi_set_wps_cb: set WPS callback system_soft_wdt_stop: stop software watchdog system_soft_wdt_restart: restart software watchdog -4.sntp_get_timezone: get SNTP timezone +4.sntp_get_timezone: get SNTP timezone -AT_v0.30 Release Note: +AT_v0.30 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. 1.Command "AT+CWSTARTSMART" need not parameter any more, SmartConfig type can be got automatically. @@ -104,15 +150,15 @@ Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than -esp_iot_sdk_1.1.2_15_06_25_p2 Release Note +esp_iot_sdk_1.1.2_15_06_25_p2 Release Note ------------------------------------------- -Here is a patch based on SDK_v1.1.2 solved problem of abnormal current during modem-sleep. +Here is a patch based on SDK_v1.1.2 solved problem of abnormal current during modem-sleep. Please replace the lib in \esp_iot_sdk_v1.1.2\lib Sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_24_p1.1 Release Note +esp_iot_sdk_v1.1.2_15_06_24_p1.1 Release Note ------------------------------------------- Here is a patch for AT firmware based on SDK_v1.1.2 resolved issue that AT user parameter area was located in wrong address,it may cause WiFi configuration goes wrong. @@ -133,7 +179,7 @@ can not FOTA upgrade,please don‘t call “AT+CIPUPDATE†So sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_16_p1 Release Note +esp_iot_sdk_v1.1.2_15_06_16_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.1.2 resolved issue that "wifi_station_scan" cause loss of wireless connectivity. @@ -144,7 +190,7 @@ Sorry for the inconvenience. -esp_iot_sdk_v1.1.2_15_06_12 Release Note +esp_iot_sdk_v1.1.2_15_06_12 Release Note ------------------------------------------- Optimization: @@ -165,7 +211,7 @@ Add Documentation: -esp_iot_sdk_v1.1.1_15_06_05 Release Note +esp_iot_sdk_v1.1.1_15_06_05 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): @@ -175,7 +221,7 @@ Resolved Issues(Bugs below are eligible for Bug Bounty Program): Optimization: 1.Update JSON parser to handle with illegal parameter and illegal calling progress. -2.Add parameter of user_esp_platform_check_ip in user_websever.c which in IOT_Demo. +2.Add parameter of user_esp_platform_check_ip in user_websever.c which in IOT_Demo. 3.Update UART driver to solve the problem that if send data through UART while ESP8266 startup may cause UART invalid. 4.Update smartconfig to version 2.2, corresponding phone APP v0.3.2. And update the description and example of smartconfig_start in document "2C_ESP8266__Programming Guide" 5.Update code in iram to solve the problem that space for text is not enough. @@ -185,12 +231,12 @@ Optimization: 9.Remove useless driver code in IOT_Demo 10.Update IOT_Demo to use the latest PWM driver in light demo. 11.Provide liblwip_536.a of which MSS size is 536 -12.Revised issue that boot may fail when 80Mhz SPI clock selected -13.Update esp_init_data_default.bin about RF option in \esp_iot_sdk\bin +12.Revised issue that boot may fail when 80Mhz SPI clock selected +13.Update esp_init_data_default.bin about RF option in \esp_iot_sdk\bin Added APIs: -1.PWM APIs: -Updated: pwm_init,add parameter to set PWM channel and GPIO pin +1.PWM APIs: +Updated: pwm_init,add parameter to set PWM channel and GPIO pin Added: (1)get_pwm_version:get version information of PWM driver (2)pwm_set_period:set PWM period @@ -200,23 +246,23 @@ Deleted: (2)pwm_get_freq:get PWM frequency 2.Read/write flash with protection (1)system_param_save_with_protect:write data into flash with backup protection -(2)system_param_load:read data which saved into flash with backup protection +(2)system_param_load:read data which saved into flash with backup protection 3.system_get_rst_info:get information about current startup,it is a normal startup or watch dog reset 4.at_response:set AT response 5.at_register_response_func:register a callback for user-define AT response. -6.Update document "2C_ESP8266__Programming Guide" to add description of interrupt definition in ets_sys.h +6.Update document "2C_ESP8266__Programming Guide" to add description of interrupt definition in ets_sys.h -AT_v0.25 Release Note: +AT_v0.25 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. Optimization: 1.Add parameter about UDP local port in command "AT+SAVETRANSLINK" -Added AT command: +Added AT command: 1.AT+CIPDINFO:set configuration whether show remote IP and remote port with “+IPD†or not -esp_iot_sdk_v1.1.0_15_05_27_p1 Release Note +esp_iot_sdk_v1.1.0_15_05_27_p1 Release Note ------------------------------------------- Here is a patch based on SDK_v1.1.0 resolved issues below: @@ -226,7 +272,7 @@ Here is a patch based on SDK_v1.1.0 resolved issues below: Sorry for the inconvenience. -esp_iot_sdk_v1.1.0_15_05_22 Release Note +esp_iot_sdk_v1.1.0_15_05_22 Release Note ---------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): 1.Predictable TLS random values leads to insecure connections [projectgus] @@ -257,26 +303,26 @@ Added APIs: 3.system_uart_de_swap : disable UART0 swap 4.system_get_flash_size_map: get flash size and flash map 5.system_phy_set_max_tpw : set maximum RF TX power -6.system_phy_set_tpw_via_vdd33 :set RF TX power according to VDD33 +6.system_phy_set_tpw_via_vdd33 :set RF TX power according to VDD33 7.system_phy_set_rfoption : set RF option 8.wifi_station_get_rssi:get rssi of AP which ESP8266 station connected to   9.wifi_softap_get_station_num :get number count of stations connected to ESP8266 soft-AP -AT_v0.24 Release Note: +AT_v0.24 Release Note: Note: For AT firmware to support FOTA, flash size need to be 1024KB or more than that. Optimization: 1.Disable data echo of command "AT+CIPSEND" 2.Optimized "AT+CWJAP?" to get channel and rssi 3.ESP8266 station IP can only be got and inquiried after ESP8266 station connected to AP -Added AT command: +Added AT command: 1.AT+RFPOWER :set maximum RF TX power -2.AT+RFVDD : set RF TX power according to VDD33 +2.AT+RFVDD : set RF TX power according to VDD33 自从ä¹é‘«ä¿¡æ¯ç§‘技于 2015-03-20 å¯åЍ Bug èµé‡‘计划以æ¥ï¼Œæˆ‘们收到了一些é‡è¦é—®é¢˜å馈åŠå»ºè®®ï¼Œæ„Ÿè°¢ä¸–界å„地的开å‘者对 ESP8266的关注,推动我们的软件更进一步,技术支æŒå›¢é˜Ÿä¹Ÿå¾—到迅速地æˆé•¿ã€‚我们将在如下å‘å¸ƒæ—¥å¿—ä¸­ç‚¹åæ„Ÿè°¢æ‚¨çš„帮助。 -esp_iot_sdk_v1.1.0_15_05_22 Release Note +esp_iot_sdk_v1.1.0_15_05_22 Release Note 修正问题(符åˆä¹é‘« Bug èµé‡‘计划): 1.éšæœºå€¼çš„生æˆå¯ä»¥è¢«é¢„æµ‹ï¼Œå®Œå–„éšæœºæ•°çš„产生机制 [projectgus] 2.softAP+station 模å¼ä¸‹ï¼Œå¯èƒ½è¿žæŽ¥ ESP8266 soft-AP 失败 [智æ·é€š] @@ -307,11 +353,11 @@ esp_iot_sdk_v1.1.0_15_05_22 Release Note 4.system_get_flash_size_map: 查询 flash size å’Œ flash map 5.system_phy_set_max_tpw : 设置 RF TX power 最大值 6.system_phy_set_tpw_via_vdd33 ï¼šæ ¹æ® VDD33 设置 RF TX power -7.system_phy_set_rfoption : 设置 RF +7.system_phy_set_rfoption : 设置 RF 8.wifi_station_get_rssi:查询 ESP8266 station 连接的 AP 的信å·å¼ºåº¦   9.wifi_softap_get_station_num :查询连接到 ESP8266 soft-AP çš„ station æ•°ç›® -AT_v0.24 Release Note: +AT_v0.24 Release Note: 注æ„:è¿è¡Œ AT 固件,支æŒäº‘端å‡çº§ï¼Œè¯·ä½¿ç”¨ 1024KB 或以上容é‡çš„ flash 优化: @@ -319,7 +365,7 @@ AT_v0.24 Release Note: 2.优化"AT+CWJAP?" å¯èŽ·å¾—ä¿¡é“和信å·å¼ºåº¦ 3.必须在 ESP8266 station 连接到 AP åŽï¼Œæ‰èƒ½æŸ¥è¯¢åˆ° ESP8266 station IP -新增 AT command: +新增 AT command: 1.AT+RFPOWER :设置 RF TX power 最大值 2.AT+RFVDD : æ ¹æ® VDD33 设置 RF TX power @@ -327,68 +373,68 @@ Thanks for your interest in ESP8266 ! esp_iot_sdk_v1.0.1_15_05_04_p1 ------------------------------------------- -Here is a patch for station+softAP issue that users may have, based on SDK_v1.0.1, +Here is a patch for station+softAP issue that users may have, based on SDK_v1.0.1, solved problem that connect to ESP8266 softAP may fail in station+softAP mode. Sorry for the inconvenience. -esp_iot_sdk_v1.0.1_15_04_24 Release Note +esp_iot_sdk_v1.0.1_15_04_24 Release Note ------------------------------------------- Resolved Issues(Bugs below are eligible for Bug Bounty Program): -1. SSL connection may fail if SSL packet size larger than 2kBytes [PeteW ] -2. UDP remote IP to be 0.0.0.0 may cause reset [Jerry S] -3. Optimize wifi_get_ip_info to fix loss of wireless connectivity problem -4. Air-Kiss restart [Orgmar] +1. SSL connection may fail if SSL packet size larger than 2kBytes [PeteW ] +2. UDP remote IP to be 0.0.0.0 may cause reset [Jerry S] +3. Optimize wifi_get_ip_info to fix loss of wireless connectivity problem +4. Air-Kiss restart [Orgmar] Optimization: -1. Optimized IOT_Espressif_EspTouch.APK (apply for access from Espressif) for improved compatibility. [???] -2. TCP server can not open again immediately with the same port [624908539] -3. Update UART driver for parity bit value may be incorrect [1062583993] -4. Add define of “ICACHE_RODATA_ATTR†for Symbol 'ICACHE_RODATA_ATTR' could not be resolved. [???] -5. Add API wifi_softap_dhcps_set_offer_option to enable/disable ESP8266 softAP DHCP server default gateway. [xyz769] -6. AT register_uart_rx_intr may enter callback twice. [???] -7.optimize document that WPA password length range : 8 ~ 64 bytes [785057041] -8. ESP8266 softAP DHCP server record 8 DHCP client's IP at most [ygjeon] -9. To set static IP (wifi_set_ip_info) has to disable DHCP first(wifi_softap_dhcps_stop or wifi_station_dhcpc_stop) +1. Optimized IOT_Espressif_EspTouch.APK (apply for access from Espressif) for improved compatibility. [???] +2. TCP server can not open again immediately with the same port [624908539] +3. Update UART driver for parity bit value may be incorrect [1062583993] +4. Add define of “ICACHE_RODATA_ATTR†for Symbol 'ICACHE_RODATA_ATTR' could not be resolved. [???] +5. Add API wifi_softap_dhcps_set_offer_option to enable/disable ESP8266 softAP DHCP server default gateway. [xyz769] +6. AT register_uart_rx_intr may enter callback twice. [???] +7.optimize document that WPA password length range : 8 ~ 64 bytes [785057041] +8. ESP8266 softAP DHCP server record 8 DHCP client's IP at most [ygjeon] +9. To set static IP (wifi_set_ip_info) has to disable DHCP first(wifi_softap_dhcps_stop or wifi_station_dhcpc_stop) 10.Add example of wifi_softap_set_dhcps_lease -11. smartconfig_start can only be called in ESP8266 station mode +11. smartconfig_start can only be called in ESP8266 station mode -Added APIs: -1. Wi-Fi related APIs: -wifi_station_set_reconnect_policy: enable/disable reconnect when ESP8266 disconnect from router,default to be enable reconnect. -wifi_set_event_handler_cb: set event handler of ESP8266 softAP or station status change. -wifi_softap_dhcps_set_offer_option: enable/disable get router information from ESP8266 softAP, default to be enable. -2. SNTP APIs: -sntp_get_current_timestamp: get current timestamp from Jan 01, 1970, 00:00 (GMT) -sntp_get_real_time: char,get real time (GTM + 8 time zone) -sntp_init: initialize SNTP -sntp_stop: stop SNTP -sntp_setserver: set SNTP server by IP -sntp_getserver: get SNTP server IP -sntp_setservername: set SNTP server by domain name -sntp_getservername: get domain name of SNTP server set by sntp_setservername -3. MDNS APIs: -espconn_mdns_init: initialize mDNS -espconn_mdns_close: close mDNS -espconn_mdns_server_register: register mDNS server -espconn_mdns_server_unregister: unregister mDNS server -espconn_mdns_get_servername: get mDNS server name -espconn_mdns_set_servername: set mDNS server name -espconn_mdns_set_hostname: get mDNS host name -espconn_mdns_get_hostname: set mDNS host name -espconn_mdns_disable: disable mDNS -espconn_mdns_enable: endisable mDNS +Added APIs: +1. Wi-Fi related APIs: +wifi_station_set_reconnect_policy: enable/disable reconnect when ESP8266 disconnect from router,default to be enable reconnect. +wifi_set_event_handler_cb: set event handler of ESP8266 softAP or station status change. +wifi_softap_dhcps_set_offer_option: enable/disable get router information from ESP8266 softAP, default to be enable. +2. SNTP APIs: +sntp_get_current_timestamp: get current timestamp from Jan 01, 1970, 00:00 (GMT) +sntp_get_real_time: char,get real time (GTM + 8 time zone) +sntp_init: initialize SNTP +sntp_stop: stop SNTP +sntp_setserver: set SNTP server by IP +sntp_getserver: get SNTP server IP +sntp_setservername: set SNTP server by domain name +sntp_getservername: get domain name of SNTP server set by sntp_setservername +3. MDNS APIs: +espconn_mdns_init: initialize mDNS +espconn_mdns_close: close mDNS +espconn_mdns_server_register: register mDNS server +espconn_mdns_server_unregister: unregister mDNS server +espconn_mdns_get_servername: get mDNS server name +espconn_mdns_set_servername: set mDNS server name +espconn_mdns_set_hostname: get mDNS host name +espconn_mdns_get_hostname: set mDNS host name +espconn_mdns_disable: disable mDNS +espconn_mdns_enable: endisable mDNS -AT_v0.23 Release Note: -Optimized: -1.AT+CWJAP add parameter "bssid", for several APs may have the same SSID +AT_v0.23 Release Note: +Optimized: +1.AT+CWJAP add parameter "bssid", for several APs may have the same SSID -New AT commands: -1. AT+CIPSENDBUF: write data into TCP-send-buffer; non-blocking. Background task automatically handles transmission. Has much higher throughput. -2. AT+CIPBUFRESET: resets segment count in TCP-send-buffer -3. AT+CIPBUFSTATUS: checks status of TCP-send-buffer -4. AT+CIPCHECKSEGID: checks if a specific segment in TCP-send-buffer has sent successfully +New AT commands: +1. AT+CIPSENDBUF: write data into TCP-send-buffer; non-blocking. Background task automatically handles transmission. Has much higher throughput. +2. AT+CIPBUFRESET: resets segment count in TCP-send-buffer +3. AT+CIPBUFSTATUS: checks status of TCP-send-buffer +4. AT+CIPCHECKSEGID: checks if a specific segment in TCP-send-buffer has sent successfully @@ -396,7 +442,7 @@ esp_iot_sdk_v1.0.1_b2_15_04_10 release note ------------------------------------------- Fix bugs: -1.Call espconn_sent to send UDP packet in user_init cause reset.[BBP#2 reporter (????)] +1.Call espconn_sent to send UDP packet in user_init cause reset.[BBP#2 reporter (????)] 2.UART & FlowControl issue: send data to FIFO without CTS flag will cause WDT [BBP#11 reporter (pvxx)] 3.SSL issue,add an API (espconn_secure_set_size) to set SSL buffer size [BBP#29 reporter (PeteW)] 4.UDP broadcast issue in WEP @@ -485,21 +531,21 @@ Add AT commands: XXX_DEF: default, set configuration and save it to Flash 2. Add SmartConfig in AT: AT+CWSTARTSMART/AT+CWSTOPSMART: start / stop SmartConfig - Notice: please refer to the document, call "AT+CWSTOPSMART" to stop SmartConfig first since "AT+CWSTARTSMART", then call other AT commands. Don't call any other AT commands during SmartConfig. + Notice: please refer to the document, call "AT+CWSTOPSMART" to stop SmartConfig first since "AT+CWSTARTSMART", then call other AT commands. Don't call any other AT commands during SmartConfig. 2. AT+SAVETRANSLINK: save transparent transmission link to Flash; Note:AT+CIPMODE=1 set to enter transparent transmission mode, won't save to Flash. Add AT APIs -1. at_customLinkMax: set the max link that allowed, most can be 10; if you want to set it, please set it before at_init; if you didn't set it, the max link allowed is 5 by default. -2. at_enter_special_state/ at_leave_special_state:Enter/leave AT processing state. In processing state, AT core will return "busy" for any further AT commands. +1. at_customLinkMax: set the max link that allowed, most can be 10; if you want to set it, please set it before at_init; if you didn't set it, the max link allowed is 5 by default. +2. at_enter_special_state/ at_leave_special_state:Enter/leave AT processing state. In processing state, AT core will return "busy" for any further AT commands. 3. at_set_custom_info:set custom version information of AT which can be got by AT+GMR; 4. at_get_version:get version information of AT lib . Optimize 1. Add UDP remote ip and remote port is allowed to be parameters of "AT+CIPSEND" 2. Move "AT+CIUPDATE" from lib to AT "demo\esp_iot_sdk\examples\at", AT demo shows how to upgrade AT firmware from a local server. Notice that AT upgrade the bin files name have to be "user1.bin" and "user2.bin". -3. Optimize "AT+CIPSTA", add gateway and netmask as parameters +3. Optimize "AT+CIPSTA", add gateway and netmask as parameters 4. Optimize transparent transmission. esp_iot_sdk_v0.9.5_15_01_22 Release Note @@ -532,7 +578,7 @@ Optimize: 6. update to boot v1.2 to support new format user.bin; 7. update ARP 8. update SSL -9. revised system_deep_sleep,system_deep_sleep(0),set no wake up timer,connect a GPIO to pin RST, the chip will wake up by a falling-edge on pin RST +9. revised system_deep_sleep,system_deep_sleep(0),set no wake up timer,connect a GPIO to pin RST, the chip will wake up by a falling-edge on pin RST esp_iot_sdk_v0.9.4_14_12_19 Release Note ---------------------------------------- diff --git a/tools/sdk/include/espconn.h b/tools/sdk/include/espconn.h index 82212103b..fc4be05c5 100644 --- a/tools/sdk/include/espconn.h +++ b/tools/sdk/include/espconn.h @@ -24,7 +24,7 @@ typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); #define ESPCONN_ISCONN -15 /* Already connected. */ #define ESPCONN_HANDSHAKE -28 /* ssl handshake failed */ -#define ESPCONN_PROTO_MSG -61 /* ssl application invalid */ +#define ESPCONN_SSL_INVALID_DATA -61 /* ssl application invalid */ /** Protocol family and type of the espconn */ enum espconn_type { @@ -267,6 +267,17 @@ sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_ sint8 espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn); +/****************************************************************************** + * FunctionName : espconn_send + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_send(struct espconn *espconn, uint8 *psent, uint16 length); + /****************************************************************************** * FunctionName : espconn_sent * Description : sent data for client or server @@ -426,6 +437,17 @@ sint8 espconn_secure_connect(struct espconn *espconn); sint8 espconn_secure_disconnect(struct espconn *espconn); +/****************************************************************************** + * FunctionName : espconn_secure_send + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_send(struct espconn *espconn, uint8 *psent, uint16 length); + /****************************************************************************** * FunctionName : espconn_encry_sent * Description : sent data for client or server diff --git a/tools/sdk/include/espnow.h b/tools/sdk/include/espnow.h index 8eeb65c12..3bb6f8339 100644 --- a/tools/sdk/include/espnow.h +++ b/tools/sdk/include/espnow.h @@ -13,12 +13,16 @@ enum esp_now_role { ESP_NOW_ROLE_MAX, }; -typedef void (*esp_now_cb_t)(u8 *mac_addr, u8 *data, u8 len); +typedef void (*esp_now_recv_cb_t)(u8 *mac_addr, u8 *data, u8 len); +typedef void (*esp_now_send_cb_t)(u8 *mac_addr, u8 status); int esp_now_init(void); int esp_now_deinit(void); -int esp_now_register_recv_cb(esp_now_cb_t cb); +int esp_now_register_send_cb(esp_now_send_cb_t cb); +int esp_now_unregister_send_cb(void); + +int esp_now_register_recv_cb(esp_now_recv_cb_t cb); int esp_now_unregister_recv_cb(void); int esp_now_send(u8 *da, u8 *data, int len); @@ -44,4 +48,6 @@ int esp_now_is_peer_exist(u8 *mac_addr); int esp_now_get_cnt_info(u8 *all_cnt, u8 *encrypt_cnt); +int esp_now_set_kok(u8 *key, u8 len); + #endif diff --git a/tools/sdk/include/gpio.h b/tools/sdk/include/gpio.h index bebd2001b..abcc4bed3 100644 --- a/tools/sdk/include/gpio.h +++ b/tools/sdk/include/gpio.h @@ -23,7 +23,7 @@ typedef enum { } GPIO_INT_TYPE; #define GPIO_OUTPUT_SET(gpio_no, bit_value) \ - gpio_output_set(bit_value<>gpio_no)&BIT0) diff --git a/tools/sdk/include/json/json.h b/tools/sdk/include/json/json.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/json/jsonparse.h b/tools/sdk/include/json/jsonparse.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/json/jsontree.h b/tools/sdk/include/json/jsontree.h old mode 100755 new mode 100644 diff --git a/tools/sdk/include/upgrade.h b/tools/sdk/include/upgrade.h old mode 100755 new mode 100644 index 3b6bb70ff..cddf8397c --- a/tools/sdk/include/upgrade.h +++ b/tools/sdk/include/upgrade.h @@ -2,6 +2,7 @@ #define __UPGRADE_H__ #define SPI_FLASH_SEC_SIZE 4096 +#define LIMIT_ERASE_SIZE 0x10000 #define USER_BIN1 0x00 #define USER_BIN2 0x01 @@ -37,14 +38,12 @@ struct upgrade_server_info { #define UPGRADE_FLAG_START 0x01 #define UPGRADE_FLAG_FINISH 0x02 -//bool system_upgrade_start(struct upgrade_server_info *server); -bool system_upgrade_start_ssl(struct upgrade_server_info *server); void system_upgrade_init(); void system_upgrade_deinit(); bool system_upgrade(uint8 *data, uint16 len); #ifdef UPGRADE_SSL_ENABLE -bool system_upgrade_start_ssl(struct upgrade_server_info *server); +bool system_upgrade_start_ssl(struct upgrade_server_info *server); // not supported now #else bool system_upgrade_start(struct upgrade_server_info *server); #endif diff --git a/tools/sdk/include/user_interface.h b/tools/sdk/include/user_interface.h index c4ab5ce75..672ee7ad2 100644 --- a/tools/sdk/include/user_interface.h +++ b/tools/sdk/include/user_interface.h @@ -28,10 +28,11 @@ enum rst_reason { REASON_EXCEPTION_RST = 2, REASON_SOFT_WDT_RST = 3, REASON_SOFT_RESTART = 4, - REASON_DEEP_SLEEP_AWAKE = 5 + REASON_DEEP_SLEEP_AWAKE = 5, + REASON_EXT_SYS_RST = 6 }; -struct rst_info { +struct rst_info{ uint32 reason; uint32 exccause; uint32 epc1; @@ -137,6 +138,7 @@ bool system_param_load(uint16 start_sec, uint16 offset, void *param, uint16 len) void system_soft_wdt_stop(void); void system_soft_wdt_restart(void); +void system_soft_wdt_feed(void); #define NULL_MODE 0x00 #define STATION_MODE 0x01 @@ -283,6 +285,7 @@ void wifi_softap_free_station_info(void); bool wifi_softap_dhcps_start(void); bool wifi_softap_dhcps_stop(void); bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); +bool wifi_softap_get_dhcps_lease(struct dhcps_lease *please); enum dhcp_status wifi_softap_dhcps_status(void); bool wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg); @@ -430,17 +433,18 @@ typedef enum wps_type { WPS_TYPE_MAX } WPS_TYPE_t; -typedef enum wps_cb_status { +enum wps_cb_status { WPS_CB_ST_SUCCESS = 0, WPS_CB_ST_FAILED, - WPS_CB_ST_TIMEOUT -} WPS_CB_STATUS_t; + WPS_CB_ST_TIMEOUT, + WPS_CB_ST_WEP, +}; bool wifi_wps_enable(WPS_TYPE_t wps_type); bool wifi_wps_disable(void); bool wifi_wps_start(void); -typedef void (*wps_st_cb_t)(WPS_CB_STATUS_t status); +typedef void (*wps_st_cb_t)(int status); bool wifi_set_wps_cb(wps_st_cb_t cb); #endif diff --git a/tools/sdk/lib/libat.a b/tools/sdk/lib/libat.a index f37884c3ef67e9a94d7f2a6b4dc844603973d945..eafb08de094c06cfe79fca0b4da049f8b95c99a3 100644 GIT binary patch literal 208568 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

wRvCFIs*PR0c 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 literal 205586 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