1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-25 20:02:37 +03:00

Merge pull request #6 from esp8266/esp8266

pull master
This commit is contained in:
Me No Dev 2015-10-01 18:53:52 +03:00
commit 2a56cefcb0
58 changed files with 2708 additions and 328 deletions

View File

@ -112,13 +112,22 @@ generic.menu.FlashSize.2M.build.spiffs_end=0x1FB000
generic.menu.FlashSize.2M.build.spiffs_blocksize=8192
generic.menu.FlashSize.2M.upload.maximum_size=1044464
generic.menu.FlashSize.4M=4M (3M SPIFFS)
generic.menu.FlashSize.4M.build.flash_size=4M
generic.menu.FlashSize.4M.build.flash_ld=eagle.flash.4m.ld
generic.menu.FlashSize.4M.build.spiffs_start=0x100000
generic.menu.FlashSize.4M.build.spiffs_end=0x3FB000
generic.menu.FlashSize.4M.build.spiffs_blocksize=8192
generic.menu.FlashSize.4M.upload.maximum_size=1044464
generic.menu.FlashSize.4M1M=4M (1M SPIFFS)
generic.menu.FlashSize.4M1M.build.flash_size=4M
generic.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld
generic.menu.FlashSize.4M1M.build.spiffs_start=0x300000
generic.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000
generic.menu.FlashSize.4M1M.build.spiffs_blocksize=8192
generic.menu.FlashSize.4M1M.build.spiffs_pagesize=256
generic.menu.FlashSize.4M1M.upload.maximum_size=1044464
generic.menu.FlashSize.4M3M=4M (3M SPIFFS)
generic.menu.FlashSize.4M3M.build.flash_size=4M
generic.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld
generic.menu.FlashSize.4M3M.build.spiffs_start=0x100000
generic.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000
generic.menu.FlashSize.4M3M.build.spiffs_blocksize=8192
generic.menu.FlashSize.4M3M.upload.maximum_size=1044464
# disabled because espressif's bootloader refuses to write above 4M
# generic.menu.FlashSize.8M=8M (7M SPIFFS)
@ -154,11 +163,6 @@ huzzah.build.variant=adafruit
huzzah.build.flash_mode=qio
huzzah.build.flash_size=4M
huzzah.build.flash_freq=40
huzzah.build.flash_ld=eagle.flash.4m.ld
huzzah.build.spiffs_start=0x100000
huzzah.build.spiffs_end=0x3FB000
huzzah.build.spiffs_pagesize=256
huzzah.build.spiffs_blocksize=8192
huzzah.menu.CpuFrequency.80=80 MHz
huzzah.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -176,6 +180,22 @@ huzzah.menu.UploadSpeed.256000.upload.speed=256000
huzzah.menu.UploadSpeed.921600=921600
huzzah.menu.UploadSpeed.921600.upload.speed=921600
huzzah.menu.FlashSize.4M3M=4M (3M SPIFFS)
huzzah.menu.FlashSize.4M3M.build.flash_size=4M
huzzah.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld
huzzah.menu.FlashSize.4M3M.build.spiffs_start=0x100000
huzzah.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000
huzzah.menu.FlashSize.4M3M.build.spiffs_blocksize=8192
huzzah.menu.FlashSize.4M3M.build.spiffs_pagesize=256
huzzah.menu.FlashSize.4M1M=4M (1M SPIFFS)
huzzah.menu.FlashSize.4M1M.build.flash_size=4M
huzzah.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld
huzzah.menu.FlashSize.4M1M.build.spiffs_start=0x300000
huzzah.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000
huzzah.menu.FlashSize.4M1M.build.spiffs_blocksize=8192
huzzah.menu.FlashSize.4M1M.build.spiffs_pagesize=256
##############################################################
nodemcu.name=NodeMCU 0.9 (ESP-12 Module)
@ -196,11 +216,6 @@ nodemcu.build.variant=nodemcu
nodemcu.build.flash_mode=qio
nodemcu.build.flash_size=4M
nodemcu.build.flash_freq=40
nodemcu.build.flash_ld=eagle.flash.4m.ld
nodemcu.build.spiffs_start=0x100000
nodemcu.build.spiffs_end=0x3FB000
nodemcu.build.spiffs_pagesize=256
nodemcu.build.spiffs_blocksize=8192
nodemcu.menu.CpuFrequency.80=80 MHz
nodemcu.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -227,6 +242,22 @@ nodemcu.menu.UploadSpeed.512000.upload.speed=512000
nodemcu.menu.UploadSpeed.921600=921600
nodemcu.menu.UploadSpeed.921600.upload.speed=921600
nodemcu.menu.FlashSize.4M3M=4M (3M SPIFFS)
nodemcu.menu.FlashSize.4M3M.build.flash_size=4M
nodemcu.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld
nodemcu.menu.FlashSize.4M3M.build.spiffs_start=0x100000
nodemcu.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000
nodemcu.menu.FlashSize.4M3M.build.spiffs_blocksize=8192
nodemcu.menu.FlashSize.4M3M.build.spiffs_pagesize=256
nodemcu.menu.FlashSize.4M1M=4M (1M SPIFFS)
nodemcu.menu.FlashSize.4M1M.build.flash_size=4M
nodemcu.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld
nodemcu.menu.FlashSize.4M1M.build.spiffs_start=0x300000
nodemcu.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000
nodemcu.menu.FlashSize.4M1M.build.spiffs_blocksize=8192
nodemcu.menu.FlashSize.4M1M.build.spiffs_pagesize=256
##############################################################
nodemcuv2.name=NodeMCU 1.0 (ESP-12E Module)
@ -247,11 +278,6 @@ nodemcuv2.build.variant=nodemcu
nodemcuv2.build.flash_mode=dio
nodemcuv2.build.flash_size=4M
nodemcuv2.build.flash_freq=40
nodemcuv2.build.flash_ld=eagle.flash.4m.ld
nodemcuv2.build.spiffs_start=0x100000
nodemcuv2.build.spiffs_end=0x3FB000
nodemcuv2.build.spiffs_pagesize=256
nodemcuv2.build.spiffs_blocksize=8192
nodemcuv2.menu.CpuFrequency.80=80 MHz
nodemcuv2.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -278,6 +304,23 @@ nodemcuv2.menu.UploadSpeed.512000.upload.speed=512000
nodemcuv2.menu.UploadSpeed.921600=921600
nodemcuv2.menu.UploadSpeed.921600.upload.speed=921600
nodemcuv2.menu.FlashSize.4M3M=4M (3M SPIFFS)
nodemcuv2.menu.FlashSize.4M3M.build.flash_size=4M
nodemcuv2.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld
nodemcuv2.menu.FlashSize.4M3M.build.spiffs_start=0x100000
nodemcuv2.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000
nodemcuv2.menu.FlashSize.4M3M.build.spiffs_blocksize=8192
nodemcuv2.menu.FlashSize.4M3M.build.spiffs_pagesize=256
nodemcuv2.menu.FlashSize.4M1M=4M (1M SPIFFS)
nodemcuv2.menu.FlashSize.4M1M.build.flash_size=4M
nodemcuv2.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld
nodemcuv2.menu.FlashSize.4M1M.build.spiffs_start=0x300000
nodemcuv2.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000
nodemcuv2.menu.FlashSize.4M1M.build.spiffs_blocksize=8192
nodemcuv2.menu.FlashSize.4M1M.build.spiffs_pagesize=256
##############################################################
modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV)
@ -355,6 +398,7 @@ thing.build.flash_freq=40
thing.build.spiffs_start=0x6B000
thing.build.spiffs_end=0x7B000
thing.build.spiffs_blocksize=4096
thing.build.spiffs_pagesize=256
thing.menu.CpuFrequency.80=80 MHz
thing.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -400,11 +444,6 @@ esp210.build.variant=generic
esp210.build.flash_mode=qio
esp210.build.flash_size=4M
esp210.build.flash_freq=40
esp210.build.flash_ld=eagle.flash.4m.ld
esp210.build.spiffs_start=0x100000
esp210.build.spiffs_end=0x3FB000
esp210.build.spiffs_pagesize=256
esp210.build.spiffs_blocksize=8192
esp210.menu.CpuFrequency.80=80 MHz
esp210.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -429,6 +468,22 @@ esp210.menu.UploadSpeed.512000.upload.speed=512000
esp210.menu.UploadSpeed.921600=921600
esp210.menu.UploadSpeed.921600.upload.speed=921600
esp210.menu.FlashSize.4M3M=4M (3M SPIFFS)
esp210.menu.FlashSize.4M3M.build.flash_size=4M
esp210.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld
esp210.menu.FlashSize.4M3M.build.spiffs_start=0x100000
esp210.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000
esp210.menu.FlashSize.4M3M.build.spiffs_blocksize=8192
esp210.menu.FlashSize.4M3M.build.spiffs_pagesize=256
esp210.menu.FlashSize.4M1M=4M (1M SPIFFS)
esp210.menu.FlashSize.4M1M.build.flash_size=4M
esp210.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld
esp210.menu.FlashSize.4M1M.build.spiffs_start=0x300000
esp210.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000
esp210.menu.FlashSize.4M1M.build.spiffs_blocksize=8192
esp210.menu.FlashSize.4M1M.build.spiffs_pagesize=256
##############################################################
# wifio.name=Wifio
#

View File

@ -280,6 +280,8 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
return (2_MB);
case 0x1440EF: // W25Q80
return (1_MB);
case 0x1340EF: // W25Q40
return (512_kB);
default:
return 0;
@ -289,7 +291,7 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
String EspClass::getResetInfo(void) {
if(resetInfo.reason != 0) {
char buff[200];
sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : resetInfo.reason == 6 ? "EXT_SYS_RST" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
return String(buff);
}
return String("flag: 0");
@ -400,3 +402,26 @@ bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool
if(restartOnSuccess) ESP.restart();
return true;
}
static const int FLASH_INT_MASK = ((B10 << 8) | B00111010);
bool EspClass::flashEraseSector(uint32_t sector) {
ets_isr_mask(FLASH_INT_MASK);
int rc = spi_flash_erase_sector(sector);
ets_isr_unmask(FLASH_INT_MASK);
return rc == 0;
}
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
ets_isr_mask(FLASH_INT_MASK);
int rc = spi_flash_write(offset, (uint32_t*) data, size);
ets_isr_unmask(FLASH_INT_MASK);
return rc == 0;
}
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
ets_isr_mask(FLASH_INT_MASK);
int rc = spi_flash_read(offset, (uint32_t*) data, size);
ets_isr_unmask(FLASH_INT_MASK);
return rc == 0;
}

View File

@ -117,6 +117,10 @@ class EspClass {
FlashMode_t getFlashChipMode();
uint32_t getFlashChipSizeByChipId();
bool flashEraseSector(uint32_t sector);
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
uint32_t getSketchSize();
uint32_t getFreeSketchSpace();
bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true);

View File

@ -29,14 +29,14 @@ size_t File::write(uint8_t c) {
if (!_p)
return 0;
_p->write(&c, 1);
return _p->write(&c, 1);
}
size_t File::write(const uint8_t *buf, size_t size) {
if (!_p)
return 0;
_p->write(buf, size);
return _p->write(buf, size);
}
int File::available() {
@ -167,6 +167,13 @@ bool FS::begin() {
return _impl->begin();
}
bool FS::format() {
if (!_impl) {
return false;
}
return _impl->format();
}
File FS::open(const String& path, const char* mode) {
return open(path.c_str(), mode);
}
@ -186,6 +193,17 @@ File FS::open(const char* path, const char* mode) {
return File(_impl->open(path, om, am));
}
bool FS::exists(const char* path) {
if (!_impl) {
return false;
}
return _impl->exists(path);
}
bool FS::exists(const String& path) {
return exists(path.c_str());
}
Dir FS::openDir(const char* path) {
if (!_impl) {
return Dir();

View File

@ -92,9 +92,14 @@ public:
bool begin();
bool format();
File open(const char* path, const char* mode);
File open(const String& path, const char* mode);
bool exists(const char* path);
bool exists(const String& path);
Dir openDir(const char* path);
Dir openDir(const String& path);

View File

@ -63,7 +63,9 @@ public:
class FSImpl {
public:
virtual bool begin() = 0;
virtual bool format() = 0;
virtual FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) = 0;
virtual bool exists(const char* path) = 0;
virtual DirImplPtr openDir(const char* path) = 0;
virtual bool rename(const char* pathFrom, const char* pathTo) = 0;
virtual bool remove(const char* path) = 0;

View File

@ -290,6 +290,7 @@ uart_t* uart_init(int uart_nr, int baudrate, byte config, byte mode) {
uart->txPin = (uart->txEnabled)?1:255;
if(uart->rxEnabled) pinMode(uart->rxPin, SPECIAL);
if(uart->txEnabled) pinMode(uart->txPin, SPECIAL);
IOSWAP &= ~(1 << IOSWAPU0);
break;
case UART1:
uart->rxEnabled = false;

View File

@ -64,3 +64,10 @@ size_t IPAddress::printTo(Print& p) const {
return n;
}
String IPAddress::toString()
{
char szRet[16];
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
return String(szRet);
}

View File

@ -21,6 +21,7 @@
#define IPAddress_h
#include <stdint.h>
#include <WString.h>
#include <Printable.h>
// A class to make it easier to handle and pass around IP addresses
@ -70,6 +71,7 @@ class IPAddress: public Printable {
IPAddress& operator=(uint32_t address);
virtual size_t printTo(Print& p) const;
String toString();
friend class EthernetClass;
friend class UDP;

View File

@ -1,18 +1,25 @@
#include "Updater.h"
#include "Arduino.h"
#include "eboot_command.h"
#include "interrupts.h"
//#define DEBUG_UPDATER Serial
extern "C" {
#include "c_types.h"
#include "spi_flash.h"
}
extern "C" uint32_t _SPIFFS_start;
UpdaterClass::UpdaterClass()
UpdaterClass::UpdaterClass()
: _error(0)
, _buffer(0)
, _bufferLen(0)
, _size(0)
, _startAddress(0)
, _currentAddress(0)
, _currentAddress(0)
, _command(U_FLASH)
{
}
@ -24,51 +31,72 @@ void UpdaterClass::_reset() {
_startAddress = 0;
_currentAddress = 0;
_size = 0;
_command = U_FLASH;
}
bool UpdaterClass::begin(size_t size){
bool UpdaterClass::begin(size_t size, int command) {
if(_size > 0){
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.println("already running");
#endif
return false;
}
if(size == 0){
#ifdef DEBUG_UPDATER
if (command == U_SPIFFS) {
DEBUG_UPDATER.println("Update SPIFFS.");
}
#endif
if(size == 0) {
_error = UPDATE_ERROR_SIZE;
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
#endif
return false;
}
_reset();
_error = 0;
//size of current sketch rounded to a sector
uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address of the end of the space available for sketch and update
uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000;
//size of the update rounded to a sector
uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address where we will start writing the update
uint32_t updateStartAddress = updateEndAddress - roundedSize;
//make sure that the size of both sketches is less than the total space (updateEndAddress)
if(updateStartAddress < currentSketchSize){
_error = UPDATE_ERROR_SPACE;
uint32_t updateStartAddress = 0;
if (command == U_FLASH) {
//size of current sketch rounded to a sector
uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address of the end of the space available for sketch and update
uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000;
//size of the update rounded to a sector
uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address where we will start writing the update
updateStartAddress = updateEndAddress - roundedSize;
//make sure that the size of both sketches is less than the total space (updateEndAddress)
if(updateStartAddress < currentSketchSize) {
_error = UPDATE_ERROR_SPACE;
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
printError(DEBUG_UPDATER);
#endif
return false;
}
}
else if (command == U_SPIFFS) {
updateStartAddress = (uint32_t)&_SPIFFS_start - 0x40200000;
}
else {
// unknown command
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.println("Unknown update command.");
#endif
return false;
}
//initialize
_startAddress = updateStartAddress;
_currentAddress = _startAddress;
_size = size;
_buffer = new uint8_t[FLASH_SECTOR_SIZE];
_command = command;
return true;
}
@ -79,50 +107,49 @@ bool UpdaterClass::end(bool evenIfRemaining){
#endif
return false;
}
if(hasError() || (!isFinished() && !evenIfRemaining)){
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
#endif
_reset();
return false;
}
if(evenIfRemaining){
if(_bufferLen > 0){
if(evenIfRemaining) {
if(_bufferLen > 0) {
_writeBuffer();
}
_size = progress();
}
eboot_command ebcmd;
ebcmd.action = ACTION_COPY_RAW;
ebcmd.args[0] = _startAddress;
ebcmd.args[1] = 0x00000;
ebcmd.args[2] = _size;
eboot_command_write(&ebcmd);
if (_command == U_FLASH) {
eboot_command ebcmd;
ebcmd.action = ACTION_COPY_RAW;
ebcmd.args[0] = _startAddress;
ebcmd.args[1] = 0x00000;
ebcmd.args[2] = _size;
eboot_command_write(&ebcmd);
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf("Staged: address:0x%08X, size:0x%08X\n", _startAddress, _size);
}
else if (_command == U_SPIFFS) {
DEBUG_UPDATER.printf("SPIFFS: address:0x%08X, size:0x%08X\n", _startAddress, _size);
#endif
}
_reset();
return true;
}
bool UpdaterClass::_writeBuffer(){
noInterrupts();
int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE);
interrupts();
yield();
if(!rc){
noInterrupts();
rc = SPIWrite(_currentAddress, _buffer, _bufferLen);
interrupts();
}
interrupts();
if (rc) {
bool result = ESP.flashEraseSector(_currentAddress/FLASH_SECTOR_SIZE) &&
ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen);
if (!result) {
_error = UPDATE_ERROR_WRITE;
_currentAddress = (_startAddress + _size);
#ifdef DEBUG_UPDATER
@ -139,10 +166,10 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
size_t left = len;
if(hasError() || !isRunning())
return 0;
if(len > remaining())
len = remaining();
while((_bufferLen + left) > FLASH_SECTOR_SIZE) {
size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen;
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
@ -170,7 +197,7 @@ size_t UpdaterClass::writeStream(Stream &data) {
size_t toRead = 0;
if(hasError() || !isRunning())
return 0;
while(remaining()) {
toRead = FLASH_SECTOR_SIZE - _bufferLen;
toRead = data.readBytes(_buffer + _bufferLen, toRead);

View File

@ -11,6 +11,11 @@
#define UPDATE_ERROR_SIZE 4
#define UPDATE_ERROR_STREAM 5
#define U_FLASH 0
#define U_SPIFFS 100
//#define DEBUG_UPDATER Serial1
class UpdaterClass {
public:
UpdaterClass();
@ -18,7 +23,7 @@ class UpdaterClass {
Call this to check the space needed for the update
Will return false if there is not enough space
*/
bool begin(size_t size);
bool begin(size_t size, int = U_FLASH);
/*
Writes a buffer to the flash and increments the address
@ -114,6 +119,7 @@ class UpdaterClass {
size_t _size;
uint32_t _startAddress;
uint32_t _currentAddress;
uint32_t _command;
};
extern UpdaterClass Update;

View File

@ -118,7 +118,7 @@ ICACHE_FLASH_ATTR String::String(double value, unsigned char decimalPlaces) {
}
ICACHE_FLASH_ATTR String::~String() {
os_free(buffer);
free(buffer);
}
// /*********************************************/
@ -133,7 +133,7 @@ inline void String::init(void) {
void ICACHE_FLASH_ATTR String::invalidate(void) {
if(buffer)
os_free(buffer);
free(buffer);
buffer = NULL;
capacity = len = 0;
}
@ -150,12 +150,18 @@ unsigned char ICACHE_FLASH_ATTR String::reserve(unsigned int size) {
}
unsigned char ICACHE_FLASH_ATTR String::changeBuffer(unsigned int maxStrLen) {
char *newbuffer = (char *) os_realloc(buffer, maxStrLen + 1);
size_t newSize = (maxStrLen + 16) & (~0xf);
char *newbuffer = (char *) malloc(newSize);
if(newbuffer) {
memset(newbuffer, 0, newSize);
memcpy(newbuffer, buffer, len);
if (buffer)
free(buffer);
capacity = newSize - 1;
buffer = newbuffer;
capacity = maxStrLen;
return 1;
}
buffer = newbuffer;
return 0;
}
@ -192,7 +198,7 @@ void ICACHE_FLASH_ATTR String::move(String &rhs) {
rhs.len = 0;
return;
} else {
os_free(buffer);
free(buffer);
}
}
buffer = rhs.buffer;

View File

@ -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 > (size_t)(_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;

View File

@ -1,8 +1,8 @@
/*
/*
postmortem.c - output of debug info on sketch crash
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
@ -42,9 +42,9 @@ void __wrap_system_restart_local() {
struct rst_info rst_info = {0};
system_rtc_mem_read(0, &rst_info, sizeof(rst_info));
if (rst_info.reason != REASON_SOFT_WDT_RST &&
if (rst_info.reason != REASON_SOFT_WDT_RST &&
rst_info.reason != REASON_EXCEPTION_RST &&
rst_info.reason != REASON_WDT_RST)
rst_info.reason != REASON_WDT_RST)
{
return;
}
@ -52,15 +52,18 @@ void __wrap_system_restart_local() {
ets_install_putc1(&uart_write_char_d);
if (rst_info.reason == REASON_EXCEPTION_RST) {
ets_printf("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n",
ets_printf("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n",
rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc);
}
else if (rst_info.reason == REASON_SOFT_WDT_RST) {
ets_printf("\nSoft WDT reset\n");
}
uint32_t cont_stack_start = (uint32_t) &(g_cont.stack);
uint32_t cont_stack_end = (uint32_t) g_cont.stack_end;
uint32_t stack_end;
// amount of stack taken by interrupt or exception handler
// amount of stack taken by interrupt or exception handler
// and everything up to __wrap_system_restart_local
// (determined empirically, might break)
uint32_t offset = 0;
@ -80,13 +83,13 @@ void __wrap_system_restart_local() {
}
else {
ets_printf("\nctx: sys \n");
stack_end = 0x3fffffb0;
// it's actually 0x3ffffff0, but the stuff below ets_run
stack_end = 0x3fffffb0;
// it's actually 0x3ffffff0, but the stuff below ets_run
// is likely not really relevant to the crash
}
ets_printf("sp: %08x end: %08x offset: %04x\n", sp, stack_end, offset);
// print_pcs(sp + offset, stack_end);
print_stack(sp + offset, stack_end);
delayMicroseconds(10000);
@ -102,7 +105,7 @@ static void print_stack(uint32_t start, uint32_t end) {
// rough indicator: stack frames usually have SP saved as the second word
bool looksLikeStackFrame = (values[2] == pos + 0x10);
ets_printf("%08x: %08x %08x %08x %08x %c\n",
ets_printf("%08x: %08x %08x %08x %08x %c\n",
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' ');
}
ets_printf("<<<stack<<<\n");

View File

@ -2,8 +2,11 @@
#define ARD_DEBUG_H
#include <stddef.h>
// #define DEBUGV(...) ets_printf(__VA_ARGS__)
//#define DEBUGV(...) ets_printf(__VA_ARGS__)
#ifndef DEBUGV
#define DEBUGV(...)
#endif
#ifdef __cplusplus
void hexdump(uint8_t *mem, uint32_t len, uint8_t cols = 16);

View File

@ -1,10 +1,10 @@
/*
/*
libc_replacements.c - replaces libc functions with functions
from Espressif SDK
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
@ -30,6 +30,7 @@
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include "ets_sys.h"
#include "os_type.h"
@ -83,6 +84,10 @@ int snprintf(char* buffer, size_t size, const char* format, ...) {
return ret;
}
int vprintf(const char * format, va_list arg) {
return ets_vprintf(format, arg);
}
int vsnprintf(char * buffer, size_t size, const char * format, va_list arg) {
return ets_vsnprintf(buffer, size, format, arg);
}
@ -144,7 +149,7 @@ char* ICACHE_FLASH_ATTR strtok_r(char* s, const char* delim, char** last) {
}
// Skip (span) leading delimiters
// Skip (span) leading delimiters
//
cont:
c = *s++;
@ -164,7 +169,7 @@ cont:
tok = s - 1;
// Scan token
// Scan token
// Note that delim must have one NUL; we stop if we see that, too.
//
for (;;) {
@ -384,14 +389,31 @@ int isblank(int c) {
static int errno_var = 0;
int* ICACHE_FLASH_ATTR __errno(void) {
DEBUGV("__errno is called last error: %d (not current)\n", errno_var);
// DEBUGV("__errno is called last error: %d (not current)\n", errno_var);
return &errno_var;
}
char * ctime(const time_t *clock) {
return 0;
}
time_t time(time_t * t) {
return 0;
}
int gettimeofday(void *tp, void *tzp) {
return 0;
}
time_t mktime(struct tm *timp) {
return 0;
}
/*
* begin newlib/string/strlcpy.c
*
*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*

View File

@ -59,7 +59,7 @@ public:
}
FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override;
bool exists(const char* path) override;
DirImplPtr openDir(const char* path) override;
bool rename(const char* pathFrom, const char* pathTo) override {
@ -102,6 +102,25 @@ public:
return _tryMount();
}
bool format() override {
bool wasMounted = (SPIFFS_mounted(&_fs) != 0);
if (_tryMount()) {
SPIFFS_unmount(&_fs);
}
auto rc = SPIFFS_format(&_fs);
if (rc != SPIFFS_OK) {
DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code);
return false;
}
if (wasMounted) {
return _tryMount();
}
return true;
}
protected:
friend class SPIFFSFileImpl;
friend class SPIFFSDirImpl;
@ -202,13 +221,9 @@ public:
: _fs(fs)
, _fd(fd)
, _stat({0})
, _written(false)
{
CHECKFD();
auto rc = SPIFFS_fstat(_fs->getFs(), _fd, &_stat);
if (rc != SPIFFS_OK) {
DEBUGV("SPIFFS_fstat rc=%d\r\n", rc);
_stat = {0};
}
_getStat();
}
~SPIFFSFileImpl() override {
@ -223,7 +238,7 @@ public:
DEBUGV("SPIFFS_write rc=%d\r\n", result);
return 0;
}
_written = true;
return result;
}
@ -246,11 +261,16 @@ public:
if (rc < 0) {
DEBUGV("SPIFFS_fflush rc=%d\r\n", rc);
}
_written = true;
}
bool seek(uint32_t pos, SeekMode mode) override {
CHECKFD();
int32_t offset = static_cast<int32_t>(pos);
if (mode == SeekEnd) {
offset = -offset;
}
auto rc = SPIFFS_lseek(_fs->getFs(), _fd, pos, (int) mode);
if (rc < 0) {
DEBUGV("SPIFFS_lseek rc=%d\r\n", rc);
@ -274,7 +294,9 @@ public:
size_t size() const override {
CHECKFD();
if (_written) {
_getStat();
}
return _stat.size;
}
@ -292,15 +314,27 @@ public:
}
protected:
void _getStat() const{
CHECKFD();
auto rc = SPIFFS_fstat(_fs->getFs(), _fd, &_stat);
if (rc != SPIFFS_OK) {
DEBUGV("SPIFFS_fstat rc=%d\r\n", rc);
_stat = {0};
}
_written = false;
}
SPIFFSImpl* _fs;
spiffs_file _fd;
spiffs_stat _stat;
mutable spiffs_stat _stat;
mutable bool _written;
};
class SPIFFSDirImpl : public DirImpl {
public:
SPIFFSDirImpl(SPIFFSImpl* fs, spiffs_DIR& dir)
: _fs(fs)
SPIFFSDirImpl(const String& pattern, SPIFFSImpl* fs, spiffs_DIR& dir)
: _pattern(pattern)
, _fs(fs)
, _dir(dir)
, _valid(false)
{
@ -340,12 +374,16 @@ public:
}
bool next() override {
spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent);
_valid = (result != nullptr);
const int n = _pattern.length();
do {
spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent);
_valid = (result != nullptr);
} while(_valid && strncmp((const char*) _dirent.name, _pattern.c_str(), n) != 0);
return _valid;
}
protected:
String _pattern;
SPIFFSImpl* _fs;
spiffs_DIR _dir;
spiffs_dirent _dirent;
@ -366,6 +404,14 @@ FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode acc
return std::make_shared<SPIFFSFileImpl>(this, fd);
}
bool SPIFFSImpl::exists(const char* path) {
char tmpName[SPIFFS_OBJ_NAME_LEN];
strlcpy(tmpName, path, sizeof(tmpName));
spiffs_stat stat;
int rc = SPIFFS_stat(&_fs, tmpName, &stat);
return rc == SPIFFS_OK;
}
DirImplPtr SPIFFSImpl::openDir(const char* path) {
spiffs_DIR dir;
char tmpName[SPIFFS_OBJ_NAME_LEN];
@ -375,7 +421,7 @@ DirImplPtr SPIFFSImpl::openDir(const char* path) {
DEBUGV("SPIFFSImpl::openDir: path=`%s` err=%d\r\n", path, _fs.err_code);
return DirImplPtr();
}
return std::make_shared<SPIFFSDirImpl>(this, dir);
return std::make_shared<SPIFFSDirImpl>(path, this, dir);
}
int getSpiffsMode(OpenMode openMode, AccessMode accessMode) {

View File

@ -23,32 +23,11 @@
#include <algorithm>
#include "spiffs/spiffs.h"
#include "debug.h"
#include "interrupts.h"
extern "C" {
#include "c_types.h"
#include "spi_flash.h"
}
static int spi_flash_read_locked(uint32_t addr, uint32_t* dst, uint32_t size) {
optimistic_yield(10000);
AutoInterruptLock(5);
return spi_flash_read(addr, dst, size);
}
static int spi_flash_write_locked(uint32_t addr, const uint32_t* src, uint32_t size) {
optimistic_yield(10000);
AutoInterruptLock(5);
return spi_flash_write(addr, (uint32_t*) src, size);
}
static int spi_flash_erase_sector_locked(uint32_t sector) {
optimistic_yield(10000);
AutoInterruptLock(5);
return spi_flash_erase_sector(sector);
}
/*
spi_flash_read function requires flash address to be aligned on word boundary.
We take care of this by reading first and last words separately and memcpy
@ -64,14 +43,19 @@ alignedEnd: ^
*/
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
optimistic_yield(10000);
uint32_t result = SPIFFS_OK;
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (alignedEnd < alignedBegin) {
alignedEnd = alignedBegin;
}
if (addr < alignedBegin) {
uint32_t nb = alignedBegin - addr;
uint32_t tmp;
if (spi_flash_read_locked(alignedBegin - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashRead(alignedBegin - 4, &tmp, 4)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -80,8 +64,8 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
}
if (alignedEnd != alignedBegin) {
if (spi_flash_read_locked(alignedBegin, (uint32_t*) (dst + alignedBegin - addr),
alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashRead(alignedBegin, (uint32_t*) (dst + alignedBegin - addr),
alignedEnd - alignedBegin)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -91,7 +75,7 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
if (addr + size > alignedEnd) {
uint32_t nb = addr + size - alignedEnd;
uint32_t tmp;
if (spi_flash_read_locked(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashRead(alignedEnd, &tmp, 4)) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -116,14 +100,19 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
static const int UNALIGNED_WRITE_BUFFER_SIZE = 512;
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
optimistic_yield(10000);
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (alignedEnd < alignedBegin) {
alignedEnd = alignedBegin;
}
if (addr < alignedBegin) {
uint32_t nb = alignedBegin - addr;
uint32_t tmp = 0xffffffff;
memcpy(((uint8_t* )&tmp) + 4 - nb, src, nb);
if (spi_flash_write_locked(alignedBegin - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashWrite(alignedBegin - 4, &tmp, 4)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -134,8 +123,8 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr);
uint32_t srcAlign = ((uint32_t) srcLeftover) & 3;
if (!srcAlign) {
if (spi_flash_write_locked(alignedBegin, (uint32_t*) srcLeftover,
alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashWrite(alignedBegin, (uint32_t*) srcLeftover,
alignedEnd - alignedBegin)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -147,8 +136,7 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
size_t willCopy = std::min(sizeLeft, sizeof(buf));
memcpy(buf, srcLeftover, willCopy);
if (spi_flash_write_locked(alignedBegin, (uint32_t*) buf,
willCopy) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -166,7 +154,7 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
uint32_t tmp = 0xffffffff;
memcpy(&tmp, src + size - nb, nb);
if (spi_flash_write_locked(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
if (!ESP.flashWrite(alignedEnd, &tmp, 4)) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
@ -185,7 +173,8 @@ int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) {
const uint32_t sector = addr / SPI_FLASH_SEC_SIZE;
const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE;
for (uint32_t i = 0; i < sectorCount; ++i) {
if (spi_flash_erase_sector_locked(sector + i) != 0) {
optimistic_yield(10000);
if (!ESP.flashEraseSector(sector + i)) {
DEBUGV("_spif_erase addr=%x size=%d i=%d\r\n", addr, size, i);
return SPIFFS_ERR_INTERNAL;
}

View File

@ -95,22 +95,18 @@ In order to use these modules, make sure to observe the following:
### Serial Adapter
There are many different USB to Serial adapters / boards.
To be able to put ESP8266 into bootloader mode using serial handshaking lines, you need the adapter which breaks out RTS and DTR outputs. CTS and DSR are not useful for upload (they are inputs). Make sure the adapter can work with 3.3V IO voltage: it should have a jumper or a switch to select between 5V and 3.3V, or be marked as 3.3V only.
* Note
- for full upload management you need RTS and DTR
- the chip need to have 3.3V TTL (5V may damage the chip)
- not all board have all pins of the ICs as breakout (check before order)
- CTS and DSR are not useful for upload (they are Inputs)
Adapters based around the following ICs should work:
* Working ICs
- FT232RL
- CP2102
- CH340G
- maybe others (drop a comment)
PL2303-based adapters are known not to work on Mac OS X. See https://github.com/igrr/esptool-ck/issues/9 for more info.
### Minimal Hardware Setup for Bootloading and Usage
ESPxx Hardware
| PIN | Resistor | Serial Adapter |
| ------------- | -------- | -------------- |
@ -125,12 +121,12 @@ ESPxx Hardware
* Note
- GPIO15 is also named MTDO
- Reset is also named RSBT or REST (adding PullUp improves the stability of the Module)
- Reset is also named RSBT or REST (adding PullUp improves the stability of the module)
- GPIO2 is alternative TX for the boot loader mode
- **Directly connecting a pin to VCC or GND is not a substitute for a PullUp or PullDown resistor, doing this can break upload management and the serial console, instability has also been noted in some cases.**
### ESP to Serial
![ESP to Serial](https://raw.githubusercontent.com/Links2004/Arduino/esp8266/docs/ESP_to_serial.png)
![ESP to Serial](https://raw.githubusercontent.com/esp8266/Arduino/esp8266/docs/ESP_to_serial.png)
#### Minimal Hardware Setup for Bootloading only ##
ESPxx Hardware

View File

@ -2,6 +2,46 @@
title: Reference
---
## Table of Contents
* [Digital IO](#digital-io)
* [Analog input](#analog-input)
* [Analog output](#analog-output)
* [Timing and delays](#timing-and-delays)
* [Serial](#serial)
* [Progmem](#progmem)
* [File system](#file-system)
* [Uploading files to file system](#uploading-files-to-file-system)
* [File system object (SPIFFS)](#file-system-object-spiffs)
* [begin](#begin)
* [format](#format)
* [open](#open)
* [exists](#exists)
* [openDir](#opendir)
* [remove](#remove)
* [rename](#rename)
* [Directory object (Dir)](#directory-object-dir)
* [File object](#file-object)
* [seek](#seek)
* [position](#position)
* [size](#size)
* [name](#name)
* [close](#close)
* [WiFi(ESP8266WiFi library)](#wifiesp8266wifi-library)
* [Ticker](#ticker)
* [EEPROM](#eeprom)
* [I2C (Wire library)](#i2c-wire-library)
* [SPI](#spi)
* [SoftwareSerial](#softwareserial)
* [ESP-specific APIs](#esp-specific-apis)
* [OneWire (from <a href="https://www.pjrc.com/teensy/td_libs_OneWire.html">https://www.pjrc.com/teensy/td_libs_OneWire.html</a>)](#onewire-from-httpswwwpjrccomteensytd_libs_onewirehtml)
* [mDNS and DNS-SD responder (ESP8266mDNS library)](#mdns-and-dns-sd-responder-esp8266mdns-library)
* [SSDP responder (ESP8266SSDP)](#ssdp-responder-esp8266ssdp)
* [DNS server (DNSServer library)](#dns-server-dnsserver-library)
* [Servo](#servo)
* [Other libraries (not included with the IDE)](#other-libraries-not-included-with-the-ide)
Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## Digital IO
Pin numbers in Arduino correspond directly to the ESP8266 GPIO pin numbers. `pinMode`, `digitalRead`, and `digitalWrite` functions work as usual, so to read GPIO2, call `digitalRead(2)`.
@ -121,12 +161,12 @@ Generic module | 512k | 64k
Generic module | 1M | 64k, 128k, 256k, 512k
Generic module | 2M | 1M
Generic module | 4M | 3M
Adafruit HUZZAH | 4M | 3M
NodeMCU 0.9 | 4M | 3M
NodeMCU 1.0 | 4M | 3M
Adafruit HUZZAH | 4M | 1M, 3M
NodeMCU 0.9 | 4M | 1M, 3M
NodeMCU 1.0 | 4M | 1M, 3M
Olimex MOD-WIFI-ESP8266(-DEV)| 2M | 1M
SparkFun Thing | 512k | 64k
SweetPea ESP-210 | 4M | 3M
SweetPea ESP-210 | 4M | 1M, 3M
**Note:** to use any of file system functions in the sketch, add the following include to the sketch:
@ -134,6 +174,21 @@ SweetPea ESP-210 | 4M | 3M
#include "FS.h"
```
### Uploading files to file system
*ESP8266FS* is a tool which integrates into the Arduino IDE. It adds a menu item to *Tools* menu for uploading the contents of sketch data directory into ESP8266 flash file system.
- Download the tool: http://arduino.esp8266.com/ESP8266FS-1.6.5-1105-g98d2458.zip
- In your Arduino sketchbook directory, create `tools` directory if it doesn't exist yet
- Unpack the tool into `tools` directory (the path will look like `<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar`)
- Restart Arduino IDE
- Open a sketch (or create a new one and save it)
- Go to sketch directory (choose Sketch > Show Sketch Folder)
- Create a directory named `data` and any files you want in the file system there
- Make sure you have selected a board, port, and closed Serial Monitor
- Select Tools > ESP8266 Sketch Data Upload. This should start uploading the files into ESP8266 flash file system. When done, IDE status bar will display `SPIFFS Image Uploaded` message.
### File system object (SPIFFS)
#### begin
@ -146,6 +201,15 @@ 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.
#### format
```c++
SPIFFS.format()
```
Formats the file system. May be called either before or after calling `begin`.
Returns *true* if formatting was successful.
#### open
```c++
@ -167,6 +231,14 @@ if (!f) {
}
```
#### exists
```c++
SPIFFS.exists(path)
```
Returns *true* if a file with given path exists, *false* otherwise.
#### openDir
```c++
@ -311,7 +383,7 @@ Size can be anywhere between 4 and 4096 bytes.
whenever you wish to save changes to flash. `EEPROM.end()` will also commit, and will
release the RAM copy of EEPROM contents.
EEPROM library uses one sector of flash located at 0x7b000 for storage.
EEPROM library uses one sector of flash located just after the SPIFFS.
Three examples included.
@ -327,6 +399,10 @@ else they default to pins 4(SDA) and 5(SCL).
SPI library supports the entire Arduino SPI API including transactions, including setting phase (CPHA).
Setting the Clock polarity (CPOL) is not supported, yet (SPI_MODE2 and SPI_MODE3 not working).
## SoftwareSerial
An ESP8266 port of SoftwareSerial library done by Peter Lerup (@plerup) supports baud rate up to 115200 and multiples SoftwareSerial instances. See the https://github.com/plerup/espsoftwareserial if you want to suggest an improvement or open an issue related to SoftwareSerial.
## ESP-specific APIs
APIs related to deep sleep and watchdog timer are available in the `ESP` object, only available in Alpha version.

View File

@ -0,0 +1,135 @@
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <EEPROM.h>
/*
* This example serves a "hello world" on a WLAN and a SoftAP at the same time.
* The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.
*
* Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
* Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN.
*
* Now the ESP8266 is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://esp8266.local too.
*
* This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
*/
/* Set these to your desired softAP credentials. They are not configurable at runtime */
const char *softAP_ssid = "ESP_ap";
const char *softAP_password = "12345678";
/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */
const char *myHostname = "esp8266";
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
char ssid[32] = "";
char password[32] = "";
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;
// Web server
ESP8266WebServer server(80);
/* Soft AP network parameters */
IPAddress apIP(192, 168, 4, 1);
IPAddress netMsk(255, 255, 255, 0);
/** Should I connect to WLAN asap? */
boolean connect;
/** Last time I tried to connect to WLAN */
long lastConnectTry = 0;
/** Current WLAN status */
int status = WL_IDLE_STATUS;
void setup() {
delay(1000);
Serial.begin(9600);
Serial.println();
Serial.print("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
WiFi.softAPConfig(apIP, apIP, netMsk);
WiFi.softAP(softAP_ssid, softAP_password);
delay(500); // Without delay I've seen the IP address blank
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
/* Setup the DNS server redirecting all the domains to the apIP */
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", apIP);
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
server.on("/", handleRoot);
server.on("/wifi", handleWifi);
server.on("/wifisave", handleWifiSave);
server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
server.onNotFound ( handleNotFound );
server.begin(); // Web server start
Serial.println("HTTP server started");
loadCredentials(); // Load WLAN credentials from network
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
}
void connectWifi() {
Serial.println("Connecting as wifi client...");
WiFi.disconnect();
WiFi.begin ( ssid, password );
int connRes = WiFi.waitForConnectResult();
Serial.print ( "connRes: " );
Serial.println ( connRes );
}
void loop() {
if (connect) {
Serial.println ( "Connect requested" );
connect = false;
connectWifi();
lastConnectTry = millis();
}
{
int s = WiFi.status();
if (s == 0 && millis() > (lastConnectTry + 60000) ) {
/* If WLAN disconnected and idle try to connect */
/* Don't set retry time too low as retry interfere the softAP operation */
connect = true;
}
if (status != s) { // WLAN status change
Serial.print ( "Status: " );
Serial.println ( s );
status = s;
if (s == WL_CONNECTED) {
/* Just connected to WLAN */
Serial.println ( "" );
Serial.print ( "Connected to " );
Serial.println ( ssid );
Serial.print ( "IP address: " );
Serial.println ( WiFi.localIP() );
// Setup MDNS responder
if (!MDNS.begin(myHostname)) {
Serial.println("Error setting up MDNS responder!");
} else {
Serial.println("mDNS responder started");
// Add service to MDNS-SD
MDNS.addService("http", "tcp", 80);
}
} else if (s == WL_NO_SSID_AVAIL) {
WiFi.disconnect();
}
}
}
// Do work:
//DNS
dnsServer.processNextRequest();
//HTTP
server.handleClient();
}

View File

@ -0,0 +1,27 @@
/** Load WLAN credentials from EEPROM */
void loadCredentials() {
EEPROM.begin(512);
EEPROM.get(0, ssid);
EEPROM.get(0+sizeof(ssid), password);
char ok[2+1];
EEPROM.get(0+sizeof(ssid)+sizeof(password), ok);
EEPROM.end();
if (String(ok) != String("OK")) {
ssid[0] = 0;
password[0] = 0;
}
Serial.println("Recovered credentials:");
Serial.println(ssid);
Serial.println(strlen(password)>0?"********":"<no password>");
}
/** Store WLAN credentials to EEPROM */
void saveCredentials() {
EEPROM.begin(512);
EEPROM.put(0, ssid);
EEPROM.put(0+sizeof(ssid), password);
char ok[2+1] = "OK";
EEPROM.put(0+sizeof(ssid)+sizeof(password), ok);
EEPROM.commit();
EEPROM.end();
}

View File

@ -0,0 +1,129 @@
/** Handle root or redirect to captive portal */
void handleRoot() {
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
return;
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.sendContent(
"<html><head></head><body>"
"<h1>HELLO WORLD!!</h1>"
);
if (server.client().localIP() == apIP) {
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>");
} else {
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>");
}
server.sendContent(
"<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
"</body></html>"
);
server.client().stop(); // Stop is needed because we sent no content length
}
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
boolean captivePortal() {
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname)+".local")) {
Serial.print("Request redirected to captive portal");
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}
/** Wifi config page handler */
void handleWifi() {
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.sendContent(
"<html><head></head><body>"
"<h1>Wifi config</h1>"
);
if (server.client().localIP() == apIP) {
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>");
} else {
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>");
}
server.sendContent(
"\r\n<br />"
"<table><tr><th align='left'>SoftAP config</th></tr>"
);
server.sendContent(String() + "<tr><td>SSID " + String(softAP_ssid) + "</td></tr>");
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.softAPIP()) + "</td></tr>");
server.sendContent(
"</table>"
"\r\n<br />"
"<table><tr><th align='left'>WLAN config</th></tr>"
);
server.sendContent(String() + "<tr><td>SSID " + String(ssid) + "</td></tr>");
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.localIP()) + "</td></tr>");
server.sendContent(
"</table>"
"\r\n<br />"
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>"
);
Serial.println("scan start");
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n > 0) {
for (int i = 0; i < n; i++) {
server.sendContent(String() + "\r\n<tr><td>SSID " + String(WiFi.SSID(i)) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>");
}
} else {
server.sendContent(String() + "<tr><td>No WLAN found</td></tr>");
}
server.sendContent(
"</table>"
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
"<input type='text' placeholder='network' name='n'/>"
"<br /><input type='password' placeholder='password' name='p'/>"
"<br /><input type='submit' value='Connect/Disconnect'/></form>"
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
"</body></html>"
);
server.client().stop(); // Stop is needed because we sent no content length
}
/** Handle the WLAN save form and redirect to WLAN config page again */
void handleWifiSave() {
Serial.println("wifi save");
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
server.arg("p").toCharArray(password, sizeof(password) - 1);
server.sendHeader("Location", "wifi", true);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.client().stop(); // Stop is needed because we sent no content length
saveCredentials();
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
}
void handleNotFound() {
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
return;
}
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send ( 404, "text/plain", message );
}

View File

@ -0,0 +1,21 @@
/** Is this an IP? */
boolean isIp(String str) {
for (int i = 0; i < str.length(); i++) {
int c = str.charAt(i);
if (c != '.' && (c < '0' || c > '9')) {
return false;
}
}
return true;
}
/** IP to String? */
String toStringIp(IPAddress ip) {
String res = "";
for (int i = 0; i < 3; i++) {
res += String((ip >> (8 * i)) & 0xFF) + ".";
}
res += String(((ip >> 8 * 3)) & 0xFF);
return res;
}

View File

@ -2,6 +2,7 @@
#include <lwip/def.h>
#include <Arduino.h>
DNSServer::DNSServer()
{
_ttl = htonl(60);
@ -110,16 +111,43 @@ void DNSServer::replyWithIP()
{
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->ANCount = _dnsHeader->QDCount;
_dnsHeader->QDCount = 0;
_dnsHeader->QDCount = _dnsHeader->QDCount;
//_dnsHeader->RA = 1;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, _currentPacketSize);
_udp.write((uint8_t)192); // answer name is a pointer
_udp.write((uint8_t)12); // pointer to offset at 0x00c
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address)
_udp.write((uint8_t)1);
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address)
_udp.write((uint8_t)1);
_udp.write((unsigned char*)&_ttl, 4);
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
_udp.write((uint8_t)0);
_udp.write((uint8_t)4);
_udp.write(_resolvedIP, sizeof(_resolvedIP));
_udp.endPacket();
#ifdef DEBUG
DEBUG_OUTPUT.print("DNS responds: ");
DEBUG_OUTPUT.print(_resolvedIP[0]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[1]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[2]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[3]);
DEBUG_OUTPUT.print(" for ");
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix());
#endif
}
void DNSServer::replyWithCustomCode()
@ -131,4 +159,4 @@ void DNSServer::replyWithCustomCode()
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, sizeof(DNSHeader));
_udp.endPacket();
}
}

View File

@ -68,4 +68,4 @@ class DNSServer
void replyWithIP();
void replyWithCustomCode();
};
#endif
#endif

View File

@ -26,15 +26,6 @@ extern "C" {
#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(...)
@ -47,8 +38,8 @@ extern "C" {
#define beget16(addr) (*addr * 256 + *(addr+1))
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),
ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq, bool reset_state, bool reset_activehigh):
_reset_pin(reset_pin), _reset_state(reset_state), _spi_freq(spi_freq), _reset_activehigh(reset_activehigh),
_server(WiFiServer(port)), _state(AVRISP_STATE_IDLE)
{
pinMode(_reset_pin, OUTPUT);
@ -68,11 +59,7 @@ void ESP8266AVRISP::setSpiFrequency(uint32_t freq) {
void ESP8266AVRISP::setReset(bool rst) {
_reset_state = rst;
if (_reset_state) {
digitalWrite(_reset_pin, AVRISP_RESET_ON);
} else {
digitalWrite(_reset_pin, AVRISP_RESET_OFF);
}
digitalWrite(_reset_pin, _resetLevel(_reset_state));
}
AVRISPState_t ESP8266AVRISP::update() {
@ -230,9 +217,9 @@ void ESP8266AVRISP::start_pmode() {
// try to sync the bus
SPI.transfer(0x00);
digitalWrite(_reset_pin, AVRISP_RESET_OFF);
digitalWrite(_reset_pin, _resetLevel(false));
delayMicroseconds(50);
digitalWrite(_reset_pin, AVRISP_RESET_ON);
digitalWrite(_reset_pin, _resetLevel(true));
delay(30);
spi_transaction(0xAC, 0x53, 0x00, 0x00);

View File

@ -47,7 +47,7 @@ typedef struct {
class ESP8266AVRISP {
public:
ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false);
ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false, bool reset_activehigh=false);
void begin();
@ -100,7 +100,7 @@ protected:
void start_pmode(void); // enter program mode
void end_pmode(void); // exit program mode
inline bool _resetLevel(bool reset_state) { return reset_state == _reset_activehigh; }
uint32_t _spi_freq;
WiFiServer _server;
@ -108,6 +108,7 @@ protected:
AVRISPState_t _state;
uint8_t _reset_pin;
bool _reset_state;
bool _reset_activehigh;
// programmer settings, set by remote end
AVRISP_parameter_t param;

View File

@ -1,21 +1,21 @@
/*
* Copyright (c) 2015, Majenko Technologies
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
*
* * Neither the name of Majenko Technologies nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@ -35,7 +35,6 @@
const char *ssid = "YourSSIDHere";
const char *password = "YourPSKHere";
MDNSResponder mdns;
ESP8266WebServer server ( 80 );
@ -109,7 +108,7 @@ void setup ( void ) {
Serial.print ( "IP address: " );
Serial.println ( WiFi.localIP() );
if ( mdns.begin ( "esp8266", WiFi.localIP() ) ) {
if ( MDNS.begin ( "esp8266" ) ) {
Serial.println ( "MDNS responder started" );
}
@ -124,7 +123,6 @@ void setup ( void ) {
}
void loop ( void ) {
mdns.update();
server.handleClient();
}

View File

@ -2,10 +2,9 @@
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
const char* ssid = "........";
const char* password = "........";
MDNSResponder mdns;
ESP8266WebServer server(80);
@ -33,7 +32,7 @@ void handleNotFound(){
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup(void){
pinMode(led, OUTPUT);
digitalWrite(led, 0);
@ -51,23 +50,23 @@ void setup(void){
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (mdns.begin("esp8266", WiFi.localIP())) {
if (MDNS.begin("esp8266")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/inline", [](){
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop(void){
server.handleClient();
}
}

View File

@ -40,8 +40,7 @@ ESP8266WebServer::ESP8266WebServer(int port)
{
}
ESP8266WebServer::~ESP8266WebServer()
{
ESP8266WebServer::~ESP8266WebServer() {
if (!_firstHandler)
return;
RequestHandler* handler = _firstHandler;
@ -56,14 +55,11 @@ void ESP8266WebServer::begin() {
_server.begin();
}
void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler)
{
void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler);
}
void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{
void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) {
_addRequestHandler(new FunctionRequestHandler(fn, uri, method));
}
@ -79,11 +75,10 @@ void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
}
void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) {
_addRequestHandler(new StaticRequestHandler(fs, uri));
_addRequestHandler(new StaticRequestHandler(fs, path, uri));
}
void ESP8266WebServer::handleClient()
{
void ESP8266WebServer::handleClient() {
WiFiClient client = _server.available();
if (!client) {
return;
@ -135,10 +130,10 @@ void ESP8266WebServer::_prepareHeader(String& response, int code, const char* co
sendHeader("Content-Type", content_type, true);
if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) {
sendHeader("Content-Length", String(_contentLength).c_str());
sendHeader("Content-Length", String(_contentLength));
}
else if (contentLength > 0){
sendHeader("Content-Length", String(contentLength).c_str());
sendHeader("Content-Length", String(contentLength));
}
sendHeader("Connection", "close");
sendHeader("Access-Control-Allow-Origin", "*");
@ -164,11 +159,22 @@ void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
}
String header;
_prepareHeader(header, code, String(FPSTR(content_type)).c_str(), contentLength);
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
sendContent(header);
sendContent_P(content);
}
void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
sendContent(header);
sendContent_P(content, contentLength);
}
void ESP8266WebServer::send(int code, char* content_type, const String& content) {
send(code, (const char*)content_type, content);
}
@ -221,6 +227,26 @@ void ESP8266WebServer::sendContent_P(PGM_P content) {
}
}
void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) {
char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];
contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';
size_t remaining_size = size;
while (content != NULL && remaining_size > 0) {
size_t contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
if (remaining_size < HTTP_DOWNLOAD_UNIT_SIZE) contentUnitLen = remaining_size;
// due to the memcpy signature, lots of casts are needed
memcpy_P((void*)contentUnit, (PGM_VOID_P)content, contentUnitLen);
content += contentUnitLen;
remaining_size -= contentUnitLen;
// write is so overloaded, had to use the cast to get it pick the right one
_currentClient.write((const char*)contentUnit, contentUnitLen);
}
}
String ESP8266WebServer::arg(const char* name) {
for (int i = 0; i < _currentArgCount; ++i) {
if (_currentArgs[i].key == name)
@ -253,6 +279,10 @@ bool ESP8266WebServer::hasArg(const char* name) {
return false;
}
String ESP8266WebServer::hostHeader() {
return _hostHeader;
}
void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}

View File

@ -26,7 +26,7 @@
#include <functional>
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE };
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END };
#define HTTP_DOWNLOAD_UNIT_SIZE 1460
@ -80,6 +80,8 @@ public:
int args(); // get arguments count
bool hasArg(const char* name); // check if argument exists
String hostHeader(); // get request host header if available or empty String if not
// 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"
@ -88,11 +90,13 @@ public:
void send(int code, char* content_type, const String& content);
void send(int code, const String& content_type, const String& content);
void send_P(int code, PGM_P content_type, PGM_P content);
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
void setContentLength(size_t contentLength) { _contentLength = contentLength; }
void sendHeader(const String& name, const String& value, bool first = false);
void sendContent(const String& content);
void sendContent_P(PGM_P content);
void sendContent_P(PGM_P content, size_t size);
template<typename T> size_t streamFile(T &file, const String& contentType){
setContentLength(file.size());
@ -111,7 +115,7 @@ protected:
bool _parseRequest(WiFiClient& client);
void _parseArguments(String data);
static const char* _responseCodeToString(int code);
void _parseForm(WiFiClient& client, String boundary, uint32_t len);
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
void _uploadWriteByte(uint8_t b);
uint8_t _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
@ -134,6 +138,8 @@ protected:
size_t _contentLength;
String _responseHeaders;
String _hostHeader;
RequestHandler* _firstHandler;
RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler;

View File

@ -1,8 +1,8 @@
/*
/*
Parsing.cpp - HTTP request parsing.
Copyright (c) 2015 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
@ -24,8 +24,8 @@
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
// #define DEBUG
#define DEBUG_OUTPUT Serial1
//#define DEBUG
#define DEBUG_OUTPUT Serial
bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
// Read the first line of HTTP request
@ -43,7 +43,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
#endif
return false;
}
String methodStr = req.substring(0, addr_start);
String url = req.substring(addr_start + 1, addr_end);
String searchStr = "";
@ -53,19 +53,21 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
url = url.substring(0, hasSearch);
}
_currentUri = url;
HTTPMethod method = HTTP_GET;
if (methodStr == "POST") {
method = HTTP_POST;
} else if (methodStr == "DELETE") {
method = HTTP_DELETE;
} else if (methodStr == "OPTIONS") {
method = HTTP_OPTIONS;
} else if (methodStr == "PUT") {
method = HTTP_PUT;
} else if (methodStr == "PATCH") {
method = HTTP_PATCH;
}
_currentMethod = method;
#ifdef DEBUG
DEBUG_OUTPUT.print("method: ");
DEBUG_OUTPUT.print(methodStr);
@ -94,6 +96,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2);
#ifdef DEBUG
DEBUG_OUTPUT.print("headerName: ");
DEBUG_OUTPUT.println(headerName);
DEBUG_OUTPUT.print("headerValue: ");
DEBUG_OUTPUT.println(headerValue);
#endif
if (headerName == "Content-Type"){
if (headerValue.startsWith("text/plain")){
isForm = false;
@ -103,9 +113,11 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
}
} else if (headerName == "Content-Length"){
contentLength = headerValue.toInt();
} else if (headerName == "Host"){
_hostHeader = headerValue;
}
}
if (!isForm){
if (searchStr != "") searchStr += '&';
//some clients send headers first and data after (like we do)
@ -131,9 +143,36 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
}
_parseArguments(searchStr);
if (isForm){
_parseForm(client, boundaryStr, contentLength);
if (!_parseForm(client, boundaryStr, contentLength)) {
return false;
}
}
} else {
String headerName;
String headerValue;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2);
#ifdef DEBUG
DEBUG_OUTPUT.print("headerName: ");
DEBUG_OUTPUT.println(headerName);
DEBUG_OUTPUT.print("headerValue: ");
DEBUG_OUTPUT.println(headerValue);
#endif
if (headerName == "Host"){
_hostHeader = headerValue;
}
}
_parseArguments(searchStr);
}
client.flush();
@ -242,8 +281,8 @@ uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){
return (uint8_t)res;
}
void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
#ifdef DEBUG
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
DEBUG_OUTPUT.print(boundary);
@ -251,7 +290,12 @@ void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
DEBUG_OUTPUT.println(len);
#endif
String line;
line = client.readStringUntil('\r');
int retry = 0;
do {
line = client.readStringUntil('\r');
++retry;
} while (line.length() == 0 && retry < 3);
client.readStringUntil('\n');
//start reading the form
if (line == ("--"+boundary)){
@ -263,7 +307,7 @@ void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
String argType;
String argFilename;
bool argIsFile = false;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.startsWith("Content-Disposition")){
@ -314,11 +358,11 @@ void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
DEBUG_OUTPUT.println(argValue);
DEBUG_OUTPUT.println();
#endif
RequestArgument& arg = postArgs[postArgsLen++];
arg.key = argName;
arg.value = argValue;
if (line == ("--"+boundary+"--")){
#ifdef DEBUG
DEBUG_OUTPUT.println("Done Parsing POST");
@ -346,7 +390,7 @@ readfile:
_uploadWriteByte(argByte);
argByte = _uploadReadByte(client);
}
argByte = _uploadReadByte(client);
if (argByte == 0x0A){
argByte = _uploadReadByte(client);
@ -365,10 +409,10 @@ readfile:
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
client.readBytes(endBuf, boundary.length());
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if (_fileUploadHandler) _fileUploadHandler();
_currentUpload.totalSize += _currentUpload.currentSize;
@ -412,7 +456,7 @@ readfile:
}
}
}
int iarg;
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
for (iarg = 0; iarg < totalArgs; iarg++){
@ -429,7 +473,11 @@ readfile:
}
_currentArgCount = iarg;
if (postArgs) delete[] postArgs;
return true;
}
#ifdef DEBUG
DEBUG_OUTPUT.print("Error: line: ");
DEBUG_OUTPUT.println(line);
#endif
return false;
}

View File

@ -4,8 +4,8 @@
class RequestHandler {
public:
RequestHandler(const char* uri, HTTPMethod method)
: uri(uri)
, method(method)
: _uri(uri)
, _method(method)
, next(NULL)
{
}
@ -15,8 +15,8 @@ public:
RequestHandler* next;
protected:
String uri;
HTTPMethod method;
String _uri;
HTTPMethod _method;
};
@ -25,47 +25,59 @@ class FunctionRequestHandler : public RequestHandler {
public:
FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
: fn(fn)
: _fn(fn)
, base(uri, method)
{
}
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (method != HTTP_ANY && method != requestMethod)
if (_method != HTTP_ANY && _method != requestMethod)
return false;
if (requestUri != uri)
if (requestUri != _uri)
return false;
fn();
_fn();
return true;
}
protected:
ESP8266WebServer::THandlerFunction fn;
ESP8266WebServer::THandlerFunction _fn;
};
class StaticRequestHandler : public RequestHandler {
typedef RequestHandler base;
public:
StaticRequestHandler(FS& fs, const char* uri)
: fs(fs)
StaticRequestHandler(FS& fs, const char* path, const char* uri)
: _fs(fs)
, base(uri, HTTP_GET)
, _path(path)
{
_isFile = fs.exists(path);
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d\r\n", path, uri, _isFile);
_baseUriLength = _uri.length();
}
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != method)
if (requestMethod != _method)
return false;
DEBUGV("StaticRequestHandler::handle: %s\r\n", requestUri.c_str());
if (!requestUri.startsWith(uri))
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.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");
String path(_path);
if (!_isFile) {
// Base URI doesn't point to a file. Append whatever follows this
// URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
}
else if (requestUri != _uri) {
// Base URI points to a file but request doesn't match this URI exactly
return false;
}
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
File f = _fs.open(path, "r");
if (!f)
return false;
@ -90,7 +102,10 @@ public:
}
protected:
FS fs;
FS _fs;
String _path;
bool _isFile;
size_t _baseUriLength;
};
#endif //REQUESTHANDLER_H

View File

@ -0,0 +1,88 @@
/*
* HTTP over TLS (HTTPS) example sketch
*
* This example demonstrates how to use
* WiFiClientSecure class to access HTTPS API.
* We fetch and display the status of
* esp8266/Arduino project continous integration
* build.
*
* Created by Ivan Grokhotkov, 2015.
* This example is in public domain.
*/
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
const char* ssid = "........";
const char* password = "........";
const char* host = "api.github.com";
const int httpsPort = 443;
// Use web browser to view and copy
// SHA1 fingerprint of the certificate
const char* fingerprint = "CF 05 98 89 CA FF 8E D8 5E 5C E0 C2 E4 F7 E6 C3 C7 50 DD 5C";
void setup() {
Serial.begin(115200);
Serial.println();
Serial.print("connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Use WiFiClientSecure class to create TLS connection
WiFiClientSecure client;
Serial.print("connecting to ");
Serial.println(host);
if (!client.connect(host, httpsPort)) {
Serial.println("connection failed");
return;
}
if (client.verify(fingerprint, host)) {
Serial.println("certificate matches");
} else {
Serial.println("certificate doesn't match");
}
String url = "/repos/esp8266/Arduino/commits/esp8266/status";
Serial.print("requesting URL: ");
Serial.println(url);
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: BuildFailureDetectorESP8266\r\n" +
"Connection: close\r\n\r\n");
Serial.println("request sent");
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("headers received");
break;
}
}
String line = client.readStringUntil('\n');
if (line.startsWith("{\"state\":\"success\"")) {
Serial.println("esp8266/Arduino CI successfull!");
} else {
Serial.println("esp8266/Arduino CI has failed");
}
Serial.println("reply was:");
Serial.println("==========");
Serial.println(line);
Serial.println("==========");
Serial.println("closing connection");
}
void loop() {
}

View File

@ -16,6 +16,7 @@ WiFi KEYWORD1
WiFiClient KEYWORD1
WiFiServer KEYWORD1
WiFiUDP KEYWORD1
WiFiClientSecure KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -40,6 +41,7 @@ localIP KEYWORD2
subnetMask KEYWORD2
gatewayIP KEYWORD2
SSID KEYWORD2
psk KEYWORD2
BSSID KEYWORD2
RSSI KEYWORD2
encryptionType KEYWORD2
@ -64,4 +66,3 @@ scanNetworks KEYWORD2
WIFI_AP LITERAL1
WIFI_STA LITERAL1
WIFI_AP_STA LITERAL1

View File

@ -43,10 +43,11 @@ extern "C" void esp_yield();
ESP8266WiFiClass::ESP8266WiFiClass()
: _smartConfigStarted(false)
, _smartConfigDone(false)
, _useApMode(false)
, _useClientMode(false)
, _useStaticIp(false)
{
uint8 m = wifi_get_opmode();
_useClientMode = (m & WIFI_STA);
_useApMode = (m & WIFI_AP);
wifi_set_event_handler_cb((wifi_event_handler_cb_t)&ESP8266WiFiClass::_eventCallback);
}
@ -73,6 +74,11 @@ void ESP8266WiFiClass::mode(WiFiMode m)
ETS_UART_INTR_ENABLE();
}
WiFiMode ESP8266WiFiClass::getMode()
{
return (WiFiMode)wifi_get_opmode();
}
void ESP8266WiFiClass::_mode(WiFiMode m)
{
if(wifi_get_opmode() == (uint8)m) {
@ -139,6 +145,17 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch
return status();
}
int ESP8266WiFiClass::begin()
{
ETS_UART_INTR_DISABLE();
wifi_station_connect();
ETS_UART_INTR_ENABLE();
if(!_useStaticIp)
wifi_station_dhcpc_start();
return status();
}
uint8_t ESP8266WiFiClass::waitForConnectResult(){
if ((wifi_get_opmode() & 1) == 0)//1 and 3 have STA enabled
return WL_DISCONNECTED;
@ -353,11 +370,18 @@ IPAddress ESP8266WiFiClass::gatewayIP()
return IPAddress(ip.gw.addr);
}
char* ESP8266WiFiClass::SSID()
String ESP8266WiFiClass::SSID() const
{
static struct station_config conf;
wifi_station_get_config(&conf);
return reinterpret_cast<char*>(conf.ssid);
return String(reinterpret_cast<char*>(conf.ssid));
}
String ESP8266WiFiClass::psk() const
{
static struct station_config conf;
wifi_station_get_config(&conf);
return String(reinterpret_cast<char*>(conf.password));
}
uint8_t* ESP8266WiFiClass::BSSID(void)

View File

@ -44,6 +44,7 @@ public:
ESP8266WiFiClass();
void mode(WiFiMode);
WiFiMode getMode();
/**
* Start Wifi connection
@ -57,6 +58,9 @@ public:
int begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL);
int begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL);
// Use sdk config to connect.
int begin();
/* Wait for Wifi connection to reach a result
* returns the status reached or disconnect if STA is off
@ -169,14 +173,21 @@ public:
*
* return: ssid string
*/
char* SSID();
String SSID() const;
/*
* Return the current pre shared key associated with the network
*
* return: psk string
*/
String psk() const;
/*
* Return the current bssid / mac associated with the network if configured
*
* return: bssid uint8_t *
*/
uint8_t * BSSID(void);
uint8_t *BSSID(void);
/*
* Return the current bssid / mac associated with the network if configured

View File

@ -177,6 +177,33 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size)
return _client->write(reinterpret_cast<const char*>(buf), size);
}
size_t WiFiClient::write_P(PGM_P buf, size_t size)
{
if (!_client || !size)
{
return 0;
}
char chunkUnit[WIFICLIENT_MAX_PACKET_SIZE + 1];
chunkUnit[WIFICLIENT_MAX_PACKET_SIZE] = '\0';
size_t remaining_size = size;
while (buf != NULL && remaining_size > 0) {
size_t chunkUnitLen = WIFICLIENT_MAX_PACKET_SIZE;
if (remaining_size < WIFICLIENT_MAX_PACKET_SIZE) chunkUnitLen = remaining_size;
// due to the memcpy signature, lots of casts are needed
memcpy_P((void*)chunkUnit, (PGM_VOID_P)buf, chunkUnitLen);
buf += chunkUnitLen;
remaining_size -= chunkUnitLen;
// write is so overloaded, had to use the cast to get it pick the right one
_client->write((const char*)chunkUnit, chunkUnitLen);
}
return size;
}
int WiFiClient::available()
{
if (!_client)
@ -263,6 +290,22 @@ uint16_t WiFiClient::remotePort()
return _client->getRemotePort();
}
IPAddress WiFiClient::localIP()
{
if (!_client)
return IPAddress(0U);
return IPAddress(_client->getLocalAddress());
}
uint16_t WiFiClient::localPort()
{
if (!_client)
return 0;
return _client->getLocalPort();
}
int8_t WiFiClient::_s_connected(void* arg, void* tpcb, int8_t err)
{
return reinterpret_cast<WiFiClient*>(arg)->_connected(tpcb, err);
@ -284,3 +327,16 @@ void WiFiClient::stopAll()
}
}
}
void WiFiClient::stopAllExcept(WiFiClient * exC) {
for (WiFiClient* it = _s_first; it; it = it->_next) {
ClientContext* c = it->_client;
if (c && c != exC->_client) {
c->abort();
c->unref();
it->_client = 0;
}
}
}

View File

@ -21,13 +21,15 @@
#ifndef wificlient_h
#define wificlient_h
#include "Arduino.h"
#include "Arduino.h"
#include "Print.h"
#include "Client.h"
#include "IPAddress.h"
#include <memory>
#include "include/slist.h"
#define WIFICLIENT_MAX_PACKET_SIZE 1460
class ClientContext;
class WiFiServer;
@ -46,6 +48,7 @@ public:
virtual int connect(const char *host, uint16_t port);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
size_t write_P(PGM_P buf, size_t size);
template <typename T>
size_t write(T& source, size_t unitSize);
@ -60,25 +63,27 @@ public:
IPAddress remoteIP();
uint16_t remotePort();
IPAddress localIP();
uint16_t localPort();
bool getNoDelay();
void setNoDelay(bool nodelay);
static void setLocalPortStart(uint16_t port) { _localPort = port; }
template<typename T> size_t write(T &src){
uint8_t obuf[1460];
uint8_t obuf[WIFICLIENT_MAX_PACKET_SIZE];
size_t doneLen = 0;
size_t sentLen;
int i;
while (src.available() > 1460){
src.read(obuf, 1460);
sentLen = write(obuf, 1460);
while (src.available() > WIFICLIENT_MAX_PACKET_SIZE){
src.read(obuf, WIFICLIENT_MAX_PACKET_SIZE);
sentLen = write(obuf, WIFICLIENT_MAX_PACKET_SIZE);
doneLen = doneLen + sentLen;
if(sentLen != 1460){
if(sentLen != WIFICLIENT_MAX_PACKET_SIZE){
return doneLen;
}
}
uint16_t leftLen = src.available();
src.read(obuf, leftLen);
sentLen = write(obuf, leftLen);
@ -91,8 +96,9 @@ public:
using Print::write;
static void stopAll();
static void stopAllExcept(WiFiClient * c);
private:
protected:
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
static void _s_err(void* arg, int8_t err);

View File

@ -0,0 +1,412 @@
/*
WiFiClientSecure.cpp - Variant of WiFiClient with TLS support
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
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define LWIP_INTERNAL
extern "C"
{
#include "osapi.h"
#include "ets_sys.h"
}
#include <errno.h>
#include "debug.h"
#include "cbuf.h"
#include "ESP8266WiFi.h"
#include "WiFiClientSecure.h"
#include "WiFiClient.h"
#include "lwip/opt.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/inet.h"
#include "lwip/netif.h"
#include "cbuf.h"
#include "include/ClientContext.h"
#include "c_types.h"
//#define DEBUG_SSL
#ifdef DEBUG_SSL
#define SSL_DEBUG_OPTS SSL_DISPLAY_STATES
#else
#define SSL_DEBUG_OPTS 0
#endif
#define SSL_RX_BUF_SIZE 1536
class SSLContext {
public:
SSLContext() {
if (_ssl_ctx_refcnt == 0) {
_ssl_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS, 0);
}
++_ssl_ctx_refcnt;
_rxbuf = new cbuf(SSL_RX_BUF_SIZE);
}
~SSLContext() {
if (_ssl) {
ssl_free(_ssl);
_ssl = nullptr;
}
--_ssl_ctx_refcnt;
if (_ssl_ctx_refcnt == 0) {
ssl_ctx_free(_ssl_ctx);
}
delete _rxbuf;
}
void ref() {
++_refcnt;
}
void unref() {
if (--_refcnt == 0) {
delete this;
}
}
void connect(ClientContext* ctx) {
_ssl = ssl_client_new(_ssl_ctx, reinterpret_cast<int>(ctx), nullptr, 0);
}
int read(uint8_t* dst, size_t size) {
if (size > _rxbuf->getSize()) {
_readAll();
}
return _rxbuf->read(reinterpret_cast<char*>(dst), size);
}
int read() {
optimistic_yield(100);
if (!_rxbuf->getSize()) {
_readAll();
}
return _rxbuf->read();
}
int peek() {
if (!_rxbuf->getSize()) {
_readAll();
}
return _rxbuf->peek();
}
int available() {
auto rc = _rxbuf->getSize();
if (rc == 0) {
_readAll();
rc = _rxbuf->getSize();
} else {
optimistic_yield(100);
}
return rc;
}
operator SSL*() {
return _ssl;
}
protected:
int _readAll() {
if (!_ssl)
return 0;
uint8_t* data;
int rc = ssl_read(_ssl, &data);
if (rc <= 0) {
if (rc < SSL_OK && rc != SSL_CLOSE_NOTIFY && rc != SSL_ERROR_CONN_LOST) {
ssl_free(_ssl);
_ssl = nullptr;
}
return 0;
}
if (rc > _rxbuf->room()) {
DEBUGV("WiFiClientSecure rx overflow");
rc = _rxbuf->room();
}
int result = 0;
size_t sizeBefore = _rxbuf->getSize();
if (rc)
result = _rxbuf->write(reinterpret_cast<const char*>(data), rc);
DEBUGV("*** rb: %d + %d = %d\r\n", sizeBefore, rc, _rxbuf->getSize());
return result;
}
static SSL_CTX* _ssl_ctx;
static int _ssl_ctx_refcnt;
SSL* _ssl = nullptr;
int _refcnt = 0;
cbuf* _rxbuf;
};
SSL_CTX* SSLContext::_ssl_ctx = nullptr;
int SSLContext::_ssl_ctx_refcnt = 0;
WiFiClientSecure::WiFiClientSecure() {
}
WiFiClientSecure::~WiFiClientSecure() {
if (_ssl) {
_ssl->unref();
}
}
WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure& other)
: WiFiClient(static_cast<const WiFiClient&>(other))
{
_ssl = other._ssl;
if (_ssl) {
_ssl->ref();
}
}
WiFiClientSecure& WiFiClientSecure::operator=(const WiFiClientSecure& rhs) {
(WiFiClient&) *this = rhs;
_ssl = rhs._ssl;
if (_ssl) {
_ssl->ref();
}
return *this;
}
int WiFiClientSecure::connect(IPAddress ip, uint16_t port) {
if (!WiFiClient::connect(ip, port))
return 0;
return _connectSSL();
}
int WiFiClientSecure::connect(const char* name, uint16_t port) {
if (!WiFiClient::connect(name, port))
return 0;
return 1;
}
int WiFiClientSecure::_connectSSL() {
if (_ssl) {
_ssl->unref();
_ssl = nullptr;
}
_ssl = new SSLContext;
_ssl->ref();
_ssl->connect(_client);
auto status = ssl_handshake_status(*_ssl);
if (status != SSL_OK) {
_ssl->unref();
_ssl = nullptr;
return 0;
}
return 1;
}
size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) {
if (!_ssl)
return 0;
int rc = ssl_write(*_ssl, buf, size);
if (rc >= 0)
return rc;
return 0;
}
int WiFiClientSecure::read(uint8_t *buf, size_t size) {
if (!_ssl)
return 0;
return _ssl->read(buf, size);
}
int WiFiClientSecure::read() {
if (!_ssl)
return -1;
return _ssl->read();
}
int WiFiClientSecure::peek() {
if (!_ssl)
return -1;
return _ssl->peek();
}
int WiFiClientSecure::available() {
if (!_ssl)
return 0;
return _ssl->available();
}
uint8_t WiFiClientSecure::connected() {
if (_client->state() == ESTABLISHED)
return 1;
if (!_ssl)
return 0;
return _ssl->available() > 0;
}
void WiFiClientSecure::stop() {
if (_ssl) {
_ssl->unref();
_ssl = nullptr;
}
return WiFiClient::stop();
}
static bool parseHexNibble(char pb, uint8_t* res) {
if (pb >= '0' && pb <= '9') {
*res = (uint8_t) (pb - '0'); return true;
}
else if (pb >= 'a' && pb <= 'f') {
*res = (uint8_t) (pb - 'a' + 10); return true;
}
else if (pb >= 'A' && pb <= 'F') {
*res = (uint8_t) (pb - 'A' + 10); return true;
}
return false;
}
bool WiFiClientSecure::verify(const char* fp, const char* url) {
if (!_ssl)
return false;
uint8_t sha1[20];
int len = strlen(fp);
int pos = 0;
for (int i = 0; i < sizeof(sha1); ++i) {
while (pos < len && fp[pos] == ' ') {
++pos;
}
DEBUGV("pos:%d ", pos);
if (pos > len - 2) {
DEBUGV("fingerprint too short\r\n");
return false;
}
uint8_t high, low;
if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) {
DEBUGV("invalid hex sequence: %c%c\r\n", fp[pos], fp[pos+1]);
return false;
}
pos += 2;
sha1[i] = low | (high << 4);
}
if (ssl_match_fingerprint(*_ssl, sha1) != 0) {
DEBUGV("fingerprint doesn't match\r\n");
return false;
}
//TODO: check URL against certificate
return true;
}
extern "C" int ax_port_read(int fd, uint8_t* buffer, size_t count) {
ClientContext* _client = reinterpret_cast<ClientContext*>(fd);
if (_client->state() != ESTABLISHED && !_client->getSize()) {
return -1;
errno = EIO;
}
size_t cb = _client->read((char*) buffer, count);
if (cb != count) {
errno = EAGAIN;
}
if (cb == 0) {
optimistic_yield(100);
return -1;
}
return cb;
}
extern "C" int ax_port_write(int fd, uint8_t* buffer, size_t count) {
ClientContext* _client = reinterpret_cast<ClientContext*>(fd);
if (_client->state() != ESTABLISHED) {
errno = EIO;
return -1;
}
size_t cb = _client->write((const char*) buffer, count);
if (cb != count) {
errno = EAGAIN;
}
return cb;
}
extern "C" int ax_get_file(const char *filename, uint8_t **buf) {
*buf = 0;
return 0;
}
#ifdef DEBUG_TLS_MEM
#define DEBUG_TLS_MEM_PRINT(...) DEBUGV(__VA_ARGS__)
#else
#define DEBUG_TLS_MEM_PRINT(...)
#endif
extern "C" void* ax_port_malloc(size_t size, const char* file, int line) {
void* result = malloc(size);
if (result == nullptr) {
DEBUG_TLS_MEM_PRINT("%s:%d malloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap());
while(true){}
}
if (size >= 1024)
DEBUG_TLS_MEM_PRINT("%s:%d malloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap());
return result;
}
extern "C" void* ax_port_calloc(size_t size, size_t count, const char* file, int line) {
void* result = ax_port_malloc(size * count, file, line);
memset(result, 0, size * count);
return result;
}
extern "C" void* ax_port_realloc(void* ptr, size_t size, const char* file, int line) {
void* result = realloc(ptr, size);
if (result == nullptr) {
DEBUG_TLS_MEM_PRINT("%s:%d realloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap());
while(true){}
}
if (size >= 1024)
DEBUG_TLS_MEM_PRINT("%s:%d realloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap());
return result;
}
extern "C" void ax_port_free(void* ptr) {
free(ptr);
uint32_t *p = (uint32_t*) ptr;
size_t size = p[-3];
if (size >= 1024)
DEBUG_TLS_MEM_PRINT("free %d, left %d\r\n", p[-3], ESP.getFreeHeap());
}

View File

@ -0,0 +1,57 @@
/*
WiFiClientSecure.h - Variant of WiFiClient with TLS support
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
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef wificlientsecure_h
#define wificlientsecure_h
#include "WiFiClient.h"
#include "include/ssl.h"
class SSLContext;
class WiFiClientSecure : public WiFiClient {
public:
WiFiClientSecure();
~WiFiClientSecure() override;
WiFiClientSecure(const WiFiClientSecure&);
WiFiClientSecure& operator=(const WiFiClientSecure&);
int connect(IPAddress ip, uint16_t port) override;
int connect(const char* name, uint16_t port) override;
bool verify(const char* fingerprint, const char* url);
uint8_t connected() override;
size_t write(const uint8_t *buf, size_t size) override;
int read(uint8_t *buf, size_t size) override;
int available() override;
int read() override;
int peek() override;
void stop() override;
protected:
int _connectSSL();
SSLContext* _ssl;
};
#endif //wificlientsecure_h

View File

@ -290,3 +290,12 @@ void WiFiUDP::stopAll()
it->stop();
}
}
void WiFiUDP::stopAllExcept(WiFiUDP * exC) {
for (WiFiUDP* it = _s_first; it; it = it->_next) {
if (it->_ctx != exC->_ctx) {
DEBUGV("%s %08x %08x\n", __func__, (uint32_t) it, (uint32_t) _s_first);
it->stop();
}
}
}

View File

@ -105,6 +105,7 @@ public:
uint16_t localPort();
static void stopAll();
static void stopAllExcept(WiFiUDP * exC);
};

View File

@ -1,9 +1,9 @@
/*
/*
ClientContext.h - TCP connection handling on top of lwIP
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
@ -39,7 +39,7 @@ class ClientContext {
tcp_sent(pcb, &_s_sent);
tcp_err(pcb, &_s_error);
}
err_t abort(){
if(_pcb) {
DEBUGV(":abort\r\n");
@ -52,7 +52,7 @@ class ClientContext {
}
return ERR_ABRT;
}
err_t close(){
err_t err = ERR_OK;
if(_pcb) {
@ -71,7 +71,7 @@ class ClientContext {
}
return err;
}
~ClientContext() {
}
@ -101,18 +101,18 @@ class ClientContext {
}
}
}
void setNoDelay(bool nodelay){
if(!_pcb) return;
if(nodelay) tcp_nagle_disable(_pcb);
else tcp_nagle_enable(_pcb);
}
bool getNoDelay(){
if(!_pcb) return false;
return tcp_nagle_disabled(_pcb);
}
uint32_t getRemoteAddress() {
if(!_pcb) return 0;
@ -125,6 +125,18 @@ class ClientContext {
return _pcb->remote_port;
}
uint32_t getLocalAddress() {
if(!_pcb) return 0;
return _pcb->local_ip.addr;
}
uint16_t getLocalPort() {
if(!_pcb) return 0;
return _pcb->local_port;
}
size_t getSize() const {
if(!_rx_buf) return 0;
@ -265,16 +277,11 @@ class ClientContext {
void _error(err_t err) {
DEBUGV(":er %d %d %d\r\n", err, _size_sent, _send_waiting);
if (err != ERR_ABRT) {
abort();
}
else {
tcp_arg(_pcb, NULL);
tcp_sent(_pcb, NULL);
tcp_recv(_pcb, NULL);
tcp_err(_pcb, NULL);
_pcb = NULL;
}
tcp_arg(_pcb, NULL);
tcp_sent(_pcb, NULL);
tcp_recv(_pcb, NULL);
tcp_err(_pcb, NULL);
_pcb = NULL;
if(_size_sent && _send_waiting) {
esp_schedule();
}

View File

@ -0,0 +1,510 @@
/*
* Copyright (c) 2007, Cameron Rich
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the axTLS project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @mainpage axTLS API
*
* @image html axolotl.jpg
*
* The axTLS library has features such as:
* - The TLSv1 SSL client/server protocol
* - No requirement to use any openssl libraries.
* - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers.
* - RSA encryption/decryption with variable sized keys (up to 4096 bits).
* - Certificate chaining and peer authentication.
* - Session resumption, session renegotiation.
* - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding.
* - Highly configurable compile time options.
* - Portable across many platforms (written in ANSI C), and has language
* bindings in C, C#, VB.NET, Java, Perl and Lua.
* - Partial openssl API compatibility (via a wrapper).
* - A very small footprint (around 50-60kB for the library in 'server-only'
* mode).
* - No dependencies on sockets - can use serial connections for example.
* - A very simple API - ~ 20 functions/methods.
*
* A list of these functions/methods are described below.
*
* @ref c_api
*
* @ref bigint_api
*
* @ref csharp_api
*
* @ref java_api
*/
#ifndef HEADER_SSL_H
#define HEADER_SSL_H
#ifdef __cplusplus
extern "C" {
#endif
/* need to predefine before ssl_lib.h gets to it */
#define SSL_SESSION_ID_SIZE 32
#define EXP_FUNC
#define STDCALL
// struct SSL_CTX_;
typedef struct SSL_CTX_ SSL_CTX;
typedef struct SSL_ SSL;
/* The optional parameters that can be given to the client/server SSL engine */
#define SSL_CLIENT_AUTHENTICATION 0x00010000
#define SSL_SERVER_VERIFY_LATER 0x00020000
#define SSL_NO_DEFAULT_KEY 0x00040000
#define SSL_DISPLAY_STATES 0x00080000
#define SSL_DISPLAY_BYTES 0x00100000
#define SSL_DISPLAY_CERTS 0x00200000
#define SSL_DISPLAY_RSA 0x00400000
#define SSL_CONNECT_IN_PARTS 0x00800000
/* errors that can be generated */
#define SSL_OK 0
#define SSL_NOT_OK -1
#define SSL_ERROR_DEAD -2
#define SSL_CLOSE_NOTIFY -3
#define SSL_ERROR_CONN_LOST -256
#define SSL_ERROR_SOCK_SETUP_FAILURE -258
#define SSL_ERROR_INVALID_HANDSHAKE -260
#define SSL_ERROR_INVALID_PROT_MSG -261
#define SSL_ERROR_INVALID_HMAC -262
#define SSL_ERROR_INVALID_VERSION -263
#define SSL_ERROR_INVALID_SESSION -265
#define SSL_ERROR_NO_CIPHER -266
#define SSL_ERROR_BAD_CERTIFICATE -268
#define SSL_ERROR_INVALID_KEY -269
#define SSL_ERROR_FINISHED_INVALID -271
#define SSL_ERROR_NO_CERT_DEFINED -272
#define SSL_ERROR_NO_CLIENT_RENOG -273
#define SSL_ERROR_NOT_SUPPORTED -274
#define SSL_X509_OFFSET -512
#define SSL_X509_ERROR(A) (SSL_X509_OFFSET+A)
/* alert types that are recognized */
#define SSL_ALERT_TYPE_WARNING 1
#define SLL_ALERT_TYPE_FATAL 2
/* these are all the alerts that are recognized */
#define SSL_ALERT_CLOSE_NOTIFY 0
#define SSL_ALERT_UNEXPECTED_MESSAGE 10
#define SSL_ALERT_BAD_RECORD_MAC 20
#define SSL_ALERT_HANDSHAKE_FAILURE 40
#define SSL_ALERT_BAD_CERTIFICATE 42
#define SSL_ALERT_ILLEGAL_PARAMETER 47
#define SSL_ALERT_DECODE_ERROR 50
#define SSL_ALERT_DECRYPT_ERROR 51
#define SSL_ALERT_INVALID_VERSION 70
#define SSL_ALERT_NO_RENEGOTIATION 100
/* The ciphers that are supported */
#define SSL_AES128_SHA 0x2f
#define SSL_AES256_SHA 0x35
#define SSL_RC4_128_SHA 0x05
#define SSL_RC4_128_MD5 0x04
/* build mode ids' */
#define SSL_BUILD_SKELETON_MODE 0x01
#define SSL_BUILD_SERVER_ONLY 0x02
#define SSL_BUILD_ENABLE_VERIFICATION 0x03
#define SSL_BUILD_ENABLE_CLIENT 0x04
#define SSL_BUILD_FULL_MODE 0x05
/* offsets to retrieve configuration information */
#define SSL_BUILD_MODE 0
#define SSL_MAX_CERT_CFG_OFFSET 1
#define SSL_MAX_CA_CERT_CFG_OFFSET 2
#define SSL_HAS_PEM 3
/* default session sizes */
#define SSL_DEFAULT_SVR_SESS 5
#define SSL_DEFAULT_CLNT_SESS 1
/* X.509/X.520 distinguished name types */
#define SSL_X509_CERT_COMMON_NAME 0
#define SSL_X509_CERT_ORGANIZATION 1
#define SSL_X509_CERT_ORGANIZATIONAL_NAME 2
#define SSL_X509_CA_CERT_COMMON_NAME 3
#define SSL_X509_CA_CERT_ORGANIZATION 4
#define SSL_X509_CA_CERT_ORGANIZATIONAL_NAME 5
/* SSL object loader types */
#define SSL_OBJ_X509_CERT 1
#define SSL_OBJ_X509_CACERT 2
#define SSL_OBJ_RSA_KEY 3
#define SSL_OBJ_PKCS8 4
#define SSL_OBJ_PKCS12 5
/**
* @defgroup c_api Standard C API
* @brief The standard interface in C.
* @{
*/
/**
* @brief Establish a new client/server context.
*
* This function is called before any client/server SSL connections are made.
*
* Each new connection will use the this context's private key and
* certificate chain. If a different certificate chain is required, then a
* different context needs to be be used.
*
* There are two threading models supported - a single thread with one
* SSL_CTX can support any number of SSL connections - and multiple threads can
* support one SSL_CTX object each (the default). But if a single SSL_CTX
* object uses many SSL objects in individual threads, then the
* CONFIG_SSL_CTX_MUTEXING option needs to be configured.
*
* @param options [in] Any particular options. At present the options
* supported are:
* - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server
* authentication fails. The certificate can be authenticated later with a
* call to ssl_verify_cert().
* - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication
* i.e. each handshake will include a "certificate request" message from the
* server. Only available if verification has been enabled.
* - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences
* during the handshake.
* - SSL_DISPLAY_STATES (full mode build only): Display the state changes
* during the handshake.
* - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that
* are passed during a handshake.
* - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that
* are passed during a handshake.
* - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of
* ssl_client_new().
* @param num_sessions [in] The number of sessions to be used for session
* caching. If this value is 0, then there is no session caching. This option
* is not used in skeleton mode.
* @return A client/server context.
*/
EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions);
/**
* @brief Remove a client/server context.
*
* Frees any used resources used by this context. Each connection will be
* sent a "Close Notify" alert (if possible).
* @param ssl_ctx [in] The client/server context.
*/
EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx);
/**
* @brief (server only) Establish a new SSL connection to an SSL client.
*
* It is up to the application to establish the logical connection (whether it
* is a socket, serial connection etc).
* @param ssl_ctx [in] The server context.
* @param client_fd [in] The client's file descriptor.
* @return An SSL object reference.
*/
EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd);
/**
* @brief (client only) Establish a new SSL connection to an SSL server.
*
* It is up to the application to establish the initial logical connection
* (whether it is a socket, serial connection etc).
*
* This is a normally a blocking call - it will finish when the handshake is
* complete (or has failed). To use in non-blocking mode, set
* SSL_CONNECT_IN_PARTS in ssl_ctx_new().
* @param ssl_ctx [in] The client context.
* @param client_fd [in] The client's file descriptor.
* @param session_id [in] A 32 byte session id for session resumption. This
* can be null if no session resumption is being used or required. This option
* is not used in skeleton mode.
* @param sess_id_size The size of the session id (max 32)
* @return An SSL object reference. Use ssl_handshake_status() to check
* if a handshake succeeded.
*/
EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size);
/**
* @brief Free any used resources on this connection.
* A "Close Notify" message is sent on this connection (if possible). It is up
* to the application to close the socket or file descriptor.
* @param ssl [in] The ssl object reference.
*/
EXP_FUNC void STDCALL ssl_free(SSL *ssl);
/**
* @brief Read the SSL data stream.
* If the socket is non-blocking and data is blocked then SSO_OK will be
* returned.
* @param ssl [in] An SSL object reference.
* @param in_data [out] If the read was successful, a pointer to the read
* buffer will be here. Do NOT ever free this memory as this buffer is used in
* sucessive calls. If the call was unsuccessful, this value will be null.
* @return The number of decrypted bytes:
* - if > 0, then the handshaking is complete and we are returning the number
* of decrypted bytes.
* - SSL_OK if the handshaking stage is successful (but not yet complete).
* - < 0 if an error.
* @see ssl.h for the error code list.
* @note Use in_data before doing any successive ssl calls.
*/
EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data);
/**
* @brief Write to the SSL data stream.
* if the socket is non-blocking and data is blocked then a check is made
* to ensure that all data is sent (i.e. blocked mode is forced).
* @param ssl [in] An SSL obect reference.
* @param out_data [in] The data to be written
* @param out_len [in] The number of bytes to be written.
* @return The number of bytes sent, or if < 0 if an error.
* @see ssl.h for the error code list.
*/
EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len);
/**
* @brief Find an ssl object based on a file descriptor.
*
* Goes through the list of SSL objects maintained in a client/server context
* to look for a file descriptor match.
* @param ssl_ctx [in] The client/server context.
* @param client_fd [in] The file descriptor.
* @return A reference to the SSL object. Returns null if the object could not
* be found.
*/
EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd);
/**
* @brief Get the session id for a handshake.
*
* This will be a 32 byte sequence and is available after the first
* handshaking messages are sent.
* @param ssl [in] An SSL object reference.
* @return The session id as a 32 byte sequence.
* @note A SSLv23 handshake may have only 16 valid bytes.
*/
EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl);
/**
* @brief Get the session id size for a handshake.
*
* This will normally be 32 but could be 0 (no session id) or something else.
* @param ssl [in] An SSL object reference.
* @return The size of the session id.
*/
EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl);
/**
* @brief Return the cipher id (in the SSL form).
* @param ssl [in] An SSL object reference.
* @return The cipher id. This will be one of the following:
* - SSL_AES128_SHA (0x2f)
* - SSL_AES256_SHA (0x35)
* - SSL_RC4_128_SHA (0x05)
* - SSL_RC4_128_MD5 (0x04)
*/
EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl);
/**
* @brief Return the status of the handshake.
* @param ssl [in] An SSL object reference.
* @return SSL_OK if the handshake is complete and ok.
* @see ssl.h for the error code list.
*/
EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl);
/**
* @brief Retrieve various parameters about the axTLS engine.
* @param offset [in] The configuration offset. It will be one of the following:
* - SSL_BUILD_MODE The build mode. This will be one of the following:
* - SSL_BUILD_SERVER_ONLY (basic server mode)
* - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication)
* - SSL_BUILD_ENABLE_CLIENT (client/server capabilties)
* - SSL_BUILD_FULL_MODE (client/server with diagnostics)
* - SSL_BUILD_SKELETON_MODE (skeleton mode)
* - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed.
* - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed.
* - SSL_HAS_PEM 1 if supported
* @return The value of the requested parameter.
*/
EXP_FUNC int STDCALL ssl_get_config(int offset);
/**
* @brief Display why the handshake failed.
*
* This call is only useful in a 'full mode' build. The output is to stdout.
* @param error_code [in] An error code.
* @see ssl.h for the error code list.
*/
EXP_FUNC void STDCALL ssl_display_error(int error_code);
/**
* @brief Authenticate a received certificate.
*
* This call is usually made by a client after a handshake is complete and the
* context is in SSL_SERVER_VERIFY_LATER mode.
* @param ssl [in] An SSL object reference.
* @return SSL_OK if the certificate is verified.
*/
EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl);
/**
* @brief Check if certificate fingerprint (SHA1) matches the one given.
*
* @param ssl [in] An SSL object reference.
* @param fp [in] SHA1 fingerprint to match against
* @return SSL_OK if the certificate is verified.
*/
EXP_FUNC int STDCALL ssl_match_fingerprint(const SSL *ssl, const uint8_t* fp);
/**
* @brief Retrieve an X.509 distinguished name component.
*
* When a handshake is complete and a certificate has been exchanged, then the
* details of the remote certificate can be retrieved.
*
* This will usually be used by a client to check that the server's common
* name matches the URL.
*
* @param ssl [in] An SSL object reference.
* @param component [in] one of:
* - SSL_X509_CERT_COMMON_NAME
* - SSL_X509_CERT_ORGANIZATION
* - SSL_X509_CERT_ORGANIZATIONAL_NAME
* - SSL_X509_CA_CERT_COMMON_NAME
* - SSL_X509_CA_CERT_ORGANIZATION
* - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME
* @return The appropriate string (or null if not defined)
* @note Verification build mode must be enabled.
*/
EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component);
/**
* @brief Retrieve a Subject Alternative DNSName
*
* When a handshake is complete and a certificate has been exchanged, then the
* details of the remote certificate can be retrieved.
*
* This will usually be used by a client to check that the server's DNS
* name matches the URL.
*
* @param ssl [in] An SSL object reference.
* @param dnsindex [in] The index of the DNS name to retrieve.
* @return The appropriate string (or null if not defined)
* @note Verification build mode must be enabled.
*/
EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int dnsindex);
/**
* @brief Force the client to perform its handshake again.
*
* For a client this involves sending another "client hello" message.
* For the server is means sending a "hello request" message.
*
* This is a blocking call on the client (until the handshake completes).
*
* @param ssl [in] An SSL object reference.
* @return SSL_OK if renegotiation instantiation was ok
*/
EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl);
/**
* @brief Process a file that is in binary DER or ASCII PEM format.
*
* These are temporary objects that are used to load private keys,
* certificates etc into memory.
* @param ssl_ctx [in] The client/server context.
* @param obj_type [in] The format of the file. Can be one of:
* - SSL_OBJ_X509_CERT (no password required)
* - SSL_OBJ_X509_CACERT (no password required)
* - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported)
* - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported)
* - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported)
*
* PEM files are automatically detected (if supported). The object type is
* also detected, and so is not relevant for these types of files.
* @param filename [in] The location of a file in DER/PEM format.
* @param password [in] The password used. Can be null if not required.
* @return SSL_OK if all ok
* @note Not available in skeleton build mode.
*/
EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password);
/**
* @brief Process binary data.
*
* These are temporary objects that are used to load private keys,
* certificates etc into memory.
* @param ssl_ctx [in] The client/server context.
* @param obj_type [in] The format of the memory data.
* @param data [in] The binary data to be loaded.
* @param len [in] The amount of data to be loaded.
* @param password [in] The password used. Can be null if not required.
* @return SSL_OK if all ok
* @see ssl_obj_load for more details on obj_type.
*/
EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password);
#ifdef CONFIG_SSL_GENERATE_X509_CERT
/**
* @brief Create an X.509 certificate.
*
* This certificate is a self-signed v1 cert with a fixed start/stop validity
* times. It is signed with an internal private key in ssl_ctx.
*
* @param ssl_ctx [in] The client/server context.
* @param options [in] Not used yet.
* @param dn [in] An array of distinguished name strings. The array is defined
* by:
* - SSL_X509_CERT_COMMON_NAME (0)
* - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the
* hostname will be used.
* - SSL_X509_CERT_ORGANIZATION (1)
* - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME
* will be used.
* - SSL_X509_CERT_ORGANIZATIONAL_NAME (2)
* - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional.
* @param cert_data [out] The certificate as a sequence of bytes.
* @return < 0 if an error, or the size of the certificate in bytes.
* @note cert_data must be freed when there is no more need for it.
*/
EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data);
#endif
/**
* @brief Return the axTLS library version as a string.
*/
EXP_FUNC const char * STDCALL ssl_version(void);
/** @} */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -128,10 +128,17 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port,
ret = HTTP_UPDATE_FAILD;
DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len);
} else {
if(ESP.updateSketch(tcp, len)) {
// may never reached!
WiFiUDP::stopAll();
WiFiClient::stopAllExcept(&tcp);
delay(100);
if(ESP.updateSketch(tcp, len, false, false)) {
ret = HTTP_UPDATE_OK;
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
tcp.stop();
ESP.restart();
} else {
ret = HTTP_UPDATE_FAILD;
DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n");

View File

@ -28,6 +28,8 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <WiFiClient.h>
//#define DEBUG_HTTP_UPDATE(...) Serial1.printf( __VA_ARGS__ )

View File

@ -0,0 +1,317 @@
/**
* @file OTA-mDNS-SPIFFS.ino
*
* @author Pascal Gollor (http://www.pgollor.de/cms/)
* @data 2015-09-18
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <FS.h>
/**
* @brief mDNS and OTA Constants
* @{
*/
#define HOSTNAME "ESP8266-OTA-" ///< Hostename. The setup function adds the Chip ID at the end.
#define APORT 8266 ///< Port for OTA update
/// @}
/**
* @brief Default WiFi connection information.
* @{
*/
const char* ap_default_ssid = "esp8266"; ///< Default SSID.
const char* ap_default_psk = "esp8266esp8266"; ///< Default PSK.
/// @}
/// OTA Update UDP server handle.
WiFiUDP OTA;
/**
* @brief Read WiFi connection information from file system.
* @param ssid String pointer for storing SSID.
* @param pass String pointer for storing PSK.
* @return True or False.
*
* The config file have to containt the WiFi SSID in the first line
* and the WiFi PSK in the second line.
* Line seperator have to be \r\n (CR LF).
*/
bool loadConfig(String *ssid, String *pass)
{
// open file for reading.
File configFile = SPIFFS.open("/cl_conf.txt", "r");
if (!configFile)
{
Serial.println("Failed to open cl_conf.txt.");
return false;
}
// Read content from config file.
String content = configFile.readString();
configFile.close();
content.trim();
// Check if ther is a second line available.
uint8_t pos = content.indexOf("\r\n");
if (pos == 0)
{
Serial.println("Infvalid content.");
Serial.println(content);
return false;
}
// Store SSID and PSK into string vars.
*ssid = content.substring(0, pos);
*pass = content.substring(pos + 2);
return true;
} // loadConfig
/**
* @brief Save WiFi SSID and PSK to configuration file.
* @param ssid SSID as string pointer.
* @param pass PSK as string pointer,
* @return True or False.
*/
bool saveConfig(String *ssid, String *pass)
{
// Open config file for writing.
File configFile = SPIFFS.open("/cl_conf.txt", "w");
if (!configFile)
{
Serial.println("Failed to open cl_conf.txt for writing");
return false;
}
// Save SSID and PSK.
configFile.println(*ssid);
configFile.println(*pass);
configFile.close();
return true;
} // saveConfig
/**
* @brief Handle OTA update stuff.
*
* This function comes from ESP8266 Arduino example:
* https://github.com/esp8266/Arduino/blob/esp8266/hardware/esp8266com/esp8266/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino
*
* Modification for uploading SPIFFS images from Pascal Gollor.
*
*/
static inline void ota_handle(void)
{
bool spiffs = false;
if (! OTA.parsePacket())
{
return;
}
// Get remote IP
IPAddress remote = OTA.remoteIP();
// Get command
int cmd = OTA.parseInt();
Serial.print("command: ");
Serial.println(cmd);
if (cmd == U_SPIFFS)
{
spiffs = true;
Serial.println("Get SPIFFS image.");
}
// Get remote port
int port = OTA.parseInt();
// Get sketch size.
int sketch_size = OTA.parseInt();
// Output stuff
Serial.print("Update Start: ip:");
Serial.print(remote);
Serial.printf(", port:%d, size:%d\r\n", port, sketch_size);
// Stop all UDP connections.
WiFiUDP::stopAll();
// OTA start Time
uint32_t startTime = millis();
// Start Updateing.
if(!Update.begin(sketch_size, cmd))
{
Serial.println("Update Begin Error");
return;
}
WiFiClient client;
if (client.connect(remote, port))
{
uint32_t written;
while(!Update.isFinished())
{
written = Update.write(client);
if(written > 0) client.print(written, DEC);
}
Serial.setDebugOutput(false);
if(Update.end())
{
client.println("OK");
Serial.printf("Update Success: %u\nRebooting...\n", (unsigned int)(millis() - startTime));
ESP.restart();
}
else
{
Update.printError(client);
Update.printError(Serial);
}
}
else
{
Serial.printf("Connect Failed: %u\n", (unsigned int)(millis() - startTime));
}
} // ota_handle
/**
* @brief Arduino setup function.
*/
void setup()
{
String station_ssid = "";
String station_psk = "";
Serial.begin(115200);
delay(100);
Serial.println("\r\n");
Serial.print("Chip ID: 0x");
Serial.println(ESP.getChipId(), HEX);
// Set Hostname.
String hostname(HOSTNAME);
hostname += String(ESP.getChipId(), HEX);
WiFi.hostname(hostname);
// Print hostname.
Serial.print("hostname: ");
Serial.println(WiFi.hostname());
// Initialize file system.
if (!SPIFFS.begin())
{
Serial.println("Failed to mount file system");
return;
}
// Load wifi connection information.
if (! loadConfig(&station_ssid, &station_psk))
{
station_ssid = "";
station_psk = "";
Serial.println("No WiFi connection information available.");
}
// Check WiFi connection
// ... check mode
if (WiFi.getMode() != WIFI_STA)
{
WiFi.mode(WIFI_STA);
delay(10);
}
// ... Compare file config with sdk config.
if (WiFi.SSID() != station_ssid || WiFi.psk() != station_psk)
{
Serial.println("WiFi config changed.");
// ... Try to connect to WiFi station.
WiFi.begin(station_ssid.c_str(), station_psk.c_str());
// ... Pritn new SSID
Serial.print("new SSID: ");
Serial.println(WiFi.SSID());
// ... Uncomment this for debugging output.
//WiFi.printDiag(Serial);
}
else
{
// ... Begin with sdk config.
WiFi.begin();
}
Serial.println("Wait for WiFi connection.");
// ... Give ESP 10 seconds to connect to station.
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000)
{
Serial.write('.');
//Serial.print(WiFi.status());
delay(500);
}
Serial.println();
// Check connection
if(WiFi.status() == WL_CONNECTED)
{
// ... print IP Address
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
else
{
Serial.println("Can not connect to WiFi station. Go into AP mode.");
// Go into software AP mode.
WiFi.mode(WIFI_AP);
delay(10);
WiFi.softAP(ap_default_ssid, ap_default_psk);
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP());
}
// Initialize mDNS service.
MDNS.begin(hostname.c_str());
// ... Add OTA service.
MDNS.addService("arduino", "tcp", APORT);
// Open OTA Server.
OTA.begin(APORT);
}
/**
* @brief Arduino loop function.
*/
void loop()
{
// Handle OTA update.
ota_handle();
}

View File

@ -0,0 +1,2 @@
YOUR_SSID
YOUR_PSK

View File

@ -146,6 +146,10 @@ Servo::Servo()
_servoIndex = s_servoCount++;
// store default values
s_servos[_servoIndex].usPulse = DEFAULT_PULSE_WIDTH;
// set default _minUs and _maxUs incase write() is called before attach()
_minUs = MIN_PULSE_WIDTH;
_maxUs = MAX_PULSE_WIDTH;
}
else {
_servoIndex = INVALID_SERVO; // too many servos

View File

@ -12,7 +12,7 @@ runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx1
runtime.tools.esptool.path={runtime.platform.path}/tools
compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/
compiler.sdk.path={runtime.platform.path}/tools/sdk/
compiler.sdk.path={runtime.platform.path}/tools/sdk
compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include"
compiler.c.cmd=xtensa-lx106-elf-gcc
@ -24,7 +24,7 @@ compiler.S.flags=-c -g -x assembler-with-cpp -MMD
compiler.c.elf.flags=-g -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy
compiler.c.elf.cmd=xtensa-lx106-elf-gcc
compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig -lwps -lcrypto
compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig -lwps -lcrypto -laxtls
compiler.cpp.cmd=xtensa-lx106-elf-g++
compiler.cpp.flags=-c -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD
@ -93,7 +93,7 @@ tools.esptool.upload.protocol=esp
tools.esptool.upload.params.verbose=-vv
tools.esptool.upload.params.quiet=
tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin"
tools.esptool.network.pattern=python "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin"
tools.esptool.network.pattern=python "{path}/espota.py" -i "{serial.port}" -p "{network.port}" -f "{build.path}/{build.project_name}.bin"
tools.espota.cmd=python
tools.espota.cmd.windows=python.exe
@ -102,4 +102,4 @@ tools.espota.path={runtime.platform.path}/tools
tools.espota.upload.protocol=espota
tools.espota.upload.params.verbose=
tools.espota.upload.params.quiet=
tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" 8266 "{build.path}/{build.project_name}.bin"
tools.espota.upload.pattern="{cmd}" "{path}/espota.py" -i "{serial.port}" -p 8266 -f "{build.path}/{build.project_name}.bin"

View File

@ -4,7 +4,7 @@
void fail(const char* msg) {
Serial.println(msg);
while(true) {
while (true) {
yield();
}
}
@ -15,6 +15,21 @@ void setup() {
WiFi.mode(WIFI_OFF);
Serial.println("\n\nFS test\n");
{
if (!SPIFFS.format()) {
fail("format failed");
}
Dir root = SPIFFS.openDir("/");
int count = 0;
while (root.next()) {
++count;
}
if (count > 0) {
fail("some files left after format");
}
}
if (!SPIFFS.begin()) {
fail("SPIFFS init failed");
}
@ -63,15 +78,15 @@ void setup() {
{
Dir root = SPIFFS.openDir("/");
while (root.next()) {
String fileName = root.fileName();
File f = root.openFile("r");
Serial.printf("%s: %d\r\n", fileName.c_str(), f.size());
String fileName = root.fileName();
File f = root.openFile("r");
Serial.printf("%s: %d\r\n", fileName.c_str(), f.size());
}
}
{
Dir root = SPIFFS.openDir("/");
while(root.next()) {
while (root.next()) {
String fileName = root.fileName();
Serial.print("deleting ");
Serial.println(fileName);
@ -96,6 +111,20 @@ void setup() {
}
}
{
if (!SPIFFS.format()) {
fail("format failed");
}
Dir root = SPIFFS.openDir("/");
int count = 0;
while (root.next()) {
++count;
}
if (count > 0) {
fail("some files left after format");
}
}
Serial.println("success");
}

View File

@ -1,45 +1,63 @@
#!/usr/bin/env python
#
# this script will push an OTA update to the ESP
# use it like: python espota.py <ESP_IP_address> <sketch.bin>
# Original espoty.py comes from ...?
#
# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor)
#
# This script will push an OTA update to the ESP
# use it like: python espota.py -i <ESP_IP_address> -p <ESP_port> -f <sketch.bin>
#
# Changes
# 2015-09-18:
# - Add option parser.
# - Add logging.
# - Send command to controller to differ between flashing and transmitting SPIFFS image.
#
from __future__ import print_function
import socket
import sys
import os
import optparse
import logging
# Commands
FLASH = 0
SPIFFS = 100
def serve(remoteAddr, remotePort, filename):
def serve(remoteAddr, remotePort, filename, command = FLASH):
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverPort = 48266
server_address = ('0.0.0.0', serverPort)
print('Starting on %s:%s' % server_address, file=sys.stderr)
logging.info('Starting on %s:%s', str(server_address[0]), str(server_address[1]))
try:
sock.bind(server_address)
sock.listen(1)
except:
print('Listen Failed', file=sys.stderr)
logging.error("Listen Failed")
return 1
content_size = os.path.getsize(filename)
print('Upload size: %d' % content_size, file=sys.stderr)
message = '%d %d %d\n' % (0, serverPort, content_size)
logging.info('Upload size: %d', content_size)
message = '%d %d %d\n' % (command, serverPort, content_size)
# Wait for a connection
print('Sending invitation to:', remoteAddr, file=sys.stderr)
logging.info('Sending invitation to: %s', remoteAddr)
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
remote_address = (remoteAddr, int(remotePort))
sent = sock2.sendto(message, remote_address)
sock2.close()
print('Waiting for device...\n', file=sys.stderr)
logging.info('Waiting for device...\n')
try:
sock.settimeout(10)
connection, client_address = sock.accept()
sock.settimeout(None)
connection.settimeout(None)
except:
print('No response from device', file=sys.stderr)
logging.error('No response from device')
sock.close()
return 1
@ -57,23 +75,23 @@ def serve(remoteAddr, remotePort, filename):
connection.sendall(chunk)
res = connection.recv(4)
except:
print('\nError Uploading', file=sys.stderr)
logging.error('\nError Uploading')
connection.close()
f.close()
sock.close()
return 1
print('\nWaiting for result...\n', file=sys.stderr)
logging.info('\nWaiting for result...\n')
try:
connection.settimeout(60)
data = connection.recv(32)
print('Result: %s' % data, file=sys.stderr)
logging.info('Result: %s' ,data)
connection.close()
f.close()
sock.close()
return 0
except:
print('Result: No Answer!', file=sys.stderr)
logging.error('Result: No Answer!')
connection.close()
f.close()
sock.close()
@ -85,12 +103,94 @@ def serve(remoteAddr, remotePort, filename):
sock.close()
return 1
# end serve
def parser():
parser = optparse.OptionParser(
usage = "%prog [options]",
description = "Transmit image over the air to the esp8266 module with OTA support."
)
# destination ip and port
group = optparse.OptionGroup(parser, "Destination")
group.add_option("-i", "--ip",
dest = "esp_ip",
action = "store",
help = "ESP8266 IP Address.",
default = False
)
group.add_option("-p", "--port",
dest = "esp_port",
type = "int",
help = "ESP8266 ota Port. Default 8266",
default = 8266
)
parser.add_option_group(group)
# image
group = optparse.OptionGroup(parser, "Image")
group.add_option("-f", "--file",
dest = "image",
help = "Image file.",
metavar="FILE",
default = None
)
group.add_option("-s", "--spiffs",
dest = "spiffs",
action = "store_true",
help = "Use this option to transmit a SPIFFS image and do not flash the module.",
default = False
)
parser.add_option_group(group)
# output group
group = optparse.OptionGroup(parser, "Output")
group.add_option("-d", "--debug",
dest = "debug",
help = "Show debug output. And override loglevel with debug.",
action = "store_true",
default = False
)
parser.add_option_group(group)
(options, args) = parser.parse_args()
return options
# end parser
def main(args):
return serve(args[1], args[2], args[3])
# get options
options = parser()
# adapt log level
loglevel = logging.WARNING
if (options.debug):
loglevel = logging.DEBUG
# end if
# logging
logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
logging.debug("Options: %s", str(options))
# check options
if (not options.esp_ip or not options.image):
logging.critical("Not enough arguments.")
return 1
# end if
command = FLASH
if (options.spiffs):
command = SPIFFS
# end if
return serve(options.esp_ip, options.esp_port, options.image, command)
# end main
if __name__ == '__main__':
sys.exit(main(sys.argv))
sys.exit(main(sys.argv))
# end if

View File

@ -46,7 +46,7 @@
#ifdef USE_OPTIMIZE_PRINTF
#define os_printf(fmt, ...) do { \
static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \
static const char flash_str[] ICACHE_RODATA_ATTR __attribute__((aligned(4))) = fmt; \
os_printf_plus(flash_str, ##__VA_ARGS__); \
} while(0)
#else

View File

@ -0,0 +1,20 @@
/* Flash Split for 4M chips */
/* sketch 1019KB */
/* empty 2048KB */
/* spiffs 1004KB */
/* eeprom 20KB */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40201010, len = 0xfeff0
}
PROVIDE ( _SPIFFS_start = 0x40500000 );
PROVIDE ( _SPIFFS_end = 0x405FB000 );
PROVIDE ( _SPIFFS_page = 0x100 );
PROVIDE ( _SPIFFS_block = 0x2000 );
INCLUDE "../ld/eagle.app.v6.common.ld"

BIN
tools/sdk/lib/libaxtls.a Normal file

Binary file not shown.

Binary file not shown.