Add Library

This commit is contained in:
Siwat Sirichai 2019-08-09 09:01:56 +07:00
parent e365b9dbd9
commit 3c47103b39
318 changed files with 56465 additions and 0 deletions

View file

@ -0,0 +1,112 @@
/*
Copyright (c) 2018 Juraj Andrassy
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _ARDUINOOTA_H_
#define _ARDUINOOTA_H_
#include "WiFiOTA.h"
#ifdef __AVR__
#if FLASHEND >= 0xFFFF
#include "InternalStorageAVR.h"
#endif
#elif defined(ESP8266) || defined(ESP32)
#include "InternalStorageESP.h"
#else
#include "InternalStorage.h"
#endif
#ifdef __SD_H__
#include "SDStorage.h"
SDStorageClass SDStorage;
#endif
#ifdef SerialFlash_h_
#include "SerialFlashStorage.h"
SerialFlashStorageClass SerialFlashStorage;
#endif
template <class NetServer, class NetClient>
class ArduinoOTAClass : public WiFiOTAClass {
private:
NetServer server;
public:
ArduinoOTAClass() : server(65280) {};
void begin(IPAddress localIP, const char* name, const char* password, OTAStorage& storage) {
WiFiOTAClass::begin(localIP, name, password, storage);
server.begin();
}
void poll() {
NetClient client = server.available();
pollServer(client);
}
void handle() { // alias
poll();
}
};
template <class NetServer, class NetClient, class NetUDP>
class ArduinoOTAMdnsClass : public ArduinoOTAClass<NetServer, NetClient> {
private:
NetUDP mdnsSocket;
public:
ArduinoOTAMdnsClass() {};
void begin(IPAddress localIP, const char* name, const char* password, OTAStorage& storage) {
ArduinoOTAClass<NetServer, NetClient>::begin(localIP, name, password, storage);
#if defined(ESP8266) && !(defined(ethernet_h_) || defined(ethernet_h) || defined(UIPETHERNET_H))
mdnsSocket.beginMulticast(localIP, IPAddress(224, 0, 0, 251), 5353);
#else
mdnsSocket.beginMulticast(IPAddress(224, 0, 0, 251), 5353);
#endif
}
void poll() {
ArduinoOTAClass<NetServer, NetClient>::poll();
WiFiOTAClass::pollMdns(mdnsSocket);
}
};
#if defined(ethernet_h_) || defined(ethernet_h) // Ethernet library
ArduinoOTAMdnsClass <EthernetServer, EthernetClient, EthernetUDP> ArduinoOTA;
#elif defined(UIPETHERNET_H) // no UDP multicast implementation yet
ArduinoOTAClass <EthernetServer, EthernetClient> ArduinoOTA;
#elif defined(WiFiNINA_h) || defined(WIFI_H) || defined(ESP8266) || defined(ESP32) // NINA, WiFi101 and Espressif WiFi
#include <WiFiUdp.h>
ArduinoOTAMdnsClass <WiFiServer, WiFiClient, WiFiUDP> ArduinoOTA;
#elif defined(WiFi_h) // WiFi and WiFiLink lib (no UDP multicast), for WiFiLink firmware handles mdns
ArduinoOTAClass <WiFiServer, WiFiClient> ArduinoOTA;
#elif defined(_WIFISPI_H_INCLUDED) // no UDP multicast implementation
ArduinoOTAClass <WiFiSpiServer, WiFiSpiClient> ArduinoOTA;
#else
#error "Network library not included or not supported"
#endif
#endif

View file

@ -0,0 +1,159 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018
by Juraj Andrassy
*/
#if !defined(__AVR__) && !defined(ESP8266) && !defined(ESP32)
#include <Arduino.h>
#include "InternalStorage.h"
InternalStorageClass::InternalStorageClass() :
MAX_PARTIONED_SKETCH_SIZE((MAX_FLASH - SKETCH_START_ADDRESS) / 2),
STORAGE_START_ADDRESS(SKETCH_START_ADDRESS + MAX_PARTIONED_SKETCH_SIZE)
{
_writeIndex = 0;
_writeAddress = nullptr;
}
extern "C" {
// these functions must be in RAM (.data) and NOT inlined
// as they erase and copy the sketch data in flash
__attribute__ ((long_call, noinline, section (".data#"))) //
void waitForReady() {
#if defined(ARDUINO_ARCH_SAMD)
while (!NVMCTRL->INTFLAG.bit.READY);
#elif defined(ARDUINO_ARCH_NRF5)
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
#endif
}
__attribute__ ((long_call, noinline, section (".data#")))
static void eraseFlash(int address, int length, int pageSize)
{
#if defined(ARDUINO_ARCH_SAMD)
int rowSize = pageSize * 4;
for (int i = 0; i < length; i += rowSize) {
NVMCTRL->ADDR.reg = ((uint32_t)(address + i)) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
waitForReady();
}
#elif defined(ARDUINO_ARCH_NRF5)
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
// Erase page(s)
int end_address = address + length;
while (address < end_address) {
waitForReady();
// Erase one 1k/4k page
NRF_NVMC->ERASEPAGE = address;
address = address + pageSize;
}
// Disable erasing, enable write
waitForReady();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
waitForReady();
#endif
}
__attribute__ ((long_call, noinline, section (".data#")))
static void copyFlashAndReset(int dest, int src, int length, int pageSize)
{
uint32_t* d = (uint32_t*)dest;
uint32_t* s = (uint32_t*)src;
eraseFlash(dest, length, pageSize);
for (int i = 0; i < length; i += 4) {
*d++ = *s++;
waitForReady();
}
NVIC_SystemReset();
}
}
int InternalStorageClass::open(int length)
{
(void)length;
_writeIndex = 0;
_writeAddress = (uint32_t*)STORAGE_START_ADDRESS;
#ifdef ARDUINO_ARCH_SAMD
// enable auto page writes
NVMCTRL->CTRLB.bit.MANW = 0;
#endif
eraseFlash(STORAGE_START_ADDRESS, MAX_PARTIONED_SKETCH_SIZE, PAGE_SIZE);
return 1;
}
size_t InternalStorageClass::write(uint8_t b)
{
_addressData.u8[_writeIndex] = b;
_writeIndex++;
if (_writeIndex == 4) {
_writeIndex = 0;
*_writeAddress = _addressData.u32;
_writeAddress++;
waitForReady();
}
return 1;
}
void InternalStorageClass::close()
{
while ((int)_writeAddress % PAGE_SIZE) {
write(0xff);
}
}
void InternalStorageClass::clear()
{
}
void InternalStorageClass::apply()
{
// disable interrupts, as vector table will be erase during flash sequence
noInterrupts();
copyFlashAndReset(SKETCH_START_ADDRESS, STORAGE_START_ADDRESS, MAX_PARTIONED_SKETCH_SIZE, PAGE_SIZE);
}
long InternalStorageClass::maxSize()
{
return MAX_PARTIONED_SKETCH_SIZE;
}
InternalStorageClass InternalStorage;
#endif

View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018
by Juraj Andrassy
*/
#ifndef _INTERNAL_STORAGE_H_INCLUDED
#define _INTERNAL_STORAGE_H_INCLUDED
#include "OTAStorage.h"
class InternalStorageClass : public OTAStorage {
public:
InternalStorageClass();
virtual int open(int length);
virtual size_t write(uint8_t);
virtual void close();
virtual void clear();
virtual void apply();
virtual long maxSize();
private:
const uint32_t MAX_PARTIONED_SKETCH_SIZE, STORAGE_START_ADDRESS;
union {
uint32_t u32;
uint8_t u8[4];
} _addressData;
int _writeIndex;
uint32_t* _writeAddress;
};
extern InternalStorageClass InternalStorage;
#endif

View file

@ -0,0 +1,77 @@
/*
Copyright (c) 2018 Juraj Andrassy
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#if defined(__AVR__) && FLASHEND >= 0xFFFF
#include "InternalStorageAVR.h"
#include "utility/optiboot.h"
InternalStorageAVRClass::InternalStorageAVRClass() {
maxSketchSize = (MAX_FLASH - bootloaderSize) / 2;
maxSketchSize = (maxSketchSize / SPM_PAGESIZE) * SPM_PAGESIZE; // align to page
pageAddress = maxSketchSize;
pageIndex = 0;
}
int InternalStorageAVRClass::open(int length) {
(void)length;
pageAddress = maxSketchSize;
pageIndex = 0;
return 1;
}
size_t InternalStorageAVRClass::write(uint8_t b) {
if (pageIndex == 0) {
optiboot_page_erase(pageAddress);
}
dataWord.u8[pageIndex % 2] = b;
if (pageIndex % 2) {
optiboot_page_fill(pageAddress + pageIndex, dataWord.u16);
}
pageIndex++;
if (pageIndex == SPM_PAGESIZE) {
optiboot_page_write(pageAddress);
pageIndex = 0;
pageAddress += SPM_PAGESIZE;
}
return 1;
}
void InternalStorageAVRClass::close() {
if (pageIndex) {
optiboot_page_write(pageAddress);
}
pageIndex = 0;
}
void InternalStorageAVRClass::clear() {
}
void InternalStorageAVRClass::apply() {
copy_flash_pages_cli(SKETCH_START_ADDRESS, maxSketchSize, (pageAddress - maxSketchSize) / SPM_PAGESIZE + 1, true);
}
long InternalStorageAVRClass::maxSize() {
return maxSketchSize;
}
InternalStorageAVRClass InternalStorage;
#endif

View file

@ -0,0 +1,50 @@
/*
Copyright (c) 2018 Juraj Andrassy
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _INTERNAL_STORAGE_AVR_H_INCLUDED
#define _INTERNAL_STORAGE_AVR_H_INCLUDED
#include "OTAStorage.h"
class InternalStorageAVRClass : public OTAStorage {
public:
InternalStorageAVRClass();
virtual int open(int length);
virtual size_t write(uint8_t);
virtual void close();
virtual void clear();
virtual void apply();
virtual long maxSize();
private:
uint32_t maxSketchSize;
uint32_t pageAddress;
uint16_t pageIndex;
union {
uint16_t u16;
uint8_t u8[2];
} dataWord;
};
extern InternalStorageAVRClass InternalStorage;
#endif

View file

@ -0,0 +1,64 @@
/*
Copyright (c) 2019 Juraj Andrassy. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(ESP8266) || defined(ESP32)
#include <Arduino.h>
#ifdef ESP32
#include <Update.h>
#endif
#include "InternalStorageESP.h"
InternalStorageESPClass::InternalStorageESPClass()
{
}
int InternalStorageESPClass::open(int length, uint8_t command)
{
return Update.begin(length, command == 0 ? U_FLASH : U_SPIFFS);
}
size_t InternalStorageESPClass::write(uint8_t b)
{
return Update.write(&b, 1);
}
void InternalStorageESPClass::close()
{
Update.end(false);
}
void InternalStorageESPClass::clear()
{
}
void InternalStorageESPClass::apply()
{
ESP.restart();
}
long InternalStorageESPClass::maxSize()
{
return ESP.getFlashChipSize() / 2; // Update.begin() in open() does the exact check
}
InternalStorageESPClass InternalStorage;
#endif

View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2019 Juraj Andrassy. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _INTERNAL_STORAGE_ESP_H_INCLUDED
#define _INTERNAL_STORAGE_ESP_H_INCLUDED
#include "OTAStorage.h"
class InternalStorageESPClass : public OTAStorage {
public:
InternalStorageESPClass();
virtual int open(int length) {
return open(length, 0);
}
virtual int open(int length, uint8_t command);
virtual size_t write(uint8_t);
virtual void close();
virtual void clear();
virtual void apply();
virtual long maxSize();
private:
};
extern InternalStorageESPClass InternalStorage;
#endif

View file

@ -0,0 +1,58 @@
#include "OTAStorage.h"
#if defined(ARDUINO_ARCH_SAMD)
static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 };
extern "C" {
char * __text_start__(); // 0x2000, 0x0 without bootloader and 0x4000 for M0 original bootloader
}
#elif defined(ARDUINO_ARCH_NRF5)
extern "C" {
char * __isr_vector();
}
#elif defined(__AVR__)
#include <avr/wdt.h>
#include <avr/boot.h>
#define MIN_BOOTSZ (4 * SPM_PAGESIZE)
#endif
OTAStorage::OTAStorage() :
#if defined(ARDUINO_ARCH_SAMD)
SKETCH_START_ADDRESS((uint32_t) __text_start__),
PAGE_SIZE(pageSizes[NVMCTRL->PARAM.bit.PSZ]),
MAX_FLASH(PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP)
#elif defined(ARDUINO_ARCH_NRF5)
SKETCH_START_ADDRESS((uint32_t) __isr_vector),
PAGE_SIZE((size_t) NRF_FICR->CODEPAGESIZE),
MAX_FLASH(PAGE_SIZE * (uint32_t) NRF_FICR->CODESIZE)
#elif defined(__AVR__)
SKETCH_START_ADDRESS(0),
PAGE_SIZE(SPM_PAGESIZE),
MAX_FLASH((uint32_t) FLASHEND + 1)
#elif defined(ESP8266) || defined(ESP32)
SKETCH_START_ADDRESS(0), // not used
PAGE_SIZE(0), // not used
MAX_FLASH(0) // not used
#endif
{
bootloaderSize = 0;
#ifdef __AVR__
cli();
uint8_t highBits = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
sei();
if (!(highBits & bit(FUSE_BOOTRST))) {
byte v = (highBits & ((~FUSE_BOOTSZ1 ) + (~FUSE_BOOTSZ0 )));
bootloaderSize = MIN_BOOTSZ << ((v >> 1) ^ 3);
}
#endif
}
void ExternalOTAStorage::apply() {
#ifdef __AVR__
wdt_enable(WDTO_15MS);
while (true);
#elif defined(ESP8266) || defined(ESP32)
ESP.restart();
#else
NVIC_SystemReset();
#endif
}

View file

@ -0,0 +1,69 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018, Apr 2019
by Juraj Andrassy
*/
#ifndef _OTA_STORAGE_H_INCLUDED
#define _OTA_STORAGE_H_INCLUDED
#include <Arduino.h>
class OTAStorage {
public:
OTAStorage();
virtual int open(int length) = 0;
virtual int open(int length, uint8_t command) {
(void) command;
return open(length);
}
virtual size_t write(uint8_t) = 0;
virtual void close() = 0;
virtual void clear() = 0;
virtual void apply() = 0;
virtual long maxSize() {
return (MAX_FLASH - SKETCH_START_ADDRESS - bootloaderSize);
}
protected:
const uint32_t SKETCH_START_ADDRESS;
const uint32_t PAGE_SIZE;
const uint32_t MAX_FLASH;
uint32_t bootloaderSize;
};
class ExternalOTAStorage : public OTAStorage {
protected:
const char* updateFileName = "UPDATE.BIN";
public:
void setUpdateFileName(const char* _updateFileName) {
updateFileName = _updateFileName;
}
virtual void apply();
};
#endif

View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SD_STORAGE_H_INCLUDED
#define _SD_STORAGE_H_INCLUDED
#include <SD.h>
#include "OTAStorage.h"
#ifndef SDCARD_SS_PIN
#define SDCARD_SS_PIN 4
#endif
class SDStorageClass : public ExternalOTAStorage {
public:
virtual int open(int length) {
_file = SD.open(updateFileName, O_CREAT | O_WRITE);
if (!_file)
return 0;
return 1;
}
virtual size_t write(uint8_t b) {
return _file.write(b);
}
virtual void close() {
_file.close();
}
virtual void clear() {
SD.remove(updateFileName);
}
private:
File _file;
};
#endif

View file

@ -0,0 +1,72 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SERIALFLASH_STORAGE_H_INCLUDED
#define _SERIALFLASH_STORAGE_H_INCLUDED
#include <SerialFlash.h>
#include "OTAStorage.h"
#define SERIAL_FLASH_BUFFER_SIZE 64
#define SERIAL_FLASH_CS 5
class SerialFlashStorageClass : public ExternalOTAStorage {
public:
virtual int open(int length) {
if (!SerialFlash.begin(SERIAL_FLASH_CS)) {
return 0;
}
while (!SerialFlash.ready()) {}
if (SerialFlash.exists(updateFileName)) {
SerialFlash.remove(updateFileName);
}
if (SerialFlash.create(updateFileName, length)) {
_file = SerialFlash.open(updateFileName);
}
if (!_file) {
return 0;
}
return 1;
}
virtual size_t write(uint8_t b) {
while (!SerialFlash.ready()) {}
int ret = _file.write(&b, 1);
return ret;
}
virtual void close() {
_file.close();
}
virtual void clear() {
SerialFlash.remove(updateFileName);
}
private:
SerialFlashFile _file;
};
#endif

View file

@ -0,0 +1,358 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018, Apr 2019
by Juraj Andrassy
*/
#include <Arduino.h>
#include "WiFiOTA.h"
#define BOARD "arduino"
#define BOARD_LENGTH (sizeof(BOARD) - 1)
static String base64Encode(const String& in)
{
static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
int b;
String out;
out.reserve((in.length()) * 4 / 3);
for (unsigned int i = 0; i < in.length(); i += 3) {
b = (in.charAt(i) & 0xFC) >> 2;
out += CODES[b];
b = (in.charAt(i) & 0x03) << 4;
if (i + 1 < in.length()) {
b |= (in.charAt(i + 1) & 0xF0) >> 4;
out += CODES[b];
b = (in.charAt(i + 1) & 0x0F) << 2;
if (i + 2 < in.length()) {
b |= (in.charAt(i + 2) & 0xC0) >> 6;
out += CODES[b];
b = in.charAt(i + 2) & 0x3F;
out += CODES[b];
} else {
out += CODES[b];
out += '=';
}
} else {
out += CODES[b];
out += "==";
}
}
return out;
}
WiFiOTAClass::WiFiOTAClass() :
_storage(NULL),
localIp(0),
_lastMdnsResponseTime(0),
beforeApplyCallback(nullptr)
{
}
void WiFiOTAClass::begin(IPAddress& localIP, const char* name, const char* password, OTAStorage& storage)
{
localIp = localIP;
_name = name;
_expectedAuthorization = "Basic " + base64Encode("arduino:" + String(password));
_storage = &storage;
}
void WiFiOTAClass::pollMdns(UDP &_mdnsSocket)
{
int packetLength = _mdnsSocket.parsePacket();
if (packetLength <= 0) {
return;
}
const byte ARDUINO_SERVICE_REQUEST[37] = {
0x00, 0x00, // transaction id
0x00, 0x00, // flags
0x00, 0x01, // questions
0x00, 0x00, // answer RRs
0x00, 0x00, // authority RRs
0x00, 0x00, // additional RRs
0x08,
0x5f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, // _arduino
0x04,
0x5f, 0x74, 0x63, 0x70, // _tcp
0x05,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, // local
0x00, 0x0c, // PTR
0x00, 0x01 // Class IN
};
if (packetLength != sizeof(ARDUINO_SERVICE_REQUEST)) {
while (packetLength) {
if (_mdnsSocket.available()) {
packetLength--;
_mdnsSocket.read();
}
}
return;
}
byte request[packetLength];
_mdnsSocket.read(request, sizeof(request));
if (memcmp(&request[2], &ARDUINO_SERVICE_REQUEST[2], packetLength - 2) != 0) {
return;
}
if ((millis() - _lastMdnsResponseTime) < 1000) {
// ignore request
return;
}
_lastMdnsResponseTime = millis();
_mdnsSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353);
const byte responseHeader[] = {
0x00, 0x00, // transaction id
0x84, 0x00, // flags
0x00, 0x00, // questions
0x00, 0x04, // answers RRs
0x00, 0x00, // authority RRs
0x00, 0x00 // additional RRS
};
_mdnsSocket.write(responseHeader, sizeof(responseHeader));
const byte ptrRecordStart[] = {
0x08,
'_', 'a', 'r', 'd', 'u', 'i', 'n', 'o',
0x04,
'_', 't', 'c', 'p',
0x05,
'l', 'o', 'c', 'a', 'l',
0x00,
0x00, 0x0c, // PTR
0x00, 0x01, // class IN
0x00, 0x00, 0x11, 0x94, // TTL
0x00, (byte)(_name.length() + 3), // length
(byte)_name.length()
};
const byte ptrRecordEnd[] = {
0xc0, 0x0c
};
_mdnsSocket.write(ptrRecordStart, sizeof(ptrRecordStart));
_mdnsSocket.write((const byte*) _name.c_str(), _name.length());
_mdnsSocket.write(ptrRecordEnd, sizeof(ptrRecordEnd));
const byte txtRecord[] = {
0xc0, 0x2b,
0x00, 0x10, // TXT strings
0x80, 0x01, // class
0x00, 0x00, 0x11, 0x94, // TTL
0x00, (50 + BOARD_LENGTH),
13,
's', 's', 'h', '_', 'u', 'p', 'l', 'o', 'a', 'd', '=', 'n', 'o',
12,
't', 'c', 'p', '_', 'c', 'h', 'e', 'c', 'k', '=', 'n', 'o',
15,
'a', 'u', 't', 'h', '_', 'u', 'p', 'l', 'o', 'a', 'd', '=', 'y', 'e', 's',
(6 + BOARD_LENGTH),
'b', 'o', 'a', 'r', 'd', '=',
};
_mdnsSocket.write(txtRecord, sizeof(txtRecord));
_mdnsSocket.write((byte*)BOARD, BOARD_LENGTH);
const byte srvRecordStart[] = {
0xc0, 0x2b,
0x00, 0x21, // SRV
0x80, 0x01, // class
0x00, 0x00, 0x00, 0x78, // TTL
0x00, (byte)(_name.length() + 9), // length
0x00, 0x00,
0x00, 0x00,
0xff, 0x00, // port
(byte)_name.length()
};
const byte srvRecordEnd[] = {
0xc0, 0x1a
};
_mdnsSocket.write(srvRecordStart, sizeof(srvRecordStart));
_mdnsSocket.write((const byte*) _name.c_str(), _name.length());
_mdnsSocket.write(srvRecordEnd, sizeof(srvRecordEnd));
byte aRecordNameOffset = sizeof(responseHeader) +
sizeof(ptrRecordStart) + _name.length() + sizeof(ptrRecordEnd) +
sizeof(txtRecord) + BOARD_LENGTH +
sizeof(srvRecordStart) - 1;
byte aRecord[] = {
0xc0, aRecordNameOffset,
0x00, 0x01, // A record
0x80, 0x01, // class
0x00, 0x00, 0x00, 0x78, // TTL
0x00, 0x04,
0xff, 0xff, 0xff, 0xff // IP
};
memcpy(&aRecord[sizeof(aRecord) - 4], &localIp, sizeof(localIp));
_mdnsSocket.write(aRecord, sizeof(aRecord));
_mdnsSocket.endPacket();
}
void WiFiOTAClass::pollServer(Client& client)
{
if (client) {
String request = client.readStringUntil('\n');
request.trim();
String header;
long contentLength = -1;
String authorization;
do {
header = client.readStringUntil('\n');
header.trim();
if (header.startsWith("Content-Length: ")) {
header.remove(0, 16);
contentLength = header.toInt();
} else if (header.startsWith("Authorization: ")) {
header.remove(0, 15);
authorization = header;
}
} while (header != "");
bool dataUpload = false;
#if defined(ESP8266) || defined(ESP32)
if (request == "POST /data HTTP/1.1") {
dataUpload = true;
} else
#endif
if (request != "POST /sketch HTTP/1.1") {
flushRequestBody(client, contentLength);
sendHttpResponse(client, 404, "Not Found");
return;
}
if (_expectedAuthorization != authorization) {
flushRequestBody(client, contentLength);
sendHttpResponse(client, 401, "Unauthorized");
return;
}
if (contentLength <= 0) {
sendHttpResponse(client, 400, "Bad Request");
return;
}
if (_storage == NULL || !_storage->open(contentLength, dataUpload)) {
flushRequestBody(client, contentLength);
sendHttpResponse(client, 500, "Internal Server Error");
return;
}
if (contentLength > _storage->maxSize()) {
_storage->close();
flushRequestBody(client, contentLength);
sendHttpResponse(client, 413, "Payload Too Large");
return;
}
long read = 0;
byte buff[64];
while (client.connected() && read < contentLength) {
while (client.available()) {
int l = client.read(buff, sizeof(buff));
for (int i = 0; i < l; i++) {
_storage->write(buff[i]);
}
read += l;
}
}
_storage->close();
if (read == contentLength) {
sendHttpResponse(client, 200, "OK");
delay(500);
if (beforeApplyCallback) {
beforeApplyCallback();
}
// apply the update
_storage->apply();
while (true);
} else {
sendHttpResponse(client, 414, "Payload size wrong");
_storage->clear();
delay(500);
client.stop();
}
}
}
void WiFiOTAClass::sendHttpResponse(Client& client, int code, const char* status)
{
while (client.available()) {
client.read();
}
client.print("HTTP/1.1 ");
client.print(code);
client.print(" ");
client.println(status);
client.println("Connection: close");
client.println();
delay(500);
client.stop();
}
void WiFiOTAClass::flushRequestBody(Client& client, long contentLength)
{
long read = 0;
while (client.connected() && read < contentLength) {
if (client.available()) {
read++;
client.read();
}
}
}

View file

@ -0,0 +1,64 @@
/*
Copyright (c) 2017 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WiFi101OTA version Feb 2017
by Sandeep Mistry (Arduino)
modified for ArduinoOTA Dec 2018
by Juraj Andrassy
*/
#ifndef _WIFI_OTA_H_INCLUDED
#define _WIFI_OTA_H_INCLUDED
#include <Arduino.h>
#include <Server.h>
#include <Client.h>
#include <IPAddress.h>
#include <Udp.h>
#include "OTAStorage.h"
class WiFiOTAClass {
protected:
WiFiOTAClass();
void begin(IPAddress& localIP, const char* name, const char* password, OTAStorage& storage);
void pollMdns(UDP &mdnsSocket);
void pollServer(Client& client);
public:
void beforeApply(void (*fn)(void)) {
beforeApplyCallback = fn;
}
private:
void sendHttpResponse(Client& client, int code, const char* status);
void flushRequestBody(Client& client, long contentLength);
private:
String _name;
String _expectedAuthorization;
OTAStorage* _storage;
uint32_t localIp;
uint32_t _lastMdnsResponseTime;
void (*beforeApplyCallback)(void);
};
#endif

View file

@ -0,0 +1,200 @@
/*------------------------ Optiboot header file ----------------------------|
| |
| June 2015 by Marek Wodzinski, https://github.com/majekw |
| Modified June 2016 by MCUdude, https://github.com/MCUdude |
| Modified Dec 2018 by Juraj Andrassy, https://github.com/jandrassy |
| Released to public domain |
| |
| This header file gives possibility to use SPM instruction |
| from Optiboot bootloader memory. |
| |
| There are 5 convenient functions available here: |
| * optiboot_page_erase - to erase a FLASH page |
| * optiboot_page_fill - to put words into temporary buffer |
| * optiboot_page_write - to write contents of temporary buffer into FLASH | |
| * optiboot_readPage - higher level function to read a flash page and |
| store it in an array |
| * optiboot_writePage - higher level function to write content to |
| a flash page |
| |
| For some hardcore users, you could use 'do_spm' as raw entry to |
| bootloader spm function. |
|-------------------------------------------------------------------------*/
#ifndef _OPTIBOOT_H_
#define _OPTIBOOT_H_ 1
#include <avr/boot.h>
#include "Arduino.h"
/*
* Main 'magic' function - enter to bootloader do_spm function
*
* address - address to write (in bytes) but must be even number
* command - one of __BOOT_PAGE_WRITE, __BOOT_PAGE_ERASE or __BOOT_PAGE_FILL
* data - data to write in __BOOT_PAGE_FILL. In __BOOT_PAGE_ERASE or
* __BOOT_PAGE_WRITE it control if boot_rww_enable is run
* (0 = run, !0 = skip running boot_rww_enable)
*
*/
// 'typedef' (in following line) and 'const' (few lines below) are a way to define external function at some arbitrary address
typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data);
typedef void (*copy_flash_pages_t)(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset);
/*
* Devices with more than 64KB of flash:
* - have larger bootloader area (1KB) (they are BIGBOOT targets)
* - have RAMPZ register :-)
* - need larger variable to hold address (pgmspace.h uses uint32_t)
*/
#ifdef RAMPZ
typedef uint32_t optiboot_addr_t;
#else
typedef uint16_t optiboot_addr_t;
#endif
#if FLASHEND > 65534
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
const copy_flash_pages_t copy_flash_pages = (copy_flash_pages_t)((FLASHEND-1023+4)>>1);
#else
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
#endif
/*
* The same as do_spm but with disable/restore interrupts state
* required to succesfull SPM execution
*
* On devices with more than 64kB flash, 16 bit address is not enough,
* so there is also RAMPZ used in that case.
*
* On devices with more than 128kB flash, 16 bit word address is not enough
* for a function call above 0x20000, so there is also EIND used in that case.
*/
void do_spm_cli(optiboot_addr_t address, uint8_t command, uint16_t data) {
uint8_t sreg_save;
sreg_save = SREG; // save old SREG value
asm volatile("cli"); // disable interrupts
#ifdef RAMPZ
RAMPZ = (address >> 16) & 0xff; // address bits 23-16 goes to RAMPZ
#ifdef EIND
uint8_t eind = EIND;
EIND = FLASHEND / 0x20000;
#endif
do_spm((address & 0xffff), command, data); // do_spm accepts only lower 16 bits of address
#ifdef EIND
EIND = eind;
#endif
#else
do_spm(address, command, data); // 16 bit address - no problems to pass directly
#endif
SREG = sreg_save; // restore last interrupts state
}
/*
* Copy contents of the flash pages. Addresses must be aligned to page boundary.
*
* On devices with more than 128kB flash, 16 bit word address is not enough
* for a function call above 0x20000, so there is also EIND used in that case.
*
* If reset_mcu is true, watchdog is used to reset the MCU after pages are copied.
* That enables to copy a new version of application from upper half of the flash.
*/
#if FLASHEND > 65534
void copy_flash_pages_cli(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset_mcu) {
uint8_t sreg_save = SREG; // save old SREG value
asm volatile("cli"); // disable interrupts
#ifdef EIND
uint8_t eind = EIND;
EIND = FLASHEND / 0x20000;
#endif
copy_flash_pages(dest, src, page_count, reset_mcu);
#ifdef EIND
EIND = eind;
#endif
SREG = sreg_save; // restore last interrupts state
}
#endif
// Erase page in FLASH
void optiboot_page_erase(optiboot_addr_t address) {
do_spm_cli(address, __BOOT_PAGE_ERASE, 0);
}
// Write word into temporary buffer
void optiboot_page_fill(optiboot_addr_t address, uint16_t data) {
do_spm_cli(address, __BOOT_PAGE_FILL, data);
}
//Write temporary buffer into FLASH
void optiboot_page_write(optiboot_addr_t address) {
do_spm_cli(address, __BOOT_PAGE_WRITE, 0);
}
/*
* Higher level functions for reading and writing from flash
* See the examples for more info on how to use these functions
*/
// Function to read a flash page and store it in an array (storage_array[])
void optiboot_readPage(const uint8_t allocated_flash_space[], uint8_t storage_array[], uint16_t page, char blank_character)
{
uint8_t read_character;
for(uint16_t j = 0; j < SPM_PAGESIZE; j++)
{
read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]);
if(read_character != 0 && read_character != 255)
storage_array[j] = read_character;
else
storage_array[j] = blank_character;
}
}
// Function to read a flash page and store it in an array (storage_array[]), but without blank_character
void optiboot_readPage(const uint8_t allocated_flash_space[], uint8_t storage_array[], uint16_t page)
{
uint8_t read_character;
for(uint16_t j = 0; j < SPM_PAGESIZE; j++)
{
read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]);
if(read_character != 0 && read_character != 255)
storage_array[j] = read_character;
}
}
// Function to write data to a flash page
void optiboot_writePage(const uint8_t allocated_flash_space[], uint8_t data_to_store[], uint16_t page)
{
uint16_t word_buffer = 0;
// Erase the flash page
optiboot_page_erase((optiboot_addr_t)(void*) &allocated_flash_space[SPM_PAGESIZE*(page-1)]);
// Copy ram buffer to temporary flash buffer
for(uint16_t i = 0; i < SPM_PAGESIZE; i++)
{
if(i % 2 == 0) // We must write words
word_buffer = data_to_store[i];
else
{
word_buffer += (data_to_store[i] << 8);
optiboot_page_fill((optiboot_addr_t)(void*) &allocated_flash_space[i + SPM_PAGESIZE*(page-1)], word_buffer);
}
}
// Writing temporary buffer to flash
optiboot_page_write((optiboot_addr_t)(void*) &allocated_flash_space[SPM_PAGESIZE*(page-1)]);
}
#endif /* _OPTIBOOT_H_ */