mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
Merge remote-tracking branch 'remotes/esp8266/esp8266' into esp8266
This commit is contained in:
commit
d6f1281c99
10
.travis.yml
10
.travis.yml
@ -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
|
||||
|
@ -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 ####
|
||||
|
22
boards.txt
22
boards.txt
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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.
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
209
cores/esp8266/Updater.cpp
Normal 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
121
cores/esp8266/Updater.h
Normal 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
|
@ -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);
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
406
libraries/ESP8266SSDP/ESP8266SSDP.cpp
Normal file
406
libraries/ESP8266SSDP/ESP8266SSDP.cpp
Normal 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;
|
119
libraries/ESP8266SSDP/ESP8266SSDP.h
Normal file
119
libraries/ESP8266SSDP/ESP8266SSDP.h
Normal 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
|
22
libraries/ESP8266SSDP/README.md
Normal file
22
libraries/ESP8266SSDP/README.md
Normal 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.
|
52
libraries/ESP8266SSDP/examples/SSDP/SSDP.ino
Normal file
52
libraries/ESP8266SSDP/examples/SSDP/SSDP.ino
Normal 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);
|
||||
}
|
53
libraries/ESP8266SSDP/keywords.txt
Normal file
53
libraries/ESP8266SSDP/keywords.txt
Normal 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
|
73
libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino
Normal file
73
libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino
Normal 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);
|
||||
}
|
@ -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){
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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 ");
|
||||
|
@ -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);
|
||||
}
|
10
platform.txt
10
platform.txt
@ -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
94
tools/espota.py
Executable 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))
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user