1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Merge remote-tracking branch 'esp8266/master'

This commit is contained in:
Me No Dev 2015-11-25 15:59:34 +02:00
commit 324576f53e
44 changed files with 2182 additions and 689 deletions

View File

@ -283,6 +283,16 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
case 0x1340EF: // W25Q40 case 0x1340EF: // W25Q40
return (512_kB); return (512_kB);
// BergMicro
case 0x1640E0: // BG25Q32
return (4_MB);
case 0x1540E0: // BG25Q16
return (2_MB);
case 0x1440E0: // BG25Q80
return (1_MB);
case 0x1340E0: // BG25Q40
return (512_kB);
default: default:
return 0; return 0;
} }

View File

@ -0,0 +1,68 @@
/**
StreamString.cpp
Copyright (c) 2015 Markus Sattler. 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
*/
#include <Arduino.h>
#include "StreamString.h"
size_t StreamString::write(const uint8_t *buffer, size_t size) {
if(reserve(length() + size + 1)) {
for(size_t i = 0; i < size; i++) {
if(write(*buffer)) {
buffer++;
} else {
return i;
}
}
}
return 0;
}
size_t StreamString::write(uint8_t data) {
return concat((char) data);
}
int StreamString::available() {
return length();
}
int StreamString::read() {
if(length()) {
char c = charAt(0);
remove(0, 1);
return c;
}
return -1;
}
int StreamString::peek() {
if(length()) {
char c = charAt(0);
return c;
}
return -1;
}
void StreamString::flush() {
}

View File

@ -0,0 +1,40 @@
/**
StreamString.h
Copyright (c) 2015 Markus Sattler. 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 STREAMSTRING_H_
#define STREAMSTRING_H_
class StreamString: public Stream, public String {
size_t write(const uint8_t *buffer, size_t size);
size_t write(uint8_t data);
int available();
int read();
int peek();
void flush();
};
#endif /* STREAMSTRING_H_ */

View File

@ -17,14 +17,16 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
#include <debug.h>
extern "C" { extern "C" {
#include "ets_sys.h" #include "ets_sys.h"
#include "os_type.h" #include "os_type.h"
#include "osapi.h" #include "osapi.h"
#include "mem.h" #include "mem.h"
#include "user_interface.h"
} }
void *operator new(size_t size) { void *operator new(size_t size) {
size = ((size + 3) & ~((size_t)0x3)); size = ((size + 3) & ~((size_t)0x3));
return os_malloc(size); return os_malloc(size);
@ -47,27 +49,26 @@ extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));
void __cxa_pure_virtual(void) { void __cxa_pure_virtual(void) {
abort(); panic();
} }
void __cxa_deleted_virtual(void) { void __cxa_deleted_virtual(void) {
abort(); panic();
} }
namespace std { namespace std {
void __throw_bad_function_call() { void __throw_bad_function_call() {
abort(); panic();
} }
void __throw_length_error(char const*) { void __throw_length_error(char const*) {
abort(); panic();
} }
void __throw_bad_alloc() { void __throw_bad_alloc() {
abort(); panic();
} }
} }
// TODO: rebuild windows toolchain to make this unnecessary: // TODO: rebuild windows toolchain to make this unnecessary:
void* __dso_handle; void* __dso_handle;

View File

@ -66,13 +66,6 @@ static os_event_t g_loop_queue[LOOP_QUEUE_SIZE];
static uint32_t g_micros_at_task_start; static uint32_t g_micros_at_task_start;
extern "C" void abort() {
do {
*((int*)0) = 0;
} while(true);
}
extern "C" void esp_yield() { extern "C" void esp_yield() {
if (cont_can_yield(&g_cont)) { if (cont_can_yield(&g_cont)) {
cont_yield(&g_cont); cont_yield(&g_cont);
@ -89,7 +82,7 @@ extern "C" void __yield() {
esp_yield(); esp_yield();
} }
else { else {
abort(); panic();
} }
} }
@ -117,9 +110,8 @@ static void loop_wrapper() {
static void loop_task(os_event_t *events) { static void loop_task(os_event_t *events) {
g_micros_at_task_start = system_get_time(); g_micros_at_task_start = system_get_time();
cont_run(&g_cont, &loop_wrapper); cont_run(&g_cont, &loop_wrapper);
if(cont_check(&g_cont) != 0) { if (cont_check(&g_cont) != 0) {
ets_printf("\r\nsketch stack overflow detected\r\n"); panic();
abort();
} }
} }

View File

@ -22,6 +22,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include "debug.h"
#include "ets_sys.h" #include "ets_sys.h"
#include "user_interface.h" #include "user_interface.h"
#include "esp8266_peri.h" #include "esp8266_peri.h"
@ -30,6 +31,11 @@
extern void __real_system_restart_local(); extern void __real_system_restart_local();
extern cont_t g_cont; extern cont_t g_cont;
static const char* s_panic_file = 0;
static int s_panic_line = 0;
static const char* s_panic_func = 0;
static bool s_abort_called = false;
void uart_write_char_d(char c); void uart_write_char_d(char c);
static void uart0_write_char_d(char c); static void uart0_write_char_d(char c);
@ -56,7 +62,13 @@ void __wrap_system_restart_local() {
ets_install_putc1(&uart_write_char_d); ets_install_putc1(&uart_write_char_d);
if (rst_info.reason == REASON_EXCEPTION_RST) { if (s_panic_line) {
ets_printf("\nPanic %s:%d %s\n", s_panic_file, s_panic_line, s_panic_func);
}
else if (s_abort_called) {
ets_printf("Abort called\n");
}
else if (rst_info.reason == REASON_EXCEPTION_RST) {
ets_printf("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n", 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); rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc);
} }
@ -158,3 +170,25 @@ static void uart1_write_char_d(char c) {
} }
USF(1) = c; USF(1) = c;
} }
void abort() __attribute__((noreturn));
void abort(){
// cause exception
s_abort_called = true;
do {
*((int*)0) = 0;
} while(true);
}
void __assert_func(const char *file, int line, const char *func, const char *what) {
s_panic_file = file;
s_panic_line = line;
s_panic_func = func;
}
void __panic_func(const char* file, int line, const char* func) {
s_panic_file = file;
s_panic_line = line;
s_panic_func = func;
abort();
}

View File

@ -2,6 +2,8 @@
#define ARD_DEBUG_H #define ARD_DEBUG_H
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
//#define DEBUGV(...) ets_printf(__VA_ARGS__) //#define DEBUGV(...) ets_printf(__VA_ARGS__)
#ifndef DEBUGV #ifndef DEBUGV
@ -14,4 +16,16 @@ void hexdump(uint8_t *mem, uint32_t len, uint8_t cols = 16);
void hexdump(uint8_t *mem, uint32_t len, uint8_t cols); void hexdump(uint8_t *mem, uint32_t len, uint8_t cols);
#endif #endif
#ifdef __cplusplus
extern "C" {
#endif
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
#define panic() __panic_func(__FILE__, __LINE__, __func__)
#ifdef __cplusplus
}
#endif
#endif//ARD_DEBUG_H #endif//ARD_DEBUG_H

View File

@ -124,4 +124,5 @@ int gettimeofday(struct timeval *tp, void *tzp)
tp->tv_sec = (s_bootTime + millis()) / 1000; tp->tv_sec = (s_bootTime + millis()) / 1000;
tp->tv_usec = micros() * 1000; tp->tv_usec = micros() * 1000;
} }
return 0;
} }

View File

@ -2,24 +2,36 @@
title: Supported Hardware title: Supported Hardware
--- ---
- [Adafruit HUZZAH ESP8266 (ESP-12)](#adafruit-huzzah-esp8266-esp-12) ## Table of contents
- [NodeMCU 0.9](#nodemcu-0-9) * [Adafruit HUZZAH ESP8266 (ESP\-12)](#adafruit-huzzah-esp8266-esp-12)
- [NodeMCU 1.0](#nodemcu-1-0) * [NodeMCU 0\.9 <a name="user\-content\-nodemcu\-0\-9"></a>](#nodemcu-09-)
- [Olimex MOD-WIFI-ESP8266-DEV](#olimex-mod-wifi-esp8266-dev) * [Pin mapping](#pin-mapping)
- [Olimex MOD-WIFI-ESP8266](#olimex-mod-wifi-esp8266) * [NodeMCU 1\.0](#nodemcu-10)
- [SparkFun ESP8266 Thing](#sparkfun-esp8266-thing) * [Olimex MOD\-WIFI\-ESP8266\-DEV](#olimex-mod-wifi-esp8266-dev)
- [SweetPea ESP-210](#sweetpea-esp-210) * [Olimex MOD\-WIFI\-ESP8266](#olimex-mod-wifi-esp8266)
- [Generic ESP8266 modules](#generic-esp8266-modules) * [SparkFun ESP8266 Thing](#sparkfun-esp8266-thing)
- [WeMos D1](#wemos-d1) * [SweetPea ESP\-210](#sweetpea-esp-210)
- [WeMos D1 mini](#wemos-d1-mini) * [Generic ESP8266 modules](#generic-esp8266-modules)
* [Serial Adapter](#serial-adapter)
* [Minimal Hardware Setup for Bootloading and Usage](#minimal-hardware-setup-for-bootloading-and-usage)
* [ESP to Serial](#esp-to-serial)
* [Minimal Hardware Setup for Bootloading only](#minimal-hardware-setup-for-bootloading-only)
* [Minimal Hardware Setup for Running only](#minimal-hardware-setup-for-running-only)
* [Minimal](#minimal)
* [Improved Stability](#improved-stability)
* [Boot Messages and Modes](#boot-messages-and-modes)
* [rst cause](#rst-cause)
* [boot mode](#boot-mode)
* [WeMos D1](#wemos-d1)
* [WeMos D1 mini](#wemos-d1-mini)
### Adafruit HUZZAH ESP8266 (ESP-12) ## Adafruit HUZZAH ESP8266 (ESP-12)
*TODO: add notes* *TODO: add notes*
### NodeMCU 0.9 <a name="nodemcu-0-9"></a> ## NodeMCU 0.9
#### Pin mapping ### Pin mapping
Pin numbers written on the board itself do not correspond to ESP8266 GPIO pin numbers. Constants are defined to make using this board easier: Pin numbers written on the board itself do not correspond to ESP8266 GPIO pin numbers. Constants are defined to make using this board easier:
@ -39,7 +51,7 @@ static const uint8_t D10 = 1;
If you want to use NodeMCU pin 5, use D5 for pin number, and it will be translated to 'real' GPIO pin 14. If you want to use NodeMCU pin 5, use D5 for pin number, and it will be translated to 'real' GPIO pin 14.
### NodeMCU 1.0 ## NodeMCU 1.0
This module is sold under many names for around $6.50 on AliExpress and it's one of the cheapest, fully integrated ESP8266 solutions. This module is sold under many names for around $6.50 on AliExpress and it's one of the cheapest, fully integrated ESP8266 solutions.
@ -51,7 +63,7 @@ The board also features a NCP1117 voltage regulator, a blue LED on GPIO16 and a
Full pinout and PDF schematics can be found [here](https://github.com/nodemcu/nodemcu-devkit-v1.0) Full pinout and PDF schematics can be found [here](https://github.com/nodemcu/nodemcu-devkit-v1.0)
### Olimex MOD-WIFI-ESP8266-DEV ## Olimex MOD-WIFI-ESP8266-DEV
This board comes with 2 MB of SPI flash and optional accessories (e.g. evaluation board ESP8266-EVB or BAT-BOX for batteries). This board comes with 2 MB of SPI flash and optional accessories (e.g. evaluation board ESP8266-EVB or BAT-BOX for batteries).
@ -65,21 +77,21 @@ UART pins for programming and serial I/O are GPIO1 (TXD, pin 3) and GPIO3 (RXD,
Get the board schematics [here](https://github.com/OLIMEX/ESP8266/blob/master/HARDWARE/MOD-WIFI-ESP8266-DEV/MOD-WIFI-ESP8266-DEV_schematic.pdf) Get the board schematics [here](https://github.com/OLIMEX/ESP8266/blob/master/HARDWARE/MOD-WIFI-ESP8266-DEV/MOD-WIFI-ESP8266-DEV_schematic.pdf)
### Olimex MOD-WIFI-ESP8266 ## Olimex MOD-WIFI-ESP8266
This is a stripped down version of the above. Behaves identically in terms of jumpers but has less pins readily available for I/O. Still 2 MB of SPI flash. This is a stripped down version of the above. Behaves identically in terms of jumpers but has less pins readily available for I/O. Still 2 MB of SPI flash.
### SparkFun ESP8266 Thing ### ## SparkFun ESP8266 Thing ###
Product page: https://www.sparkfun.com/products/13231 Product page: https://www.sparkfun.com/products/13231
*TODO: add notes* *TODO: add notes*
### SweetPea ESP-210 ## SweetPea ESP-210
*TODO: add notes* *TODO: add notes*
### Generic ESP8266 modules ## Generic ESP8266 modules
These modules come in different form factors and pinouts. See the page at ESP8266 community wiki for more info: These modules come in different form factors and pinouts. See the page at ESP8266 community wiki for more info:
[ESP8266 Module Family](http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family). [ESP8266 Module Family](http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family).
@ -94,7 +106,7 @@ In order to use these modules, make sure to observe the following:
- **Put ESP8266 into bootloader mode** before uploading code. - **Put ESP8266 into bootloader mode** before uploading code.
### Serial Adapter ## Serial Adapter
There are many different USB to Serial adapters / boards. 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. 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.
@ -107,7 +119,7 @@ Adapters based around the following ICs should work:
PL2303-based adapters are known not to work on Mac OS X. See https://github.com/igrr/esptool-ck/issues/9 for more info. 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 ## Minimal Hardware Setup for Bootloading and Usage
| PIN | Resistor | Serial Adapter | | PIN | Resistor | Serial Adapter |
@ -127,10 +139,10 @@ PL2303-based adapters are known not to work on Mac OS X. See https://github.com/
- GPIO2 is alternative TX for the boot loader mode - 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.** - **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
![ESP to Serial](ESP_to_serial.png) ![ESP to Serial](ESP_to_serial.png)
#### Minimal Hardware Setup for Bootloading only ## ### Minimal Hardware Setup for Bootloading only ##
ESPxx Hardware ESPxx Hardware
| PIN | Resistor | Serial Adapter | | PIN | Resistor | Serial Adapter |
@ -147,7 +159,7 @@ ESPxx Hardware
* Note * Note
- if no RTS is used a manual power toggle is needed - if no RTS is used a manual power toggle is needed
#### Minimal Hardware Setup for Running only ## ### Minimal Hardware Setup for Running only ##
ESPxx Hardware ESPxx Hardware
@ -159,13 +171,13 @@ ESPxx Hardware
| GPIO15 | PullDown | | | GPIO15 | PullDown | |
| CH_PD | PullUp | | | CH_PD | PullUp | |
### Minimal ## Minimal
![ESP min](ESP_min.png) ![ESP min](ESP_min.png)
### Improved Stability ## Improved Stability
![ESP improved stability](ESP_improved_stability.png) ![ESP improved stability](ESP_improved_stability.png)
### Boot Messages and Modes ## Boot Messages and Modes
The ESP module checks at every boot the Pins 0, 2 and 15. The ESP module checks at every boot the Pins 0, 2 and 15.
based on them its boots in different modes: based on them its boots in different modes:
@ -185,7 +197,7 @@ rst cause:2, boot mode:(3,6)
note: note:
- GPIO2 is used as TX output and the internal Pullup is enabled on boot. - GPIO2 is used as TX output and the internal Pullup is enabled on boot.
#### rst cause ### rst cause
| Number | Description | | Number | Description |
| ------ | ---------------------- | | ------ | ---------------------- |
@ -196,7 +208,7 @@ note:
| 4 | watchdog reset | | 4 | watchdog reset |
#### boot mode ### boot mode
the first value respects the pin setup of the Pins 0, 2 and 15. the first value respects the pin setup of the Pins 0, 2 and 15.
@ -214,7 +226,8 @@ the first value respects the pin setup of the Pins 0, 2 and 15.
note: note:
- number = ((GPIO15 << 2) | (GPIO0 << 1) | GPIO2); - number = ((GPIO15 << 2) | (GPIO0 << 1) | GPIO2);
### WeMos D1 ## WeMos D1
Product page: http://wemos.cc Product page: http://wemos.cc
### WeMos D1 mini
## WeMos D1 mini
Product page: http://wemos.cc Product page: http://wemos.cc

View File

@ -1,5 +1,5 @@
--- ---
title: Change log title: Change Log
--- ---
*Current release* *Current release*

253
doc/filesystem.md Normal file
View File

@ -0,0 +1,253 @@
---
title: File System
---
## Table of Contents
* [Flash layout](#flash-layout)
* [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)
* [info](#info)
* [Filesystem information structure](#filesystem-information-structure)
* [Directory object (Dir)](#directory-object-dir)
* [File object](#file-object)
* [seek](#seek)
* [position](#position)
* [size](#size)
* [name](#name)
* [close](#close)
## Flash layout
Even though file system is stored on the same flash chip as the program, programming new sketch will not modify file system contents. This allows to use file system to store sketch data, configuration files, or content for Web server.
The following diagram illustrates flash layout used in Arduino environment:
|--------------|-------|---------------|--|--|--|--|--|
^ ^ ^ ^ ^
Sketch OTA update File system EEPROM WiFi config (SDK)
File system size depends on the flash chip size. Depending on the board which is selected in IDE, you have the following options for flash size:
Board | Flash chip size, bytes | File system size, bytes
------|-----------------|-----------------
Generic module | 512k | 64k
Generic module | 1M | 64k, 128k, 256k, 512k
Generic module | 2M | 1M
Generic module | 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 | 1M, 3M
WeMos D1 & D1 mini | 4M | 1M, 3M
**Note:** to use any of file system functions in the sketch, add the following include to the sketch:
```c++
#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: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.1.3/ESP8266FS-0.1.3.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
```c++
SPIFFS.begin()
```
This method mounts SPIFFS file system. It must be called before any other
FS APIs are used. Returns *true* if file system was mounted successfully, false
otherwise.
### 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++
SPIFFS.open(path, mode)
```
Opens a file. `path` should be an absolute path starting with a slash
(e.g. `/dir/filename.txt`). `mode` is a string specifying access mode. It can be
one of "r", "w", "a", "r+", "w+", "a+". Meaning of these modes is the same as
for `fopen` C function.
Returns *File* object. To check whether the file was opened successfully, use
the boolean operator.
```c++
File f = SPIFFS.open("/f.txt", "w");
if (!f) {
Serial.println("file open failed");
}
```
### exists
```c++
SPIFFS.exists(path)
```
Returns *true* if a file with given path exists, *false* otherwise.
### openDir
```c++
SPIFFS.openDir(path)
```
Opens a directory given its absolute path. Returns a *Dir* object. To check if
directory was opened successfully, use the boolean operator, similar to opening
a file.
### remove
```c++
SPIFFS.remove(path)
```
Deletes the file given its absolute path. Returns *true* if file was deleted successfully.
### rename
```c++
SPIFFS.rename(pathFrom, pathTo)
```
Renames file from `pathFrom` to `pathTo`. Paths must be absolute. Returns *true*
if file was renamed successfully.
### info
```c++
FSInfo fs_info;
SPIFFS.info(fs_info);
```
Fills [FSInfo structure](#filesystem-information-structure) with information about
the file system. Returns `true` is successful, `false` otherwise.
## Filesystem information structure
```c++
struct FSInfo {
size_t totalBytes;
size_t usedBytes;
size_t blockSize;
size_t pageSize;
size_t maxOpenFiles;
size_t maxPathLength;
};
```
This is the structure which may be filled using FS::info method. Field names
are self-explanatory.
## Directory object (Dir)
The purpose of *Dir* object is to iterate over files inside a directory.
It provides three methods: `next()`, `fileName()`, and `openFile(mode)`.
The following example shows how it should be used:
```c++
Dir dir = SPIFFS.openDir("/data");
while (dir.next()) {
Serial.print(dir.fileName());
File f = dir.openFile("r");
Serial.println(f.size());
}
```
`dir.next()` returns true while there are files in the directory to iterate over.
It must be called before calling `fileName` and `openFile` functions.
`openFile` method takes *mode* argument which has the same meaning as for `SPIFFS.open` function.
## File object
`SPIFFS.open` and `dir.openFile` functions return a *File* object. This object
supports all the functions of *Stream*, so you can use `readBytes`, `findUntil`,
`parseInt`, `println`, and all other *Stream* methods.
There are also some functions which are specific to *File* object.
### seek
```c++
file.seek(offset, mode)
```
This function behaves like `fseek` C function. Depending on the value of `mode`,
it moves current position in a file as follows:
- if `mode` is `SeekSet`, position is set to `offset` bytes from the beginning.
- if `mode` is `SeekCur`, current position is moved by `offset` bytes.
- if `mode` is `SeekEnd`, position is set to `offset` bytes from the end of the
file.
Returns *true* if position was set successfully.
### position
```c++
file.position()
```
Returns the current position inside the file, in bytes.
### size
```c++
file.size()
```
Returns file size, in bytes.
### name
```c++
String name = file.name();
```
Returns file name, as `const char*`. Convert it to *String* for storage.
### close
```c++
file.close()
```
Close the file. No other operations should be performed on *File* object after `close` function was called.

80
doc/installing.md Normal file
View File

@ -0,0 +1,80 @@
---
title: Installation
---
## Boards Manager ##
This is the suggested installation method for end users.
### Prerequisites
- Arduino 1.6.5, get it from [Arduino website](https://www.arduino.cc/en/Main/OldSoftwareReleases#previous). Arduino 1.6.6 has several issues, so we recommend to stick with 1.6.5 for now.
- Internet connection
### Instructions
- Start Arduino and open Preferences window.
- Enter ```http://arduino.esp8266.com/stable/package_esp8266com_index.json``` into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
- Open Boards Manager from Tools > Board menu and find *esp8266* platform.
- Select the version you need from a drop-down box.
- Click *install* button.
- Don't forget to select your ESP8266 board from Tools > Board menu after installation.
You may optionally use *staging* boards manager package link:
`http://arduino.esp8266.com/staging/package_esp8266com_index.json`. This may contain some new features, but at the same time, some things might be broken.
## Using git version
This is the suggested installation method for contributors and library developers.
### Prerequisites
- Arduino 1.6.5 (or newer, if you know what you are doing)
- git
- python 2.7
- terminal, console, or command prompt (depending on you OS)
- Internet connection
### Instructions
- Open the console and go to Arduino directory. This can be either your *sketchbook* directory (usually `<Documents>/Arduino`), or the directory of Arduino application itself, the choice is up to you.
- Clone this repository into hardware/esp8266com/esp8266 directory. Alternatively, clone it elsewhere and create a symlink, if your OS supports them.
```bash
cd hardware
mkdir esp8266com
cd esp8266com
git clone https://github.com/esp8266/Arduino.git esp8266
```
You should end up with the following directory structure:
```bash
Arduino
|
--- hardware
|
--- esp8266com
|
--- esp8266
|
--- bootloaders
--- cores
--- doc
--- libraries
--- package
--- tests
--- tools
--- variants
--- platform.txt
--- programmers.txt
--- README.md
--- boards.txt
--- LICENSE
```
- Download binary tools
```bash
cd esp8266/tools
python get.py
```
- Restart Arduino

153
doc/libraries.md Normal file
View File

@ -0,0 +1,153 @@
---
title: Libraries
---
## Table of Contents
* [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](#onewire)
* [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)
## WiFi(ESP8266WiFi library)
This is mostly similar to WiFi shield library. Differences include:
- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, `WIFI_AP_STA` or `WIFI_OFF`.
- call `WiFi.softAP(ssid)` to set up an open network
- call `WiFi.softAP(ssid, password)` to set up a WPA2-PSK network (password should be at least 8 characters)
- `WiFi.macAddress(mac)` is for STA, `WiFi.softAPmacAddress(mac)` is for AP.
- `WiFi.localIP()` is for STA, `WiFi.softAPIP()` is for AP.
- `WiFi.printDiag(Serial)` will print out some diagnostic info
- `WiFiUDP` class supports sending and receiving multicast packets on STA interface.
When sending a multicast packet, replace `udp.beginPacket(addr, port)` with
`udp.beginPacketMulticast(addr, port, WiFi.localIP())`.
When listening to multicast packets, replace `udp.begin(port)` with
`udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`.
You can use `udp.destinationIP()` to tell whether the packet received was
sent to the multicast or unicast address.
`WiFiServer`, `WiFiClient`, and `WiFiUDP` behave mostly the same way as with WiFi shield library.
Four samples are provided for this library.
You can see more commands here: [http://www.arduino.cc/en/Reference/WiFi](http://www.arduino.cc/en/Reference/WiFi)
## Ticker
Library for calling functions repeatedly with a certain period. Two examples included.
It is currently not recommended to do blocking IO operations (network, serial, file) from Ticker
callback functions. Instead, set a flag inside the ticker callback and check for that flag inside the loop function.
## EEPROM
This is a bit different from standard EEPROM class. You need to call `EEPROM.begin(size)`
before you start reading or writing, size being the number of bytes you want to use.
Size can be anywhere between 4 and 4096 bytes.
`EEPROM.write` does not write to flash immediately, instead you must call `EEPROM.commit()`
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 just after the SPIFFS.
Three examples included.
## I2C (Wire library)
Wire library currently supports master mode up to approximately 450KHz.
Before using I2C, pins for SDA and SCL need to be set by calling
`Wire.begin(int sda, int scl)`, i.e. `Wire.begin(0, 2)` on ESP-01,
else they default to pins 4(SDA) and 5(SCL).
## SPI
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 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.
`ESP.deepSleep(microseconds, mode)` will put the chip into deep sleep. `mode` is one of `WAKE_RF_DEFAULT`, `WAKE_RFCAL`, `WAKE_NO_RFCAL`, `WAKE_RF_DISABLED`. (GPIO16 needs to be tied to RST to wake from deepSleep.)
`ESP.restart()` restarts the CPU.
`ESP.getFreeHeap()` returns the free heap size.
`ESP.getChipId()` returns the ESP8266 chip ID as a 32-bit integer.
Several APIs may be used to get flash chip info:
`ESP.getFlashChipId()` returns the flash chip ID as a 32-bit integer.
`ESP.getFlashChipSize()` returns the flash chip size, in bytes, as seen by the SDK (may be less than actual size).
`ESP.getFlashChipSpeed(void)` returns the flash chip frequency, in Hz.
`ESP.getCycleCount()` returns the cpu instruction cycle count since start as an unsigned 32-bit. This is useful for accurate timing of very short actions like bit banging.
`ESP.getVcc()` may be used to measure supply voltage. ESP needs to reconfigure the ADC
at startup in order for this feature to be available. Add the following line to the top
of your sketch to use `getVcc`:
```c++
ADC_MODE(ADC_VCC);
```
TOUT pin has to be disconnected in this mode.
Note that by default ADC is configured to read from TOUT pin using `analogRead(A0)`, and
`ESP.getVCC()` is not available.
## OneWire
Library was adapted to work with ESP8266 by including register definitions into OneWire.h
Note that if you already have OneWire library in your Arduino/libraries folder, it will be used
instead of the one that comes with this package.
## mDNS and DNS-SD responder (ESP8266mDNS library)
Allows the sketch to respond to multicast DNS queries for domain names like "foo.local", and DNS-SD (service dicovery) queries.
See attached example for details.
## SSDP responder (ESP8266SSDP)
SSDP is another service discovery protocol, supported on Windows out of the box. See attached example for reference.
## DNS server (DNSServer library)
Implements a simple DNS server that can be used in both STA and AP modes. The DNS server currently supports only one domain (for all other domains it will reply with NXDOMAIN or custom status code). With it clients can open a web server running on ESP8266 using a domain name, not an IP address.
See attached example for details.
## Servo
This library exposes the ability to control RC (hobby) servo motors. It will support upto 24 servos on any available output pin. By defualt the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be effected.
While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply.
## Other libraries (not included with the IDE)
Libraries that don't rely on low-level access to AVR registers should work well. Here are a few libraries that were verified to work:
- [arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) - WebSocket Server and Client compatible with ESP8266 (RFC6455)
- [aREST](https://github.com/marcoschwartz/aREST) REST API handler library.
- [Blynk](https://github.com/blynkkk/blynk-library) - easy IoT framework for Makers (check out the [Kickstarter page](http://tiny.cc/blynk-kick)).
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library.git)
- [DHT-sensor-library](https://github.com/adafruit/DHT-sensor-library) - Arduino library for the DHT11/DHT22 temperature and humidity sensors. Download latest v1.1.1 library and no changes are necessary. Older versions should initialize DHT as follows: `DHT dht(DHTPIN, DHTTYPE, 15)`
- [NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel) - Adafruit's NeoPixel library, now with support for the ESP8266 (use version 1.0.2 or higher from Arduino's library manager).
- [NeoPixelBus](https://github.com/Makuna/NeoPixelBus) - Arduino NeoPixel library compatible with ESP8266. Use the "NeoPixelAnimator" branch for ESP8266 to get HSL color support and more.
- [PubSubClient](https://github.com/Imroy/pubsubclient) MQTT library by @Imroy.
- [RTC](https://github.com/Makuna/Rtc) - Arduino Library for Ds1307 & Ds3231 compatible with ESP8266.
- [Souliss, Smart Home](https://github.com/souliss/souliss) - Framework for Smart Home based on Arduino, Android and openHAB.
- [ST7735](https://github.com/nzmichaelh/Adafruit-ST7735-Library) - Adafruit's ST7735 library modified to be compatible with ESP8266. Just make sure to modify the pins in the examples as they are still AVR specific.

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -3,34 +3,64 @@ title: OTA Update
--- ---
## Table of Contents ## Table of Contents
* [Introduction](#introduction)
* [Security](#security)
* [Safety](#safety)
* [Basic Requirements](#basic-requirements) * [Basic Requirements](#basic-requirements)
* [Arduino IDE](#arduino-ide) * [Arduino IDE](#arduino-ide)
* [Requirements](#requirements)
* [Application Example](#application-example)
* [Classic OTA](#classic-ota)
* [ArduinoOTA](#arduinoota)
* [Web Browser](#web-browser)
* [Requirements](#requirements-1)
* [Implementation Overview](#implementation-overview)
* [Application Example](#application-example-1)
* [HTTP Server](#http-server) * [HTTP Server](#http-server)
* [Requirements](#requirements-2)
* [Arduino code](#arduino-code)
* [Simple updater](#simple-updater)
* [Advanced updater](#advanced-updater)
* [Server request handling](#server-request-handling)
* [Simple updater](#simple-updater-1)
* [Advanced updater](#advanced-updater-1)
* [Stream Interface](#stream-interface) * [Stream Interface](#stream-interface)
* [Updater class](#updater-class)
## Introduction ## Introduction
OTA (Over the Air) update is the process of loading the firmware to ESP module using WiFi connection rather that a serial port. Such functionality became extremely useful in case of limited or no physical access to the module. OTA (Over the Air) update is the process of loading the firmware to ESP module using Wi-Fi connection rather that a serial port. Such functionality became extremely useful in case of limited or no physical access to the module.
OTA may be done from: OTA may be done using:
- [Arduino IDE](#arduino-ide)
- [HTTP server](#http-server)
In any case first firmware upload have to be done over a serial port. If OTA routines are correctly implemented in sketch, then all subsequent uploads may be done over the air. * [Arduino IDE](#arduino-ide)
* [Web Browser](#web-browser)
* [HTTP Server](#http-server)
Arduino IDE option is intended primarily for software development phase. The two other options would be more useful after deployment, to provide module with application updates manually with a web browser or automatically using a http server.
In any case first firmware upload have to be done over a serial port. If OTA routines are correctly implemented in a sketch, then all subsequent uploads may be done over the air.
There is no imposed security on OTA process from being hacked. It is up to developer to ensure that updates are allowed only from legitimate / trusted source. Once update is complete, module restarts and new code is executed. Developer should ensure that application running on module is shut down and restarted in a safe manner. Chapters below provide additional information regarding security and safety of OTA process.
There is no imposed security on OTA process from being hacked. It is up to developer to ensure that updates are allowed only from legitimate / trusted source. Once update is complete module restarts and new code is executed. Developer should ensure that application running on module is shut down and restarted in safe manner. Chapters below provide additinal information regarding security and safety of OTA process.
### Security ### Security
Module has to be exposed wirelessly to get it updated with a new code. That poses chances of module being violently hacked and loaded with some other firmware. To reduce likelihood of being hacked consider protecting your uploads with a password, selecting certain OTA port, etc. Module has to be exposed wirelessly to get it updated with a new sketch. That poses chances of module being violently hacked and loaded with some other code. To reduce likelihood of being hacked consider protecting your uploads with a password, selecting certain OTA port, etc.
Check functionality provided with [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library that may improve security: Check functionality provided with [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library that may improve security:
```cpp ```cpp
void setPort(uint16_t port); void setPort(uint16_t port);
void setHostname(const char *hostname); void setHostname(const char* hostname);
void setPassword(const char *password); void setPassword(const char* password);
``` ```
If possible implement other means of protection from being hacked, e.g. exposing module for uploads only according to specific schedule, trigger OTA only be user pressing dedicated “Update” button, etc.
Certain protection functionality is already built in and do not require any additional coding by developer. [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) and espota.py use [Digest-MD5](https://en.wikipedia.org/wiki/Digest_access_authentication) to authenticate upload. Integrity of transferred data is verified on ESP side using [MD5](https://en.wikipedia.org/wiki/MD5) checksum.
Make your own risk analysis and depending on application decide what library functions to implement. If required consider implementation of other means of protection from being hacked, e.g. exposing module for uploads only according to specific schedule, trigger OTA only be user pressing dedicated “Update” button, etc.
### Safety ### Safety
@ -38,7 +68,8 @@ OTA process takes ESPs resources and bandwidth during upload. Then module is
If ESP is placed in remote location and controlling some equipment, you should put additional attention what happens if operation of this equipment is suddenly interrupted by update process. Therefore decide how to put this equipment into safe state before starting the update. For instance your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve left open, your garden may be flooded if this valve is not closed after OTA is finished and module restarts. If ESP is placed in remote location and controlling some equipment, you should put additional attention what happens if operation of this equipment is suddenly interrupted by update process. Therefore decide how to put this equipment into safe state before starting the update. For instance your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve left open, your garden may be flooded if this valve is not closed after OTA is finished and module restarts.
The following functions are provided with [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library and intended to handle functionality of your application during specific stages of OTA on or on an OTA error: The following functions are provided with [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library and intended to handle functionality of your application during specific stages of OTA or on an OTA error:
```cpp ```cpp
void onStart(OTA_CALLBACK(fn)); void onStart(OTA_CALLBACK(fn));
void onEnd(OTA_CALLBACK(fn)); void onEnd(OTA_CALLBACK(fn));
@ -46,14 +77,14 @@ void onProgress(OTA_CALLBACK_PROGRESS(fn));
void onError(OTA_CALLBACK_ERROR (fn)); void onError(OTA_CALLBACK_ERROR (fn));
``` ```
The following chapters provide more details and specific methods of doing OTA. ### Basic Requirements
## Basic Requirements
- Flash chip size is 2x the size of the sketch. - Flash chip size is 2x the size of the sketch.
The following chapters provide more details and specific methods of doing OTA.
## Arduino IDE ## Arduino IDE
Uploading modules wirelessly from Arduino IDE is intended for the following typical scenarios: Uploading modules wirelessly from Arduino IDE is intended for the following typical scenarios:
@ -61,44 +92,42 @@ Uploading modules wirelessly from Arduino IDE is intended for the following typi
- for updating small quantity of modules - for updating small quantity of modules
- only if modules are available on the same network as the computer with Arduino IDE - only if modules are available on the same network as the computer with Arduino IDE
#### Requirements
### Requirements
- The ESP and the computer must be connected to the same network. - The ESP and the computer must be connected to the same network.
#### Let's Do It
Currently there are two software configurations that support OTA updates ### Application Example
- [Classic OTA](#classic-ota-configuration): Arduino IDE 1.6.5 and [stable](https://github.com/esp8266/Arduino#staging-version-) (July 23, 2015) or [staging](https://github.com/esp8266/Arduino#staging-version-) (Sep 30, 2015) platform package that provides first OTA implementation, yet without support for [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This particular configuration is intended for less experienced users. It soon will be depreciated once implementation below is fully released.
Currently there are two software configurations that support OTA updates.
- [Classic OTA](#classic-ota-configuration): Arduino IDE 1.6.5 and 1.6.5-947-g39819f0 (of July 23, 2015) or 1.6.5-1160-gef26c5f (of Sep 30, 2015) version of platform package that provides first OTA implementation, yet without support for [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This particular configuration is easier to configure in Arduino IDE and therefore suggested for less experienced users. It soon will be depreciated once implementation below is fully released.
- [ArduinoOTA](#arduinoota-configuration): Arduino-PR-4107-BUILD-421 and latest git version of platform package that includes [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This configuration features preliminary build of Arduino IDE and is intended for more experienced users. Please mid your step. - [ArduinoOTA](#arduinoota-configuration): Arduino-PR-4107-BUILD-421 and latest git version of platform package that includes [ArduinoOTA](https://github.com/esp8266/Arduino/tree/master/libraries/ArduinoOTA) library. This configuration features preliminary build of Arduino IDE and is intended for more experienced users. Please mid your step.
Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-configuration) and [ArduinoOTA](#arduinoota-configuration) using NodeMCU 1.0 board with ESP-12E. Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-configuration) and [ArduinoOTA](#arduinoota-configuration) using NodeMCU 1.0 (ESP-12E Module) board.
##### Classic OTA Configuration
#### Classic OTA
1. Before you begin, please make sure that you have the following installed: 1. Before you begin, please make sure that you have the following installed:
- Arduino IDE and ESP8266 board support as described under https://github.com/esp8266/Arduino#installing-with-boards-manager - Arduino IDE and ESP8266 board support as described under https://github.com/esp8266/Arduino#installing-with-boards-manager
- [Python](https://www.python.org/) 2.7.10 (do not install Python 3.5.0 that is not supported): - [Python](https://www.python.org/) 2.7 (do not install Python 3.5 that is not supported):
**Note:** Windows users should select “Add python.exe to Path” (see below this option is not selected by default) **Note:** Windows users should select “Add python.exe to Path” (see below this option is not selected by default).
![Python installation set up](ota-ide-python-configuration.png) ![Python installation set up](ota-ide-python-configuration.png)
2. Now prepare the sketch and configuration for the upload over a serial port. 2. Now prepare the sketch and configuration for the upload over a serial port.
- Start Arduino IDE and load sketch DNS_SD_Arduino_OTA.ino available under File > Examples > ESP8266mDNS - Start Arduino IDE and load sketch DNS_SD_Arduino_OTA.ino available under File > Examples > ESP8266mDNS
![OTA sketch selection](ota-ide-sketch-selection.png) ![OTA sketch selection](ota-ide-sketch-selection.png)
**Note:** This sketch is available only for 1.6.5-947-g39819f0 (of July 23, 2015) and 1.6.5-1160-gef26c5f (of Sep 30, 2015) versions of platform packages installed in Arduino IDE using https://github.com/esp8266/Arduino#installing-with-boards-manager. It was removed in [#980](https://github.com/esp8266/Arduino/pull/980) from GitHub repository.
**Note:** This sketch is available only for stable (July 23, 2015) and staging (Sep 30, 2015) releases installed in Arduino IDE using https://github.com/esp8266/Arduino#installing-with-boards-manager. It was removed in [#980](https://github.com/esp8266/Arduino/pull/980) from Github repository. - Update ssid and pass in the sketch so the module can join your Wi-Fi network
- Update ssid and pass in the sketch so the module can join your WiFi network
![ssid and pass entry](ota-ide-ssid-pass-entry.png) ![ssid and pass entry](ota-ide-ssid-pass-entry.png)
- Configure upload parameters as below (you may need to adjust configuration if you are using a different module): - Configure upload parameters as below (you may need to adjust configuration if you are using a different module):
![configuration of serial upload](ota-ide-serial-upload-configuration.png) ![configuration of serial upload](ota-ide-serial-upload-configuration.png)
3. Upload the sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if the module has joined your WiFi network. 3. Upload the sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if module has joined your Wi-Fi network.
![check if module joined network](ota-ide-module-joined-wifi.png) ![check if module joined network](ota-ide-module-joined-wifi.png)
@ -110,7 +139,7 @@ Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-
![configuration of OTA upload](ota-ide-ota-upload-configuration.png) ![configuration of OTA upload](ota-ide-ota-upload-configuration.png)
**Note:** If you do not see “Upload Using: OTA” option available for “NodeMCU 1.0 (ESP-12E Module)” board, please upload the latest [boards.txt](https://github.com/esp8266/Arduino/blob/master/boards.txt) file from Github repository, replace existing file and restart Arduino IDE. **Note:** If you do not see “Upload Using: OTA” option available for “NodeMCU 1.0 (ESP-12E Module)” board, please upload the latest [boards.txt](https://github.com/esp8266/Arduino/blob/master/boards.txt) file from GitHub repository, replace existing file and restart Arduino IDE.
6. If you have successfully completed all the above steps, you can upload (Ctrl+U) the same (or any other) sketch over OTA: 6. If you have successfully completed all the above steps, you can upload (Ctrl+U) the same (or any other) sketch over OTA:
@ -118,18 +147,109 @@ Instructions below demonstrate how to configure both [Classic OTA](#classic-ota-
**Note** To be able to upload your sketch over and over again using OTA, you need to embed OTA routines inside. Please use DNS_SD_Arduino_OTA.ino as an example. **Note** To be able to upload your sketch over and over again using OTA, you need to embed OTA routines inside. Please use DNS_SD_Arduino_OTA.ino as an example.
##### ArduinoOTA Configuration
1. Get the following software: #### ArduinoOTA
1. Upload and install the following software:
- Arduino-PR-4107-BUILD-421 - https://github.com/esp8266/Arduino/pull/984#issuecomment-155905800 - Arduino-PR-4107-BUILD-421 - https://github.com/esp8266/Arduino/pull/984#issuecomment-155905800
- Latest git version of pacakge - https://github.com/esp8266/Arduino#using-git-version- - Latest git version of platform package - https://github.com/esp8266/Arduino#using-git-version-
- Python 2.7.10 - Python 2.7
2. Proceed to step 2 under [Classic OTA Configuration](#classic-ota-configuration) using BasicOTA.ino or OTALeds.ino skech instead. 2. Proceed to step 2 under [Classic OTA Configuration](#classic-ota-configuration) using BasicOTA.ino or OTALeds.ino sketch instead.
3. Carry on with remaining steps. 3. Carry on with remaining steps.
## Web Browser
Updates described in this chapter are done with a web browser that can be useful in the following typical scenarios:
- after application deployment if loading directly from Arduino IDE is inconvenient or not possible
- after deployment if user is unable to expose module for OTA from external update server
- to provide updates after deployment to small quantity of modules when setting an update server is not practicable
### Requirements
- The ESP and the computer must be connected to the same network.
### Implementation Overview
Updates with a web browswer are implemented using ```ESP8266HTTPUpdateServer``` class together with ```ESP8266WebServer``` and ```ESP8266mDNS``` classes. The following code is required to get it work:
setup()
```cpp
MDNS.begin(host);
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
```
loop()
```cpp
httpServer.handleClient();
```
### Application Example
The sample implementation provided below has been done using:
- example sketch WebUpdater.ino available in ESP8266HTTPUpdateServer library
- NodeMCU 1.0 (ESP-12E Module)
You can use another module if it meets “Flash chip size is 2x the size of the sketch” requirement.
1. Before you begin, please make sure that you have the following software installed:
- Arduino IDE and 2.0.0-rc1 (of Nov 17, 2015) version of platform package as described under https://github.com/esp8266/Arduino#installing-with-boards-manager
- Host software depending on O/S you use:
1. Avahi http://avahi.org/ for Linux
2. Bonjour http://www.apple.com/support/bonjour/ for Windows
3. Mac OSX and iOS - support is already built in / no any extra s/w is required
2. Prepare the sketch and configuration for initial upload with a serial port.
- Start Arduino IDE and load sketch WebUpdater.ino available under File > Examples > ESP8266HTTPUpdateServer.
- Update ssid and pass in the sketch so the module can join your Wi-Fi network.
- Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option.
![Preferences - enablig verbose output during compilation](ota-web-show-verbose-compilation.png)
**Note:** This setting will be required in step 5 below. You can uncheck this setting afterwards.
3. Upload sketch (Ctrl+U). Once done open Serial Monitor (Ctrl+Shift+M) and check if you see the following message displayed, that contains url for OTA update.
![Serial Monitor - after first load using serial](ota-web-serial-monitor-ready.png)
**Note:** Such message will be shown only after module successfully joins network and is ready for an OTA upload:
4. Now open web browser and enter the url provided on Serial Monitor, i.e. http://esp8266-webupdate.local/update. Once entered, browser should display a form like below that has been served by your module. The form invites you to choose a file for update.
![OTA update form in web browser](ota-web-browser-form.png)
**Note:** If entering “http://esp8266-webupdate.local/update” does not work, try replacing “esp8266-webupdate” with modules IP address. For example, if your module IP is “192.168.1.100” then url should be “http://192.168.1.100/update”. This workaround is useful in case the host software installed in step 2 does not work. If still nothing works and there are no clues on Serial Monitor, try to diagnose issue by opening provided url in Google Chrome, pressing F12 and checking contents of “Console” and “Network” tabs. Chrome provides some advanced logging on these tabs.
5. To obtain the file navigate to directory used by Arduino IDE to store results of compilation. You can check the path to this file in compilation log shown in IDE debug window as marked below.
![Compilation complete - path to binary file](ota-web-path-to-binary.png)
6. Now press “Choose File” in web browser, go to directory identified in step 5 above, find the file “WebUpdater.cpp.bin” and upload it. If upload is successful you will see “OK” on web browser like below.
![OTA update complete](ota-web-browser-form-ok.png)
Module will reboot that should be visible on Serial Monitor:
![Serial Monitor - after OTA update](ota-web-serial-monitor-reboot.png)
Just after reboot you should see exactly the same message “HTTPUpdateServer ready! Open http:// esp8266-webupdate.local /update in your browser” like in step 3. This is because module has been loaded again with the same code first using serial port, and then using OTA.
Once you are comfortable with this procedure go ahead and modify WebUpdater.ino sketch to print some additional messages, compile it, locate new binary file and upload it using web browser to see entered changes on a Serial Monitor.
You can also add OTA routines to your own sketch following guidelines in [Implementation Overview](#implementation-overview) above. If this is done correctly you should be always able to upload new sketch over the previous one using a web browser.
In case OTA update fails dead after entering modifications in your sketch, you can always recover module by loading it over a serial port. Then diagnose the issue with sketch using Serial Monitor. Once the issue is fixed try OTA again.
## HTTP Server ## HTTP Server
```ESPhttpUpdate``` class can check for updates and download a binary file from HTTP web server. ```ESPhttpUpdate``` class can check for updates and download a binary file from HTTP web server.
@ -199,6 +319,7 @@ Example header data:
With this information the script now can check if a update is needed. It is also possible to deliver different binaries based on the MAC address for example. With this information the script now can check if a update is needed. It is also possible to deliver different binaries based on the MAC address for example.
Script example: Script example:
```php ```php
<?PHP <?PHP
@ -261,6 +382,15 @@ header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500);
``` ```
## Stream Interface
TODO describe Stream Interface
The Stream Interface is the base for all other update modes like OTA, http Server / client.
## Updater class ## Updater class
TODO describe Updater class TODO describe Updater class
Updater is in the Core and deals with writing the firmware to the flash, checking its integrity and telling the bootloader to load the new firmware on the next boot.

View File

@ -3,44 +3,13 @@ title: Reference
--- ---
## Table of Contents ## Table of Contents
* [Table of Contents](#table-of-contents)
* [Digital IO](#digital-io) * [Digital IO](#digital-io)
* [Analog input](#analog-input) * [Analog input](#analog-input)
* [Analog output](#analog-output) * [Analog output](#analog-output)
* [Timing and delays](#timing-and-delays) * [Timing and delays](#timing-and-delays)
* [Serial](#serial) * [Serial](#serial)
* [Progmem](#progmem) * [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 ## Digital IO
@ -142,366 +111,3 @@ const char HTTP[] PROGMEM = "http:";
response2 += FPSTR(HTTP); response2 += FPSTR(HTTP);
} }
``` ```
## File system
Even though file system is stored on the same flash chip as the program, programming new sketch will not modify file system contents. This allows to use file system to store sketch data, configuration files, or content for Web server.
The following diagram illustrates flash layout used in Arduino environment:
|--------------|-------|---------------|--|--|--|--|--|
^ ^ ^ ^ ^
Sketch OTA update File system EEPROM WiFi config (SDK)
File system size depends on the flash chip size. Depending on the board which is selected in IDE, you have the following options for flash size:
Board | Flash chip size, bytes | File system size, bytes
------|-----------------|-----------------
Generic module | 512k | 64k
Generic module | 1M | 64k, 128k, 256k, 512k
Generic module | 2M | 1M
Generic module | 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 | 1M, 3M
WeMos D1 & D1 mini | 4M | 1M, 3M
**Note:** to use any of file system functions in the sketch, add the following include to the sketch:
```c++
#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: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.1.3/ESP8266FS-0.1.3.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
```c++
SPIFFS.begin()
```
This method mounts SPIFFS file system. It must be called before any other
FS APIs are used. Returns *true* if file system was mounted successfully, false
otherwise.
#### 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++
SPIFFS.open(path, mode)
```
Opens a file. `path` should be an absolute path starting with a slash
(e.g. `/dir/filename.txt`). `mode` is a string specifying access mode. It can be
one of "r", "w", "a", "r+", "w+", "a+". Meaning of these modes is the same as
for `fopen` C function.
Returns *File* object. To check whether the file was opened successfully, use
the boolean operator.
```c++
File f = SPIFFS.open("/f.txt", "w");
if (!f) {
Serial.println("file open failed");
}
```
#### exists
```c++
SPIFFS.exists(path)
```
Returns *true* if a file with given path exists, *false* otherwise.
#### openDir
```c++
SPIFFS.openDir(path)
```
Opens a directory given its absolute path. Returns a *Dir* object. To check if
directory was opened successfully, use the boolean operator, similar to opening
a file.
#### remove
```c++
SPIFFS.remove(path)
```
Deletes the file given its absolute path. Returns *true* if file was deleted successfully.
#### rename
```c++
SPIFFS.rename(pathFrom, pathTo)
```
Renames file from `pathFrom` to `pathTo`. Paths must be absolute. Returns *true*
if file was renamed successfully.
#### info
```c++
FSInfo fs_info;
SPIFFS.info(fs_info);
```
Fills [FSInfo structure](#filesystem-information-structure) with information about
the file system. Returns `true` is successful, `false` otherwise.
### Filesystem information structure
```c++
struct FSInfo {
size_t totalBytes;
size_t usedBytes;
size_t blockSize;
size_t pageSize;
size_t maxOpenFiles;
size_t maxPathLength;
};
```
This is the structure which may be filled using FS::info method. Field names
are self-explanatory.
### Directory object (Dir)
The purpose of *Dir* object is to iterate over files inside a directory.
It provides three methods: `next()`, `fileName()`, and `openFile(mode)`.
The following example shows how it should be used:
```c++
Dir dir = SPIFFS.openDir("/data");
while (dir.next()) {
Serial.print(dir.fileName());
File f = dir.openFile("r");
Serial.println(f.size());
}
```
`dir.next()` returns true while there are files in the directory to iterate over.
It must be called before calling `fileName` and `openFile` functions.
`openFile` method takes *mode* argument which has the same meaning as for `SPIFFS.open` function.
### File object
`SPIFFS.open` and `dir.openFile` functions return a *File* object. This object
supports all the functions of *Stream*, so you can use `readBytes`, `findUntil`,
`parseInt`, `println`, and all other *Stream* methods.
There are also some functions which are specific to *File* object.
#### seek
```c++
file.seek(offset, mode)
```
This function behaves like `fseek` C function. Depending on the value of `mode`,
it moves current position in a file as follows:
- if `mode` is `SeekSet`, position is set to `offset` bytes from the beginning.
- if `mode` is `SeekCur`, current position is moved by `offset` bytes.
- if `mode` is `SeekEnd`, position is set to `offset` bytes from the end of the
file.
Returns *true* if position was set successfully.
#### position
```c++
file.position()
```
Returns the current position inside the file, in bytes.
#### size
```c++
file.size()
```
Returns file size, in bytes.
#### name
```c++
String name = file.name();
```
Returns file name, as `const char*`. Convert it to *String* for storage.
#### close
```c++
file.close()
```
Close the file. No other operations should be performed on *File* object after `close` function was called.
## WiFi(ESP8266WiFi library)
This is mostly similar to WiFi shield library. Differences include:
- `WiFi.mode(m)`: set mode to `WIFI_AP`, `WIFI_STA`, `WIFI_AP_STA` or `WIFI_OFF`.
- call `WiFi.softAP(ssid)` to set up an open network
- call `WiFi.softAP(ssid, password)` to set up a WPA2-PSK network (password should be at least 8 characters)
- `WiFi.macAddress(mac)` is for STA, `WiFi.softAPmacAddress(mac)` is for AP.
- `WiFi.localIP()` is for STA, `WiFi.softAPIP()` is for AP.
- `WiFi.printDiag(Serial)` will print out some diagnostic info
- `WiFiUDP` class supports sending and receiving multicast packets on STA interface.
When sending a multicast packet, replace `udp.beginPacket(addr, port)` with
`udp.beginPacketMulticast(addr, port, WiFi.localIP())`.
When listening to multicast packets, replace `udp.begin(port)` with
`udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)`.
You can use `udp.destinationIP()` to tell whether the packet received was
sent to the multicast or unicast address.
`WiFiServer`, `WiFiClient`, and `WiFiUDP` behave mostly the same way as with WiFi shield library.
Four samples are provided for this library.
You can see more commands here: [http://www.arduino.cc/en/Reference/WiFi](http://www.arduino.cc/en/Reference/WiFi)
## Ticker
Library for calling functions repeatedly with a certain period. Two examples included.
It is currently not recommended to do blocking IO operations (network, serial, file) from Ticker
callback functions. Instead, set a flag inside the ticker callback and check for that flag inside the loop function.
## EEPROM
This is a bit different from standard EEPROM class. You need to call `EEPROM.begin(size)`
before you start reading or writing, size being the number of bytes you want to use.
Size can be anywhere between 4 and 4096 bytes.
`EEPROM.write` does not write to flash immediately, instead you must call `EEPROM.commit()`
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 just after the SPIFFS.
Three examples included.
## I2C (Wire library)
Wire library currently supports master mode up to approximately 450KHz.
Before using I2C, pins for SDA and SCL need to be set by calling
`Wire.begin(int sda, int scl)`, i.e. `Wire.begin(0, 2)` on ESP-01,
else they default to pins 4(SDA) and 5(SCL).
## SPI
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.
`ESP.deepSleep(microseconds, mode)` will put the chip into deep sleep. `mode` is one of `WAKE_RF_DEFAULT`, `WAKE_RFCAL`, `WAKE_NO_RFCAL`, `WAKE_RF_DISABLED`. (GPIO16 needs to be tied to RST to wake from deepSleep.)
`ESP.restart()` restarts the CPU.
`ESP.getFreeHeap()` returns the free heap size.
`ESP.getChipId()` returns the ESP8266 chip ID as a 32-bit integer.
Several APIs may be used to get flash chip info:
`ESP.getFlashChipId()` returns the flash chip ID as a 32-bit integer.
`ESP.getFlashChipSize()` returns the flash chip size, in bytes, as seen by the SDK (may be less than actual size).
`ESP.getFlashChipSpeed(void)` returns the flash chip frequency, in Hz.
`ESP.getCycleCount()` returns the cpu instruction cycle count since start as an unsigned 32-bit. This is useful for accurate timing of very short actions like bit banging.
`ESP.getVcc()` may be used to measure supply voltage. ESP needs to reconfigure the ADC
at startup in order for this feature to be available. Add the following line to the top
of your sketch to use `getVcc`:
```c++
ADC_MODE(ADC_VCC);
```
TOUT pin has to be disconnected in this mode.
Note that by default ADC is configured to read from TOUT pin using `analogRead(A0)`, and
`ESP.getVCC()` is not available.
## OneWire (from https://www.pjrc.com/teensy/td_libs_OneWire.html)
Library was adapted to work with ESP8266 by including register definitions into OneWire.h
Note that if you already have OneWire library in your Arduino/libraries folder, it will be used
instead of the one that comes with this package.
## mDNS and DNS-SD responder (ESP8266mDNS library)
Allows the sketch to respond to multicast DNS queries for domain names like "foo.local", and DNS-SD (service dicovery) queries.
See attached example for details.
## SSDP responder (ESP8266SSDP)
SSDP is another service discovery protocol, supported on Windows out of the box. See attached example for reference.
## DNS server (DNSServer library)
Implements a simple DNS server that can be used in both STA and AP modes. The DNS server currently supports only one domain (for all other domains it will reply with NXDOMAIN or custom status code). With it clients can open a web server running on ESP8266 using a domain name, not an IP address.
See attached example for details.
## Servo
This library exposes the ability to control RC (hobby) servo motors. It will support upto 24 servos on any available output pin. By defualt the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be effected.
While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply.
## Other libraries (not included with the IDE)
Libraries that don't rely on low-level access to AVR registers should work well. Here are a few libraries that were verified to work:
- [arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) - WebSocket Server and Client compatible with ESP8266 (RFC6455)
- [aREST](https://github.com/marcoschwartz/aREST) REST API handler library.
- [Blynk](https://github.com/blynkkk/blynk-library) - easy IoT framework for Makers (check out the [Kickstarter page](http://tiny.cc/blynk-kick)).
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library.git)
- [DHT-sensor-library](https://github.com/adafruit/DHT-sensor-library) - Arduino library for the DHT11/DHT22 temperature and humidity sensors. Download latest v1.1.1 library and no changes are necessary. Older versions should initialize DHT as follows: `DHT dht(DHTPIN, DHTTYPE, 15)`
- [NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel) - Adafruit's NeoPixel library, now with support for the ESP8266 (use version 1.0.2 or higher from Arduino's library manager).
- [NeoPixelBus](https://github.com/Makuna/NeoPixelBus) - Arduino NeoPixel library compatible with ESP8266. Use the "NeoPixelAnimator" branch for ESP8266 to get HSL color support and more.
- [PubSubClient](https://github.com/Imroy/pubsubclient) MQTT library by @Imroy.
- [RTC](https://github.com/Makuna/Rtc) - Arduino Library for Ds1307 & Ds3231 compatible with ESP8266.
- [Souliss, Smart Home](https://github.com/souliss/souliss) - Framework for Smart Home based on Arduino, Android and openHAB.
- [ST7735](https://github.com/nzmichaelh/Adafruit-ST7735-Library) - Adafruit's ST7735 library modified to be compatible with ESP8266. Just make sure to modify the pins in the examples as they are still AVR specific.

15
doc/reference_items.yml Normal file
View File

@ -0,0 +1,15 @@
# This file defines the order in which documents will appear in the menu
- path: /doc/installing.html
- path: /doc/reference.html
- path: /doc/libraries.html
- path: /doc/filesystem.html
- path: /doc/ota_updates/ota_updates.html
- path: /doc/boards.html
- path: /doc/changes.html

View File

@ -1,9 +1,23 @@
#include <ESP8266WiFi.h> #define LWIP_OPEN_SRC
#include <ESP8266mDNS.h> #include <functional>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include "ArduinoOTA.h" #include "ArduinoOTA.h"
#include "MD5Builder.h" #include "MD5Builder.h"
extern "C" {
#include "osapi.h"
#include "ets_sys.h"
#include "user_interface.h"
}
#include "lwip/opt.h"
#include "lwip/udp.h"
#include "lwip/inet.h"
#include "lwip/igmp.h"
#include "lwip/mem.h"
#include "include/UdpContext.h"
#include <ESP8266mDNS.h>
//#define OTA_DEBUG 1 //#define OTA_DEBUG 1
ArduinoOTAClass::ArduinoOTAClass() ArduinoOTAClass::ArduinoOTAClass()
@ -16,9 +30,17 @@ ArduinoOTAClass::ArduinoOTAClass()
, _end_callback(NULL) , _end_callback(NULL)
, _progress_callback(NULL) , _progress_callback(NULL)
, _error_callback(NULL) , _error_callback(NULL)
, _udp_ota(0)
{ {
} }
ArduinoOTAClass::~ArduinoOTAClass(){
if(_udp_ota){
_udp_ota->unref();
_udp_ota = 0;
}
}
void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)) { void ArduinoOTAClass::onStart(OTA_CALLBACK(fn)) {
_start_callback = fn; _start_callback = fn;
} }
@ -35,9 +57,6 @@ void ArduinoOTAClass::onError(OTA_CALLBACK_ERROR(fn)) {
_error_callback = fn; _error_callback = fn;
} }
ArduinoOTAClass::~ArduinoOTAClass() {
}
void ArduinoOTAClass::setPort(uint16_t port) { void ArduinoOTAClass::setPort(uint16_t port) {
if (!_initialized && !_port && port) { if (!_initialized && !_port && port) {
_port = port; _port = port;
@ -59,7 +78,6 @@ void ArduinoOTAClass::setPassword(const char * password) {
void ArduinoOTAClass::begin() { void ArduinoOTAClass::begin() {
if (_initialized) if (_initialized)
return; return;
_initialized = true;
if (!_hostname.length()) { if (!_hostname.length()) {
char tmp[15]; char tmp[15];
@ -70,7 +88,17 @@ void ArduinoOTAClass::begin() {
_port = 8266; _port = 8266;
} }
_udp_ota.begin(_port); if(_udp_ota){
_udp_ota->unref();
_udp_ota = 0;
}
_udp_ota = new UdpContext;
_udp_ota->ref();
if(!_udp_ota->listen(*IP_ADDR_ANY, _port))
return;
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
MDNS.begin(_hostname.c_str()); MDNS.begin(_hostname.c_str());
if (_password.length()) { if (_password.length()) {
@ -78,12 +106,123 @@ void ArduinoOTAClass::begin() {
} else { } else {
MDNS.enableArduino(_port); MDNS.enableArduino(_port);
} }
_initialized = true;
_state = OTA_IDLE; _state = OTA_IDLE;
#if OTA_DEBUG #if OTA_DEBUG
Serial.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); Serial.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
#endif #endif
} }
int ArduinoOTAClass::parseInt(){
char data[16];
uint8_t index = 0;
char value;
while(_udp_ota->peek() == ' ') _udp_ota->read();
while(true){
value = _udp_ota->peek();
if(value < '0' || value > '9'){
data[index++] = '\0';
return atoi(data);
}
data[index++] = _udp_ota->read();
}
return 0;
}
String ArduinoOTAClass::readStringUntil(char end){
String res = "";
char value;
while(true){
value = _udp_ota->read();
if(value == '\0' || value == end){
return res;
}
res += value;
}
return res;
}
void ArduinoOTAClass::_onRx(){
if(!_udp_ota->next()) return;
ip_addr_t ota_ip;
if (_state == OTA_IDLE) {
int cmd = parseInt();
if (cmd != U_FLASH && cmd != U_SPIFFS)
return;
_ota_ip = _udp_ota->getRemoteAddress();
_cmd = cmd;
_ota_port = parseInt();
_size = parseInt();
_udp_ota->read();
_md5 = readStringUntil('\n');
_md5.trim();
if(_md5.length() != 32)
return;
ota_ip.addr = (uint32_t)_ota_ip;
if (_password.length()){
MD5Builder nonce_md5;
nonce_md5.begin();
nonce_md5.add(String(micros()));
nonce_md5.calculate();
_nonce = nonce_md5.toString();
char auth_req[38];
sprintf(auth_req, "AUTH %s", _nonce.c_str());
_udp_ota->append((const char *)auth_req, strlen(auth_req));
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_WAITAUTH;
return;
} else {
_udp_ota->append("OK", 2);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_RUNUPDATE;
}
} else if (_state == OTA_WAITAUTH) {
int cmd = parseInt();
if (cmd != U_AUTH) {
_state = OTA_IDLE;
return;
}
_udp_ota->read();
String cnonce = readStringUntil(' ');
String response = readStringUntil('\n');
if (cnonce.length() != 32 || response.length() != 32) {
_state = OTA_IDLE;
return;
}
MD5Builder _passmd5;
_passmd5.begin();
_passmd5.add(_password);
_passmd5.calculate();
String passmd5 = _passmd5.toString();
String challenge = passmd5 + ":" + String(_nonce) + ":" + cnonce;
MD5Builder _challengemd5;
_challengemd5.begin();
_challengemd5.add(challenge);
_challengemd5.calculate();
String result = _challengemd5.toString();
ota_ip.addr = (uint32_t)_ota_ip;
if(result.equals(response)){
_udp_ota->append("OK", 2);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
_state = OTA_RUNUPDATE;
} else {
_udp_ota->append("Authentication Failed", 21);
_udp_ota->send(&ota_ip, _udp_ota->getRemotePort());
if (_error_callback) _error_callback(OTA_AUTH_ERROR);
_state = OTA_IDLE;
}
}
while(_udp_ota->next()) _udp_ota->flush();
}
void ArduinoOTAClass::_runUpdate() { void ArduinoOTAClass::_runUpdate() {
if (!Update.begin(_size, _cmd)) { if (!Update.begin(_size, _cmd)) {
#if OTA_DEBUG #if OTA_DEBUG
@ -92,7 +231,7 @@ void ArduinoOTAClass::_runUpdate() {
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_BEGIN_ERROR); _error_callback(OTA_BEGIN_ERROR);
} }
_udp_ota.begin(_port); _udp_ota->listen(*IP_ADDR_ANY, _port);
_state = OTA_IDLE; _state = OTA_IDLE;
return; return;
} }
@ -112,7 +251,7 @@ void ArduinoOTAClass::_runUpdate() {
#if OTA_DEBUG #if OTA_DEBUG
Serial.printf("Connect Failed\n"); Serial.printf("Connect Failed\n");
#endif #endif
_udp_ota.begin(_port); _udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_CONNECT_ERROR); _error_callback(OTA_CONNECT_ERROR);
} }
@ -128,7 +267,7 @@ void ArduinoOTAClass::_runUpdate() {
#if OTA_DEBUG #if OTA_DEBUG
Serial.printf("Recieve Failed\n"); Serial.printf("Recieve Failed\n");
#endif #endif
_udp_ota.begin(_port); _udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_RECIEVE_ERROR); _error_callback(OTA_RECIEVE_ERROR);
} }
@ -156,7 +295,7 @@ void ArduinoOTAClass::_runUpdate() {
} }
ESP.restart(); ESP.restart();
} else { } else {
_udp_ota.begin(_port); _udp_ota->listen(*IP_ADDR_ANY, _port);
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_END_ERROR); _error_callback(OTA_END_ERROR);
} }
@ -169,94 +308,9 @@ void ArduinoOTAClass::_runUpdate() {
} }
void ArduinoOTAClass::handle() { void ArduinoOTAClass::handle() {
if (!_udp_ota) {
_udp_ota.begin(_port);
#if OTA_DEBUG
Serial.println("OTA restarted");
#endif
}
if (!_udp_ota.parsePacket()) return;
if (_state == OTA_IDLE) {
int cmd = _udp_ota.parseInt();
if (cmd != U_FLASH && cmd != U_SPIFFS)
return;
_ota_ip = _udp_ota.remoteIP();
_cmd = cmd;
_ota_port = _udp_ota.parseInt();
_size = _udp_ota.parseInt();
_udp_ota.read();
_md5 = _udp_ota.readStringUntil('\n');
_md5.trim();
if(_md5.length() != 32)
return;
#if OTA_DEBUG
Serial.print("Update Start: ip:");
Serial.print(_ota_ip);
Serial.printf(", port:%d, size:%d, md5:%s\n", _ota_port, _size, _md5.c_str());
#endif
_udp_ota.beginPacket(_ota_ip, _udp_ota.remotePort());
if (_password.length()){
MD5Builder nonce_md5;
nonce_md5.begin();
nonce_md5.add(String(micros()));
nonce_md5.calculate();
_nonce = nonce_md5.toString();
_udp_ota.printf("AUTH %s", _nonce.c_str());
_udp_ota.endPacket();
_state = OTA_WAITAUTH;
return;
} else {
_udp_ota.print("OK");
_udp_ota.endPacket();
_state = OTA_RUNUPDATE;
}
} else if (_state == OTA_WAITAUTH) {
int cmd = _udp_ota.parseInt();
if (cmd != U_AUTH) {
_state = OTA_IDLE;
return;
}
_udp_ota.read();
String cnonce = _udp_ota.readStringUntil(' ');
String response = _udp_ota.readStringUntil('\n');
if (cnonce.length() != 32 || response.length() != 32) {
_state = OTA_IDLE;
return;
}
MD5Builder _passmd5;
_passmd5.begin();
_passmd5.add(_password);
_passmd5.calculate();
String passmd5 = _passmd5.toString();
String challenge = passmd5 + ":" + String(_nonce) + ":" + cnonce;
MD5Builder _challengemd5;
_challengemd5.begin();
_challengemd5.add(challenge);
_challengemd5.calculate();
String result = _challengemd5.toString();
if(result.equals(response)){
_udp_ota.beginPacket(_ota_ip, _udp_ota.remotePort());
_udp_ota.print("OK");
_udp_ota.endPacket();
_state = OTA_RUNUPDATE;
} else {
_udp_ota.beginPacket(_ota_ip, _udp_ota.remotePort());
_udp_ota.print("Authentication Failed");
_udp_ota.endPacket();
if (_error_callback) _error_callback(OTA_AUTH_ERROR);
_state = OTA_IDLE;
}
}
if (_state == OTA_RUNUPDATE) { if (_state == OTA_RUNUPDATE) {
_runUpdate(); _runUpdate();
_state = OTA_IDLE;
} }
} }

View File

@ -1,7 +1,10 @@
#ifndef __ARDUINO_OTA_H #ifndef __ARDUINO_OTA_H
#define __ARDUINO_OTA_H #define __ARDUINO_OTA_H
class WiFiUDP; #include <ESP8266WiFi.h>
#include <WiFiUdp.h>
class UdpContext;
#define OTA_CALLBACK(callback) void (*callback)() #define OTA_CALLBACK(callback) void (*callback)()
#define OTA_CALLBACK_PROGRESS(callback) void (*callback)(unsigned int, unsigned int) #define OTA_CALLBACK_PROGRESS(callback) void (*callback)(unsigned int, unsigned int)
@ -41,7 +44,7 @@ class ArduinoOTAClass
String _password; String _password;
String _hostname; String _hostname;
String _nonce; String _nonce;
WiFiUDP _udp_ota; UdpContext *_udp_ota;
bool _initialized; bool _initialized;
ota_state_t _state; ota_state_t _state;
int _size; int _size;
@ -56,6 +59,9 @@ class ArduinoOTAClass
OTA_CALLBACK_PROGRESS(_progress_callback); OTA_CALLBACK_PROGRESS(_progress_callback);
void _runUpdate(void); void _runUpdate(void);
void _onRx(void);
int parseInt(void);
String readStringUntil(char end);
}; };
extern ArduinoOTAClass ArduinoOTA; extern ArduinoOTAClass ArduinoOTA;

View File

@ -36,12 +36,9 @@ void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server)
_server->sendHeader("Access-Control-Allow-Origin", "*"); _server->sendHeader("Access-Control-Allow-Origin", "*");
_server->send(200, "text/plain", (Update.hasError())?"FAIL":"OK"); _server->send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
ESP.restart(); ESP.restart();
}); },[&](){
// handler for the file upload, get's the sketch bytes, and writes // handler for the file upload, get's the sketch bytes, and writes
// them through the Update object. // them through the Update object
_server->onFileUpload([&](){
if(_server->uri() != "/update") return;
HTTPUpload& upload = _server->upload(); HTTPUpload& upload = _server->upload();
if(upload.status == UPLOAD_FILE_START){ if(upload.status == UPLOAD_FILE_START){
if (_serial_output) if (_serial_output)
@ -70,6 +67,6 @@ void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server)
Update.end(); Update.end();
if (_serial_output) Serial.println("Update was aborted"); if (_serial_output) Serial.println("Update was aborted");
} }
yield(); delay(0);
}); });
} }

View File

@ -207,10 +207,9 @@ void setup(void){
server.on("/edit", HTTP_PUT, handleFileCreate); server.on("/edit", HTTP_PUT, handleFileCreate);
//delete file //delete file
server.on("/edit", HTTP_DELETE, handleFileDelete); server.on("/edit", HTTP_DELETE, handleFileDelete);
//called after file upload //first callback is called after the request has ended with all parsed arguments
server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }); //second callback handles file uploads at that location
//called when a file is received inside POST data server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload);
server.onFileUpload(handleFileUpload);
//called when the url is not defined here //called when the url is not defined here
//use it to load content from SPIFFS //use it to load content from SPIFFS

View File

@ -256,9 +256,8 @@ void setup(void){
server.on("/list", HTTP_GET, printDirectory); server.on("/list", HTTP_GET, printDirectory);
server.on("/edit", HTTP_DELETE, handleDelete); server.on("/edit", HTTP_DELETE, handleDelete);
server.on("/edit", HTTP_PUT, handleCreate); server.on("/edit", HTTP_PUT, handleCreate);
server.on("/edit", HTTP_POST, [](){ returnOK(); }); server.on("/edit", HTTP_POST, [](){ returnOK(); }, handleFileUpload);
server.onNotFound(handleNotFound); server.onNotFound(handleNotFound);
server.onFileUpload(handleFileUpload);
server.begin(); server.begin();
DBG_OUTPUT_PORT.println("HTTP server started"); DBG_OUTPUT_PORT.println("HTTP server started");

View File

@ -27,8 +27,12 @@ void setup(void){
server.sendHeader("Access-Control-Allow-Origin", "*"); server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/html", serverIndex); server.send(200, "text/html", serverIndex);
}); });
server.onFileUpload([](){ server.on("/update", HTTP_POST, [](){
if(server.uri() != "/update") return; server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
ESP.restart();
},[](){
HTTPUpload& upload = server.upload(); HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){ if(upload.status == UPLOAD_FILE_START){
Serial.setDebugOutput(true); Serial.setDebugOutput(true);
@ -52,12 +56,6 @@ void setup(void){
} }
yield(); yield();
}); });
server.on("/update", HTTP_POST, [](){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
ESP.restart();
});
server.begin(); server.begin();
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);

View File

@ -33,6 +33,7 @@
ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port) ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port)
: _server(addr, port) : _server(addr, port)
, _currentHandler(0)
, _firstHandler(0) , _firstHandler(0)
, _lastHandler(0) , _lastHandler(0)
, _currentArgCount(0) , _currentArgCount(0)
@ -44,6 +45,7 @@ ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port)
ESP8266WebServer::ESP8266WebServer(int port) ESP8266WebServer::ESP8266WebServer(int port)
: _server(port) : _server(port)
, _currentHandler(0)
, _firstHandler(0) , _firstHandler(0)
, _lastHandler(0) , _lastHandler(0)
, _currentArgCount(0) , _currentArgCount(0)
@ -74,7 +76,11 @@ void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction ha
} }
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)); on(uri, method, fn, _fileUploadHandler);
}
void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
} }
void ESP8266WebServer::addHandler(RequestHandler* handler) { void ESP8266WebServer::addHandler(RequestHandler* handler) {
@ -352,13 +358,7 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) {
} }
void ESP8266WebServer::_handleRequest() { void ESP8266WebServer::_handleRequest() {
RequestHandler* handler; if (!_currentHandler){
for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->handle(*this, _currentMethod, _currentUri))
break;
}
if (!handler){
#ifdef DEBUG #ifdef DEBUG
DEBUG_OUTPUT.println("request handler not found"); DEBUG_OUTPUT.println("request handler not found");
#endif #endif
@ -369,6 +369,8 @@ void ESP8266WebServer::_handleRequest() {
else { else {
send(404, "text/plain", String("Not found: ") + _currentUri); send(404, "text/plain", String("Not found: ") + _currentUri);
} }
} else {
_currentHandler->handle(*this, _currentMethod, _currentUri);
} }
uint16_t maxWait = HTTP_MAX_CLOSE_WAIT; uint16_t maxWait = HTTP_MAX_CLOSE_WAIT;

View File

@ -40,12 +40,6 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
class ESP8266WebServer; class ESP8266WebServer;
#include "detail/RequestHandler.h"
namespace fs {
class FS;
}
typedef struct { typedef struct {
HTTPUploadStatus status; HTTPUploadStatus status;
String filename; String filename;
@ -56,6 +50,12 @@ typedef struct {
uint8_t buf[HTTP_UPLOAD_BUFLEN]; uint8_t buf[HTTP_UPLOAD_BUFLEN];
} HTTPUpload; } HTTPUpload;
#include "detail/RequestHandler.h"
namespace fs {
class FS;
}
class ESP8266WebServer class ESP8266WebServer
{ {
public: public:
@ -69,6 +69,7 @@ public:
typedef std::function<void(void)> THandlerFunction; typedef std::function<void(void)> THandlerFunction;
void on(const char* uri, THandlerFunction handler); void on(const char* uri, THandlerFunction handler);
void on(const char* uri, HTTPMethod method, THandlerFunction fn); void on(const char* uri, HTTPMethod method, THandlerFunction fn);
void on(const char* uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
void addHandler(RequestHandler* handler); void addHandler(RequestHandler* handler);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
void onNotFound(THandlerFunction fn); //called when handler is not assigned void onNotFound(THandlerFunction fn); //called when handler is not assigned
@ -132,6 +133,7 @@ protected:
uint8_t _uploadReadByte(WiFiClient& client); uint8_t _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue); bool _collectHeader(const char* headerName, const char* headerValue);
String urlDecode(const String& text);
struct RequestArgument { struct RequestArgument {
String key; String key;
@ -155,6 +157,7 @@ protected:
String _hostHeader; String _hostHeader;
RequestHandler* _currentHandler;
RequestHandler* _firstHandler; RequestHandler* _firstHandler;
RequestHandler* _lastHandler; RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler; THandlerFunction _notFoundHandler;

View File

@ -81,6 +81,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
DEBUG_OUTPUT.println(searchStr); DEBUG_OUTPUT.println(searchStr);
#endif #endif
//attach handler
RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->canHandle(_currentMethod, _currentUri))
break;
}
_currentHandler = handler;
String formData; String formData;
// below is needed only when POST type request // below is needed only when POST type request
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
@ -99,7 +107,8 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
break; break;
} }
headerName = req.substring(0, headerDiv); headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2); headerValue = req.substring(headerDiv + 1);
headerValue.trim();
_collectHeader(headerName.c_str(),headerValue.c_str()); _collectHeader(headerName.c_str(),headerValue.c_str());
#ifdef DEBUG #ifdef DEBUG
@ -255,7 +264,7 @@ void ESP8266WebServer::_parseArguments(String data) {
} }
RequestArgument& arg = _currentArgs[iarg]; RequestArgument& arg = _currentArgs[iarg];
arg.key = data.substring(pos, equal_sign_index); arg.key = data.substring(pos, equal_sign_index);
arg.value = data.substring(equal_sign_index + 1, next_arg_index); arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
#ifdef DEBUG #ifdef DEBUG
DEBUG_OUTPUT.print("arg "); DEBUG_OUTPUT.print("arg ");
DEBUG_OUTPUT.print(iarg); DEBUG_OUTPUT.print(iarg);
@ -279,7 +288,8 @@ void ESP8266WebServer::_parseArguments(String data) {
void ESP8266WebServer::_uploadWriteByte(uint8_t b){ void ESP8266WebServer::_uploadWriteByte(uint8_t b){
if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){ if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
if (_fileUploadHandler) _fileUploadHandler(); if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.totalSize += _currentUpload.currentSize; _currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.currentSize = 0; _currentUpload.currentSize = 0;
} }
@ -397,7 +407,8 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
DEBUG_OUTPUT.print(" Type: "); DEBUG_OUTPUT.print(" Type: ");
DEBUG_OUTPUT.println(_currentUpload.type); DEBUG_OUTPUT.println(_currentUpload.type);
#endif #endif
if (_fileUploadHandler) _fileUploadHandler(); if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.status = UPLOAD_FILE_WRITE; _currentUpload.status = UPLOAD_FILE_WRITE;
uint8_t argByte = _uploadReadByte(client); uint8_t argByte = _uploadReadByte(client);
readfile: readfile:
@ -433,10 +444,12 @@ readfile:
client.readBytes(endBuf, boundary.length()); client.readBytes(endBuf, boundary.length());
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if (_fileUploadHandler) _fileUploadHandler(); if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
_currentUpload.totalSize += _currentUpload.currentSize; _currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.status = UPLOAD_FILE_END; _currentUpload.status = UPLOAD_FILE_END;
if (_fileUploadHandler) _fileUploadHandler(); if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
#ifdef DEBUG #ifdef DEBUG
DEBUG_OUTPUT.print("End File: "); DEBUG_OUTPUT.print("End File: ");
DEBUG_OUTPUT.print(_currentUpload.filename); DEBUG_OUTPUT.print(_currentUpload.filename);
@ -501,8 +514,40 @@ readfile:
return false; return false;
} }
String ESP8266WebServer::urlDecode(const String& text)
{
String decoded = "";
char temp[] = "0x00";
unsigned int len = text.length();
unsigned int i = 0;
while (i < len)
{
char decodedChar;
char encodedChar = text.charAt(i++);
if ((encodedChar == '%') && (i + 1 < len))
{
temp[2] = text.charAt(i++);
temp[3] = text.charAt(i++);
decodedChar = strtol(temp, NULL, 16);
}
else {
if (encodedChar == '+')
{
decodedChar = ' ';
}
else {
decodedChar = encodedChar; // normal ascii char
}
}
decoded += decodedChar;
}
return decoded;
}
bool ESP8266WebServer::_parseFormUploadAborted(){ bool ESP8266WebServer::_parseFormUploadAborted(){
_currentUpload.status = UPLOAD_FILE_ABORTED; _currentUpload.status = UPLOAD_FILE_ABORTED;
if (_fileUploadHandler) _fileUploadHandler(); if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
return false; return false;
} }

View File

@ -3,7 +3,10 @@
class RequestHandler { class RequestHandler {
public: public:
virtual bool canHandle(HTTPMethod method, String uri) { return false; }
virtual bool canUpload(String uri) { return false; }
virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) { return false; } virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) { return false; }
virtual void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) {}
RequestHandler* next() { return _next; } RequestHandler* next() { return _next; }
void next(RequestHandler* r) { _next = r; } void next(RequestHandler* r) { _next = r; }

View File

@ -5,28 +5,49 @@
class FunctionRequestHandler : public RequestHandler { class FunctionRequestHandler : public RequestHandler {
public: public:
FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn, const char* uri, HTTPMethod method)
: _fn(fn) : _fn(fn)
, _ufn(ufn)
, _uri(uri) , _uri(uri)
, _method(method) , _method(method)
{ {
} }
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (_method != HTTP_ANY && _method != requestMethod) if (_method != HTTP_ANY && _method != requestMethod)
return false; return false;
if (requestUri != _uri) if (requestUri != _uri)
return false; return false;
return true;
}
bool canUpload(String requestUri) override {
if (!_ufn || !canHandle(HTTP_POST, requestUri))
return false;
return true;
}
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
_fn(); _fn();
return true; return true;
} }
void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) override {
if (canUpload(requestUri))
_ufn();
}
protected: protected:
String _uri; String _uri;
HTTPMethod _method; HTTPMethod _method;
ESP8266WebServer::THandlerFunction _fn; ESP8266WebServer::THandlerFunction _fn;
ESP8266WebServer::THandlerFunction _ufn;
}; };
class StaticRequestHandler : public RequestHandler { class StaticRequestHandler : public RequestHandler {
@ -41,13 +62,23 @@ public:
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header); DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
_baseUriLength = _uri.length(); _baseUriLength = _uri.length();
} }
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != HTTP_GET) if (requestMethod != HTTP_GET)
return false; return false;
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
if (!requestUri.startsWith(_uri)) if (_isFile && requestUri != _uri || !requestUri.startsWith(_uri))
return false; return false;
return true;
}
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
String path(_path); String path(_path);
if(path.endsWith("/")) path += "index.htm"; if(path.endsWith("/")) path += "index.htm";
@ -57,11 +88,6 @@ public:
// URI in request to get the file path. // URI in request to get the file path.
path += requestUri.substring(_baseUriLength); 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); DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = getContentType(path); String contentType = getContentType(path);

View File

@ -36,6 +36,7 @@ extern "C" {
#include "WiFiClient.h" #include "WiFiClient.h"
#include "WiFiUdp.h" #include "WiFiUdp.h"
#include "debug.h"
extern "C" void esp_schedule(); extern "C" void esp_schedule();
extern "C" void esp_yield(); extern "C" void esp_yield();
@ -44,6 +45,7 @@ ESP8266WiFiClass::ESP8266WiFiClass()
: _smartConfigStarted(false) : _smartConfigStarted(false)
, _smartConfigDone(false) , _smartConfigDone(false)
, _useStaticIp(false) , _useStaticIp(false)
, _persistent(true)
{ {
uint8 m = wifi_get_opmode(); uint8 m = wifi_get_opmode();
_useClientMode = (m & WIFI_STA); _useClientMode = (m & WIFI_STA);
@ -51,6 +53,12 @@ ESP8266WiFiClass::ESP8266WiFiClass()
wifi_set_event_handler_cb((wifi_event_handler_cb_t)&ESP8266WiFiClass::_eventCallback); wifi_set_event_handler_cb((wifi_event_handler_cb_t)&ESP8266WiFiClass::_eventCallback);
} }
void ESP8266WiFiClass::persistent(bool persistent)
{
_persistent = persistent;
}
void ESP8266WiFiClass::mode(WiFiMode m) void ESP8266WiFiClass::mode(WiFiMode m)
{ {
if(wifi_get_opmode() == (uint8)m) { if(wifi_get_opmode() == (uint8)m) {
@ -69,9 +77,7 @@ void ESP8266WiFiClass::mode(WiFiMode m)
_useClientMode = false; _useClientMode = false;
} }
ETS_UART_INTR_DISABLE(); _mode(m);
wifi_set_opmode(m);
ETS_UART_INTR_ENABLE();
} }
WiFiMode ESP8266WiFiClass::getMode() WiFiMode ESP8266WiFiClass::getMode()
@ -86,15 +92,44 @@ void ESP8266WiFiClass::_mode(WiFiMode m)
} }
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if (_persistent)
wifi_set_opmode(m); wifi_set_opmode(m);
else
wifi_set_opmode_current(m);
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
} }
int ESP8266WiFiClass::begin(char* ssid, char *passphrase, int32_t channel, uint8_t bssid[6]){ static bool sta_config_equal(const station_config& lhs, const station_config& rhs)
{
if (strcmp(reinterpret_cast<const char*>(lhs.ssid), reinterpret_cast<const char*>(rhs.ssid)) != 0)
return false;
if (strcmp(reinterpret_cast<const char*>(lhs.password), reinterpret_cast<const char*>(rhs.password)) != 0)
return false;
if (lhs.bssid_set) {
if (!rhs.bssid_set)
return false;
if (memcmp(lhs.bssid, rhs.bssid, 6) != 0)
return false;
}
else {
if (rhs.bssid_set)
return false;
}
return true;
}
int ESP8266WiFiClass::begin(char* ssid, char *passphrase, int32_t channel, const uint8_t* bssid)
{
return begin((const char*) ssid, (const char*) passphrase, channel, bssid); return begin((const char*) ssid, (const char*) passphrase, channel, bssid);
} }
int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t channel, uint8_t bssid[6]){ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid)
{
_useClientMode = true; _useClientMode = true;
if(_useApMode) { if(_useApMode) {
@ -106,12 +141,12 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch
} }
if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
// fail SSID to long or missing! // fail SSID too long or missing!
return WL_CONNECT_FAILED; return WL_CONNECT_FAILED;
} }
if(passphrase && strlen(passphrase) > 63) { if(passphrase && strlen(passphrase) > 63) {
// fail passphrase to long! // fail passphrase too long!
return WL_CONNECT_FAILED; return WL_CONNECT_FAILED;
} }
@ -131,8 +166,18 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t ch
conf.bssid_set = 0; conf.bssid_set = 0;
} }
struct station_config current_conf;
wifi_station_get_config(&current_conf);
if (sta_config_equal(current_conf, conf)) {
DEBUGV("sta config unchanged");
return status();
}
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if (_persistent)
wifi_station_set_config(&conf); wifi_station_set_config(&conf);
else
wifi_station_set_config_current(&conf);
wifi_station_connect(); wifi_station_connect();
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
@ -203,8 +248,10 @@ int ESP8266WiFiClass::softAPdisconnect(bool wifioff)
*conf.ssid = 0; *conf.ssid = 0;
*conf.password = 0; *conf.password = 0;
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if (_persistent)
wifi_softap_set_config(&conf); wifi_softap_set_config(&conf);
wifi_station_disconnect(); else
wifi_softap_set_config_current(&conf);
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
if(wifioff) { if(wifioff) {
@ -228,7 +275,10 @@ int ESP8266WiFiClass::disconnect(bool wifioff)
*conf.ssid = 0; *conf.ssid = 0;
*conf.password = 0; *conf.password = 0;
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if (_persistent)
wifi_station_set_config(&conf); wifi_station_set_config(&conf);
else
wifi_station_set_config_current(&conf);
wifi_station_disconnect(); wifi_station_disconnect();
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
@ -247,6 +297,20 @@ int ESP8266WiFiClass::disconnect(bool wifioff)
return 0; return 0;
} }
static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs)
{
if (strcmp(reinterpret_cast<const char*>(lhs.ssid), reinterpret_cast<const char*>(rhs.ssid)) != 0)
return false;
if (strcmp(reinterpret_cast<const char*>(lhs.password), reinterpret_cast<const char*>(rhs.password)) != 0)
return false;
if (lhs.channel != rhs.channel)
return false;
if (lhs.ssid_hidden != rhs.ssid_hidden)
return false;
return true;
}
void ESP8266WiFiClass::softAP(const char* ssid) void ESP8266WiFiClass::softAP(const char* ssid)
{ {
softAP(ssid, 0); softAP(ssid, 0);
@ -264,8 +328,8 @@ void ESP8266WiFiClass::softAP(const char* ssid, const char* passphrase, int chan
_mode(WIFI_AP); _mode(WIFI_AP);
} }
if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { if(!ssid || *ssid == 0 || strlen(ssid) > 31) {
// fail SSID to long or missing! // fail SSID too long or missing!
return; return;
} }
@ -294,8 +358,19 @@ void ESP8266WiFiClass::softAP(const char* ssid, const char* passphrase, int chan
strcpy(reinterpret_cast<char*>(conf.password), passphrase); strcpy(reinterpret_cast<char*>(conf.password), passphrase);
} }
struct softap_config conf_current;
wifi_softap_get_config(&conf_current);
if (!softap_config_equal(conf, conf_current))
{
DEBUGV("softap config unchanged");
return;
}
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
if (_persistent)
wifi_softap_set_config(&conf); wifi_softap_set_config(&conf);
else
wifi_softap_set_config_current(&conf);
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
} }

View File

@ -44,6 +44,8 @@ public:
ESP8266WiFiClass(); ESP8266WiFiClass();
void persistent(bool persistent);
void mode(WiFiMode); void mode(WiFiMode);
WiFiMode getMode(); WiFiMode getMode();
@ -56,8 +58,8 @@ public:
* @param channel Optional. Channel of AP * @param channel Optional. Channel of AP
* @return * @return
*/ */
int begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL); int begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL);
int begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, uint8_t bssid[6] = NULL); int begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL);
// Use sdk config to connect. // Use sdk config to connect.
int begin(); int begin();
@ -385,6 +387,7 @@ protected:
bool _useApMode; bool _useApMode;
bool _useClientMode; bool _useClientMode;
bool _useStaticIp; bool _useStaticIp;
bool _persistent;
static bool _scanAsync; static bool _scanAsync;
static bool _scanStarted; static bool _scanStarted;

View File

@ -384,8 +384,7 @@ extern "C" void* ax_port_malloc(size_t size, const char* file, int line) {
if (result == nullptr) { if (result == nullptr) {
DEBUG_TLS_MEM_PRINT("%s:%d malloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap()); DEBUG_TLS_MEM_PRINT("%s:%d malloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap());
panic();
while(true){}
} }
if (size >= 1024) if (size >= 1024)
DEBUG_TLS_MEM_PRINT("%s:%d malloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap()); DEBUG_TLS_MEM_PRINT("%s:%d malloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap());
@ -402,7 +401,7 @@ extern "C" void* ax_port_realloc(void* ptr, size_t size, const char* file, int l
void* result = realloc(ptr, size); void* result = realloc(ptr, size);
if (result == nullptr) { if (result == nullptr) {
DEBUG_TLS_MEM_PRINT("%s:%d realloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap()); DEBUG_TLS_MEM_PRINT("%s:%d realloc %d failed, left %d\r\n", file, line, size, ESP.getFreeHeap());
while(true){} panic();
} }
if (size >= 1024) if (size >= 1024)
DEBUG_TLS_MEM_PRINT("%s:%d realloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap()); DEBUG_TLS_MEM_PRINT("%s:%d realloc %d, left %d\r\n", file, line, size, ESP.getFreeHeap());

View File

@ -97,6 +97,7 @@ class ClientContext {
close(); close();
if(_discard_cb) if(_discard_cb)
_discard_cb(_discard_cb_arg, this); _discard_cb(_discard_cb_arg, this);
DEBUGV(":del\r\n");
delete this; delete this;
} }
} }

View File

@ -0,0 +1,68 @@
/**
* BasicHttpClient.ino
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266httpClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if((WiFiMulti.run() == WL_CONNECTED)) {
httpClient http;
USE_SERIAL.print("[HTTP] begin...\n");
// configure traged server and url
//http.begin("192.168.1.12", 443, "/test.html", true, "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS
http.begin("192.168.1.12", 80, "/test.html"); //HTTP
USE_SERIAL.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();
if(httpCode) {
// HTTP header has been send and Server response header has been handled
USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if(httpCode == 200) {
String payload = http.getString();
USE_SERIAL.println(payload);
}
} else {
USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n");
}
}
delay(10000);
}

View File

@ -0,0 +1,98 @@
/**
* StreamHttpClient.ino
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266httpClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if((WiFiMulti.run() == WL_CONNECTED)) {
httpClient http;
USE_SERIAL.print("[HTTP] begin...\n");
// configure traged server and url
http.begin("192.168.1.12", 80, "/test.html");
USE_SERIAL.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();
if(httpCode) {
// HTTP header has been send and Server response header has been handled
USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if(httpCode == 200) {
// get lenght of document (is -1 when Server sends no Content-Length header)
int len = http.getSize();
// create buffer for read
uint8_t buff[128] = { 0 };
// get tcp stream
WiFiClient * stream = http.getStreamPtr();
// read all data from server
while(http.connected() && (len > 0 || len == -1)) {
// get available data size
size_t size = stream->available();
if(size) {
// read up to 128 byte
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
// write it to Serial
USE_SERIAL.write(buff, c);
if(len > 0) {
len -= c;
}
}
delay(1);
}
USE_SERIAL.println();
USE_SERIAL.print("[HTTP] connection closed or file end.\n");
}
} else {
USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n");
}
}
delay(10000);
}

View File

@ -0,0 +1,65 @@
/**
* reuseConnection.ino
*
* Created on: 22.11.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266httpClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
httpClient http;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if((WiFiMulti.run() == WL_CONNECTED)) {
http.begin("192.168.1.12", 80, "/test.html");
int httpCode = http.GET();
if(httpCode) {
USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if(httpCode == 200) {
http.writeToStream(&USE_SERIAL);
}
} else {
USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n");
}
}
delay(1000);
}

View File

@ -0,0 +1,9 @@
name=ESP8266httpClient
version=1.0
author=Markus Sattler
maintainer=Markus Sattler
sentence=http Client for ESP8266
paragraph=
category=Communication
url=https://github.com/Links2004/Arduino/tree/libraries/ESP8266httpClient
architectures=esp8266

View File

@ -0,0 +1,508 @@
/**
* ESP8266httpClient.cpp
*
* Created on: 02.11.2015
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP8266httpClient for Arduino.
*
* 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
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <StreamString.h>
#include "ESP8266httpClient.h"
/**
* constractor
*/
httpClient::httpClient() {
_tcp = NULL;
_tcps = NULL;
_reuse = false;
_headerKeysCount = 0;
_currentHeaders = NULL;
_returnCode = 0;
_size = -1;
_canReuse = false;
}
/**
* deconstractor
*/
httpClient::~httpClient() {
if(_tcps) {
_tcps->stop();
_tcps->~WiFiClientSecure();
_tcps = NULL;
_tcp = NULL;
} else if(_tcp) {
_tcp->stop();
_tcp->~WiFiClient();
_tcp = NULL;
}
if(_currentHeaders) {
delete[] _currentHeaders;
}
}
/**
* begin
* @param host const char *
* @param port uint16_t
* @param url const char *
* @param https bool
* @param httpsFingerprint const char *
*/
void httpClient::begin(const char *host, uint16_t port, const char * url, bool https, const char * httpsFingerprint) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint);
_host = host;
_port = port;
_url = url;
_https = https;
_httpsFingerprint = httpsFingerprint;
_returnCode = 0;
_size = -1;
_Headers = "";
}
void httpClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) {
begin(host.c_str(), port, url.c_str(), https, httpsFingerprint.c_str());
}
/**
* end
* called after the payload is handled
*/
void httpClient::end(void) {
if(connected()) {
if(_reuse && _canReuse) {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp keep open for reuse\n");
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp stop\n");
_tcp->stop();
}
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp is closed\n");
}
}
/**
* connected
* @return connected status
*/
bool httpClient::connected() {
if(_tcp) {
return (_tcp->connected() || (_tcp->available() > 0));
}
return false;
}
/**
* try to reuse the connection to the server
* keep-alive
* @param reuse bool
*/
void httpClient::setReuse(bool reuse) {
_reuse = reuse;
}
/**
* send a GET request
* @return http code
*/
int httpClient::GET() {
return sendRequest("GET");
}
/**
* sends a post request to the server
* @param payload uint8_t *
* @param size size_t
* @return http code
*/
int httpClient::POST(uint8_t * payload, size_t size) {
return sendRequest("POST", payload, size);
}
int httpClient::POST(String payload) {
return POST((uint8_t *) payload.c_str(), payload.length());
}
/**
* sendRequest
* @param type const char * "GET", "POST", ....
* @param payload uint8_t * data for the message body if null not send
* @param size size_t size for the message body if 0 not send
* @return -1 if no info or > 0 when Content-Length is set by server
*/
int httpClient::sendRequest(const char * type, uint8_t * payload, size_t size) {
// connect ro server
if(!connect()) {
return HTTPC_ERROR_CONNECTION_REFUSED;
}
if(payload && size > 0) {
addHeader("Content-Length", String(size));
}
// send Header
if(!sendHeader(type)) {
return HTTPC_ERROR_SEND_HEADER_FAILED;
}
// send Payload if needed
if(payload && size > 0) {
if(_tcp->write(&payload[0], size) != size) {
return HTTPC_ERROR_SEND_PAYLOAD_FAILED;
}
}
// handle Server Response (Header)
return handleHeaderResponse();
}
/**
* size of message body / payload
* @return -1 if no info or > 0 when Content-Length is set by server
*/
int httpClient::getSize(void) {
return _size;
}
/**
* deprecated Note: this is not working with https!
* returns the stream of the tcp connection
* @return WiFiClient
*/
WiFiClient & httpClient::getStream(void) {
if(connected()) {
return *_tcp;
}
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
// todo return error?
}
/**
* returns the stream of the tcp connection
* @return WiFiClient *
*/
WiFiClient * httpClient::getStreamPtr(void) {
if(connected()) {
return _tcp;
}
DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n");
return NULL;
}
WiFiClient * getStreamPtr(void);
/**
* write all message body / payload to Stream
* @param stream Stream *
* @return bytes written ( negative values are error codes )
*/
int httpClient::writeToStream(Stream * stream) {
if(!stream) {
return HTTPC_ERROR_NO_STREAM;
}
if(!connected()) {
return HTTPC_ERROR_NOT_CONNECTED;
}
// get length of document (is -1 when Server sends no Content-Length header)
int len = _size;
int bytesWritten = 0;
// create buffer for read
uint8_t buff[1460] = { 0 };
// read all data from server
while(connected() && (len > 0 || len == -1)) {
// get available data size
size_t size = _tcp->available();
if(size) {
int c = _tcp->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
// write it to Stream
bytesWritten += stream->write(buff, c);
if(len > 0) {
len -= c;
}
delay(0);
} else {
delay(1);
}
}
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] connection closed or file end (written: %d).\n", bytesWritten);
if(_size && _size != bytesWritten) {
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] bytesWritten %d and size %d missmatch!.\n", bytesWritten, _size);
}
end();
return bytesWritten;
}
/**
* return all payload as String (may need lot of ram or trigger out of memory!)
* @return String
*/
String httpClient::getString(void) {
StreamString sstring;
if(_size) {
// try to reserve needed memmory
if(!sstring.reserve((_size + 1))) {
DEBUG_HTTPCLIENT("[HTTP-Client][getString] too less memory to resive as string! need: %d\n", (_size + 1));
return String("--too less memory--");
}
}
writeToStream(&sstring);
return sstring;
}
/**
* adds Header to the request
* @param name
* @param value
* @param first
*/
void httpClient::addHeader(const String& name, const String& value, bool first) {
String headerLine = name;
headerLine += ": ";
headerLine += value;
headerLine += "\r\n";
if(first) {
_Headers = headerLine + _Headers;
} else {
_Headers += headerLine;
}
}
void httpClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
_headerKeysCount = headerKeysCount;
if(_currentHeaders)
delete[] _currentHeaders;
_currentHeaders = new RequestArgument[_headerKeysCount];
for(size_t i = 0; i < _headerKeysCount; i++) {
_currentHeaders[i].key = headerKeys[i];
}
}
String httpClient::header(const char* name) {
for(size_t i = 0; i < _headerKeysCount; ++i) {
if(_currentHeaders[i].key == name)
return _currentHeaders[i].value;
}
return String();
}
String httpClient::header(size_t i) {
if(i < _headerKeysCount)
return _currentHeaders[i].value;
return String();
}
String httpClient::headerName(size_t i) {
if(i < _headerKeysCount)
return _currentHeaders[i].key;
return String();
}
int httpClient::headers() {
return _headerKeysCount;
}
bool httpClient::hasHeader(const char* name) {
for(size_t i = 0; i < _headerKeysCount; ++i) {
if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0))
return true;
}
return false;
}
/**
* init TCP connection and handle ssl verify if needed
* @return true if connection is ok
*/
bool httpClient::connect(void) {
if(connected()) {
DEBUG_HTTPCLIENT("[HTTP-Client] connect. already connected, try reuse!\n");
return true;
}
if(_https) {
DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n");
if(_tcps) {
_tcps->~WiFiClient();
_tcps = NULL;
_tcp = NULL;
}
_tcps = new WiFiClientSecure();
_tcp = _tcps;
} else {
DEBUG_HTTPCLIENT("[HTTP-Client] connect http...\n");
if(_tcp) {
_tcp->~WiFiClient();
_tcp = NULL;
}
_tcp = new WiFiClient();
}
if(!_tcp->connect(_host.c_str(), _port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u.\n", _host.c_str(), _port);
return false;
}
DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u.\n", _host.c_str(), _port);
if(_https && _httpsFingerprint.length() > 0) {
if(_tcps->verify(_httpsFingerprint.c_str(), _host.c_str())) {
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate matches\n");
} else {
DEBUG_HTTPCLIENT("[HTTP-Client] https certificate doesn't match!\n");
_tcp->stop();
return false;
}
}
// set Timeout for readBytesUntil and readStringUntil
_tcp->setTimeout(HTTPCLIENT_TCP_TIMEOUT);
#ifdef ESP8266
_tcp->setNoDelay(true);
#endif
return connected();
}
/**
* sends HTTP request header
* @param type (GET, POST, ...)
* @return status
*/
bool httpClient::sendHeader(const char * type) {
if(!connected()) {
return false;
}
String header = String(type) + " " + _url + " HTTP/1.1\r\n"
"Host: " + _host + "\r\n"
"User-Agent: ESP8266httpClient\r\n"
"Connection: ";
if(_reuse) {
header += "keep-alive";
} else {
header += "close";
}
header += "\r\n" + _Headers + "\r\n";
return _tcp->write(header.c_str(), header.length());
}
/**
* reads the response from the server
* @return int http code
*/
int httpClient::handleHeaderResponse() {
if(!connected()) {
return HTTPC_ERROR_NOT_CONNECTED;
}
_returnCode = -1;
_size = -1;
while(connected()) {
size_t len = _tcp->available();
if(len > 0) {
String headerLine = _tcp->readStringUntil('\n');
headerLine.trim(); // remove \r
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str());
if(headerLine.startsWith("HTTP/1.")) {
_returnCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
} else if(headerLine.indexOf(':')) {
String headerName = headerLine.substring(0, headerLine.indexOf(':'));
String headerValue = headerLine.substring(headerLine.indexOf(':') + 2);
if(headerName.equalsIgnoreCase("Content-Length")) {
_size = headerValue.toInt();
}
if(headerName.equalsIgnoreCase("Connection")) {
_canReuse = headerValue.equalsIgnoreCase("keep-alive");
}
for(size_t i = 0; i < _headerKeysCount; i++) {
if(_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
_currentHeaders[i].value = headerValue;
break;
}
}
}
if(headerLine == "") {
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] code: %d\n", _returnCode);
if(_size) {
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] size: %d\n", _size);
}
if(_returnCode) {
return _returnCode;
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Remote host is not an HTTP Server!");
return HTTPC_ERROR_NO_HTTP_SERVER;
}
}
} else {
delay(0);
}
}
return HTTPC_ERROR_CONNECTION_LOST;
}

View File

@ -0,0 +1,122 @@
/**
* ESP8266httpClient.h
*
* Created on: 02.11.2015
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP8266httpClient for Arduino.
*
* 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 ESP8266HTTPCLIENT_H_
#define ESP8266HTTPCLIENT_H_
//#define DEBUG_HTTPCLIENT(...) Serial1.printf( __VA_ARGS__ )
#ifndef DEBUG_HTTPCLIENT
#define DEBUG_HTTPCLIENT(...)
#endif
#define HTTPCLIENT_TCP_TIMEOUT (1000)
/// HTTP client errors
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
#define HTTPC_ERROR_NOT_CONNECTED (-4)
#define HTTPC_ERROR_CONNECTION_LOST (-5)
#define HTTPC_ERROR_NO_STREAM (-6)
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
class httpClient {
public:
httpClient();
~httpClient();
void begin(const char *host, uint16_t port, const char * url = "/", bool https = false, const char * httpsFingerprint = "");
void begin(String host, uint16_t port, String url = "/", bool https = false, String httpsFingerprint = "");
void end(void);
bool connected(void);
void setReuse(bool reuse); /// keep-alive
/// request handling
int GET();
int POST(uint8_t * payload, size_t size);
int POST(String payload);
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
void addHeader(const String& name, const String& value, bool first = false);
/// Response handling
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
String header(const char* name); // get request header value by name
String header(size_t i); // get request header value by number
String headerName(size_t i); // get request header name by number
int headers(); // get header count
bool hasHeader(const char* name); // check if header exists
int getSize(void);
WiFiClient & getStream(void) __attribute__ ((deprecated)) ;
WiFiClient * getStreamPtr(void);
int writeToStream(Stream * stream);
String getString(void);
protected:
struct RequestArgument {
String key;
String value;
};
WiFiClient * _tcp;
WiFiClientSecure * _tcps;
/// request handling
String _host;
uint16_t _port;
bool _reuse;
String _url;
bool _https;
String _httpsFingerprint;
String _Headers;
/// Response handling
RequestArgument* _currentHeaders;
size_t _headerKeysCount;
int _returnCode;
int _size;
bool _canReuse;
bool connect(void);
bool sendHeader(const char * type);
int handleHeaderResponse();
};
#endif /* ESP8266HTTPCLIENT_H_ */

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# @file esp8266-arudino-doc.bash # @file esp8266-arudino-doc.bash
@ -11,15 +11,16 @@
# Packages needed by this script: # Packages needed by this script:
# * linux commands: ln, cp, mkdir, rm, wget # * linux commands: ln, cp, mkdir, rm, wget
# * git # * git
# * jekyll
# #
# ruby libraries: # ruby gems:
# * jekyll
# * redcarpet # * redcarpet
# * rb-pygments # * rb-pygments
# #
# gem install [lib] # gem install [lib]
# #
set -e
# some variable definitions # some variable definitions
tmp_path=$1 tmp_path=$1
@ -37,11 +38,11 @@ echo " version: "$version
echo " release date: "$release_date echo " release date: "$release_date
echo " build date: "$build_date echo " build date: "$build_date
echo " put documentation into: "$destination_path echo " put documentation into: "$destination_path
echo "documentatino template url: "$doc_template_url echo "documentation template url: "$doc_template_url
echo " url: "$url echo " url: "$url
# continue? # continue?
read -e -p "Dou you wish to continue (y/n)? " -n 1 -i "y" decision read -e -p "Dou you wish to continue (y/n)? " -n 1 decision
if echo "$decision" | grep -iq "^y" ;then if echo "$decision" | grep -iq "^y" ;then
echo "okay" echo "okay"
else else
@ -99,8 +100,10 @@ ln -s ../$version _site
# add subtitle and basurl # add subtitle and basurl
echo "url: \"$url\"" > _config_local.yml echo "url: \"$url\"" > _config_local.yml
echo "subtitle: \"ver. $version, built on $build_date\"" >> _config_local.yml echo "version: $version" >> _config_local.yml
echo "build_date: $build_date" >> _config_local.yml
echo "baseurl: /Arduino/versions/$version" >> _config_local.yml echo "baseurl: /Arduino/versions/$version" >> _config_local.yml
mv doc/reference_items.yml _data/reference_items.yml
# build with jekyll # build with jekyll
jekyll build --config _config.yml,_config_local.yml jekyll build --config _config.yml,_config_local.yml