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
|
sudo: true
|
||||||
language: java
|
language: java
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- openjdk6
|
- oraclejdk8
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- sudo apt-get update -qq
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
|
||||||
- sudo apt-get install -qq ant
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install -qq ant; fi
|
||||||
- pushd build
|
- pushd build
|
||||||
- echo "" | ant dist
|
- echo "" | ant dist
|
||||||
- popd
|
- 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);```.
|
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.
|
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 ####
|
#### Progmem ####
|
||||||
|
22
boards.txt
22
boards.txt
@ -1,7 +1,9 @@
|
|||||||
menu.UploadSpeed=Upload Speed
|
menu.UploadSpeed=Upload Speed
|
||||||
menu.CpuFrequency=CPU Frequency
|
menu.CpuFrequency=CPU Frequency
|
||||||
menu.FlashSize=Flash Size
|
menu.FlashSize=Flash Size
|
||||||
|
menu.FlashMode=Flash Mode
|
||||||
menu.FlashFreq=Flash Frequency
|
menu.FlashFreq=Flash Frequency
|
||||||
|
menu.UploadTool=Upload Using
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
generic.name=Generic ESP8266 Module
|
generic.name=Generic ESP8266 Module
|
||||||
@ -23,11 +25,26 @@ generic.build.variant=generic
|
|||||||
generic.build.flash_mode=qio
|
generic.build.flash_mode=qio
|
||||||
generic.build.spiffs_pagesize=256
|
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=80 MHz
|
||||||
generic.menu.CpuFrequency.80.build.f_cpu=80000000L
|
generic.menu.CpuFrequency.80.build.f_cpu=80000000L
|
||||||
generic.menu.CpuFrequency.160=160 MHz
|
generic.menu.CpuFrequency.160=160 MHz
|
||||||
generic.menu.CpuFrequency.160.build.f_cpu=160000000L
|
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=115200
|
||||||
generic.menu.UploadSpeed.115200.upload.speed=115200
|
generic.menu.UploadSpeed.115200.upload.speed=115200
|
||||||
generic.menu.UploadSpeed.9600=9600
|
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_end=0x1000000
|
||||||
# generic.menu.FlashSize.16M.build.spiffs_blocksize=8192
|
# 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)
|
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 := ./
|
BIN_DIR := ./
|
||||||
TARGET_DIR := ./
|
TARGET_DIR := ./
|
||||||
@ -6,7 +7,7 @@ TARGET_DIR := ./
|
|||||||
TARGET_OBJ_FILES := \
|
TARGET_OBJ_FILES := \
|
||||||
eboot.o \
|
eboot.o \
|
||||||
eboot_command.o \
|
eboot_command.o \
|
||||||
flash.o \
|
|
||||||
|
|
||||||
TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))
|
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 dst_addr,
|
||||||
const uint32_t size)
|
const uint32_t size)
|
||||||
{
|
{
|
||||||
ets_putc('\n');
|
|
||||||
ets_putc('c');
|
|
||||||
ets_putc('p');
|
|
||||||
ets_putc('\n');
|
|
||||||
// require regions to be aligned
|
// require regions to be aligned
|
||||||
if (src_addr & 0xfff != 0 ||
|
if (src_addr & 0xfff != 0 ||
|
||||||
dst_addr & 0xfff != 0) {
|
dst_addr & 0xfff != 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SPIEraseAreaEx(dst_addr, size)) {
|
const uint32_t buffer_size = FLASH_SECTOR_SIZE;
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t buffer_size = 4096;
|
|
||||||
uint8_t buffer[buffer_size];
|
uint8_t buffer[buffer_size];
|
||||||
|
uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
|
||||||
const uint32_t end = src_addr + size;
|
|
||||||
uint32_t saddr = src_addr;
|
uint32_t saddr = src_addr;
|
||||||
uint32_t daddr = dst_addr;
|
uint32_t daddr = dst_addr;
|
||||||
uint32_t left = size;
|
|
||||||
while (saddr < end) {
|
while (left) {
|
||||||
uint32_t will_copy = (left < buffer_size) ? left : buffer_size;
|
if (SPIEraseSector(daddr/buffer_size)) {
|
||||||
if (SPIRead(saddr, buffer, will_copy)) {
|
return 2;
|
||||||
|
}
|
||||||
|
if (SPIRead(saddr, buffer, buffer_size)) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
if (SPIWrite(daddr, buffer, will_copy)) {
|
if (SPIWrite(daddr, buffer, buffer_size)) {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
saddr += will_copy;
|
saddr += buffer_size;
|
||||||
daddr += will_copy;
|
daddr += buffer_size;
|
||||||
left -= will_copy;
|
left -= buffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -123,14 +116,16 @@ void main()
|
|||||||
if (eboot_command_read(&cmd)) {
|
if (eboot_command_read(&cmd)) {
|
||||||
cmd.action = ACTION_LOAD_APP;
|
cmd.action = ACTION_LOAD_APP;
|
||||||
cmd.args[0] = 0;
|
cmd.args[0] = 0;
|
||||||
ets_putc('e');
|
ets_putc('~');
|
||||||
} else {
|
} else {
|
||||||
ets_putc('@');
|
ets_putc('@');
|
||||||
}
|
}
|
||||||
eboot_command_clear();
|
eboot_command_clear();
|
||||||
|
|
||||||
if (cmd.action == ACTION_COPY_RAW) {
|
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]);
|
res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
|
||||||
|
ets_putc('0'+res); ets_putc('\n');
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
cmd.action = ACTION_LOAD_APP;
|
cmd.action = ACTION_LOAD_APP;
|
||||||
cmd.args[0] = cmd.args[1];
|
cmd.args[0] = cmd.args[1];
|
||||||
@ -138,14 +133,13 @@ void main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.action == ACTION_LOAD_APP) {
|
if (cmd.action == ACTION_LOAD_APP) {
|
||||||
|
ets_putc('l'); ets_putc('d'); ets_putc('\n');
|
||||||
res = load_app_from_flash_raw(cmd.args[0]);
|
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) {
|
if (res) {
|
||||||
ets_putc('\n');
|
|
||||||
ets_putc('#');
|
|
||||||
ets_putc('0' + res);
|
|
||||||
ets_putc('\n');
|
|
||||||
SWRST;
|
SWRST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
@ -233,6 +233,7 @@ void loop(void);
|
|||||||
|
|
||||||
#include "HardwareSerial.h"
|
#include "HardwareSerial.h"
|
||||||
#include "Esp.h"
|
#include "Esp.h"
|
||||||
|
#include "Updater.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#define min(a,b) ((a)<(b)?(a):(b))
|
#define min(a,b) ((a)<(b)?(a):(b))
|
||||||
|
@ -365,83 +365,38 @@ uint32_t EspClass::getFreeSketchSpace() {
|
|||||||
return freeSpaceEnd - freeSpaceStart;
|
return freeSpaceEnd - freeSpaceStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EspClass::updateSketch(Stream& in, uint32_t size) {
|
bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) {
|
||||||
|
if(!Update.begin(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));
|
|
||||||
|
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
DEBUG_SERIAL.printf("erase @0x%x size=0x%x\r\n", freeSpaceStart, roundedSize);
|
DEBUG_SERIAL.print("Update ");
|
||||||
#endif
|
Update.printError(DEBUG_SERIAL);
|
||||||
|
|
||||||
noInterrupts();
|
|
||||||
int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize);
|
|
||||||
interrupts();
|
|
||||||
if (rc)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef DEBUG_SERIAL
|
|
||||||
DEBUG_SERIAL.println("erase done");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
#ifdef DEBUG_SERIAL
|
|
||||||
DEBUG_SERIAL.println("writing");
|
|
||||||
#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
|
#endif
|
||||||
|
if(restartOnFail) ESP.restart();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == freeSpaceStart) {
|
if(Update.writeStream(in) != size){
|
||||||
// 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
|
#ifdef DEBUG_SERIAL
|
||||||
DEBUG_SERIAL.println("write failed");
|
DEBUG_SERIAL.print("Update ");
|
||||||
|
Update.printError(DEBUG_SERIAL);
|
||||||
#endif
|
#endif
|
||||||
|
if(restartOnFail) ESP.restart();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr += willRead;
|
if(!Update.end()){
|
||||||
left -= willRead;
|
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
DEBUG_SERIAL.print(".");
|
DEBUG_SERIAL.print("Update ");
|
||||||
|
Update.printError(DEBUG_SERIAL);
|
||||||
#endif
|
#endif
|
||||||
|
if(restartOnFail) ESP.restart();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
DEBUG_SERIAL.println("\r\nrestarting");
|
DEBUG_SERIAL.println("Update SUCCESS");
|
||||||
#endif
|
#endif
|
||||||
eboot_command ebcmd;
|
if(restartOnSuccess) ESP.restart();
|
||||||
ebcmd.action = ACTION_COPY_RAW;
|
return true;
|
||||||
ebcmd.args[0] = freeSpaceStart;
|
|
||||||
ebcmd.args[1] = 0x00000;
|
|
||||||
ebcmd.args[2] = size;
|
|
||||||
eboot_command_write(&ebcmd);
|
|
||||||
|
|
||||||
ESP.restart();
|
|
||||||
return true; // never happens
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class EspClass {
|
|||||||
|
|
||||||
uint32_t getSketchSize();
|
uint32_t getSketchSize();
|
||||||
uint32_t getFreeSketchSpace();
|
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();
|
String getResetInfo();
|
||||||
struct rst_info * getResetInfoPtr();
|
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, ...) {
|
size_t Print::printf(const char *format, ...) {
|
||||||
va_list arg;
|
va_list arg;
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
char temp[256];
|
char temp[1460];
|
||||||
size_t len = ets_vsnprintf(temp, 256, format, arg);
|
size_t len = ets_vsnprintf(temp, 1460, format, arg);
|
||||||
len = print(temp);
|
len = print(temp);
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
return len;
|
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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include "stdlib_noniso.h"
|
#include "stdlib_noniso.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
|
|
||||||
#define sprintf ets_sprintf
|
#define sprintf ets_sprintf
|
||||||
#define strcpy ets_strcpy
|
|
||||||
|
|
||||||
int atoi(const char* s) {
|
int atoi(const char* s) {
|
||||||
return (int) atol(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);
|
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) {
|
size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
|
||||||
// there is no ets_strnlen
|
// there is no ets_strnlen
|
||||||
const char *cp;
|
const char *cp;
|
||||||
@ -126,10 +94,6 @@ size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
|
|||||||
return (size_t)(cp - s);
|
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) {
|
char* ICACHE_FLASH_ATTR strchr(const char * str, int character) {
|
||||||
while(1) {
|
while(1) {
|
||||||
if(*str == 0x00) {
|
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) {
|
char* ICACHE_FLASH_ATTR strncat(char * dest, const char * src, size_t n) {
|
||||||
uint32_t offset = strlen(dest);
|
size_t i;
|
||||||
for(uint32_t i = 0; i < n; i++) {
|
size_t offset = strlen(dest);
|
||||||
*(dest + i + offset) = *(src + i);
|
for(i = 0; i < n && src[i]; i++) {
|
||||||
if(*(src + i) == 0x00) {
|
dest[i + offset] = src[i];
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
dest[i + offset] = 0;
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,3 +439,56 @@ int* ICACHE_FLASH_ATTR __errno(void) {
|
|||||||
return &errno_var;
|
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,9 +108,21 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
|
|||||||
|
|
||||||
if (!isForm){
|
if (!isForm){
|
||||||
if (searchStr != "") searchStr += '&';
|
if (searchStr != "") searchStr += '&';
|
||||||
searchStr += client.readStringUntil('\r');
|
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');
|
client.readStringUntil('\n');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_parseArguments(searchStr);
|
_parseArguments(searchStr);
|
||||||
if (isForm){
|
if (isForm){
|
||||||
_parseForm(client, boundaryStr, contentLength);
|
_parseForm(client, boundaryStr, contentLength);
|
||||||
|
@ -110,7 +110,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
|
|||||||
netif* interface = ip_route(&addr);
|
netif* interface = ip_route(&addr);
|
||||||
if (!interface) {
|
if (!interface) {
|
||||||
DEBUGV("no route to host\r\n");
|
DEBUGV("no route to host\r\n");
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_pcb* pcb = tcp_new();
|
tcp_pcb* pcb = tcp_new();
|
||||||
|
@ -41,11 +41,9 @@ extern "C" {
|
|||||||
|
|
||||||
/* This is the aligned version of ip_addr_t,
|
/* This is the aligned version of ip_addr_t,
|
||||||
used as local variable, on the stack, etc. */
|
used as local variable, on the stack, etc. */
|
||||||
#if !defined(IP2STR)
|
|
||||||
struct ip_addr {
|
struct ip_addr {
|
||||||
u32_t addr;
|
u32_t addr;
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is the packed version of ip_addr_t,
|
/* This is the packed version of ip_addr_t,
|
||||||
used in network headers that are itself packed */
|
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
|
/** 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. */
|
* operate both on ip_addr_t as well as on ip_addr_p_t. */
|
||||||
#if !defined(IP2STR)
|
|
||||||
typedef struct ip_addr ip_addr_t;
|
typedef struct ip_addr ip_addr_t;
|
||||||
#endif
|
|
||||||
typedef struct ip_addr_packed ip_addr_p_t;
|
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)
|
#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast)
|
||||||
|
|
||||||
/** 255.255.255.255 */
|
/** 255.255.255.255 */
|
||||||
#if !defined(IPADDR_NONE)
|
|
||||||
#define IPADDR_NONE ((u32_t)0xffffffffUL)
|
#define IPADDR_NONE ((u32_t)0xffffffffUL)
|
||||||
#endif
|
|
||||||
/** 127.0.0.1 */
|
/** 127.0.0.1 */
|
||||||
#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
|
#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
|
||||||
/** 0.0.0.0 */
|
/** 0.0.0.0 */
|
||||||
#if !defined(IPADDR_ANY)
|
|
||||||
#define IPADDR_ANY ((u32_t)0x00000000UL)
|
#define IPADDR_ANY ((u32_t)0x00000000UL)
|
||||||
#endif
|
|
||||||
/** 255.255.255.255 */
|
/** 255.255.255.255 */
|
||||||
#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
|
#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
|
||||||
|
|
||||||
@ -142,7 +134,6 @@ extern const ip_addr_t ip_addr_broadcast;
|
|||||||
#define IP_LOOPBACKNET 127 /* official! */
|
#define IP_LOOPBACKNET 127 /* official! */
|
||||||
|
|
||||||
|
|
||||||
#if !defined(IP4_ADDR)
|
|
||||||
#if BYTE_ORDER == BIG_ENDIAN
|
#if BYTE_ORDER == BIG_ENDIAN
|
||||||
/** Set an IP address given by the four byte-parts */
|
/** Set an IP address given by the four byte-parts */
|
||||||
#define IP4_ADDR(ipaddr, a,b,c,d) \
|
#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)((b) & 0xff) << 8) | \
|
||||||
(u32_t)((a) & 0xff)
|
(u32_t)((a) & 0xff)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
/** MEMCPY-like copying of IP addresses where addresses are known to be
|
/** 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
|
* 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))
|
ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0))
|
||||||
|
|
||||||
/* Get one byte from the 4-byte address */
|
/* Get one byte from the 4-byte address */
|
||||||
#if !defined(IP2STR)
|
|
||||||
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
|
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
|
||||||
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
|
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
|
||||||
#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
|
#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_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
|
||||||
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
|
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
|
||||||
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
|
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
|
||||||
#endif
|
|
||||||
|
|
||||||
/** For backwards compatibility */
|
/** For backwards compatibility */
|
||||||
#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
|
#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
|
||||||
|
|
||||||
#if !defined(IP2STR)
|
|
||||||
u32_t ipaddr_addr(const char *cp)ICACHE_FLASH_ATTR;
|
u32_t ipaddr_addr(const char *cp)ICACHE_FLASH_ATTR;
|
||||||
#endif
|
|
||||||
int ipaddr_aton(const char *cp, ip_addr_t *addr)ICACHE_FLASH_ATTR;
|
int ipaddr_aton(const char *cp, ip_addr_t *addr)ICACHE_FLASH_ATTR;
|
||||||
/** returns ptr to static buffer; not reentrant! */
|
/** returns ptr to static buffer; not reentrant! */
|
||||||
char *ipaddr_ntoa(const ip_addr_t *addr)ICACHE_FLASH_ATTR;
|
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;
|
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), \
|
#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
|
||||||
ip4_addr2_16(ipaddr), \
|
ip4_addr2_16(ipaddr), \
|
||||||
ip4_addr3_16(ipaddr), \
|
ip4_addr3_16(ipaddr), \
|
||||||
@ -264,7 +249,6 @@ struct ip_info {
|
|||||||
struct ip_addr netmask;
|
struct ip_addr netmask;
|
||||||
struct ip_addr gw;
|
struct ip_addr gw;
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,7 +32,8 @@ License (MIT license):
|
|||||||
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
|
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
|
||||||
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
|
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
|
||||||
|
|
||||||
#define LWIP_INTERNAL
|
#define LWIP_OPEN_SRC
|
||||||
|
|
||||||
#include "ESP8266mDNS.h"
|
#include "ESP8266mDNS.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@ -41,7 +42,6 @@ License (MIT license):
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include "osapi.h"
|
#include "osapi.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "ip_addr.h"
|
|
||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,11 +66,16 @@ extern "C" {
|
|||||||
#define MDNS_TYPE_PTR 0x000C
|
#define MDNS_TYPE_PTR 0x000C
|
||||||
#define MDNS_TYPE_SRV 0x0021
|
#define MDNS_TYPE_SRV 0x0021
|
||||||
#define MDNS_TYPE_TXT 0x0010
|
#define MDNS_TYPE_TXT 0x0010
|
||||||
#define MDNS_TYPE_NSEC 0x002F
|
|
||||||
|
|
||||||
#define MDNS_CLASS_IN 0x0001
|
#define MDNS_CLASS_IN 0x0001
|
||||||
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001
|
#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_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_read16() (((uint16_t)_conn->read() << 8) | _conn->read())
|
||||||
#define _conn_read8() _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_PTR) os_printf(" PTR ");
|
||||||
else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV ");
|
else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV ");
|
||||||
else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT ");
|
else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT ");
|
||||||
else if(currentType == MDNS_TYPE_NSEC) os_printf(" NSEC ");
|
|
||||||
else os_printf(" 0x%04X ", currentType);
|
else os_printf(" 0x%04X ", currentType);
|
||||||
|
|
||||||
if(currentClass == MDNS_CLASS_IN) os_printf(" IN ");
|
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.verbose=-vv
|
||||||
tools.esptool.upload.params.quiet=
|
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.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;
|
typedef struct ip_addr ip_addr_t;
|
||||||
|
|
||||||
struct ip_info {
|
struct ip_info {
|
||||||
ip_addr_t ip;
|
struct ip_addr ip;
|
||||||
ip_addr_t netmask;
|
struct ip_addr netmask;
|
||||||
ip_addr_t gw;
|
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.
|
* Determine if two address are on the same network.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user