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

Merge remote-tracking branch 'remotes/esp8266/esp8266' into esp8266

This commit is contained in:
Markus Sattler 2015-07-09 19:06:35 +02:00
commit d6f1281c99
29 changed files with 1431 additions and 183 deletions

View File

@ -1,12 +1,16 @@
sudo: true
language: java
os:
- linux
- osx
jdk:
- openjdk6
- oraclejdk8
script:
- sudo apt-get update -qq
- sudo apt-get install -qq ant
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install -qq ant; fi
- pushd build
- echo "" | ant dist
- popd

View File

@ -83,6 +83,8 @@ more than 20 milliseconds is not recommended.
By default the diagnostic output from WiFi libraries is disabled when you call ```Serial.begin```. To enable debug output again, call ```Serial.setDebugOutput(true);```. To redirect debug output to ```Serial1``` instead, call ```Serial1.setDebugOutput(true);```.
You also need to use ```Serial.setDebugOutput(true)``` to enable output from the Arduino ```printf()``` function.
Both ```Serial``` and ```Serial1``` objects support 5, 6, 7, 8 data bits, odd (O), even (E), and no (N) parity, and 1 or 2 stop bits. To set the desired mode, call ```Serial.begin(baudrate, SERIAL_8N1);```, ```Serial.begin(baudrate, SERIAL_6E2);```, etc.
#### Progmem ####

View File

@ -1,7 +1,9 @@
menu.UploadSpeed=Upload Speed
menu.CpuFrequency=CPU Frequency
menu.FlashSize=Flash Size
menu.FlashMode=Flash Mode
menu.FlashFreq=Flash Frequency
menu.UploadTool=Upload Using
##############################################################
generic.name=Generic ESP8266 Module
@ -23,11 +25,26 @@ generic.build.variant=generic
generic.build.flash_mode=qio
generic.build.spiffs_pagesize=256
generic.menu.UploadTool.esptool=Serial
generic.menu.UploadTool.esptool.upload.tool=esptool
generic.menu.UploadTool.espota=OTA
generic.menu.UploadTool.espota.upload.tool=espota
generic.menu.CpuFrequency.80=80 MHz
generic.menu.CpuFrequency.80.build.f_cpu=80000000L
generic.menu.CpuFrequency.160=160 MHz
generic.menu.CpuFrequency.160.build.f_cpu=160000000L
generic.menu.FlashFreq.40=40MHz
generic.menu.FlashFreq.40.build.flash_freq=40
generic.menu.FlashFreq.80=80MHz
generic.menu.FlashFreq.80.build.flash_freq=80
generic.menu.FlashMode.dio=DIO
generic.menu.FlashMode.dio.build.flash_mode=dio
generic.menu.FlashMode.qio=QIO
generic.menu.FlashMode.qio.build.flash_mode=qio
generic.menu.UploadSpeed.115200=115200
generic.menu.UploadSpeed.115200.upload.speed=115200
generic.menu.UploadSpeed.9600=9600
@ -117,11 +134,6 @@ generic.menu.FlashSize.4M.upload.maximum_size=1044464
# generic.menu.FlashSize.16M.build.spiffs_end=0x1000000
# generic.menu.FlashSize.16M.build.spiffs_blocksize=8192
generic.menu.FlashFreq.40=40MHz
generic.menu.FlashFreq.40.build.flash_freq=40
generic.menu.FlashFreq.80=80MHz
generic.menu.FlashFreq.80.build.flash_freq=80
##############################################################
modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV)

View File

@ -1,4 +1,5 @@
XTENSA_TOOLCHAIN ?=
XTENSA_TOOLCHAIN ?= ../../tools/xtensa-lx106-elf/bin/
ESPTOOL ?= ../../tools/esptool
BIN_DIR := ./
TARGET_DIR := ./
@ -6,7 +7,7 @@ TARGET_DIR := ./
TARGET_OBJ_FILES := \
eboot.o \
eboot_command.o \
flash.o \
TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))

View File

@ -76,38 +76,31 @@ int copy_raw(const uint32_t src_addr,
const uint32_t dst_addr,
const uint32_t size)
{
ets_putc('\n');
ets_putc('c');
ets_putc('p');
ets_putc('\n');
// require regions to be aligned
if (src_addr & 0xfff != 0 ||
dst_addr & 0xfff != 0) {
return 1;
}
if (SPIEraseAreaEx(dst_addr, size)) {
return 2;
}
const uint32_t buffer_size = 4096;
const uint32_t buffer_size = FLASH_SECTOR_SIZE;
uint8_t buffer[buffer_size];
const uint32_t end = src_addr + size;
uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
uint32_t saddr = src_addr;
uint32_t daddr = dst_addr;
uint32_t left = size;
while (saddr < end) {
uint32_t will_copy = (left < buffer_size) ? left : buffer_size;
if (SPIRead(saddr, buffer, will_copy)) {
return 3;
}
if (SPIWrite(daddr, buffer, will_copy)) {
return 4;
}
saddr += will_copy;
daddr += will_copy;
left -= will_copy;
while (left) {
if (SPIEraseSector(daddr/buffer_size)) {
return 2;
}
if (SPIRead(saddr, buffer, buffer_size)) {
return 3;
}
if (SPIWrite(daddr, buffer, buffer_size)) {
return 4;
}
saddr += buffer_size;
daddr += buffer_size;
left -= buffer_size;
}
return 0;
@ -123,14 +116,16 @@ void main()
if (eboot_command_read(&cmd)) {
cmd.action = ACTION_LOAD_APP;
cmd.args[0] = 0;
ets_putc('e');
ets_putc('~');
} else {
ets_putc('@');
}
eboot_command_clear();
if (cmd.action == ACTION_COPY_RAW) {
ets_putc('c'); ets_putc('p'); ets_putc(':');
res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
ets_putc('0'+res); ets_putc('\n');
if (res == 0) {
cmd.action = ACTION_LOAD_APP;
cmd.args[0] = cmd.args[1];
@ -138,15 +133,14 @@ void main()
}
if (cmd.action == ACTION_LOAD_APP) {
res = load_app_from_flash_raw(cmd.args[0]);
ets_putc('l'); ets_putc('d'); ets_putc('\n');
res = load_app_from_flash_raw(cmd.args[0]);
//we will get to this only on load fail
ets_putc('e'); ets_putc(':'); ets_putc('0'+res); ets_putc('\n');
}
if (res) {
ets_putc('\n');
ets_putc('#');
ets_putc('0' + res);
ets_putc('\n');
SWRST;
SWRST;
}
while(true){}

Binary file not shown.

View File

@ -4,7 +4,7 @@
* Redistribution and use is permitted according to the conditions of the
* 3-clause BSD license to be found in the LICENSE file.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

View File

@ -233,6 +233,7 @@ void loop(void);
#include "HardwareSerial.h"
#include "Esp.h"
#include "Updater.h"
#include "debug.h"
#define min(a,b) ((a)<(b)?(a):(b))

View File

@ -30,7 +30,7 @@ extern struct rst_info resetInfo;
}
// #define DEBUG_SERIAL Serial
//#define DEBUG_SERIAL Serial
/**
@ -365,83 +365,38 @@ uint32_t EspClass::getFreeSketchSpace() {
return freeSpaceEnd - freeSpaceStart;
}
bool EspClass::updateSketch(Stream& in, uint32_t size) {
if (size > getFreeSketchSpace())
return false;
uint32_t usedSize = getSketchSize();
uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) {
if(!Update.begin(size)){
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.printf("erase @0x%x size=0x%x\r\n", freeSpaceStart, roundedSize);
DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
if(restartOnFail) ESP.restart();
return false;
}
noInterrupts();
int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize);
interrupts();
if (rc)
return false;
if(Update.writeStream(in) != size){
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("erase done");
DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
if(restartOnFail) ESP.restart();
return false;
}
uint32_t addr = freeSpaceStart;
uint32_t left = size;
const uint32_t bufferSize = FLASH_SECTOR_SIZE;
std::unique_ptr<uint8_t> buffer(new uint8_t[bufferSize]);
if(!Update.end()){
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("writing");
DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
while (left > 0) {
size_t willRead = (left < bufferSize) ? left : bufferSize;
size_t rd = in.readBytes(buffer.get(), willRead);
if (rd != willRead) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("stream read failed");
#endif
return false;
}
if(addr == freeSpaceStart) {
// check for valid first magic byte
if(*((uint8 *) buffer.get()) != 0xE9) {
return false;
}
}
noInterrupts();
rc = SPIWrite(addr, buffer.get(), willRead);
interrupts();
if (rc) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("write failed");
#endif
return false;
}
addr += willRead;
left -= willRead;
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(".");
#endif
}
if(restartOnFail) ESP.restart();
return false;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("\r\nrestarting");
#endif
eboot_command ebcmd;
ebcmd.action = ACTION_COPY_RAW;
ebcmd.args[0] = freeSpaceStart;
ebcmd.args[1] = 0x00000;
ebcmd.args[2] = size;
eboot_command_write(&ebcmd);
ESP.restart();
return true; // never happens
DEBUG_SERIAL.println("Update SUCCESS");
#endif
if(restartOnSuccess) ESP.restart();
return true;
}

View File

@ -116,7 +116,7 @@ class EspClass {
uint32_t getSketchSize();
uint32_t getFreeSketchSpace();
bool updateSketch(Stream& in, uint32_t size);
bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true);
String getResetInfo();
struct rst_info * getResetInfoPtr();

View File

@ -47,8 +47,8 @@ size_t ICACHE_FLASH_ATTR Print::write(const uint8_t *buffer, size_t size) {
size_t Print::printf(const char *format, ...) {
va_list arg;
va_start(arg, format);
char temp[256];
size_t len = ets_vsnprintf(temp, 256, format, arg);
char temp[1460];
size_t len = ets_vsnprintf(temp, 1460, format, arg);
len = print(temp);
va_end(arg);
return len;

209
cores/esp8266/Updater.cpp Normal file
View File

@ -0,0 +1,209 @@
#include "Updater.h"
#include "Arduino.h"
#include "eboot_command.h"
//#define DEBUG_UPDATER Serial
extern "C" uint32_t _SPIFFS_start;
UpdaterClass::UpdaterClass()
: _error(0)
, _buffer(0)
, _bufferLen(0)
, _size(0)
, _startAddress(0)
, _currentAddress(0)
{
}
void UpdaterClass::_reset() {
if (_buffer)
delete[] _buffer;
_buffer = 0;
_bufferLen = 0;
_startAddress = 0;
_currentAddress = 0;
_size = 0;
}
bool UpdaterClass::begin(size_t size){
if(_size > 0){
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.println("already running");
#endif
return false;
}
if(size == 0){
_error = UPDATE_ERROR_SIZE;
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
#endif
return false;
}
_reset();
_error = 0;
//size of current sketch rounded to a sector
uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address of the end of the space available for sketch and update
uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000;
//size of the update rounded to a sector
uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
//address where we will start writing the update
uint32_t updateStartAddress = updateEndAddress - roundedSize;
//make sure that the size of both sketches is less than the total space (updateEndAddress)
if(updateStartAddress < currentSketchSize){
_error = UPDATE_ERROR_SPACE;
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
#endif
return false;
}
//initialize
_startAddress = updateStartAddress;
_currentAddress = _startAddress;
_size = size;
_buffer = new uint8_t[FLASH_SECTOR_SIZE];
return true;
}
bool UpdaterClass::end(bool evenIfRemaining){
if(_size == 0){
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.println("no update");
#endif
return false;
}
if(hasError() || (!isFinished() && !evenIfRemaining)){
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
#endif
_reset();
return false;
}
if(evenIfRemaining){
if(_bufferLen > 0){
_writeBuffer();
}
_size = progress();
}
eboot_command ebcmd;
ebcmd.action = ACTION_COPY_RAW;
ebcmd.args[0] = _startAddress;
ebcmd.args[1] = 0x00000;
ebcmd.args[2] = _size;
eboot_command_write(&ebcmd);
#ifdef DEBUG_UPDATER
DEBUG_UPDATER.printf("Staged: address:0x%08X, size:0x%08X\n", _startAddress, _size);
#endif
_reset();
return true;
}
bool UpdaterClass::_writeBuffer(){
noInterrupts();
int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE);
if (!rc) {
rc = SPIWrite(_currentAddress, _buffer, _bufferLen);
}
interrupts();
if (rc) {
_error = UPDATE_ERROR_WRITE;
_currentAddress = (_startAddress + _size);
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
#endif
return false;
}
_currentAddress += _bufferLen;
_bufferLen = 0;
return true;
}
size_t UpdaterClass::write(uint8_t *data, size_t len) {
size_t left = len;
if(hasError() || !isRunning())
return 0;
if(len > remaining())
len = remaining();
while((_bufferLen + left) > FLASH_SECTOR_SIZE) {
size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen;
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
_bufferLen += toBuff;
if(!_writeBuffer()){
return len - left;
}
left -= toBuff;
yield();
}
//lets see whats left
memcpy(_buffer + _bufferLen, data + (len - left), left);
_bufferLen += left;
if(_bufferLen == remaining()){
//we are at the end of the update, so should write what's left to flash
if(!_writeBuffer()){
return len - left;
}
}
return len;
}
size_t UpdaterClass::writeStream(Stream &data) {
size_t written = 0;
size_t toRead = 0;
if(hasError() || !isRunning())
return 0;
while(remaining()) {
toRead = FLASH_SECTOR_SIZE - _bufferLen;
toRead = data.readBytes(_buffer + _bufferLen, toRead);
if(toRead == 0){ //Timeout
_error = UPDATE_ERROR_STREAM;
_currentAddress = (_startAddress + _size);
#ifdef DEBUG_UPDATER
printError(DEBUG_UPDATER);
#endif
return written;
}
_bufferLen += toRead;
if((_bufferLen == remaining() || _bufferLen == FLASH_SECTOR_SIZE) && !_writeBuffer())
return written;
written += toRead;
yield();
}
return written;
}
void UpdaterClass::printError(Stream &out){
out.printf("ERROR[%u]: ", _error);
if(_error == UPDATE_ERROR_OK){
out.println("No Error");
} else if(_error == UPDATE_ERROR_WRITE){
out.println("Flash Write Failed");
} else if(_error == UPDATE_ERROR_ERASE){
out.println("Flash Erase Failed");
} else if(_error == UPDATE_ERROR_SPACE){
out.println("Not Enough Space");
} else if(_error == UPDATE_ERROR_SIZE){
out.println("Bad Size Given");
} else if(_error == UPDATE_ERROR_STREAM){
out.println("Stream Read Timeout");
} else {
out.println("UNKNOWN");
}
}
UpdaterClass Update;

121
cores/esp8266/Updater.h Normal file
View File

@ -0,0 +1,121 @@
#ifndef ESP8266UPDATER_H
#define ESP8266UPDATER_H
#include "Arduino.h"
#include "flash_utils.h"
#define UPDATE_ERROR_OK 0
#define UPDATE_ERROR_WRITE 1
#define UPDATE_ERROR_ERASE 2
#define UPDATE_ERROR_SPACE 3
#define UPDATE_ERROR_SIZE 4
#define UPDATE_ERROR_STREAM 5
class UpdaterClass {
public:
UpdaterClass();
/*
Call this to check the space needed for the update
Will return false if there is not enough space
*/
bool begin(size_t size);
/*
Writes a buffer to the flash and increments the address
Returns the amount written
*/
size_t write(uint8_t *data, size_t len);
/*
Writes the remaining bytes from the Stream to the flash
Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout
Returns the bytes written
Should be equal to the remaining bytes when called
Usable for slow streams like Serial
*/
size_t writeStream(Stream &data);
/*
If all bytes are written
this call will write the config to eboot
and return true
If there is already an update running but is not finished and !evenIfRemainanig
or there is an error
this will clear everything and return false
the last error is available through getError()
evenIfRemaining is helpfull when you update without knowing the final size first
*/
bool end(bool evenIfRemaining = false);
/*
Prints the last error to an output stream
*/
void printError(Stream &out);
//Helpers
uint8_t getError(){ return _error; }
void clearError(){ _error = UPDATE_ERROR_OK; }
bool hasError(){ return _error != UPDATE_ERROR_OK; }
bool isRunning(){ return _size > 0; }
bool isFinished(){ return _currentAddress == (_startAddress + _size); }
size_t size(){ return _size; }
size_t progress(){ return _currentAddress - _startAddress; }
size_t remaining(){ return _size - (_currentAddress - _startAddress); }
/*
Template to write from objects that expose
available() and read(uint8_t*, size_t) methods
faster than the writeStream method
writes only what is available
*/
template<typename T>
size_t write(T &data){
size_t written = 0;
if (hasError() || !isRunning())
return 0;
size_t available = data.available();
while(available) {
if(_bufferLen + available > remaining()){
available = remaining() - _bufferLen;
}
if(_bufferLen + available > FLASH_SECTOR_SIZE) {
size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen;
data.read(_buffer + _bufferLen, toBuff);
_bufferLen += toBuff;
if(!_writeBuffer())
return written;
written += toBuff;
} else {
data.read(_buffer + _bufferLen, available);
_bufferLen += available;
written += available;
if(_bufferLen == remaining()) {
if(!_writeBuffer()) {
return written;
}
}
}
if(remaining() == 0)
return written;
yield();
available = data.available();
}
return written;
}
private:
void _reset();
bool _writeBuffer();
uint8_t *_buffer;
size_t _bufferLen;
size_t _size;
uint32_t _startAddress;
uint32_t _currentAddress;
uint8_t _error;
};
extern UpdaterClass Update;
#endif

View File

@ -23,11 +23,11 @@
*/
#include <stdlib.h>
#include <string.h>
#include "stdlib_noniso.h"
#include "ets_sys.h"
#define sprintf ets_sprintf
#define strcpy ets_strcpy
int atoi(const char* s) {
return (int) atol(s);

View File

@ -87,38 +87,6 @@ int vsnprintf(char * buffer, size_t size, const char * format, va_list arg) {
return ets_vsnprintf(buffer, size, format, arg);
}
int memcmp(const void *s1, const void *s2, size_t n) {
return ets_memcmp(s1, s2, n);
}
void* memcpy(void *dest, const void *src, size_t n) {
return ets_memcpy(dest, src, n);
}
void* memset(void *s, int c, size_t n) {
return ets_memset(s, c, n);
}
int strcmp(const char *s1, const char *s2) {
return ets_strcmp(s1, s2);
}
char* strcpy(char *dest, const char *src) {
return ets_strcpy(dest, src);
}
size_t strlen(const char *s) {
return ets_strlen(s);
}
int strncmp(const char *s1, const char *s2, size_t len) {
return ets_strncmp(s1, s2, len);
}
char* strncpy(char * dest, const char * src, size_t n) {
return ets_strncpy(dest, src, n);
}
size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
// there is no ets_strnlen
const char *cp;
@ -126,10 +94,6 @@ size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
return (size_t)(cp - s);
}
char* strstr(const char *haystack, const char *needle) {
return ets_strstr(haystack, needle);
}
char* ICACHE_FLASH_ATTR strchr(const char * str, int character) {
while(1) {
if(*str == 0x00) {
@ -160,13 +124,12 @@ char* ICACHE_FLASH_ATTR strcat(char * dest, const char * src) {
}
char* ICACHE_FLASH_ATTR strncat(char * dest, const char * src, size_t n) {
uint32_t offset = strlen(dest);
for(uint32_t i = 0; i < n; i++) {
*(dest + i + offset) = *(src + i);
if(*(src + i) == 0x00) {
break;
}
size_t i;
size_t offset = strlen(dest);
for(i = 0; i < n && src[i]; i++) {
dest[i + offset] = src[i];
}
dest[i + offset] = 0;
return dest;
}
@ -476,3 +439,56 @@ int* ICACHE_FLASH_ATTR __errno(void) {
return &errno_var;
}
/*
* begin newlib/string/strlcpy.c
*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
size_t ICACHE_FLASH_ATTR strlcpy(char* dst, const char* src, size_t size) {
const char *s = src;
size_t n = size;
if (n != 0 && --n != 0) {
do {
if ((*dst++ = *s++) == 0)
break;
} while (--n != 0);
}
if (n == 0) {
if (size != 0)
*dst = 0;
while (*s++);
}
return(s - src - 1);
}
/*
* end of newlib/string/strlcpy.c
*/

View File

@ -0,0 +1,406 @@
/*
ESP8266 Simple Service Discovery
Copyright (c) 2015 Hristo Gochkov
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
Can be found at: https://github.com/nomadnt/uSSDP
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define LWIP_OPEN_SRC
#include <functional>
#include "ESP8266SSDP.h"
#include "WiFiUdp.h"
#include "debug.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"
// #define DEBUG_SSDP Serial
#define SSDP_INTERVAL 1200
#define SSDP_PORT 1900
#define SSDP_METHOD_SIZE 10
#define SSDP_URI_SIZE 2
#define SSDP_BUFFER_SIZE 64
#define SSDP_MULTICAST_TTL 1
static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250);
static const char* _ssdp_response_template =
"HTTP/1.1 200 OK\r\n"
"EXT:\r\n"
"ST: upnp:rootdevice\r\n";
static const char* _ssdp_notify_template =
"NOTIFY * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"NT: upnp:rootdevice\r\n"
"NTS: ssdp:alive\r\n";
static const char* _ssdp_packet_template =
"%s" // _ssdp_response_template / _ssdp_notify_template
"CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL
"SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber
"USN: uuid:%s\r\n" // _uuid
"LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
"\r\n";
static const char* _ssdp_schema_template =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml\r\n"
"Connection: close\r\n"
"Access-Control-Allow-Origin: *\r\n"
"\r\n"
"<?xml version=\"1.0\"?>"
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
"<specVersion>"
"<major>1</major>"
"<minor>0</minor>"
"</specVersion>"
"<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
"<device>"
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
"<friendlyName>%s</friendlyName>"
"<presentationURL>%s</presentationURL>"
"<serialNumber>%s</serialNumber>"
"<modelName>%s</modelName>"
"<modelNumber>%s</modelNumber>"
"<modelURL>%s</modelURL>"
"<manufacturer>%s</manufacturer>"
"<manufacturerURL>%s</manufacturerURL>"
"<UDN>uuid:%s</UDN>"
"</device>"
// "<iconList>"
// "<icon>"
// "<mimetype>image/png</mimetype>"
// "<height>48</height>"
// "<width>48</width>"
// "<depth>24</depth>"
// "<url>icon48.png</url>"
// "</icon>"
// "<icon>"
// "<mimetype>image/png</mimetype>"
// "<height>120</height>"
// "<width>120</width>"
// "<depth>24</depth>"
// "<url>icon120.png</url>"
// "</icon>"
// "</iconList>"
"</root>\r\n"
"\r\n";
struct SSDPTimer {
ETSTimer timer;
};
SSDPClass::SSDPClass() :
_server(0),
_port(80),
_pending(false),
_timer(new SSDPTimer)
{
_uuid[0] = '\0';
_modelNumber[0] = '\0';
_friendlyName[0] = '\0';
_presentationURL[0] = '\0';
_serialNumber[0] = '\0';
_modelName[0] = '\0';
_modelURL[0] = '\0';
_manufacturer[0] = '\0';
_manufacturerURL[0] = '\0';
sprintf(_schemaURL, "ssdp/schema.xml");
}
SSDPClass::~SSDPClass(){
delete _timer;
}
bool SSDPClass::begin(){
_pending = false;
uint32_t chipId = ESP.getChipId();
sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x",
(uint16_t) ((chipId >> 16) & 0xff),
(uint16_t) ((chipId >> 8) & 0xff),
(uint16_t) chipId & 0xff );
#ifdef DEBUG_SSDP
DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid);
#endif
if (_server) {
_server->unref();
_server = 0;
}
_server = new UdpContext;
_server->ref();
ip_addr_t ifaddr;
ifaddr.addr = WiFi.localIP();
ip_addr_t multicast_addr;
multicast_addr.addr = (uint32_t) SSDP_MULTICAST_ADDR;
if (igmp_joingroup(&ifaddr, &multicast_addr) != ERR_OK ) {
DEBUGV("SSDP failed to join igmp group");
return false;
}
if (!_server->listen(*IP_ADDR_ANY, SSDP_PORT)) {
return false;
}
_server->setMulticastInterface(ifaddr);
_server->setMulticastTTL(SSDP_MULTICAST_TTL);
_server->onRx(std::bind(&SSDPClass::_update, this));
if (!_server->connect(multicast_addr, SSDP_PORT)) {
return false;
}
_startTimer();
return true;
}
void SSDPClass::_send(ssdp_method_t method){
char buffer[1460];
uint32_t ip = WiFi.localIP();
int len = snprintf(buffer, sizeof(buffer),
_ssdp_packet_template,
(method == NONE)?_ssdp_response_template:_ssdp_notify_template,
SSDP_INTERVAL,
_modelName, _modelNumber,
_uuid,
IP2STR(&ip), _port, _schemaURL
);
_server->append(buffer, len);
ip_addr_t remoteAddr;
uint16_t remotePort;
if(method == NONE) {
remoteAddr.addr = _respondToAddr;
remotePort = _respondToPort;
#ifdef DEBUG_SSDP
DEBUG_SSDP.print("Sending Response to ");
#endif
} else {
remoteAddr.addr = SSDP_MULTICAST_ADDR;
remotePort = SSDP_PORT;
#ifdef DEBUG_SSDP
DEBUG_SSDP.println("Sending Notify to ");
#endif
}
#ifdef DEBUG_SSDP
DEBUG_SSDP.print(IPAddress(remoteAddr.addr));
DEBUG_SSDP.print(":");
DEBUG_SSDP.println(remotePort);
#endif
_server->send(&remoteAddr, remotePort);
}
void SSDPClass::schema(WiFiClient client){
uint32_t ip = WiFi.localIP();
client.printf(_ssdp_schema_template,
IP2STR(&ip), _port,
_friendlyName,
_presentationURL,
_serialNumber,
_modelName,
_modelNumber,
_modelURL,
_manufacturer,
_manufacturerURL,
_uuid
);
}
void SSDPClass::_update(){
if(!_pending && _server->next()) {
ssdp_method_t method = NONE;
_respondToAddr = _server->getRemoteAddress();
_respondToPort = _server->getRemotePort();
typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states;
states state = METHOD;
typedef enum {START, MAN, ST, MX} headers;
headers header = START;
uint8_t cursor = 0;
uint8_t cr = 0;
char buffer[SSDP_BUFFER_SIZE] = {0};
while(_server->getSize() > 0){
char c = _server->read();
(c == '\r' || c == '\n') ? cr++ : cr = 0;
switch(state){
case METHOD:
if(c == ' '){
if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH;
else if(strcmp(buffer, "NOTIFY") == 0) method = NOTIFY;
if(method == NONE) state = ABORT;
else state = URI;
cursor = 0;
} else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
break;
case URI:
if(c == ' '){
if(strcmp(buffer, "*")) state = ABORT;
else state = PROTO;
cursor = 0;
} else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
break;
case PROTO:
if(cr == 2){ state = KEY; cursor = 0; }
break;
case KEY:
if(cr == 4){ _pending = true; _process_time = millis(); }
else if(c == ' '){ cursor = 0; state = VALUE; }
else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
break;
case VALUE:
if(cr == 2){
switch(header){
case MAN:
#ifdef DEBUG_SSDP
DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer);
#endif
break;
case ST:
if(strcmp(buffer, "ssdp:all")){
state = ABORT;
#ifdef DEBUG_SSDP
DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer);
#endif
}
break;
case MX:
_delay = random(0, atoi(buffer)) * 1000L;
break;
}
if(state != ABORT){ state = KEY; header = START; cursor = 0; }
} else if(c != '\r' && c != '\n'){
if(header == START){
if(strncmp(buffer, "MA", 2) == 0) header = MAN;
else if(strcmp(buffer, "ST") == 0) header = ST;
else if(strcmp(buffer, "MX") == 0) header = MX;
}
if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
}
break;
case ABORT:
_pending = false; _delay = 0;
break;
}
}
}
if(_pending && (millis() - _process_time) > _delay){
_pending = false; _delay = 0;
_send(NONE);
} else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){
_notify_time = millis();
_send(NOTIFY);
}
if (_pending) {
while (_server->next())
_server->flush();
}
}
void SSDPClass::setSchemaURL(const char *url){
strlcpy(_schemaURL, url, sizeof(_schemaURL));
}
void SSDPClass::setHTTPPort(uint16_t port){
_port = port;
}
void SSDPClass::setName(const char *name){
strlcpy(_friendlyName, name, sizeof(_friendlyName));
}
void SSDPClass::setURL(const char *url){
strlcpy(_presentationURL, url, sizeof(_presentationURL));
}
void SSDPClass::setSerialNumber(const char *serialNumber){
strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber));
}
void SSDPClass::setModelName(const char *name){
strlcpy(_modelName, name, sizeof(_modelName));
}
void SSDPClass::setModelNumber(const char *num){
strlcpy(_modelNumber, num, sizeof(_modelNumber));
}
void SSDPClass::setModelURL(const char *url){
strlcpy(_modelURL, url, sizeof(_modelURL));
}
void SSDPClass::setManufacturer(const char *name){
strlcpy(_manufacturer, name, sizeof(_manufacturer));
}
void SSDPClass::setManufacturerURL(const char *url){
strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL));
}
void SSDPClass::_onTimerStatic(SSDPClass* self) {
self->_update();
}
void SSDPClass::_startTimer() {
ETSTimer* tm = &(_timer->timer);
const int interval = 1000;
os_timer_disarm(tm);
os_timer_setfn(tm, reinterpret_cast<ETSTimerFunc*>(&SSDPClass::_onTimerStatic), reinterpret_cast<void*>(this));
os_timer_arm(tm, interval, 1 /* repeat */);
}
SSDPClass SSDP;

View File

@ -0,0 +1,119 @@
/*
ESP8266 Simple Service Discovery
Copyright (c) 2015 Hristo Gochkov
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
Can be found at: https://github.com/nomadnt/uSSDP
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ESP8266SSDP_H
#define ESP8266SSDP_H
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
class UdpContext;
#define SSDP_UUID_SIZE 37
#define SSDP_SCHEMA_URL_SIZE 64
#define SSDP_FRIENDLY_NAME_SIZE 64
#define SSDP_SERIAL_NUMBER_SIZE 32
#define SSDP_PRESENTATION_URL_SIZE 128
#define SSDP_MODEL_NAME_SIZE 64
#define SSDP_MODEL_URL_SIZE 128
#define SSDP_MODEL_VERSION_SIZE 32
#define SSDP_MANUFACTURER_SIZE 64
#define SSDP_MANUFACTURER_URL_SIZE 128
typedef enum {
NONE,
SEARCH,
NOTIFY
} ssdp_method_t;
struct SSDPTimer;
class SSDPClass{
public:
SSDPClass();
~SSDPClass();
bool begin();
void schema(WiFiClient client);
void setName(const String& name) { setName(name.c_str()); }
void setName(const char *name);
void setURL(const String& url) { setURL(url.c_str()); }
void setURL(const char *url);
void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); }
void setSchemaURL(const char *url);
void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); }
void setSerialNumber(const char *serialNumber);
void setModelName(const String& name) { setModelName(name.c_str()); }
void setModelName(const char *name);
void setModelNumber(const String& num) { setModelNumber(num.c_str()); }
void setModelNumber(const char *num);
void setModelURL(const String& url) { setModelURL(url.c_str()); }
void setModelURL(const char *url);
void setManufacturer(const String& name) { setManufacturer(name.c_str()); }
void setManufacturer(const char *name);
void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); }
void setManufacturerURL(const char *url);
void setHTTPPort(uint16_t port);
protected:
void _send(ssdp_method_t method);
void _update();
void _startTimer();
static void _onTimerStatic(SSDPClass* self);
UdpContext* _server;
SSDPTimer* _timer;
IPAddress _respondToAddr;
uint16_t _respondToPort;
bool _pending;
unsigned short _delay;
unsigned long _process_time;
unsigned long _notify_time;
uint16_t _port;
char _schemaURL[SSDP_SCHEMA_URL_SIZE];
char _uuid[SSDP_UUID_SIZE];
char _friendlyName[SSDP_FRIENDLY_NAME_SIZE];
char _serialNumber[SSDP_SERIAL_NUMBER_SIZE];
char _presentationURL[SSDP_PRESENTATION_URL_SIZE];
char _manufacturer[SSDP_MANUFACTURER_SIZE];
char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE];
char _modelName[SSDP_MODEL_NAME_SIZE];
char _modelURL[SSDP_MODEL_URL_SIZE];
char _modelNumber[SSDP_MODEL_VERSION_SIZE];
};
extern SSDPClass SSDP;
#endif

View File

@ -0,0 +1,22 @@
ESP8266 Simple Service Discovery
Copyright (c) 2015 Hristo Gochkov
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
Can be found at: https://github.com/nomadnt/uSSDP
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,52 @@
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
#include <ESP8266WebServer.h>
#include <ESP8266SSDP.h>
const char* ssid = "************";
const char* password = "***********";
ESP8266WebServer HTTP(80);
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("Starting WiFi...");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if(WiFi.waitForConnectResult() == WL_CONNECTED){
Serial.printf("Starting HTTP...\n");
HTTP.on("/index.html", HTTP_GET, [](){
HTTP.send(200, "text/plain", "Hello World!");
});
HTTP.on("/description.xml", HTTP_GET, [](){
SSDP.schema(HTTP.client());
});
HTTP.begin();
Serial.printf("Starting SSDP...\n");
SSDP.setSchemaURL("description.xml");
SSDP.setHTTPPort(80);
SSDP.setName("Philips hue clone");
SSDP.setSerialNumber("001788102201");
SSDP.setURL("index.html");
SSDP.setModelName("Philips hue bridge 2012");
SSDP.setModelNumber("929000226503");
SSDP.setModelURL("http://www.meethue.com");
SSDP.setManufacturer("Royal Philips Electronics");
SSDP.setManufacturerURL("http://www.philips.com");
SSDP.begin();
Serial.printf("Ready!\n");
} else {
Serial.printf("WiFi Failed\n");
while(1) delay(100);
}
}
void loop() {
HTTP.handleClient();
delay(1);
}

View File

@ -0,0 +1,53 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ESP8266SSDP KEYWORD1
SSDP KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
schema KEYWORD2
setName KEYWORD2
setURL KEYWORD2
setHTTPPort KEYWORD2
setSchemaURL KEYWORD2
setSerialNumber KEYWORD2
setModelName KEYWORD2
setModelNumber KEYWORD2
setModelURL KEYWORD2
setManufacturer KEYWORD2
setManufacturerURL KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SSDP_INTERVAL LITERAL1
SSDP_PORT LITERAL1
SSDP_METHOD_SIZE LITERAL1
SSDP_URI_SIZE LITERAL1
SSDP_BUFFER_SIZE LITERAL1
SSDP_BASE_SIZE LITERAL1
SSDP_FRIENDLY_NAME_SIZE LITERAL1
SSDP_SERIAL_NUMBER_SIZE LITERAL1
SSDP_PRESENTATION_URL_SIZE LITERAL1
SSDP_MODEL_NAME_SIZE LITERAL1
SSDP_MODEL_URL_SIZE LITERAL1
SSDP_MODEL_VERSION_SIZE LITERAL1
SSDP_MANUFACTURER_SIZE LITERAL1
SSDP_MANUFACTURER_URL_SIZE LITERAL1
SEARCH LITERAL1
NOTIFY LITERAL1
BASIC LITERAL1
MANAGEABLE LITERAL1
SOLARPROTECTIONBLIND LITERAL1
DIGITALSECURITYCAMERA LITERAL1
HVAC LITERAL1
LIGHTINGCONTROL LITERAL1

View File

@ -0,0 +1,73 @@
/*
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
const char* host = "esp8266-webupdate";
const char* ssid = "........";
const char* password = "........";
ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
void setup(void){
Serial.begin(115200);
Serial.println();
Serial.println("Booting Sketch...");
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
if(WiFi.waitForConnectResult() == WL_CONNECTED){
MDNS.begin(host);
server.on("/", HTTP_GET, [](){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/html", serverIndex);
});
server.onFileUpload([](){
if(server.uri() != "/update") return;
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
Serial.setDebugOutput(true);
WiFiUDP::stopAll();
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_WRITE){
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
Serial.setDebugOutput(false);
}
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();
MDNS.addService("http", "tcp", 80);
Serial.printf("Ready! Open http://%s.local in your browser\n", host);
} else {
Serial.println("WiFi Failed");
}
}
void loop(void){
server.handleClient();
delay(1);
}

View File

@ -108,8 +108,20 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
if (!isForm){
if (searchStr != "") searchStr += '&';
searchStr += client.readStringUntil('\r');
client.readStringUntil('\n');
String bodyLine = client.readStringUntil('\r');
#ifdef DEBUG
DEBUG_OUTPUT.print("Plain: ");
DEBUG_OUTPUT.println(bodyLine);
#endif
if(bodyLine.startsWith("{") || bodyLine.startsWith("[") || bodyLine.indexOf('=') == -1){
//plain post json or other data
searchStr += "plain=";
searchStr += bodyLine;
searchStr += client.readString();
} else {
searchStr += bodyLine;
client.readStringUntil('\n');
}
}
_parseArguments(searchStr);
if (isForm){

View File

@ -110,7 +110,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
netif* interface = ip_route(&addr);
if (!interface) {
DEBUGV("no route to host\r\n");
return 1;
return 0;
}
tcp_pcb* pcb = tcp_new();

View File

@ -41,11 +41,9 @@ extern "C" {
/* This is the aligned version of ip_addr_t,
used as local variable, on the stack, etc. */
#if !defined(IP2STR)
struct ip_addr {
u32_t addr;
};
#endif
/* This is the packed version of ip_addr_t,
used in network headers that are itself packed */
@ -63,9 +61,7 @@ PACK_STRUCT_END
/** ip_addr_t uses a struct for convenience only, so that the same defines can
* operate both on ip_addr_t as well as on ip_addr_p_t. */
#if !defined(IP2STR)
typedef struct ip_addr ip_addr_t;
#endif
typedef struct ip_addr_packed ip_addr_p_t;
/*
@ -97,15 +93,11 @@ extern const ip_addr_t ip_addr_broadcast;
#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast)
/** 255.255.255.255 */
#if !defined(IPADDR_NONE)
#define IPADDR_NONE ((u32_t)0xffffffffUL)
#endif
/** 127.0.0.1 */
#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
/** 0.0.0.0 */
#if !defined(IPADDR_ANY)
#define IPADDR_ANY ((u32_t)0x00000000UL)
#endif
/** 255.255.255.255 */
#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
@ -142,7 +134,6 @@ extern const ip_addr_t ip_addr_broadcast;
#define IP_LOOPBACKNET 127 /* official! */
#if !defined(IP4_ADDR)
#if BYTE_ORDER == BIG_ENDIAN
/** Set an IP address given by the four byte-parts */
#define IP4_ADDR(ipaddr, a,b,c,d) \
@ -159,7 +150,6 @@ extern const ip_addr_t ip_addr_broadcast;
((u32_t)((b) & 0xff) << 8) | \
(u32_t)((a) & 0xff)
#endif
#endif
/** MEMCPY-like copying of IP addresses where addresses are known to be
* 16-bit-aligned if the port is correctly configured (so a port could define
@ -227,7 +217,6 @@ u8_t ip4_addr_netmask_valid(u32_t netmask)ICACHE_FLASH_ATTR;
ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0))
/* Get one byte from the 4-byte address */
#if !defined(IP2STR)
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
@ -238,20 +227,16 @@ u8_t ip4_addr_netmask_valid(u32_t netmask)ICACHE_FLASH_ATTR;
#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
#endif
/** For backwards compatibility */
#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
#if !defined(IP2STR)
u32_t ipaddr_addr(const char *cp)ICACHE_FLASH_ATTR;
#endif
int ipaddr_aton(const char *cp, ip_addr_t *addr)ICACHE_FLASH_ATTR;
/** returns ptr to static buffer; not reentrant! */
char *ipaddr_ntoa(const ip_addr_t *addr)ICACHE_FLASH_ATTR;
char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)ICACHE_FLASH_ATTR;
#if !defined(IP2STR)
#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
ip4_addr2_16(ipaddr), \
ip4_addr3_16(ipaddr), \
@ -264,7 +249,6 @@ struct ip_info {
struct ip_addr netmask;
struct ip_addr gw;
};
#endif
#ifdef __cplusplus
}
#endif

View File

@ -32,7 +32,8 @@ License (MIT license):
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
#define LWIP_INTERNAL
#define LWIP_OPEN_SRC
#include "ESP8266mDNS.h"
#include <functional>
@ -41,7 +42,6 @@ License (MIT license):
extern "C" {
#include "osapi.h"
#include "ets_sys.h"
#include "ip_addr.h"
#include "user_interface.h"
}
@ -66,11 +66,16 @@ extern "C" {
#define MDNS_TYPE_PTR 0x000C
#define MDNS_TYPE_SRV 0x0021
#define MDNS_TYPE_TXT 0x0010
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_CLASS_IN 0x0001
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001
#define MDNS_ANSWERS_ALL 0x0F
#define MDNS_ANSWER_PTR 0x08
#define MDNS_ANSWER_TXT 0x04
#define MDNS_ANSWER_SRV 0x02
#define MDNS_ANSWER_A 0x01
#define _conn_read32() (((uint32_t)_conn->read() << 24) | ((uint32_t)_conn->read() << 16) | ((uint32_t)_conn->read() << 8) | _conn->read())
#define _conn_read16() (((uint16_t)_conn->read() << 8) | _conn->read())
#define _conn_read8() _conn->read()
@ -341,7 +346,6 @@ void MDNSResponder::_parsePacket(){
else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR ");
else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV ");
else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT ");
else if(currentType == MDNS_TYPE_NSEC) os_printf(" NSEC ");
else os_printf(" 0x%04X ", currentType);
if(currentClass == MDNS_CLASS_IN) os_printf(" IN ");

View File

@ -0,0 +1,98 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUDP.h>
const char* host = "esp8266-ota";
const char* ssid = "**********";
const char* pass = "**********";
const uint16_t aport = 8266;
WiFiServer TelnetServer(aport);
WiFiClient Telnet;
WiFiUDP OTA;
void setup() {
Serial.begin(115200);
Serial.println("");
Serial.println("Arduino OTA Test");
Serial.printf("Sketch size: %u\n", ESP.getSketchSize());
Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace());
WiFi.begin(ssid, pass);
if(WiFi.waitForConnectResult() == WL_CONNECTED){
MDNS.begin(host);
MDNS.addService("arduino", "tcp", aport);
OTA.begin(aport);
TelnetServer.begin();
TelnetServer.setNoDelay(true);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
}
void loop() {
//OTA Sketch
if (OTA.parsePacket()) {
IPAddress remote = OTA.remoteIP();
int cmd = OTA.parseInt();
int port = OTA.parseInt();
int size = OTA.parseInt();
Serial.print("Update Start: ip:");
Serial.print(remote);
Serial.printf(", port:%d, size:%d\n", port, size);
uint32_t startTime = millis();
WiFiUDP::stopAll();
if(!Update.begin(size)){
Serial.println("Update Begin Error");
return;
}
WiFiClient client;
if (client.connect(remote, port)) {
Serial.setDebugOutput(true);
while(!Update.isFinished()) Update.write(client);
Serial.setDebugOutput(false);
if(Update.end()){
client.println("OK");
Serial.printf("Update Success: %u\nRebooting...\n", millis() - startTime);
ESP.restart();
} else {
Update.printError(client);
Update.printError(Serial);
}
} else {
Serial.printf("Connect Failed: %u\n", millis() - startTime);
}
}
//IDE Monitor (connected to Serial)
if (TelnetServer.hasClient()){
if (!Telnet || !Telnet.connected()){
if(Telnet) Telnet.stop();
Telnet = TelnetServer.available();
} else {
WiFiClient toKill = TelnetServer.available();
toKill.stop();
}
}
if (Telnet && Telnet.connected() && Telnet.available()){
while(Telnet.available())
Serial.write(Telnet.read());
}
if(Serial.available()){
size_t len = Serial.available();
uint8_t * sbuf = (uint8_t *)malloc(len);
Serial.readBytes(sbuf, len);
if (Telnet && Telnet.connected()){
Telnet.write((uint8_t *)sbuf, len);
yield();
}
free(sbuf);
}
delay(1);
}

View File

@ -93,3 +93,13 @@ tools.esptool.upload.protocol=esp
tools.esptool.upload.params.verbose=-vv
tools.esptool.upload.params.quiet=
tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin"
tools.esptool.network.pattern=python "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin"
tools.espota.cmd=python
tools.espota.cmd.windows=python.exe
tools.espota.path={runtime.platform.path}/tools
tools.espota.upload.protocol=espota
tools.espota.upload.params.verbose=
tools.espota.upload.params.quiet=
tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" 8266 "{build.path}/{build.project_name}.bin"

94
tools/espota.py Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python
#
# this script will push an OTA update to the ESP
# use it like: python espota.py <ESP_IP_address> <sketch.bin>
from __future__ import print_function
import socket
import sys
import os
def serve(remoteAddr, remotePort, filename):
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverPort = 48266
server_address = ('0.0.0.0', serverPort)
print('Starting on %s:%s' % server_address, file=sys.stderr)
try:
sock.bind(server_address)
sock.listen(1)
except:
print('Listen Failed', file=sys.stderr)
return 1
content_size = os.path.getsize(filename)
print('Upload size: %d' % content_size, file=sys.stderr)
message = '%d %d %d\n' % (0, serverPort, content_size)
# Wait for a connection
print('Sending invitation to:', remoteAddr, file=sys.stderr)
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
remote_address = (remoteAddr, int(remotePort))
sent = sock2.sendto(message, remote_address)
sock2.close()
print('Waiting for device...\n', file=sys.stderr)
try:
sock.settimeout(10)
connection, client_address = sock.accept()
sock.settimeout(None)
connection.settimeout(None)
except:
print('No response from device', file=sys.stderr)
sock.close()
return 1
try:
f = open(filename, "rb")
sys.stderr.write('Uploading')
sys.stderr.flush()
while True:
chunk = f.read(4096)
if not chunk: break
sys.stderr.write('.')
sys.stderr.flush()
try:
connection.sendall(chunk)
except:
print('\nError Uploading', file=sys.stderr)
connection.close()
f.close()
sock.close()
return 1
print('\nWaiting for result...\n', file=sys.stderr)
try:
connection.settimeout(60)
data = connection.recv(32)
print('Result: %s' % data, file=sys.stderr)
connection.close()
f.close()
sock.close()
return 0
except:
print('Result: No Answer!', file=sys.stderr)
connection.close()
f.close()
sock.close()
return 1
finally:
connection.close()
f.close()
sock.close()
return 1
def main(args):
return serve(args[1], args[2], args[3])
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -10,11 +10,17 @@ struct ip_addr {
typedef struct ip_addr ip_addr_t;
struct ip_info {
ip_addr_t ip;
ip_addr_t netmask;
ip_addr_t gw;
struct ip_addr ip;
struct ip_addr netmask;
struct ip_addr gw;
};
#define IP4_ADDR(ipaddr, a,b,c,d) \
(ipaddr)->addr = ((uint32)((d) & 0xff) << 24) | \
((uint32)((c) & 0xff) << 16) | \
((uint32)((b) & 0xff) << 8) | \
(uint32)((a) & 0xff)
/**
* Determine if two address are on the same network.
*