Compare commits
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server
|
||||
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino
|
||||
ignore_words_list: ser,dout
|
||||
|
||||
# Consistent style
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3]
|
||||
chunk: [0, 1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
BUILD_PARITY: custom
|
||||
mod: 4
|
||||
mod: 6
|
||||
rem: ${{ matrix.chunk }}
|
||||
run: |
|
||||
cd pico-sdk
|
||||
@@ -221,3 +221,5 @@ jobs:
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DUSE_TINYUSB" libraries/Adafruit_TinyUSB_Arduino/examples/CDC/cdc_multi/cdc_multi.ino
|
||||
- name: Build WiFi Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
|
||||
- name: Build Signed OTA Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.DS_Store
|
||||
system
|
||||
tools/dist
|
||||
docs/_build
|
||||
ota/build
|
||||
tools/libpico/build
|
||||
platform.local.txt
|
||||
|
||||
+7
-1
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/earlephilhower/ArduinoCore-API.git
|
||||
[submodule "pico-sdk"]
|
||||
path = pico-sdk
|
||||
url = https://github.com/raspberrypi/pico-sdk.git
|
||||
url = https://github.com/earlephilhower/pico-sdk.git
|
||||
[submodule "system/pyserial"]
|
||||
path = tools/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
@@ -31,3 +31,9 @@
|
||||
[submodule "tools/libbearssl/bearssl"]
|
||||
path = tools/libbearssl/bearssl
|
||||
url = https://github.com/earlephilhower/bearssl-esp8266.git
|
||||
[submodule "ota/uzlib"]
|
||||
path = ota/uzlib
|
||||
url = https://github.com/pfalcon/uzlib.git
|
||||
[submodule "libraries/http_parser/lib/http-parser"]
|
||||
path = libraries/http-parser/lib/http-parser
|
||||
url = https://github.com/nodejs/http-parser.git
|
||||
|
||||
@@ -24,6 +24,8 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* Cytron Maker Nano RP2040
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
* ExtremeElectronics RC2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
@@ -145,6 +147,10 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
* Filesystems (LittleFS and SD/SDFS)
|
||||
* Multicore support (setup1() and loop1())
|
||||
* FreeRTOS SMP support
|
||||
@@ -183,6 +189,11 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [FreeRTOS](https://freertos.org) is Copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [lwIP](https://savannah.nongnu.org/projects/lwip/) is (c) the Swedish Institute of Computer Science and licenced under the BSD license.
|
||||
* [BearSSL](https://bearssl.org) library written by Thomas Pornin, is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
* [UZLib](https://github.com/pfalcon/uzlib) is copyright (c) 2003 Joergen Ibsen and distributed under the zlib license.
|
||||
* [LEAmDNS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [http-parser](https://github.com/nodejs/http-parser) is copyright Joyent, Inc. and other Node contributors.
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others
|
||||
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
+1453
-566
File diff suppressed because it is too large
Load Diff
@@ -90,6 +90,15 @@ extern bool __isFreeRTOS;
|
||||
#define HAVE_HWSERIAL1
|
||||
#define HAVE_HWSERIAL2
|
||||
|
||||
// PSTR/etc.
|
||||
#ifndef FPSTR
|
||||
#define FPSTR (const char *)
|
||||
#endif
|
||||
|
||||
#ifndef PGM_VOID_P
|
||||
#define PGM_VOID_P void *
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
class CoreMutex {
|
||||
public:
|
||||
CoreMutex(mutex_t *mutex) {
|
||||
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
|
||||
uint32_t owner;
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
if (debugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
|
||||
+4
-3
@@ -50,7 +50,9 @@ enum SeekMode {
|
||||
|
||||
class File : public Stream {
|
||||
public:
|
||||
File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
|
||||
File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) {
|
||||
_startMillis = millis(); /* workaround -O3 spurious warning #768 */
|
||||
}
|
||||
|
||||
// Print methods:
|
||||
size_t write(uint8_t) override;
|
||||
@@ -90,9 +92,8 @@ public:
|
||||
uint8_t obuf[256];
|
||||
size_t doneLen = 0;
|
||||
size_t sentLen;
|
||||
int i;
|
||||
|
||||
while (src.available() > sizeof(obuf)) {
|
||||
while ((size_t)src.available() > sizeof(obuf)) {
|
||||
src.read(obuf, sizeof(obuf));
|
||||
sentLen = write(obuf, sizeof(obuf));
|
||||
doneLen = doneLen + sentLen;
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <hardware/exception.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
@@ -32,6 +34,18 @@
|
||||
|
||||
extern "C" volatile bool __otherCoreIdled;
|
||||
|
||||
// Halt the FreeRTOS PendSV task switching magic
|
||||
extern "C" int __holdUpPendSV;
|
||||
|
||||
// FreeRTOS weak functions, to be overridden when we really are running FreeRTOS
|
||||
extern "C" {
|
||||
extern void vTaskSuspendAll() __attribute__((weak));
|
||||
extern int32_t xTaskResumeAll() __attribute__((weak));
|
||||
typedef struct tskTaskControlBlock * TaskHandle_t;
|
||||
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
|
||||
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
|
||||
}
|
||||
|
||||
class _MFIFO {
|
||||
public:
|
||||
_MFIFO() { /* noop */ };
|
||||
@@ -86,6 +100,11 @@ public:
|
||||
if (!_multicore) {
|
||||
return;
|
||||
}
|
||||
__holdUpPendSV = 1;
|
||||
if (__isFreeRTOS) {
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
vTaskSuspendAll();
|
||||
}
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
@@ -98,6 +117,12 @@ public:
|
||||
}
|
||||
mutex_exit(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
if (__isFreeRTOS) {
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
}
|
||||
__holdUpPendSV = 0;
|
||||
|
||||
// Other core will exit busy-loop and return to operation
|
||||
// once __otherCoreIdled == false.
|
||||
}
|
||||
@@ -288,6 +313,25 @@ public:
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
watchdog_reboot(0, 0, 10);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
inline void restart() {
|
||||
reboot();
|
||||
}
|
||||
|
||||
const char *getChipID() {
|
||||
static char id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1] = { 0 };
|
||||
if (!id[0]) {
|
||||
pico_get_unique_board_id_string(id, sizeof(id));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// Multicore comms FIFO
|
||||
_MFIFO fifo;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 3
|
||||
#define ARDUINO_PICO_REVISION 3
|
||||
#define ARDUINO_PICO_VERSION_STR "2.3.3"
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "2.5.0"
|
||||
|
||||
@@ -292,7 +292,7 @@ int SerialUART::available() {
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
return (_writer - _reader) % _fifoSize;
|
||||
return (_fifoSize + _writer - _reader) % _fifoSize;
|
||||
}
|
||||
|
||||
int SerialUART::availableForWrite() {
|
||||
|
||||
@@ -64,7 +64,7 @@ void SerialUSB::end() {
|
||||
}
|
||||
|
||||
int SerialUSB::peek() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ int SerialUSB::peek() {
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ int SerialUSB::read() {
|
||||
}
|
||||
|
||||
int SerialUSB::available() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ int SerialUSB::available() {
|
||||
}
|
||||
|
||||
int SerialUSB::availableForWrite() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ int SerialUSB::availableForWrite() {
|
||||
}
|
||||
|
||||
void SerialUSB::flush() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ size_t SerialUSB::write(uint8_t c) {
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
}
|
||||
|
||||
SerialUSB::operator bool() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
StreamString.h
|
||||
|
||||
Copyright (c) 2020 D. Gauchard. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __STREAMSTRING_H
|
||||
#define __STREAMSTRING_H
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include "Stream.h"
|
||||
#include "api/String.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// S2Stream points to a String and makes it a Stream
|
||||
// (it is also the helper for StreamString)
|
||||
|
||||
class S2Stream: public Stream {
|
||||
public:
|
||||
S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { }
|
||||
|
||||
S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { }
|
||||
|
||||
virtual int available() override {
|
||||
return string->length();
|
||||
}
|
||||
|
||||
virtual int availableForWrite() override {
|
||||
return std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
|
||||
virtual int read() override {
|
||||
if (peekPointer < 0) {
|
||||
// consume chars
|
||||
if (string->length()) {
|
||||
char c = string->charAt(0);
|
||||
string->remove(0, 1);
|
||||
return c;
|
||||
}
|
||||
} else if (peekPointer < (int)string->length()) {
|
||||
// return pointed and move pointer
|
||||
return string->charAt(peekPointer++);
|
||||
}
|
||||
|
||||
// everything is read
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t data) override {
|
||||
return string->concat((char)data);
|
||||
}
|
||||
|
||||
// virtual int read(uint8_t* buffer, size_t len) override
|
||||
// {
|
||||
// if (peekPointer < 0)
|
||||
// {
|
||||
// // string will be consumed
|
||||
// size_t l = std::min(len, (size_t)string->length());
|
||||
// memcpy(buffer, string->c_str(), l);
|
||||
// string->remove(0, l);
|
||||
// return l;
|
||||
// }
|
||||
//
|
||||
// if (peekPointer >= (int)string->length())
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// // only the pointer is moved
|
||||
// size_t l = std::min(len, (size_t)(string->length() - peekPointer));
|
||||
// memcpy(buffer, string->c_str() + peekPointer, l);
|
||||
// peekPointer += l;
|
||||
// return l;
|
||||
// }
|
||||
|
||||
virtual size_t write(const uint8_t* buffer, size_t len) override {
|
||||
return string->concat((const char*)buffer, len) ? len : 0;
|
||||
}
|
||||
|
||||
virtual int peek() override {
|
||||
if (peekPointer < 0) {
|
||||
if (string->length()) {
|
||||
return string->charAt(0);
|
||||
}
|
||||
} else if (peekPointer < (int)string->length()) {
|
||||
return string->charAt(peekPointer);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual void flush() override {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual bool inputCanTimeout() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool outputCanTimeout() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Stream's peekBufferAPI
|
||||
|
||||
virtual bool hasPeekBufferAPI() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual size_t peekAvailable() {
|
||||
if (peekPointer < 0) {
|
||||
return string->length();
|
||||
}
|
||||
return string->length() - peekPointer;
|
||||
}
|
||||
|
||||
virtual const char* peekBuffer() override {
|
||||
if (peekPointer < 0) {
|
||||
return string->c_str();
|
||||
}
|
||||
if (peekPointer < (int)string->length()) {
|
||||
return string->c_str() + peekPointer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void peekConsume(size_t consume) override {
|
||||
if (peekPointer < 0) {
|
||||
// string is really consumed
|
||||
string->remove(0, consume);
|
||||
} else {
|
||||
// only the pointer is moved
|
||||
peekPointer = std::min((size_t)string->length(), peekPointer + consume);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ssize_t streamRemaining() override {
|
||||
return peekPointer < 0 ? string->length() : string->length() - peekPointer;
|
||||
}
|
||||
|
||||
// calling setConsume() will consume bytes as the stream is read
|
||||
// (enabled by default)
|
||||
void setConsume() {
|
||||
peekPointer = -1;
|
||||
}
|
||||
#endif
|
||||
// Reading this stream will mark the string as read without consuming
|
||||
// (not enabled by default)
|
||||
// Calling resetPointer() resets the read state and allows rereading.
|
||||
void resetPointer(int pointer = 0) {
|
||||
peekPointer = pointer;
|
||||
}
|
||||
|
||||
protected:
|
||||
String* string;
|
||||
int peekPointer; // -1:String is consumed / >=0:resettable pointer
|
||||
};
|
||||
|
||||
// StreamString is a S2Stream holding the String
|
||||
|
||||
class StreamString: public String, public S2Stream {
|
||||
protected:
|
||||
void resetpp() {
|
||||
if (peekPointer > 0) {
|
||||
peekPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
StreamString(StreamString&& bro) : String(bro), S2Stream(this) { }
|
||||
StreamString(const StreamString& bro) : String(bro), S2Stream(this) { }
|
||||
|
||||
// duplicate String constructors and operator=:
|
||||
|
||||
StreamString(const char* text = nullptr) : String(text), S2Stream(this) { }
|
||||
StreamString(const String& string) : String(string), S2Stream(this) { }
|
||||
StreamString(const __FlashStringHelper* str) : String(str), S2Stream(this) { }
|
||||
StreamString(String&& string) : String(string), S2Stream(this) { }
|
||||
|
||||
explicit StreamString(char c) : String(c), S2Stream(this) { }
|
||||
explicit StreamString(unsigned char c, unsigned char base = 10) :
|
||||
String(c, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(int i, unsigned char base = 10) : String(i, base), S2Stream(this) { }
|
||||
explicit StreamString(unsigned int i, unsigned char base = 10) : String(i, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(long l, unsigned char base = 10) : String(l, base), S2Stream(this) { }
|
||||
explicit StreamString(unsigned long l, unsigned char base = 10) :
|
||||
String(l, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(float f, unsigned char decimalPlaces = 2) :
|
||||
String(f, decimalPlaces), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(double d, unsigned char decimalPlaces = 2) :
|
||||
String(d, decimalPlaces), S2Stream(this) {
|
||||
}
|
||||
|
||||
StreamString& operator=(const StreamString& rhs) {
|
||||
String::operator=(rhs);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const String& rhs) {
|
||||
String::operator=(rhs);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const char* cstr) {
|
||||
String::operator=(cstr);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const __FlashStringHelper* str) {
|
||||
String::operator=(str);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(String&& rval) {
|
||||
String::operator=(rval);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __STREAMSTRING_H
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
libb64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Authors:
|
||||
-------
|
||||
|
||||
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include <stdint.h>
|
||||
#include "cdecode.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static int base64_decode_value_signed(int8_t value_in) {
|
||||
static const int8_t decoding[] PROGMEM = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
|
||||
static const int8_t decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) {
|
||||
return -1;
|
||||
}
|
||||
return pgm_read_byte(&decoding[(int)value_in]);
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in) {
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in) {
|
||||
const int8_t* codechar = code_in;
|
||||
int8_t* plainchar = plaintext_out;
|
||||
int8_t fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step) {
|
||||
while (1) {
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
// falls through
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
// falls through
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
// falls through
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out) {
|
||||
base64_decodestate _state;
|
||||
base64_init_decodestate(&_state);
|
||||
int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state);
|
||||
if (len > 0) {
|
||||
plaintext_out[len] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int base64_decode_value(char value_in) {
|
||||
return base64_decode_value_signed(*((int8_t *) &value_in));
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) {
|
||||
return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in);
|
||||
}
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) {
|
||||
return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out);
|
||||
}
|
||||
|
||||
};
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define base64_decode_expected_len(n) ((n * 3) / 4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in) {
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
state_in->stepsnewline = BASE64_CHARS_PER_LINE;
|
||||
}
|
||||
|
||||
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in) {
|
||||
base64_init_encodestate(state_in);
|
||||
state_in->stepsnewline = -1;
|
||||
}
|
||||
|
||||
char base64_encode_value(const char n) {
|
||||
char r;
|
||||
|
||||
if (n < 26) {
|
||||
r = n + 'A';
|
||||
} else if (n < 26 + 26) {
|
||||
r = n - 26 + 'a';
|
||||
} else if (n < 26 + 26 + 10) {
|
||||
r = n - 26 - 26 + '0';
|
||||
} else if (n == 62) {
|
||||
r = '+';
|
||||
} else {
|
||||
r = '/';
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) {
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step) {
|
||||
while (1) {
|
||||
case step_A:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
// falls through
|
||||
case step_B:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
// falls through
|
||||
case step_C:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if ((state_in->stepcount == BASE64_CHARS_PER_LINE / 4) && (state_in->stepsnewline > 0)) {
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in) {
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step) {
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(plaintext_in, length_in, code_out, &_state);
|
||||
return len + base64_encode_blockend((code_out + len), &_state);
|
||||
}
|
||||
|
||||
};
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BASE64_CHARS_PER_LINE 72
|
||||
|
||||
#define base64_encode_expected_len_nonewlines(n) ((((4 * (n)) / 3) + 3) & ~3)
|
||||
#define base64_encode_expected_len(n) \
|
||||
(base64_encode_expected_len_nonewlines(n) + ((n / ((BASE64_CHARS_PER_LINE * 3) / 4)) + 1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
int stepsnewline;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -28,6 +28,7 @@
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
|
||||
volatile bool __inLWIP = false;
|
||||
|
||||
// note same code
|
||||
@@ -89,7 +88,7 @@ semaphore_t cyw43_irq_sem;
|
||||
|
||||
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
|
||||
static void periodic_worker(void) {
|
||||
#if CYW43_USE_STATS
|
||||
#if CYW43_USE_STATS && LWIP_SYS_CHECK_MS
|
||||
static uint32_t counter;
|
||||
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
|
||||
cyw43_dump_stats();
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.3.3'
|
||||
version = u'2.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.3.3'
|
||||
release = u'2.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+11
-1
@@ -98,9 +98,19 @@ The included ``SD`` library is the Arduino standard one. Please refer to
|
||||
the [Arduino SD reference](https://www.arduino.cc/en/reference/SD) for
|
||||
more information.
|
||||
|
||||
Using Second SPI port for SD
|
||||
----------------------------
|
||||
The ``SD`` library ``begin()`` has been modified to allow you to use the
|
||||
second SPI port, ``SPI1``. Just use the following call in place of
|
||||
``SD.begin(cspin)``
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
SD.begin(cspin, SPI1);
|
||||
|
||||
|
||||
File system object (LittleFS/SD/SDFS)
|
||||
--------------------------------------------
|
||||
-------------------------------------
|
||||
|
||||
setConfig
|
||||
~~~~~~~~~
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
HTTPClient Library
|
||||
==================
|
||||
|
||||
A simple HTTP requestor that can handle both HTTP and HTTP requests is
|
||||
included as the ``HTTPClient`` library.
|
||||
|
||||
Check the examples for use under HTTP and HTTPS configurations. In general,
|
||||
for HTTP connections (unsecured and very uncommon on the internet today) simply
|
||||
passing in a URL and performiung a GET is sufficient to transfer data.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Error checking is left as an exercise for the reader...
|
||||
HTTPClient http;
|
||||
if (http.begin("http://my.server/url")) {
|
||||
if (http.GET() > 0) {
|
||||
String data = http.getString();
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
For HTTPS connections, simply add the appropriate WiFiClientSecure calls
|
||||
as needed (i.e. ``setInsecure()``, ``setTrustAnchor``, etc.). See the
|
||||
WiFiClientSecure documentation for more details.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Error checking is left as an exercise for the reader...
|
||||
HTTPClient https;
|
||||
https.setInsecure(); // Use certs, but do not check their authenticity
|
||||
if (https.begin("https://my.secure.server/url")) {
|
||||
if (http.GET() > 0) {
|
||||
String data = http.getString();
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
Unlike the ESP8266 and ESP32 ``HTTPClient`` implementations it is not necessary
|
||||
to create a ``WiFiClient`` or ``WiFiClientSecure`` to pass in to the ``HTTPClient``
|
||||
object.
|
||||
+1
-1
@@ -38,7 +38,7 @@ for normal operations.
|
||||
Generic RP2040 Support
|
||||
----------------------
|
||||
If your RP2040 board isn't in the menus you can still use it with the
|
||||
IDE bu using the `Board->Generic RP2040` menu option. You will need to
|
||||
IDE by using the `Board->Generic RP2040` menu option. You will need to
|
||||
then set the flash size (see above) and tell the IDE how to communicate
|
||||
with the flash chip using the `Tools->Boot Stage 2` menu.
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
WiFiClientSecure (TLS/SSL/HTTPS) <bearssl-client-secure-class>
|
||||
WiFiServerSecure (TLS/SSL/HTTPS) <bearssl-server-secure-class>
|
||||
|
||||
HTTP/HTTPS Client <httpclient>
|
||||
|
||||
Over-the-Air (OTA) Updates <ota>
|
||||
|
||||
Ported/Optimized Libraries <libraries>
|
||||
Using Pico-SDK <sdk>
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ The Arduino-Pico core can be installed using the Arduino IDE Boards Manager
|
||||
or using `git`. If you want to simply write programs for your RP2040 board,
|
||||
the Boards Manager installation will suffice, but if you want to try the
|
||||
latest pre-release versions and submit improvements, you will need the `git`
|
||||
instllation.
|
||||
installation.
|
||||
|
||||
Installing via Arduino Boards Manager
|
||||
-------------------------------------
|
||||
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
OTA Updates
|
||||
===========
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
OTA (Over the Air) update is the process of uploading firmware to a Pico using a Wi-Fi, Ethernet, or other connection rather than a serial port. This is especially useful for WiFi enabled Picos, like the Pico W, because it lets systems be updated remotely, without needing physical access.
|
||||
|
||||
OTA may be done using:
|
||||
|
||||
- `Arduino IDE <#arduino-ide>`__
|
||||
- `Web Browser <#web-browser>`__
|
||||
- `HTTP Server <#http-server>`__
|
||||
- Any other method (ZModen receive over a UART port, etc.) by using the ``Updater`` object in your sketch
|
||||
|
||||
The Arduino IDE option is intended primarily for the software development phase. The other two options would be more useful after deployment, to provide the module with application updates either manually with a web browser, or automatically using an HTTP server.
|
||||
|
||||
In any case, the first firmware upload has to be done over a serial port. If the OTA routines are correctly implemented in the sketch, then all subsequent uploads may be done over the air.
|
||||
|
||||
By default, there is no imposed security for the OTA process. It is up to the developer to ensure that updates are allowed only from legitimate / trusted sources. Once the update is complete, the module restarts, and the new code is executed. The developer should ensure that the application running on the module is shut down and restarted in a safe manner. Chapters below provide additional information regarding security and safety of OTA updates.
|
||||
|
||||
OTA Requirements
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
OTA requires a LittleFS partition to store firmware upgrade files. Make sure that you configure the sketch with a filesystem large enough to handle whatever size firmware binary you expect. Updates may be compressed, minimizing the total space needed.
|
||||
|
||||
Power Fail Safety
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The update commands are all stored in flash, so a power cycle during update (except if the OTA bootloader is being changed) should not brick the device because when power is restored the OTA bootloader will begin the process from scratch once again.
|
||||
|
||||
|
||||
Security Disclaimer
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
No guarantees as to the level of security provided for your application by the following methods is implied. Please refer to the GNU LGPL license associated for this project for full disclaimers. If you do find security weaknesses, please don't hesitate to contact the maintainers or supply pull requests with fixes. The MD5 verification and password protection schemes are already known to supply a very weak level of security.
|
||||
|
||||
Basic Security
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The module has to be exposed wirelessly to get it updated with a new sketch. That poses a risk of the module being violently hacked and programmed with some other code. To reduce the likelihood of being hacked, consider protecting your uploads with a password, selecting certain OTA port, etc.
|
||||
|
||||
Check functionality provided with the `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ library that may improve security:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setPort(uint16_t port);
|
||||
void setHostname(const char* hostname);
|
||||
void setPassword(const char* password);
|
||||
|
||||
Certain basic protection is already built in and does not require any additional coding by the developer. `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ and espota.py use `Digest-MD5 <https://en.wikipedia.org/wiki/Digest_access_authentication>`__ to authenticate uploads. Integrity of transferred data is verified on the Pico side using `MD5 <https://en.wikipedia.org/wiki/MD5>`__ checksum.
|
||||
|
||||
Make your own risk analysis and, depending on the application, decide what library functions to implement. If required, consider implementation of other means of protection from being hacked, like exposing modules for uploads only according to a specific schedule, triggering OTA only when the user presses a dedicated “Update” button wired to the Pico, etc.
|
||||
|
||||
Advanced Security - Signed Updates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While the above password-based security will dissuade casual hacking attempts, it is not highly secure. For applications where a higher level of security is needed, cryptographically signed OTA updates can be required. This uses SHA256 hashing in place of MD5 (which is known to be cryptographically broken) and RSA-2048 bit level public-key encryption to guarantee that only the holder of a cryptographic private key can produce signed updates accepted by the OTA update mechanisms.
|
||||
|
||||
Signed updates are updates whose compiled binaries are signed with a private key (held by the developer) and verified with a public key (stored in the application and available for all to see). The signing process computes a hash of the binary code, encrypts the hash with the developer's private key, and appends this encrypted hash (also called a signature) to the binary that is uploaded (via OTA, web, or HTTP server). If the code is modified or replaced in any way by anyone except the holder of the developer's private key, the signature will not match and the Pico will reject the upload.
|
||||
|
||||
Cryptographic signing only protects against tampering with binaries delivered via OTA. If someone has physical access, they will always be able to flash the device over the serial port. Signing also does not encrypt anything but the hash (so that it can't be modified), so this does not protect code inside the device: if a user has physical access they can read out your program.
|
||||
|
||||
**Securing your private key is paramount. The same private/public key pair that was used with the original upload must also be used to sign later binaries. Loss of the private key associated with a binary means that you will not be able to OTA-update any of your devices in the field. Alternatively, if someone else copies the private key, then they will be able to use it to sign binaries which will be accepted by the Pico.**
|
||||
|
||||
Signed Binary Format
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The format of a signed binary is compatible with the standard binary format, and can be uploaded to a non-signed Pico via serial or OTA without any conditions. Note, however, that once an unsigned OTA app is overwritten by this signed version, further updates will require signing.
|
||||
|
||||
As shown below, the signed hash is appended to the unsigned binary, followed by the total length of the signed hash (i.e., if the signed hash was 64 bytes, then this uint32 data segment will contain 64). This format allows for extensibility (such as adding a CA-based validation scheme allowing multiple signing keys all based on a trust anchor). Pull requests are always welcome. (currently it uses SHA256 with RSASSA-PKCS1-V1_5-SIGN signature scheme from RSA PKCS #1 v1.5)
|
||||
|
||||
.. code:: bash
|
||||
|
||||
NORMAL-BINARY <SIGNATURE> <uint32 LENGTH-OF-SIGNATURE>
|
||||
|
||||
Signed Binary Prerequisites
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
OpenSSL is required to run the standard signing steps, and should be available on any UNIX-like or Windows system. As usual, the latest stable version of OpenSSL is recommended.
|
||||
|
||||
Signing requires the generation of an RSA-2048 key (other bit lengths are supported as well, but 2048 is a good selection today) using any appropriate tool. The following shell commands will generate a new public/private key pair. Run them in the sketch directory:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
openssl genrsa -out private.key 2048
|
||||
openssl rsa -in private.key -outform PEM -pubout -out public.key
|
||||
|
||||
Automatic Signing
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable sigining verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
|
||||
To enable this mode, just include `private.key` and `public.key` in the sketch `.ino` directory. The IDE will call a helper script (`tools/signing.py`) before the build begins to create a header to enable key validation using the given public key, and to actually do the signing after the build process, generating a `sketch.bin.signed` file. When OTA is enabled (ArduinoOTA, Web, or HTTP), the binary will automatically only accept signed updates.
|
||||
|
||||
When the signing process starts, the message:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Enabling binary signing
|
||||
|
||||
will appear in the IDE window before a compile is launched. At the completion of the build, the signed binary file well be displayed in the IDE build window as:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Signed binary: /full/path/to/sketch.bin.signed
|
||||
|
||||
If you receive either of the following messages in the IDE window, the signing was not completed and you will need to verify the `public.key` and `private.key`:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Not enabling binary signing
|
||||
... or ...
|
||||
Not signing the generated binary
|
||||
|
||||
Manual Signing of Binaries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Users may also manually sign executables and require the OTA process to verify their signature. In the main code, before enabling any update methods, add the following declarations and function call:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
<in globals>
|
||||
BearSSL::PublicKey signPubKey( ... key contents ... );
|
||||
BearSSL::HashSHA256 hash;
|
||||
BearSSL::SigningVerifier sign( &signPubKey );
|
||||
...
|
||||
<in setup()>
|
||||
Update.installSignature( &hash, &sign );
|
||||
|
||||
The above snippet creates a BearSSL public key and a SHA256 hash verifier, and tells the Update object to use them to validate any updates it receives from any method.
|
||||
|
||||
Compile the sketch normally and, once a `.bin` file is available, sign it using the signer script:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<PicoArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
|
||||
|
||||
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin # Maximum compression, output sketch.bin.gz
|
||||
<Upload the resultant sketch.bin.gz>
|
||||
|
||||
If signing is desired, sign the gzip compressed file *after* compression.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin
|
||||
<PicoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
|
||||
Safety
|
||||
~~~~~~
|
||||
|
||||
The OTA process consumes some of the Pico’s resources and bandwidth during upload. Then, the module is restarted and a new sketch executed. Analyse and test how this affects the functionality of the existing and new sketches.
|
||||
|
||||
If the Pico is in a remote location and controlling some equipment, you should devote additional attention to what happens if operation of this equipment is suddenly interrupted by the update process. Therefore, decide how to put this equipment into a safe state before starting the update. For instance, your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve is left open, the garden may be flooded.
|
||||
|
||||
The following functions are provided with the `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ library and intended to handle functionality of your application during specific stages of OTA, or on an OTA error:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void onStart(OTA_CALLBACK(fn));
|
||||
void onEnd(OTA_CALLBACK(fn));
|
||||
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
||||
void onError(OTA_CALLBACK_ERROR (fn));
|
||||
|
||||
Uploading from the Arduino IDE
|
||||
------------------------------
|
||||
|
||||
Uploading modules wirelessly from Arduino IDE is intended for the following typical scenarios:
|
||||
|
||||
- During firmware development as a quicker alternative to loading over a serial port,
|
||||
|
||||
- For updating a small number of modules,
|
||||
|
||||
- Only if modules are accessible on the same network as the computer with the Arduino IDE.
|
||||
|
||||
- For all IDE uploads,m the Pico W and the computer must be connected to the same network.
|
||||
|
||||
To upload wirelessly from the IDE:
|
||||
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriare calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
|
||||
2. Upload using standard USB connection the first time.
|
||||
|
||||
3. The ``Tools->Port`` should now list ``pico-######`` under the ``Network Ports``. Select it (you won't be able to use the serial monitor, of course).
|
||||
|
||||
4. Try another upload. It should display the OTA process in place of the serial port upload.
|
||||
|
||||
Password Protection
|
||||
-------------------
|
||||
|
||||
Protecting your OTA uploads with password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
ArduinoOTA.setPassword((const char *)"123");
|
||||
|
||||
Where ``123`` is a sample password that you should replace with your own.
|
||||
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This is including original simple ``123`` OTA password. Then attempt to upload sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see prompt for password.
|
||||
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in upload log.
|
||||
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
|
||||
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
|
||||
|
||||
|
||||
|
||||
|
||||
Web Browser
|
||||
-----------
|
||||
|
||||
Updates described in this chapter are done with a web browser that can be useful in the following typical scenarios:
|
||||
|
||||
- after application deployment if loading directly from Arduino IDE is inconvenient or not possible,
|
||||
- after deployment if user is unable to expose module for OTA from external update server,
|
||||
- to provide updates after deployment to small quantity of modules when setting an update server is not practicable.
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- The Pico and the computer must be connected to the same network, or the IP of the Pico should be known if on a different network.
|
||||
|
||||
Implementation Overview
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Updates with a web browser are implemented using ``HTTPUpdateServer`` class together with ``WebServer`` and ``LEAmDNS`` classes. The following code is required to get it work:
|
||||
|
||||
setup()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
loop()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
httpServer.handleClient();
|
||||
|
||||
In case OTA update fails dead after entering modifications in your sketch, you can always recover module by loading it over a serial port. Then diagnose the issue with sketch using Serial Monitor. Once the issue is fixed try OTA again.
|
||||
|
||||
|
||||
HTTP Server
|
||||
-----------
|
||||
|
||||
``HTTPUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet.
|
||||
|
||||
Note that by default this class closes all other connections except the one used by the update, this is because the update method blocks. This means that if there's another application receiving data then TCP packets will build up in the buffer leading to out of memory errors causing the OTA update to fail. There's also a limited number of receive buffers available and all may be used up by other applications.
|
||||
|
||||
There are some cases where you know that you won't be receiving any data but would still like to send progress updates.
|
||||
It's possible to disable the default behaviour (and keep connections open) by calling closeConnectionsOnUpdate(false).
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- web server
|
||||
|
||||
Arduino code
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Simple updater downloads the file every time the function is called.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
HTTPUpdate.update(client, "192.168.0.2", 80, "/arduino.bin");
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
|
||||
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
t_httpUpdate_return ret = HTTPUpdate.update(client, "192.168.0.2", 80, "/pico/update/arduino.php", "optional current version string here");
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.println("[update] Update failed.");
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
|
||||
break;
|
||||
}
|
||||
|
||||
TLS updater
|
||||
^^^^^^^^^^^
|
||||
|
||||
Please read and try the examples provided with the library.
|
||||
|
||||
Server request handling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
For the simple updater the server only needs to deliver the binary file for update.
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
For advanced update management a script (such as a PHP script) can run on the server side. It will receive the following headers which it may use to choose a specific firmware file to serve:
|
||||
|
||||
::
|
||||
[User-Agent] => Pico-HTTP-Update
|
||||
[x-Pico-STA-MAC] => 18:FE:AA:AA:AA:AA
|
||||
[x-Pico-AP-MAC] => 1A:FE:AA:AA:AA:AA
|
||||
[x-Pico-Version] => DOOR-7-g14f53a19
|
||||
[x-Pico-Mode] => sketch
|
||||
|
||||
|
||||
Stream Interface
|
||||
----------------
|
||||
|
||||
The Stream Interface is the base for all other update modes like OTA, HTTP Server / client. Given a Stream-class variable `streamVar` providing `byteCount` bytes of firmware, it can store the firmware as follows:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Update.begin(firmwareLengthInBytes);
|
||||
Update.writeStream(streamVar);
|
||||
Update.end();
|
||||
|
||||
OTA Bootloader and Memory Map
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A firmware file is uploaded via any method (Ethernet, WiFi, serial ZModem, etc.) and stored on the LittleFS filesystem as a normal file. The Updater class (or the underlying PicoOTA) will make a special "OTA command" file on the filesystem, which will be read by the OTA bootloader. On a reboot, this OTA bootloader will check for an upgrade file, verify its contents, and then perform the requested update and reboot. If no upgrade file is present, the OTA bootloader simply jumps to the main sketch.
|
||||
|
||||
The ROM layout consists of:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
[boot2.S] [OTA Bootloader] [0-pad] [OTA partition table] [Main sketch] [LittleFS filesystem] [EEPROM]
|
||||
|
||||
+6
-2
@@ -33,6 +33,10 @@ values returned may not meet the most stringent random tests. **If your
|
||||
application needs absolute bulletproof random numbers, consider using
|
||||
dedicated external hardware.**
|
||||
|
||||
void rp2040.reboot()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Forces a hardware reboot of the Pico.
|
||||
|
||||
Memory Information
|
||||
------------------
|
||||
|
||||
@@ -43,8 +47,8 @@ that because there is some overhead, and there may be heap fragmentation,
|
||||
this number is an *upper bound* and you generally will only be able to allocate
|
||||
less than this returned number.
|
||||
|
||||
int rp2040.getUsedHeap
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
int rp2040.getUsedHeap()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of bytes allocated out of the heap.
|
||||
|
||||
int rp2040.getTotalHeap()
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
WiFi (Raspberry Pi Pico W) Support
|
||||
==================================
|
||||
|
||||
WiFi is supported on the Raspberry Pi Pico W by selecting the "Raspbery Pi Pico W" board in the Boards Manager. It is generally compatible with the `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ and the `ESP8266 Arduino WiFi library <https://github.com/esp8266/Arduino>`__.
|
||||
WiFi is supported on the Raspberry Pi Pico W by selecting the "Raspberry Pi Pico W" board in the Boards Manager. It is generally compatible with the `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ and the `ESP8266 Arduino WiFi library <https://github.com/esp8266/Arduino>`__.
|
||||
|
||||
Enable WiFi support by selecting the `Raspberry Pi Pico W` board in the IDE and adding ``#include <WiFi.h>`` in your sketch.
|
||||
|
||||
|
||||
+5
-1
@@ -21,7 +21,7 @@ extern void interrupts();
|
||||
#define MEM_LIBC_MALLOC 0
|
||||
|
||||
#define MEM_ALIGNMENT 4
|
||||
#define MEM_SIZE 4000
|
||||
#define MEM_SIZE 16384
|
||||
#define MEMP_NUM_TCP_SEG 32
|
||||
#define MEMP_NUM_ARP_QUEUE 10
|
||||
#define PBUF_POOL_SIZE 24
|
||||
@@ -53,6 +53,10 @@ extern void interrupts();
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#endif
|
||||
|
||||
// NTP
|
||||
extern void __setSystemTime(unsigned long long sec, unsigned long us);
|
||||
#define SNTP_SET_SYSTEM_TIME_US(sec, us) __setSystemTime(sec, us)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
ota_command.h - OTA stub that copies from LittleFS to flash
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define _OTA_WRITE 1
|
||||
#define _OTA_VERIFY 1
|
||||
|
||||
typedef struct {
|
||||
uint32_t command;
|
||||
union {
|
||||
struct {
|
||||
char filename[64];
|
||||
uint32_t fileOffset;
|
||||
uint32_t fileLength;
|
||||
uint32_t flashAddress; // Normally XIP_BASE
|
||||
} write;
|
||||
};
|
||||
} commandEntry;
|
||||
|
||||
// Must fit within 4K page
|
||||
typedef struct {
|
||||
uint8_t sign[8]; // "Pico OTA"
|
||||
|
||||
// List of operations
|
||||
uint32_t count;
|
||||
commandEntry cmd[8];
|
||||
|
||||
uint32_t crc32; // CRC32 over just the contents of this struct, up until just before this value
|
||||
} OTACmdPage;
|
||||
|
||||
#define _OTA_COMMAND_FILE "otacommand.bin"
|
||||
@@ -2,6 +2,8 @@
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
Arduino KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
@@ -23,14 +25,29 @@ pop KEYWORD2
|
||||
pop_nb KEYWORD2
|
||||
|
||||
rp2040 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
RP2040 KEYWORD2
|
||||
usToPIOCycles KEYWORD2
|
||||
f_cpu KEYWORD2
|
||||
getCycleCount KEYWORD2
|
||||
getCycleCount64 KEYWORD2
|
||||
|
||||
getFreeHeap KEYWORD2
|
||||
getUsedHeap KEYWORD2
|
||||
getTotalHeap KEYWORD2
|
||||
|
||||
idleOtherCore KEYWORD2
|
||||
resumeOtherCore KEYWORD2
|
||||
|
||||
restartCore1 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
|
||||
PIOProgram KEYWORD2
|
||||
prepare KEYWORD2
|
||||
SerialPIO KEYWORD2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -55,6 +55,28 @@ SECTIONS
|
||||
ASSERT(__boot2_end__ - __boot2_start__ == 256,
|
||||
"ERROR: Pico second stage bootloader must be 256 bytes in size")
|
||||
|
||||
.ota : {
|
||||
/* Start image with OTA */
|
||||
KEEP (*(.OTA))
|
||||
/* Align to the last 16-bytes of the OTA region */
|
||||
/* If anyone has a better way of doing this, please submit a PR! */
|
||||
/* . = __flash_binary_start + 0x2ff0;
|
||||
LONG(__FS_START__)
|
||||
LONG(__FS_END__)
|
||||
LONG(__EEPROM_START__)
|
||||
LONG(__FLASH_LENGTH__)*/
|
||||
} > FLASH
|
||||
|
||||
.partition : {
|
||||
/* Align to the last 16-bytes of the OTA region */
|
||||
/* If anyone has a better way of doing this, please submit a PR! */
|
||||
. = __flash_binary_start + 0x2ff0;
|
||||
LONG(__FS_START__)
|
||||
LONG(__FS_END__)
|
||||
LONG(__EEPROM_START__)
|
||||
LONG(__FLASH_LENGTH__)
|
||||
} > FLASH
|
||||
|
||||
/* The second stage will always enter the image at the start of .text.
|
||||
The debugger will use the ELF entry point, which is the _entry_point
|
||||
symbol if present, otherwise defaults to start of .text.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
const char* host = "OTA-LEDS";
|
||||
|
||||
int led_pin = 13;
|
||||
#define N_DIMMERS 3
|
||||
int dimmer_pin[] = { 14, 5, 15 };
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/* switch on led */
|
||||
pinMode(led_pin, OUTPUT);
|
||||
digitalWrite(led_pin, LOW);
|
||||
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("Retrying connection...");
|
||||
}
|
||||
/* switch off led */
|
||||
digitalWrite(led_pin, HIGH);
|
||||
|
||||
/* configure dimmers, and OTA server events */
|
||||
analogWriteRange(1000);
|
||||
analogWrite(led_pin, 990);
|
||||
|
||||
for (int i = 0; i < N_DIMMERS; i++) {
|
||||
pinMode(dimmer_pin[i], OUTPUT);
|
||||
analogWrite(dimmer_pin[i], 50);
|
||||
}
|
||||
|
||||
ArduinoOTA.setHostname(host);
|
||||
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
|
||||
for (int i = 0; i < N_DIMMERS; i++) {
|
||||
analogWrite(dimmer_pin[i], 0);
|
||||
}
|
||||
analogWrite(led_pin, 0);
|
||||
});
|
||||
|
||||
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
|
||||
for (int i = 0; i < 30; i++) {
|
||||
analogWrite(led_pin, (i * 100) % 1001);
|
||||
delay(50);
|
||||
}
|
||||
});
|
||||
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
(void)error;
|
||||
rp2040.restart();
|
||||
});
|
||||
|
||||
/* setup the OTA server */
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// To be used with SignedOTA. The IDE will sign the binary
|
||||
// automatically and upload over WiFi
|
||||
|
||||
// Released to the public domain, Earle Philhower, 2022
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
srand(123);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int del = rand() % 100;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(del * 10);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(del * 10);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAu1Pt7yEk/xI+6cozLj5Bu4xV8gXDXcHS0rSJFfl4wBTk4UXp
|
||||
aJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibONx0VVoWmeqN2HBc3zkA1eqCksI0Q
|
||||
Uudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLoSjVTbsJmGuwx8RGMBXozpg/uL0hH
|
||||
flihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w3J2nNjtuPuVN5vcQkd8ncMexVfy9
|
||||
AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1WIo75bZHKZNFw/iXe2xoPpm74qri
|
||||
MNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2zQIDAQABAoIBAE5GpuDKb8Qp4qIc
|
||||
fMBxAVSWMn+cSuONj0O+bp4BDaTt1ioP5ZVukDQtt0ehLOEePFgf9LEc+1a6Ozy3
|
||||
EaJTTs4W2Ai8djE+xqa8SPRlPjOMluSzPUP3NRHuTpTXd3YiXksrZjP1U02+/Cos
|
||||
8ZIROtFvcPqSPso3MjMyitjrFFPqEtf1P+UiamjDrMSM72YX4W55kOkiCWCnAOmw
|
||||
mGTlXOIqDSTBb1lloKWJfpB3RdnNo2izkU1HMBn7hVi433NUBA22o+RZhDFSZdD4
|
||||
3kbkUqXd4p+vc/sh6muJtWS/COSIPFkLzdEYpBdt3XQ4FhlsRtILJaPWXa4OPjR6
|
||||
ZoOwMB0CgYEA6OHfIofQiu4+HlTDN5YdyTmtYEYcrtbaQUxuQSEa2mshBphHP8uT
|
||||
mYRVl2BzuprFmXZPz+FcjnPnfxqEehljvA3wMjA/PE+nQo9yyOC0N4ulXpkkqHdR
|
||||
f+4KZVR7D+hesGe+57OQmvTqYZSHEt/ubjC9wZ90UFonLjsa4zibbrsCgYEAzexn
|
||||
XDnThb3ffyBgvprP0IJjgMAEY0pXD++PKPQqPu9JMz68t7roYzkKFCFVOsaWpKxC
|
||||
vX9mvYjTBjLpWh+ltIAN+EFz6seIbeSJ0RNybsAXYwT/mFWGHx2tMtlW6DgBu3UD
|
||||
J2Yf76n0JaddBkfNMQI00Dl41+MU+AwwTB9fTBcCgYB2+f6Pm6d1cyYVROS/X1g0
|
||||
V9011FwPDwFOXwftCka31Ad5YQ71jsIHqk44GjTF3xCYyJMZ917cAGcCzr9jydjk
|
||||
WJKgcXm9DEy9ep//9Jzdy+BepgrObrcajriM8E424FaP9VDY+yojoICl/cXMZM9h
|
||||
SFGJvDcmXgiqW9PuxhrSxQKBgAMN2oqXoPd+1W3BQS4ShbqF9IvYTThbxebKmsj0
|
||||
thuw2NkVuR7Qetnd4rRhui3g/CL9GxBMb22oNdkFsEhR59dBfvOLpPh6dR+MIC8l
|
||||
prDV0IL7c/8CZbbYbdUvPAa9rejl12IiNZ8MWj6kuNB7CCQN8FKWR6CMEaeMJrs6
|
||||
S+OJAoGAbehNOUwEzmUKkfxf+279kBkgabcQ3NTaeSx0QOnI9KWHFGLYLQk9cMSu
|
||||
maQJ1TYpbIoP1njzJ4bI2tynhwEuSMEhh4afP6U5H10NJX4PqSd0Rqc1vSJYcszr
|
||||
5mUWil8FfbCBZ8jod2NQ55KYMVY5CphCqaK/s2bw2pvIR3uqJGg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Pt7yEk/xI+6cozLj5B
|
||||
u4xV8gXDXcHS0rSJFfl4wBTk4UXpaJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibO
|
||||
Nx0VVoWmeqN2HBc3zkA1eqCksI0QUudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLo
|
||||
SjVTbsJmGuwx8RGMBXozpg/uL0hHflihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w
|
||||
3J2nNjtuPuVN5vcQkd8ncMexVfy9AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1
|
||||
WIo75bZHKZNFw/iXe2xoPpm74qriMNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2
|
||||
zQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,91 @@
|
||||
// Simple Signed OTA example
|
||||
// Released to the public domain by Earle Philhower, Aug 2022
|
||||
//
|
||||
// Note that the actual code of this is the same as the BasicOTA. No user
|
||||
// code changes are needed, only the presence of public.key and private.key
|
||||
// in the sketch directory. The core will automatically sign any binaries
|
||||
// and include the necessary code to verify signatures. For more info
|
||||
// check the documentation
|
||||
//
|
||||
// After uploading this sketch, try uploading the SignedOTA-Blink sketch
|
||||
// All unsigned binaries, or binaries signed with a different private
|
||||
// key will fail to upload.
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAu1Pt7yEk/xI+6cozLj5Bu4xV8gXDXcHS0rSJFfl4wBTk4UXp
|
||||
aJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibONx0VVoWmeqN2HBc3zkA1eqCksI0Q
|
||||
Uudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLoSjVTbsJmGuwx8RGMBXozpg/uL0hH
|
||||
flihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w3J2nNjtuPuVN5vcQkd8ncMexVfy9
|
||||
AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1WIo75bZHKZNFw/iXe2xoPpm74qri
|
||||
MNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2zQIDAQABAoIBAE5GpuDKb8Qp4qIc
|
||||
fMBxAVSWMn+cSuONj0O+bp4BDaTt1ioP5ZVukDQtt0ehLOEePFgf9LEc+1a6Ozy3
|
||||
EaJTTs4W2Ai8djE+xqa8SPRlPjOMluSzPUP3NRHuTpTXd3YiXksrZjP1U02+/Cos
|
||||
8ZIROtFvcPqSPso3MjMyitjrFFPqEtf1P+UiamjDrMSM72YX4W55kOkiCWCnAOmw
|
||||
mGTlXOIqDSTBb1lloKWJfpB3RdnNo2izkU1HMBn7hVi433NUBA22o+RZhDFSZdD4
|
||||
3kbkUqXd4p+vc/sh6muJtWS/COSIPFkLzdEYpBdt3XQ4FhlsRtILJaPWXa4OPjR6
|
||||
ZoOwMB0CgYEA6OHfIofQiu4+HlTDN5YdyTmtYEYcrtbaQUxuQSEa2mshBphHP8uT
|
||||
mYRVl2BzuprFmXZPz+FcjnPnfxqEehljvA3wMjA/PE+nQo9yyOC0N4ulXpkkqHdR
|
||||
f+4KZVR7D+hesGe+57OQmvTqYZSHEt/ubjC9wZ90UFonLjsa4zibbrsCgYEAzexn
|
||||
XDnThb3ffyBgvprP0IJjgMAEY0pXD++PKPQqPu9JMz68t7roYzkKFCFVOsaWpKxC
|
||||
vX9mvYjTBjLpWh+ltIAN+EFz6seIbeSJ0RNybsAXYwT/mFWGHx2tMtlW6DgBu3UD
|
||||
J2Yf76n0JaddBkfNMQI00Dl41+MU+AwwTB9fTBcCgYB2+f6Pm6d1cyYVROS/X1g0
|
||||
V9011FwPDwFOXwftCka31Ad5YQ71jsIHqk44GjTF3xCYyJMZ917cAGcCzr9jydjk
|
||||
WJKgcXm9DEy9ep//9Jzdy+BepgrObrcajriM8E424FaP9VDY+yojoICl/cXMZM9h
|
||||
SFGJvDcmXgiqW9PuxhrSxQKBgAMN2oqXoPd+1W3BQS4ShbqF9IvYTThbxebKmsj0
|
||||
thuw2NkVuR7Qetnd4rRhui3g/CL9GxBMb22oNdkFsEhR59dBfvOLpPh6dR+MIC8l
|
||||
prDV0IL7c/8CZbbYbdUvPAa9rejl12IiNZ8MWj6kuNB7CCQN8FKWR6CMEaeMJrs6
|
||||
S+OJAoGAbehNOUwEzmUKkfxf+279kBkgabcQ3NTaeSx0QOnI9KWHFGLYLQk9cMSu
|
||||
maQJ1TYpbIoP1njzJ4bI2tynhwEuSMEhh4afP6U5H10NJX4PqSd0Rqc1vSJYcszr
|
||||
5mUWil8FfbCBZ8jod2NQ55KYMVY5CphCqaK/s2bw2pvIR3uqJGg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Pt7yEk/xI+6cozLj5B
|
||||
u4xV8gXDXcHS0rSJFfl4wBTk4UXpaJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibO
|
||||
Nx0VVoWmeqN2HBc3zkA1eqCksI0QUudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLo
|
||||
SjVTbsJmGuwx8RGMBXozpg/uL0hHflihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w
|
||||
3J2nNjtuPuVN5vcQkd8ncMexVfy9AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1
|
||||
WIo75bZHKZNFw/iXe2xoPpm74qriMNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2
|
||||
zQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ArduinoOTA KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setup KEYWORD2
|
||||
handle KEYWORD2
|
||||
onStart KEYWORD2
|
||||
onEnd KEYWORD2
|
||||
onError KEYWORD2
|
||||
onProgress KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name=ArduinoOTA
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov and Miguel Angel Ajo
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download.
|
||||
paragraph=With this library you can enable your sketch to be upgraded over network. Includes mdns announces to get discovered by the arduino IDE.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,402 @@
|
||||
#include <functional>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ArduinoOTA.h"
|
||||
#include "MD5Builder.h"
|
||||
#include <PicoOTA.h>
|
||||
#include <StreamString.h>
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "include/UdpContext.h"
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
#include <LEAmDNS.h>
|
||||
#endif
|
||||
|
||||
//#ifdef DEBUG_ESP_OTA
|
||||
//#ifdef DEBUG_ESP_PORT
|
||||
//#define OTA_DEBUG DEBUG_ESP_PORT
|
||||
//#endif
|
||||
//#endif
|
||||
#define OTA_DEBUG Serial
|
||||
|
||||
ArduinoOTAClass::ArduinoOTAClass() {
|
||||
}
|
||||
|
||||
ArduinoOTAClass::~ArduinoOTAClass() {
|
||||
if (_udp_ota) {
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onStart(THandlerFunction fn) {
|
||||
_start_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onEnd(THandlerFunction fn) {
|
||||
_end_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) {
|
||||
_progress_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onError(THandlerFunction_Error fn) {
|
||||
_error_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPort(uint16_t port) {
|
||||
if (!_initialized && !_port && port) {
|
||||
_port = port;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setHostname(const char * hostname) {
|
||||
if (!_initialized && !_hostname.length() && hostname) {
|
||||
_hostname = hostname;
|
||||
}
|
||||
}
|
||||
|
||||
String ArduinoOTAClass::getHostname() {
|
||||
return _hostname;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPassword(const char * password) {
|
||||
if (!_initialized && !_password.length() && password) {
|
||||
MD5Builder passmd5;
|
||||
passmd5.begin();
|
||||
passmd5.add(password);
|
||||
passmd5.calculate();
|
||||
_password = passmd5.toString();
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPasswordHash(const char * password) {
|
||||
if (!_initialized && !_password.length() && password) {
|
||||
_password = password;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setRebootOnSuccess(bool reboot) {
|
||||
_rebootOnSuccess = reboot;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
if (_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
_useMDNS = useMDNS;
|
||||
|
||||
if (!_hostname.length()) {
|
||||
char tmp[15];
|
||||
sprintf(tmp, "pico-%s", rp2040.getChipID());
|
||||
_hostname = tmp;
|
||||
}
|
||||
if (!_port) {
|
||||
_port = 2040;
|
||||
}
|
||||
|
||||
if (_udp_ota) {
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
}
|
||||
|
||||
_udp_ota = new UdpContext;
|
||||
_udp_ota->ref();
|
||||
|
||||
if (!_udp_ota->listen(IP_ADDR_ANY, _port)) {
|
||||
return;
|
||||
}
|
||||
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.begin(_hostname.c_str());
|
||||
|
||||
if (_password.length()) {
|
||||
MDNS.enableArduino(_port, true);
|
||||
} else {
|
||||
MDNS.enableArduino(_port);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_initialized = true;
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ArduinoOTAClass::parseInt() {
|
||||
char data[16];
|
||||
uint8_t index;
|
||||
char value;
|
||||
while (_udp_ota->peek() == ' ') {
|
||||
_udp_ota->read();
|
||||
}
|
||||
for (index = 0; index < sizeof(data); ++index) {
|
||||
value = _udp_ota->peek();
|
||||
if (value < '0' || value > '9') {
|
||||
data[index] = '\0';
|
||||
return atoi(data);
|
||||
}
|
||||
data[index] = _udp_ota->read();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ArduinoOTAClass::readStringUntil(char end) {
|
||||
String res;
|
||||
int value;
|
||||
while (true) {
|
||||
value = _udp_ota->read();
|
||||
if (value < 0 || value == '\0' || value == end) {
|
||||
return res;
|
||||
}
|
||||
res += static_cast<char>(value);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::_onRx() {
|
||||
if (!_udp_ota->next()) {
|
||||
return;
|
||||
}
|
||||
IPAddress ota_ip;
|
||||
|
||||
if (_state == OTA_IDLE) {
|
||||
int cmd = parseInt();
|
||||
if (cmd != U_FLASH && cmd != U_FS) {
|
||||
return;
|
||||
}
|
||||
_ota_ip = _udp_ota->getRemoteAddress();
|
||||
_cmd = cmd;
|
||||
_ota_port = parseInt();
|
||||
_ota_udp_port = _udp_ota->getRemotePort();
|
||||
_size = parseInt();
|
||||
_udp_ota->read();
|
||||
_md5 = readStringUntil('\n');
|
||||
_md5.trim();
|
||||
if (_md5.length() != 32) {
|
||||
return;
|
||||
}
|
||||
|
||||
ota_ip = _ota_ip;
|
||||
|
||||
if (_password.length()) {
|
||||
MD5Builder nonce_md5;
|
||||
nonce_md5.begin();
|
||||
nonce_md5.add(String(micros()));
|
||||
nonce_md5.calculate();
|
||||
_nonce = nonce_md5.toString();
|
||||
|
||||
char auth_req[38];
|
||||
sprintf(auth_req, "AUTH %s", _nonce.c_str());
|
||||
_udp_ota->append((const char *)auth_req, strlen(auth_req));
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
_state = OTA_WAITAUTH;
|
||||
return;
|
||||
} else {
|
||||
_state = OTA_RUNUPDATE;
|
||||
}
|
||||
} else if (_state == OTA_WAITAUTH) {
|
||||
int cmd = parseInt();
|
||||
if (cmd != U_AUTH) {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
_udp_ota->read();
|
||||
String cnonce = readStringUntil(' ');
|
||||
String response = readStringUntil('\n');
|
||||
if (cnonce.length() != 32 || response.length() != 32) {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
String challenge = _password + ':' + String(_nonce) + ':' + cnonce;
|
||||
MD5Builder _challengemd5;
|
||||
_challengemd5.begin();
|
||||
_challengemd5.add(challenge);
|
||||
_challengemd5.calculate();
|
||||
String result = _challengemd5.toString();
|
||||
|
||||
ota_ip = _ota_ip;
|
||||
// if(result.equalsConstantTime(response)) {
|
||||
if (result.equals(response)) {
|
||||
_state = OTA_RUNUPDATE;
|
||||
} else {
|
||||
_udp_ota->append("Authentication Failed", 21);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_AUTH_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
while (_udp_ota->next()) {
|
||||
_udp_ota->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::_runUpdate() {
|
||||
IPAddress ota_ip = _ota_ip;
|
||||
|
||||
if (!LittleFS.begin()) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("LittleFS Begin Error");
|
||||
#endif
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append("No Filesystem", 13);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Update.begin(_size, _cmd)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("Update Begin Error");
|
||||
#endif
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_BEGIN_ERROR);
|
||||
}
|
||||
|
||||
StreamString ss;
|
||||
Update.printError(ss);
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append(ss.c_str(), ss.length());
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
_udp_ota->append("OK", 2);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
|
||||
Update.setMD5(_md5.c_str());
|
||||
|
||||
if (_start_callback) {
|
||||
_start_callback();
|
||||
}
|
||||
if (_progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
|
||||
WiFiClient client;
|
||||
if (!client.connect(_ota_ip, _ota_port)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Connect Failed\n");
|
||||
#endif
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_CONNECT_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
// OTA sends little packets
|
||||
client.setNoDelay(true);
|
||||
|
||||
uint32_t written, total = 0;
|
||||
while (!Update.isFinished() && (client.connected() || client.available())) {
|
||||
int waited = 1000;
|
||||
while (!client.available() && waited--) {
|
||||
delay(1);
|
||||
}
|
||||
if (!waited) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Receive Failed\n");
|
||||
#endif
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_RECEIVE_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
written = Update.write(client);
|
||||
if (written > 0) {
|
||||
client.print(written, DEC);
|
||||
total += written;
|
||||
if (_progress_callback) {
|
||||
_progress_callback(total, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Update.end()) {
|
||||
// Ensure last count packet has been sent out and not combined with the final OK
|
||||
client.flush();
|
||||
delay(1000);
|
||||
client.print("OK");
|
||||
client.flush();
|
||||
delay(1000);
|
||||
client.stop();
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Update Success\n");
|
||||
#endif
|
||||
if (_end_callback) {
|
||||
_end_callback();
|
||||
}
|
||||
if (_rebootOnSuccess) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Rebooting...\n");
|
||||
#endif
|
||||
LittleFS.end();
|
||||
//let serial/network finish tasks that might be given in _end_callback
|
||||
delay(100);
|
||||
rp2040.reboot();
|
||||
}
|
||||
} else {
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_END_ERROR);
|
||||
}
|
||||
Update.printError(client);
|
||||
#ifdef OTA_DEBUG
|
||||
Update.printError(OTA_DEBUG);
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::end() {
|
||||
_initialized = false;
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.end();
|
||||
}
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server stopped.\n");
|
||||
#endif
|
||||
}
|
||||
//this needs to be called in the loop()
|
||||
void ArduinoOTAClass::handle() {
|
||||
if (_state == OTA_RUNUPDATE) {
|
||||
_runUpdate();
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.update(); //handle MDNS update as well, given that ArduinoOTA relies on it anyways
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ArduinoOTAClass::getCommand() {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA)
|
||||
ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <functional>
|
||||
#include <LittleFS.h>
|
||||
#include <Updater.h>
|
||||
|
||||
class UdpContext;
|
||||
|
||||
typedef enum {
|
||||
OTA_IDLE,
|
||||
OTA_WAITAUTH,
|
||||
OTA_RUNUPDATE
|
||||
} ota_state_t;
|
||||
|
||||
typedef enum {
|
||||
OTA_AUTH_ERROR,
|
||||
OTA_BEGIN_ERROR,
|
||||
OTA_CONNECT_ERROR,
|
||||
OTA_RECEIVE_ERROR,
|
||||
OTA_END_ERROR
|
||||
} ota_error_t;
|
||||
|
||||
|
||||
class ArduinoOTAClass {
|
||||
public:
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<void(ota_error_t)> THandlerFunction_Error;
|
||||
typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress;
|
||||
|
||||
ArduinoOTAClass();
|
||||
~ArduinoOTAClass();
|
||||
|
||||
//Sets the service port. Default 2040
|
||||
void setPort(uint16_t port);
|
||||
|
||||
//Sets the device hostname. Default pico-xxxxxx
|
||||
void setHostname(const char *hostname);
|
||||
String getHostname();
|
||||
|
||||
//Sets the password that will be required for OTA. Default NULL
|
||||
void setPassword(const char *password);
|
||||
|
||||
//Sets the password as above but in the form MD5(password). Default NULL
|
||||
void setPasswordHash(const char *password);
|
||||
|
||||
//Sets if the device should be rebooted after successful update. Default true
|
||||
void setRebootOnSuccess(bool reboot);
|
||||
|
||||
//This callback will be called when OTA connection has begun
|
||||
void onStart(THandlerFunction fn);
|
||||
|
||||
//This callback will be called when OTA has finished
|
||||
void onEnd(THandlerFunction fn);
|
||||
|
||||
//This callback will be called when OTA encountered Error
|
||||
void onError(THandlerFunction_Error fn);
|
||||
|
||||
//This callback will be called when OTA is receiving data
|
||||
void onProgress(THandlerFunction_Progress fn);
|
||||
|
||||
//Starts the ArduinoOTA service
|
||||
void begin(bool useMDNS = true);
|
||||
|
||||
//Ends the ArduinoOTA service
|
||||
void end();
|
||||
//Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
|
||||
void handle();
|
||||
|
||||
//Gets update command type after OTA has started. Either U_FLASH or U_FS
|
||||
int getCommand();
|
||||
|
||||
private:
|
||||
void _runUpdate(void);
|
||||
void _onRx(void);
|
||||
int parseInt(void);
|
||||
String readStringUntil(char end);
|
||||
|
||||
int _port = 0;
|
||||
String _password;
|
||||
String _hostname;
|
||||
String _nonce;
|
||||
UdpContext *_udp_ota = nullptr;
|
||||
bool _initialized = false;
|
||||
bool _rebootOnSuccess = true;
|
||||
bool _useMDNS = true;
|
||||
ota_state_t _state = OTA_IDLE;
|
||||
int _size = 0;
|
||||
int _cmd = 0;
|
||||
uint16_t _ota_port = 0;
|
||||
uint16_t _ota_udp_port = 0;
|
||||
IPAddress _ota_ip;
|
||||
String _md5;
|
||||
|
||||
THandlerFunction _start_callback = nullptr;
|
||||
THandlerFunction _end_callback = nullptr;
|
||||
THandlerFunction_Error _error_callback = nullptr;
|
||||
THandlerFunction_Progress _progress_callback = nullptr;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA)
|
||||
extern ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
String responseHTML = ""
|
||||
"<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Hello World!</h1><p>This is a captive portal example."
|
||||
" All requests will be redirected here.</p></body></html>";
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("DNSServer CaptivePortal example");
|
||||
|
||||
// if DNSServer is started with "*" for domain name, it will reply with
|
||||
// provided IP to all DNS request
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
// replay to all requests with same HTML
|
||||
webServer.onNotFound([]() {
|
||||
webServer.send(200, "text/html", responseHTML);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
/*
|
||||
This example serves a "hello world" on a WLAN and a SoftAP at the same time.
|
||||
The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.
|
||||
|
||||
Connect your computer or cell phone to wifi network pico_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
|
||||
Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from pico_ap and return to your regular WLAN.
|
||||
|
||||
Now the Pico is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://picow.local too.
|
||||
|
||||
This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
|
||||
*/
|
||||
|
||||
/* Set these to your desired softAP credentials. They are not configurable at runtime */
|
||||
#ifndef APSSID
|
||||
#define APSSID "pico_ap"
|
||||
#define APPSK "12345678"
|
||||
#endif
|
||||
|
||||
const char *softAP_ssid = APSSID;
|
||||
const char *softAP_password = APPSK;
|
||||
|
||||
/* hostname for mDNS. Should work at least on windows. Try http://picow.local */
|
||||
const char *myHostname = "picow";
|
||||
|
||||
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
|
||||
char ssid[33] = "";
|
||||
char password[65] = "";
|
||||
|
||||
// DNS server
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
|
||||
// Web server
|
||||
WebServer server(80);
|
||||
|
||||
/* Soft AP network parameters */
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
IPAddress netMsk(255, 255, 255, 0);
|
||||
|
||||
|
||||
/** Should I connect to WLAN asap? */
|
||||
boolean connect;
|
||||
|
||||
/** Last time I tried to connect to WLAN */
|
||||
unsigned long lastConnectTry = 0;
|
||||
|
||||
/** Current WLAN status */
|
||||
unsigned int status = WL_IDLE_STATUS;
|
||||
|
||||
void setup() {
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
/* You can remove the password parameter if you want the AP to be open. */
|
||||
WiFi.softAPConfig(apIP, apIP, netMsk);
|
||||
WiFi.softAP(softAP_ssid, softAP_password);
|
||||
delay(500); // Without delay I've seen the IP address blank
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
/* Setup the DNS server redirecting all the domains to the apIP */
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
|
||||
server.on("/", handleRoot);
|
||||
server.on("/wifi", handleWifi);
|
||||
server.on("/wifisave", handleWifiSave);
|
||||
server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin(); // Web server start
|
||||
Serial.println("HTTP server started");
|
||||
//loadCredentials(); // Load WLAN credentials from network
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
Serial.println("Connecting as wifi client...");
|
||||
WiFi.disconnect();
|
||||
WiFi.end();
|
||||
WiFi.begin(ssid, password);
|
||||
int connRes = WiFi.waitForConnectResult();
|
||||
Serial.print("connRes: ");
|
||||
Serial.println(connRes);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (connect) {
|
||||
Serial.println("Connect requested");
|
||||
connect = false;
|
||||
connectWifi();
|
||||
lastConnectTry = millis();
|
||||
}
|
||||
{
|
||||
unsigned int s = WiFi.status();
|
||||
if (s == 0 && millis() > (lastConnectTry + 60000)) {
|
||||
/* If WLAN disconnected and idle try to connect */
|
||||
/* Don't set retry time too low as retry interfere the softAP operation */
|
||||
connect = true;
|
||||
}
|
||||
if (status != s) { // WLAN status change
|
||||
Serial.print("Status: ");
|
||||
Serial.println(s);
|
||||
status = s;
|
||||
if (s == WL_CONNECTED) {
|
||||
/* Just connected to WLAN */
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Setup MDNS responder
|
||||
if (!MDNS.begin(myHostname)) {
|
||||
Serial.println("Error setting up MDNS responder!");
|
||||
} else {
|
||||
Serial.println("mDNS responder started");
|
||||
// Add service to MDNS-SD
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
} else if (s == WL_NO_SSID_AVAIL) {
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
||||
if (s == WL_CONNECTED) { MDNS.update(); }
|
||||
}
|
||||
// Do work:
|
||||
// DNS
|
||||
dnsServer.processNextRequest();
|
||||
// HTTP
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Load WLAN credentials from EEPROM */
|
||||
void loadCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.get(0, ssid);
|
||||
EEPROM.get(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1];
|
||||
EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.end();
|
||||
if (String(ok) != String("OK")) {
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
}
|
||||
Serial.println("Recovered credentials:");
|
||||
Serial.println(ssid);
|
||||
Serial.println(strlen(password) > 0 ? "********" : "<no password>");
|
||||
}
|
||||
|
||||
/** Store WLAN credentials to EEPROM */
|
||||
void saveCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.put(0, ssid);
|
||||
EEPROM.put(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1] = "OK";
|
||||
EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.commit();
|
||||
EEPROM.end();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/** Handle root or redirect to captive portal */
|
||||
void handleRoot() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||
return;
|
||||
}
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>HELLO WORLD!!</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += F("<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
"</body></html>");
|
||||
|
||||
server.send(200, "text/html", Page);
|
||||
}
|
||||
|
||||
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
|
||||
boolean captivePortal() {
|
||||
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
|
||||
Serial.println("Request redirected to captive portal");
|
||||
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Wifi config page handler */
|
||||
void handleWifi() {
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Wifi config</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += String(F("\r\n<br />"
|
||||
"<table><tr><th align='left'>SoftAP config</th></tr>"
|
||||
"<tr><td>SSID "))
|
||||
+ String(softAP_ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.softAPIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN config</th></tr>"
|
||||
"<tr><td>SSID ")
|
||||
+ String(ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.localIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
|
||||
Serial.println("scan start");
|
||||
int n = WiFi.scanNetworks();
|
||||
Serial.println("scan done");
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) { Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>"); }
|
||||
} else {
|
||||
Page += F("<tr><td>No WLAN found</td></tr>");
|
||||
}
|
||||
Page += F("</table>"
|
||||
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
|
||||
"<input type='text' placeholder='network' name='n'/>"
|
||||
"<br /><input type='password' placeholder='password' name='p'/>"
|
||||
"<br /><input type='submit' value='Connect/Disconnect'/></form>"
|
||||
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
|
||||
"</body></html>");
|
||||
server.send(200, "text/html", Page);
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
}
|
||||
|
||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
||||
void handleWifiSave() {
|
||||
Serial.println("wifi save");
|
||||
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
|
||||
server.arg("p").toCharArray(password, sizeof(password) - 1);
|
||||
server.sendHeader("Location", "wifi", true);
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
saveCredentials();
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
|
||||
return;
|
||||
}
|
||||
String message = F("File Not Found\n\n");
|
||||
message += F("URI: ");
|
||||
message += server.uri();
|
||||
message += F("\nMethod: ");
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += F("\nArguments: ");
|
||||
message += server.args();
|
||||
message += F("\n");
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); }
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/** Is this an IP? */
|
||||
boolean isIp(String str) {
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c != '.' && (c < '0' || c > '9')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** IP to String? */
|
||||
String toStringIp(IPAddress ip) {
|
||||
String res = "";
|
||||
for (int i = 0; i < 3; i++) { res += String((ip >> (8 * i)) & 0xFF) + "."; }
|
||||
res += String(((ip >> 8 * 3)) & 0xFF);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("picow", "12345678");
|
||||
|
||||
// modify TTL associated with the domain name (in seconds)
|
||||
// default is 60 seconds
|
||||
dnsServer.setTTL(300);
|
||||
// set which return code will be used for all other domains (e.g. sending
|
||||
// ServerFailure instead of NonExistentDomain will reduce number of queries
|
||||
// sent by clients)
|
||||
// default is DNSReplyCode::NonExistentDomain
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
|
||||
|
||||
// start DNS server for a specific domain name
|
||||
dnsServer.start(DNS_PORT, "www.example.com", apIP);
|
||||
|
||||
// simple HTTP server to see that DNS server is working
|
||||
webServer.onNotFound([]() {
|
||||
String message = "Hello World!\n\n";
|
||||
message += "URI: ";
|
||||
message += webServer.uri();
|
||||
|
||||
webServer.send(200, "text/plain", message);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For DNSServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
DNSServer KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
DNSReplyCode KEYWORD1 DATA_TYPE
|
||||
DNSHeader KEYWORD1 DATA_TYPE
|
||||
DNSServer KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
processNextRequest KEYWORD2
|
||||
setErrorReplyCode KEYWORD2
|
||||
setTTL KEYWORD2
|
||||
start KEYWORD2
|
||||
stop KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
DNS_QR_QUERY LITERAL1 RESERVED_WORD_2
|
||||
DNS_QR_RESPONSE LITERAL1 RESERVED_WORD_2
|
||||
DNS_OPCODE_QUERY LITERAL1 RESERVED_WORD_2
|
||||
MAX_DNSNAME_LENGTH LITERAL1 RESERVED_WORD_2
|
||||
NoError LITERAL1 RESERVED_WORD_2
|
||||
FormError LITERAL1 RESERVED_WORD_2
|
||||
ServerFailure LITERAL1 RESERVED_WORD_2
|
||||
NonExistentDomain LITERAL1 RESERVED_WORD_2
|
||||
NotImplemented LITERAL1 RESERVED_WORD_2
|
||||
Refused LITERAL1 RESERVED_WORD_2
|
||||
YXDomain LITERAL1 RESERVED_WORD_2
|
||||
YXRRSet LITERAL1 RESERVED_WORD_2
|
||||
NXRRSet LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=DNSServer
|
||||
version=1.1.1
|
||||
author=Kristijan Novoselić
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=A simple DNS server for ESP8266, ported to the Pico
|
||||
paragraph=This library implements a simple DNS server.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,447 @@
|
||||
#include "WiFi.h"
|
||||
#include "DNSServer.h"
|
||||
#include <lwip/def.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
extern struct rst_info resetInfo;
|
||||
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define CONSOLE DEBUG_ESP_PORT
|
||||
#else
|
||||
#define CONSOLE Serial
|
||||
#endif
|
||||
|
||||
#define _PRINTF(a, ...) printf(PSTR(a), ##__VA_ARGS__)
|
||||
#define _PRINT(a) print(String(F(a)))
|
||||
#define _PRINTLN(a) println(String(F(a)))
|
||||
#define _PRINTLN2(a, b) println(String(F(a)) + b )
|
||||
|
||||
#define CONSOLE_PRINTF CONSOLE._PRINTF
|
||||
#define CONSOLE_PRINT CONSOLE._PRINT
|
||||
#define CONSOLE_PRINTLN CONSOLE._PRINTLN
|
||||
#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2
|
||||
|
||||
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
#define DEBUG_PRINTF CONSOLE_PRINTF
|
||||
#define DEBUG_PRINT CONSOLE_PRINT
|
||||
#define DEBUG_PRINTLN CONSOLE_PRINTLN
|
||||
#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2
|
||||
#define DBGLOG_FAIL LOG_FAIL
|
||||
|
||||
#define DEBUG_(...) do { (__VA_ARGS__); } while(false)
|
||||
#define DEBUG__(...) __VA_ARGS__
|
||||
#define LOG_FAIL(a, fmt, ...) do { if (!(a)) { CONSOLE.printf( PSTR(fmt " line: %d, function: %s\r\n"), ##__VA_ARGS__, __LINE__, __FUNCTION__ ); } } while(false);
|
||||
|
||||
#else
|
||||
#define DEBUG_PRINTF(...) do { } while(false)
|
||||
#define DEBUG_PRINT(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN2(...) do { } while(false)
|
||||
#define DEBUG_(...) do { } while(false)
|
||||
#define DEBUG__(...) do { } while(false)
|
||||
#define LOG_FAIL(a, ...) do { a; } while(false)
|
||||
#define DBGLOG_FAIL(...) do { } while(false)
|
||||
#endif
|
||||
|
||||
#define DNS_HEADER_SIZE sizeof(DNSHeader)
|
||||
|
||||
// Want to keep IDs unique across restarts and continquious
|
||||
static uint32_t _ids __attribute__((section(".noinit")));
|
||||
|
||||
DNSServer::DNSServer()
|
||||
{
|
||||
// I have observed that using 0 for captive and non-zero (600) when
|
||||
// forwarding, will help Android devices recognize the change in connectivity.
|
||||
// They will then report connected.
|
||||
_ttl = lwip_htonl(60);
|
||||
|
||||
srand(rp2040.getCycleCount());
|
||||
_ids = random(0, (1UL << 16) - 1);
|
||||
|
||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
||||
}
|
||||
|
||||
void DNSServer::disableForwarder(const String &domainName, bool freeResources)
|
||||
{
|
||||
_forwarder = false;
|
||||
if (domainName != "") {
|
||||
_domainName = domainName;
|
||||
downcaseAndRemoveWwwPrefix(_domainName);
|
||||
}
|
||||
if (freeResources) {
|
||||
_dns = (uint32_t)0;
|
||||
if (_que) {
|
||||
_que = nullptr;
|
||||
DEBUG_PRINTF("from stop, deleted _que\r\n");
|
||||
DEBUG_(({
|
||||
if (_que_ov) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que overflow or no reply to request: ", (_que_ov));
|
||||
}
|
||||
if (_que_drop) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que wrapped, reply dropped: ", (_que_drop));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DNSServer::enableForwarder(const String &domainName, const IPAddress &dns)
|
||||
{
|
||||
disableForwarder(domainName, false); // Just happens to have the same logic needed here.
|
||||
|
||||
if (dns.isSet()) {
|
||||
_dns = dns;
|
||||
}
|
||||
|
||||
if (_dns.isSet()) {
|
||||
if (!_que) {
|
||||
_que = std::unique_ptr<DNSS_REQUESTER[]> (new (std::nothrow) DNSS_REQUESTER[kDNSSQueSize]);
|
||||
DEBUG_PRINTF("Created new _que\r\n");
|
||||
if (_que) {
|
||||
for (size_t i = 0; i < kDNSSQueSize; i++) {
|
||||
_que[i].ip = 0;
|
||||
}
|
||||
DEBUG_((_que_ov = 0));
|
||||
DEBUG_((_que_drop = 0));
|
||||
}
|
||||
}
|
||||
if (_que) {
|
||||
_forwarder = true;
|
||||
}
|
||||
}
|
||||
return _forwarder;
|
||||
}
|
||||
|
||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
||||
const IPAddress &resolvedIP, const IPAddress &dns)
|
||||
{
|
||||
_port = (port) ? port : IANA_DNS_PORT;
|
||||
|
||||
_resolvedIP[0] = resolvedIP[0];
|
||||
_resolvedIP[1] = resolvedIP[1];
|
||||
_resolvedIP[2] = resolvedIP[2];
|
||||
_resolvedIP[3] = resolvedIP[3];
|
||||
|
||||
if (!enableForwarder(domainName, dns) && (dns.isSet() || _dns.isSet())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _udp.begin(_port) == 1;
|
||||
}
|
||||
|
||||
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
|
||||
{
|
||||
_errorReplyCode = replyCode;
|
||||
}
|
||||
|
||||
void DNSServer::setTTL(const uint32_t &ttl)
|
||||
{
|
||||
_ttl = lwip_htonl(ttl);
|
||||
}
|
||||
|
||||
uint32_t DNSServer::getTTL()
|
||||
{
|
||||
return lwip_ntohl(_ttl);
|
||||
}
|
||||
|
||||
void DNSServer::stop()
|
||||
{
|
||||
_udp.stop();
|
||||
disableForwarder("", true);
|
||||
}
|
||||
|
||||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
|
||||
{
|
||||
domainName.toLowerCase();
|
||||
if (domainName.startsWith("www."))
|
||||
domainName.remove(0, 4);
|
||||
}
|
||||
|
||||
void DNSServer::forwardReply(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
uint16_t id = dnsHeader->ID;
|
||||
// if (kDNSSQueSize <= (uint16_t)((uint16_t)_ids - id)) {
|
||||
if ((uint16_t)kDNSSQueSize <= (uint16_t)_ids - id) {
|
||||
DEBUG_((++_que_drop));
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" dropped!")));
|
||||
return;
|
||||
}
|
||||
size_t i = id & (kDNSSQueSize - 1);
|
||||
|
||||
// Drop duplicate packets
|
||||
if (0 == _que[i].ip) {
|
||||
DEBUG_PRINTLN2("Duplicate reply dropped ID: 0x", String(id, HEX));
|
||||
return;
|
||||
}
|
||||
dnsHeader->ID = _que[i].id;
|
||||
_udp.beginPacket(_que[i].ip, _que[i].port);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" to ") + IPAddress(_que[i].ip).toString()));
|
||||
_que[i].ip = 0; // This gets used to detect duplicate packets and overflow
|
||||
}
|
||||
|
||||
void DNSServer::forwardRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_dns.isSet() || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
++_ids;
|
||||
size_t i = _ids & (kDNSSQueSize - 1);
|
||||
DEBUG_(({
|
||||
if (0 != _que[i].ip) {
|
||||
++_que_ov;
|
||||
}
|
||||
}));
|
||||
_que[i].ip = _udp.remoteIP();
|
||||
_que[i].port = _udp.remotePort();
|
||||
_que[i].id = dnsHeader->ID;
|
||||
dnsHeader->ID = (uint16_t)_ids;
|
||||
_udp.beginPacket(_dns, IANA_DNS_PORT);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward request ID: 0x", (String(dnsHeader->ID, HEX) + F(" to ") + _dns.toString()));
|
||||
}
|
||||
|
||||
bool DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
DNSHeader *dnsHeader;
|
||||
uint8_t *query, *start;
|
||||
const char *matchString;
|
||||
size_t remaining, labelLength, queryLength;
|
||||
uint16_t qtype, qclass;
|
||||
|
||||
dnsHeader = (DNSHeader *)buffer;
|
||||
|
||||
// Must be a query for us to do anything with it
|
||||
if (dnsHeader->QR != DNS_QR_QUERY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If operation is anything other than query, we don't do it
|
||||
if (dnsHeader->OPCode != DNS_OPCODE_QUERY) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NotImplemented);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only support requests containing single queries - everything else
|
||||
// is badly defined
|
||||
if (dnsHeader->QDCount != lwip_htons(1)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must return a FormError in the case of a non-zero ARCount to
|
||||
// be minimally compatible with EDNS resolvers
|
||||
if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0
|
||||
|| dnsHeader->ARCount != 0) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even if we're not going to use the query, we need to parse it
|
||||
// so we can check the address type that's being queried
|
||||
|
||||
query = start = buffer + DNS_HEADER_SIZE;
|
||||
remaining = length - DNS_HEADER_SIZE;
|
||||
while (remaining != 0 && *start != 0) {
|
||||
labelLength = *start;
|
||||
if (labelLength + 1 > remaining) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
remaining -= (labelLength + 1);
|
||||
start += (labelLength + 1);
|
||||
}
|
||||
|
||||
// 1 octet labelLength, 2 octet qtype, 2 octet qclass
|
||||
if (remaining < 5) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
start += 1; // Skip the 0 length label that we found above
|
||||
|
||||
memcpy(&qtype, start, sizeof(qtype));
|
||||
start += 2;
|
||||
memcpy(&qclass, start, sizeof(qclass));
|
||||
start += 2;
|
||||
|
||||
queryLength = start - query;
|
||||
|
||||
if (qclass != lwip_htons(DNS_QCLASS_ANY)
|
||||
&& qclass != lwip_htons(DNS_QCLASS_IN)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qtype != lwip_htons(DNS_QTYPE_A)
|
||||
&& qtype != lwip_htons(DNS_QTYPE_ANY)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have no domain name configured, just return an error
|
||||
if (_domainName == "") {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're running with a wildcard we can just return a result now
|
||||
if (_domainName == "*") {
|
||||
DEBUG_PRINTF("dnsServer - replyWithIP\r\n");
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
matchString = _domainName.c_str();
|
||||
|
||||
start = query;
|
||||
|
||||
// If there's a leading 'www', skip it
|
||||
if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0)
|
||||
start += 4;
|
||||
|
||||
while (*start != 0) {
|
||||
labelLength = *start;
|
||||
start += 1;
|
||||
while (labelLength > 0) {
|
||||
if (tolower(*start) != *matchString) {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++start;
|
||||
++matchString;
|
||||
--labelLength;
|
||||
}
|
||||
if (*start == 0 && *matchString == '\0') {
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*matchString != '.') {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
++matchString;
|
||||
}
|
||||
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DNSServer::processNextRequest()
|
||||
{
|
||||
size_t currentPacketSize;
|
||||
|
||||
currentPacketSize = _udp.parsePacket();
|
||||
if (currentPacketSize == 0)
|
||||
return;
|
||||
|
||||
// The DNS RFC requires that DNS packets be less than 512 bytes in size,
|
||||
// so just discard them if they are larger
|
||||
if (currentPacketSize > MAX_DNS_PACKETSIZE)
|
||||
return;
|
||||
|
||||
// If the packet size is smaller than the DNS header, then someone is
|
||||
// messing with us
|
||||
if (currentPacketSize < DNS_HEADER_SIZE)
|
||||
return;
|
||||
|
||||
std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[currentPacketSize]);
|
||||
if (buffer == nullptr)
|
||||
return;
|
||||
|
||||
_udp.read(buffer.get(), currentPacketSize);
|
||||
if (_dns.isSet() && _udp.remoteIP() == _dns) {
|
||||
// _forwarder may have been set to false; however, for now allow in-flight
|
||||
// replies to finish. //??
|
||||
forwardReply(buffer.get(), currentPacketSize);
|
||||
} else
|
||||
if (respondToRequest(buffer.get(), currentPacketSize)) {
|
||||
forwardRequest(buffer.get(), currentPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSServer::writeNBOShort(uint16_t value)
|
||||
{
|
||||
_udp.write((unsigned char *)&value, 2);
|
||||
}
|
||||
|
||||
void DNSServer::replyWithIP(DNSHeader *dnsHeader,
|
||||
unsigned char * query,
|
||||
size_t queryLength)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
dnsHeader->QDCount = lwip_htons(1);
|
||||
dnsHeader->ANCount = lwip_htons(1);
|
||||
dnsHeader->NSCount = 0;
|
||||
dnsHeader->ARCount = 0;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader));
|
||||
_udp.write(query, queryLength);
|
||||
|
||||
// Rather than restate the name here, we use a pointer to the name contained
|
||||
// in the query section. Pointers have the top two bits set.
|
||||
value = 0xC000 | DNS_HEADER_SIZE;
|
||||
writeNBOShort(lwip_htons(value));
|
||||
|
||||
// Answer is type A (an IPv4 address)
|
||||
writeNBOShort(lwip_htons(DNS_QTYPE_A));
|
||||
|
||||
// Answer is in the Internet Class
|
||||
writeNBOShort(lwip_htons(DNS_QCLASS_IN));
|
||||
|
||||
// Output TTL (already NBO)
|
||||
_udp.write((unsigned char*)&_ttl, 4);
|
||||
|
||||
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
|
||||
writeNBOShort(lwip_htons(sizeof(_resolvedIP)));
|
||||
_udp.write(_resolvedIP, sizeof(_resolvedIP));
|
||||
_udp.endPacket();
|
||||
}
|
||||
|
||||
void DNSServer::replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode,
|
||||
unsigned char *query,
|
||||
size_t queryLength)
|
||||
{
|
||||
dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
dnsHeader->RCode = (unsigned char) rcode;
|
||||
if (query)
|
||||
dnsHeader->QDCount = lwip_htons(1);
|
||||
else
|
||||
dnsHeader->QDCount = 0;
|
||||
dnsHeader->ANCount = 0;
|
||||
dnsHeader->NSCount = 0;
|
||||
dnsHeader->ARCount = 0;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader));
|
||||
if (query != NULL)
|
||||
_udp.write(query, queryLength);
|
||||
_udp.endPacket();
|
||||
}
|
||||
|
||||
void DNSServer::replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode)
|
||||
{
|
||||
replyWithError(dnsHeader, rcode, NULL, 0);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#ifndef DNSServer_h
|
||||
#define DNSServer_h
|
||||
|
||||
#include <memory>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
// #define DEBUG_DNSSERVER
|
||||
|
||||
// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
|
||||
#ifndef IANA_DNS_PORT
|
||||
#define IANA_DNS_PORT 53 // AKA domain
|
||||
constexpr inline uint16_t kIanaDnsPort = IANA_DNS_PORT;
|
||||
#endif
|
||||
|
||||
#define DNS_QR_QUERY 0
|
||||
#define DNS_QR_RESPONSE 1
|
||||
#define DNS_OPCODE_QUERY 0
|
||||
|
||||
#define DNS_QCLASS_IN 1
|
||||
#define DNS_QCLASS_ANY 255
|
||||
|
||||
#define DNS_QTYPE_A 1
|
||||
#define DNS_QTYPE_ANY 255
|
||||
|
||||
#define MAX_DNSNAME_LENGTH 253
|
||||
#define MAX_DNS_PACKETSIZE 512
|
||||
|
||||
enum class DNSReplyCode
|
||||
{
|
||||
NoError = 0,
|
||||
FormError = 1,
|
||||
ServerFailure = 2,
|
||||
NonExistentDomain = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
YXDomain = 6,
|
||||
YXRRSet = 7,
|
||||
NXRRSet = 8
|
||||
};
|
||||
|
||||
struct DNSHeader
|
||||
{
|
||||
uint16_t ID; // identification number
|
||||
unsigned char RD : 1; // recursion desired
|
||||
unsigned char TC : 1; // truncated message
|
||||
unsigned char AA : 1; // authoritative answer
|
||||
unsigned char OPCode : 4; // message_type
|
||||
unsigned char QR : 1; // query/response flag
|
||||
unsigned char RCode : 4; // response code
|
||||
unsigned char Z : 3; // its z! reserved
|
||||
unsigned char RA : 1; // recursion available
|
||||
uint16_t QDCount; // number of question entries
|
||||
uint16_t ANCount; // number of answer entries
|
||||
uint16_t NSCount; // number of authority entries
|
||||
uint16_t ARCount; // number of resource entries
|
||||
};
|
||||
|
||||
constexpr inline size_t kDNSSQueSizeAddrBits = 3; // The number of bits used to address que entries
|
||||
constexpr inline size_t kDNSSQueSize = (1UL << (kDNSSQueSizeAddrBits));
|
||||
|
||||
struct DNSS_REQUESTER {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
uint16_t id;
|
||||
};
|
||||
|
||||
class DNSServer
|
||||
{
|
||||
public:
|
||||
DNSServer();
|
||||
~DNSServer() {
|
||||
stop();
|
||||
};
|
||||
/*
|
||||
If specified, `enableForwarder` will update the `domainName` that is used
|
||||
to match DNS request to this AP's IP Address. A non-matching request will
|
||||
be forwarded to the DNS server specified by `dns`.
|
||||
|
||||
Returns `true` on success.
|
||||
|
||||
Returns `false`,
|
||||
* when forwarding `dns` is not set, or
|
||||
* unable to allocate resources for managing the DNS forward function.
|
||||
*/
|
||||
bool enableForwarder(const String &domainName = String(""), const IPAddress &dns = (uint32_t)0);
|
||||
/*
|
||||
`disableForwarder` will stop forwarding DNS requests. If specified,
|
||||
updates the `domainName` that is matched for returning this AP's IP Address.
|
||||
Optionally, resources used for the DNS forward function can be freed.
|
||||
*/
|
||||
void disableForwarder(const String &domainName = String(""), bool freeResources = false);
|
||||
bool isForwarding() { return _forwarder && _dns.isSet(); }
|
||||
void setDNS(const IPAddress& dns) { _dns = dns; }
|
||||
IPAddress getDNS() { return _dns; }
|
||||
bool isDNSSet() { return _dns.isSet(); }
|
||||
|
||||
void processNextRequest();
|
||||
void setErrorReplyCode(const DNSReplyCode &replyCode);
|
||||
void setTTL(const uint32_t &ttl);
|
||||
uint32_t getTTL();
|
||||
String getDomainName() { return _domainName; }
|
||||
|
||||
// Returns true if successful, false if there are no sockets available
|
||||
bool start(const uint16_t &port,
|
||||
const String &domainName,
|
||||
const IPAddress &resolvedIP,
|
||||
const IPAddress &dns = (uint32_t)0);
|
||||
// stops the DNS server
|
||||
void stop();
|
||||
|
||||
private:
|
||||
WiFiUDP _udp;
|
||||
String _domainName;
|
||||
IPAddress _dns;
|
||||
std::unique_ptr<DNSS_REQUESTER[]> _que;
|
||||
uint32_t _ttl;
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
// There are 2 possibilities for overflow:
|
||||
// 1) we have more than kDNSSQueSize request already outstanding.
|
||||
// 2) we have request that never received a reply.
|
||||
uint32_t _que_ov;
|
||||
uint32_t _que_drop;
|
||||
#endif
|
||||
DNSReplyCode _errorReplyCode;
|
||||
bool _forwarder;
|
||||
unsigned char _resolvedIP[4];
|
||||
uint16_t _port;
|
||||
|
||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
||||
void replyWithIP(DNSHeader *dnsHeader,
|
||||
unsigned char * query,
|
||||
size_t queryLength);
|
||||
void replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode,
|
||||
unsigned char *query,
|
||||
size_t queryLength);
|
||||
void replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode);
|
||||
bool respondToRequest(uint8_t *buffer, size_t length);
|
||||
void forwardRequest(uint8_t *buffer, size_t length);
|
||||
void forwardReply(uint8_t *buffer, size_t length);
|
||||
void writeNBOShort(uint16_t value);
|
||||
};
|
||||
#endif
|
||||
@@ -62,6 +62,10 @@ public:
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T &update(int const address, const T &t) {
|
||||
return put(address, t);
|
||||
}
|
||||
size_t length() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
Submodule libraries/FreeRTOS/lib/FreeRTOS-Kernel updated: f5fe79dacd...b5942ddd07
@@ -26,6 +26,7 @@
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||
#define configSTACK_DEPTH_TYPE uint32_t
|
||||
#define configUSE_TASK_PREEMPTION_DISABLE 1
|
||||
|
||||
#define configUSE_NEWLIB_REENTRANT 1
|
||||
#define configNEWLIB_REENTRANT_IS_DYNAMIC 0 /* Note that we have a different config option, portSET_IMPURE_PTR */
|
||||
@@ -34,8 +35,8 @@ extern void __register_impure_ptr(struct _reent *p);
|
||||
#define portSET_IMPURE_PTR(x) __register_impure_ptr(x)
|
||||
|
||||
/* Run time stats related definitions. */
|
||||
void vMainConfigureTimerForRunTimeStats( void );
|
||||
unsigned long ulMainGetRunTimeCounterValue( void );
|
||||
void vMainConfigureTimerForRunTimeStats(void);
|
||||
unsigned long ulMainGetRunTimeCounterValue(void);
|
||||
#define configGENERATE_RUN_TIME_STATS 1
|
||||
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() //vMainConfigureTimerForRunTimeStats()
|
||||
#define portGET_RUN_TIME_COUNTER_VALUE() ulMainGetRunTimeCounterValue()
|
||||
@@ -48,10 +49,10 @@ unsigned long ulMainGetRunTimeCounterValue( void );
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 5
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 80 )
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 1024 )
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
@@ -62,10 +63,10 @@ to exclude the API function. */
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
|
||||
/* This demo makes use of one or more example stats formatting functions. These
|
||||
format the raw data provided by the uxTaskGetSystemState() function in to human
|
||||
readable ASCII form. See the notes in the implementation of vTaskList() within
|
||||
FreeRTOS/Source/tasks.c for limitations. */
|
||||
/* This demo makes use of one or more example stats formatting functions. These
|
||||
format the raw data provided by the uxTaskGetSystemState() function in to human
|
||||
readable ASCII form. See the notes in the implementation of vTaskList() within
|
||||
FreeRTOS/Source/tasks.c for limitations. */
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
|
||||
|
||||
#define configUSE_MUTEXES 1
|
||||
@@ -73,50 +74,50 @@ FreeRTOS/Source/tasks.c for limitations. */
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 2
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
|
||||
/* Cortex-M specific definitions. */
|
||||
#undef __NVIC_PRIO_BITS
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#define configPRIO_BITS 3 /* 8 priority levels */
|
||||
#define configPRIO_BITS 3 /* 8 priority levels */
|
||||
#endif
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x7
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
|
||||
extern void rtosFatalError(void);
|
||||
#define configASSERT( x ) \
|
||||
if( ( x ) == 0 ) { portDISABLE_INTERRUPTS(); rtosFatalError(); }
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names - or at least those used in the unmodified vector table. */
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names - or at least those used in the unmodified vector table. */
|
||||
//#define xPortPendSVHandler PendSV_Handler
|
||||
//#define xPortSysTickHandler SysTick_Handler
|
||||
//#define vPortSVCHandler SVC_Handler
|
||||
|
||||
/* The size of the global output buffer that is available for use when there
|
||||
are multiple command interpreters running at once (for example, one on a UART
|
||||
and one on TCP/IP). This is done to prevent an output buffer being defined by
|
||||
each implementation - which would waste RAM. In this case, there is only one
|
||||
command interpreter running. */
|
||||
/* The size of the global output buffer that is available for use when there
|
||||
are multiple command interpreters running at once (for example, one on a UART
|
||||
and one on TCP/IP). This is done to prevent an output buffer being defined by
|
||||
each implementation - which would waste RAM. In this case, there is only one
|
||||
command interpreter running. */
|
||||
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048
|
||||
|
||||
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Phillip Stevens All Rights Reserved.
|
||||
* Modifications by Earle F. Philhower, III, for Arduino-Pico
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* This file is NOT part of the FreeRTOS distribution.
|
||||
*
|
||||
*/
|
||||
Copyright (C) 2021 Phillip Stevens All Rights Reserved.
|
||||
Modifications by Earle F. Philhower, III, for Arduino-Pico
|
||||
|
||||
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.
|
||||
|
||||
|
||||
This file is NOT part of the FreeRTOS distribution.
|
||||
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Arduino Core includes */
|
||||
#include <Arduino.h>
|
||||
#include <RP2040USB.h>
|
||||
#include "tusb.h"
|
||||
#define USB_TASK_IRQ 31
|
||||
|
||||
|
||||
/* Raspberry PI Pico includes */
|
||||
@@ -43,8 +42,7 @@
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void initFreeRTOS(void)
|
||||
{
|
||||
void initFreeRTOS(void) {
|
||||
}
|
||||
|
||||
extern void setup() __attribute__((weak));
|
||||
@@ -55,8 +53,7 @@ extern void loop1() __attribute__((weak));
|
||||
extern void __loop();
|
||||
volatile bool __usbInitted = false;
|
||||
|
||||
static void __core0(void *params)
|
||||
{
|
||||
static void __core0(void *params) {
|
||||
(void) params;
|
||||
#ifndef NO_USB
|
||||
while (!__usbInitted) {
|
||||
@@ -78,8 +75,7 @@ static void __core0(void *params)
|
||||
}
|
||||
}
|
||||
|
||||
static void __core1(void *params)
|
||||
{
|
||||
static void __core1(void *params) {
|
||||
(void) params;
|
||||
#ifndef NO_USB
|
||||
while (!__usbInitted) {
|
||||
@@ -98,7 +94,7 @@ static void __core1(void *params)
|
||||
vTaskDelay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void delay(unsigned long ms) {
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
@@ -112,17 +108,16 @@ extern mutex_t __usb_mutex;
|
||||
static TaskHandle_t __usbTask;
|
||||
static void __usb(void *param);
|
||||
|
||||
void startFreeRTOS(void)
|
||||
{
|
||||
void startFreeRTOS(void) {
|
||||
|
||||
TaskHandle_t c0;
|
||||
xTaskCreate(__core0, "CORE0", 1024, 0, configMAX_PRIORITIES / 2, &c0);
|
||||
vTaskCoreAffinitySet( c0, 1 << 0 );
|
||||
vTaskCoreAffinitySet(c0, 1 << 0);
|
||||
|
||||
if (setup1 || loop1) {
|
||||
TaskHandle_t c1;
|
||||
xTaskCreate(__core1, "CORE1", 1024, 0, configMAX_PRIORITIES / 2, &c1);
|
||||
vTaskCoreAffinitySet( c1, 1 << 1 );
|
||||
vTaskCoreAffinitySet(c1, 1 << 1);
|
||||
}
|
||||
|
||||
// Initialise and run the freeRTOS scheduler. Execution should never return here.
|
||||
@@ -136,33 +131,30 @@ void startFreeRTOS(void)
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void prvDisableInterrupts()
|
||||
{
|
||||
void prvDisableInterrupts() {
|
||||
portDISABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
void prvEnableInterrupts()
|
||||
{
|
||||
void prvEnableInterrupts() {
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
#if ( configUSE_IDLE_HOOK == 1 )
|
||||
/*
|
||||
* Call the user defined loop() function from within the idle task.
|
||||
* This allows the application designer to add background functionality
|
||||
* without the overhead of a separate task.
|
||||
*
|
||||
* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
*
|
||||
*/
|
||||
Call the user defined loop() function from within the idle task.
|
||||
This allows the application designer to add background functionality
|
||||
without the overhead of a separate task.
|
||||
|
||||
NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
|
||||
*/
|
||||
|
||||
extern "C"
|
||||
void vApplicationIdleHook( void ) __attribute__((weak));
|
||||
void vApplicationIdleHook(void) __attribute__((weak));
|
||||
|
||||
|
||||
void vApplicationIdleHook( void )
|
||||
{
|
||||
void vApplicationIdleHook(void) {
|
||||
__wfe(); // Low power idle if nothing to do...
|
||||
}
|
||||
|
||||
@@ -171,22 +163,21 @@ void vApplicationIdleHook( void )
|
||||
|
||||
#if ( configUSE_MINIMAL_IDLE_HOOK == 1 )
|
||||
/*
|
||||
* Call the user defined minimalIdle() function from within the idle task.
|
||||
* This allows the application designer to add background functionality
|
||||
* without the overhead of a separate task.
|
||||
*
|
||||
* NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
*
|
||||
*/
|
||||
void minimalIdle( void ) __attribute__((weak));
|
||||
Call the user defined minimalIdle() function from within the idle task.
|
||||
This allows the application designer to add background functionality
|
||||
without the overhead of a separate task.
|
||||
|
||||
NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
|
||||
*/
|
||||
void minimalIdle(void) __attribute__((weak));
|
||||
void minimalIdle() {} //Empty minimalIdle function
|
||||
|
||||
extern "C"
|
||||
void vApplicationMinimalIdleHook( void ) __attribute__((weak));
|
||||
void vApplicationMinimalIdleHook(void) __attribute__((weak));
|
||||
|
||||
void vApplicationMinimalIdleHook( void )
|
||||
{
|
||||
minimalIdle();
|
||||
void vApplicationMinimalIdleHook(void) {
|
||||
minimalIdle();
|
||||
}
|
||||
|
||||
#endif /* configUSE_MINIMAL_IDLE_HOOK == 1 */
|
||||
@@ -194,22 +185,21 @@ void vApplicationMinimalIdleHook( void )
|
||||
|
||||
#if ( configUSE_TICK_HOOK == 1 )
|
||||
/*
|
||||
* Call the user defined minimalIdle() function from within the idle task.
|
||||
* This allows the application designer to add background functionality
|
||||
* without the overhead of a separate task.
|
||||
*
|
||||
* NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
*
|
||||
*/
|
||||
void tick( void ) __attribute__((weak));
|
||||
Call the user defined minimalIdle() function from within the idle task.
|
||||
This allows the application designer to add background functionality
|
||||
without the overhead of a separate task.
|
||||
|
||||
NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
|
||||
|
||||
*/
|
||||
void tick(void) __attribute__((weak));
|
||||
void tick() {} //Empty minimalIdle function
|
||||
|
||||
extern "C"
|
||||
void vApplicationTickHook( void ) __attribute__((weak));
|
||||
void vApplicationTickHook(void) __attribute__((weak));
|
||||
|
||||
void vApplicationTickHook( void )
|
||||
{
|
||||
tick();
|
||||
void vApplicationTickHook(void) {
|
||||
tick();
|
||||
}
|
||||
|
||||
#endif /* configUSE_TICK_HOOK == 1 */
|
||||
@@ -218,37 +208,33 @@ void vApplicationTickHook( void )
|
||||
#if ( configUSE_MALLOC_FAILED_HOOK == 1 || configCHECK_FOR_STACK_OVERFLOW >= 1 || configDEFAULT_ASSERT == 1 )
|
||||
|
||||
/**
|
||||
* Private function to enable board led to use it in application hooks
|
||||
*/
|
||||
void prvSetMainLedOn( void )
|
||||
{
|
||||
gpio_init(LED_BUILTIN);
|
||||
gpio_set_dir(LED_BUILTIN, true);
|
||||
gpio_put(LED_BUILTIN, true);
|
||||
Private function to enable board led to use it in application hooks
|
||||
*/
|
||||
void prvSetMainLedOn(void) {
|
||||
gpio_init(LED_BUILTIN);
|
||||
gpio_set_dir(LED_BUILTIN, true);
|
||||
gpio_put(LED_BUILTIN, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private function to blink board led to use it in application hooks
|
||||
*/
|
||||
void prvBlinkMainLed( void )
|
||||
{
|
||||
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
|
||||
Private function to blink board led to use it in application hooks
|
||||
*/
|
||||
void prvBlinkMainLed(void) {
|
||||
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Usage:
|
||||
/* ---------------------------------------------------------------------------*\
|
||||
Usage:
|
||||
called on fatal error (interrupts disabled already)
|
||||
\*---------------------------------------------------------------------------*/
|
||||
\*---------------------------------------------------------------------------*/
|
||||
extern "C"
|
||||
void rtosFatalError(void)
|
||||
{
|
||||
void rtosFatalError(void) {
|
||||
prvSetMainLedOn(); // Main LED on.
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Main LED slow flash
|
||||
|
||||
for (;;) {
|
||||
// Main LED slow flash
|
||||
sleep_ms(100);
|
||||
prvBlinkMainLed();
|
||||
sleep_ms(2000);
|
||||
@@ -257,30 +243,28 @@ void rtosFatalError(void)
|
||||
}
|
||||
|
||||
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Usage:
|
||||
/* ---------------------------------------------------------------------------*\
|
||||
Usage:
|
||||
called by task system when a malloc failure is noticed
|
||||
Description:
|
||||
Description:
|
||||
Malloc failure handler -- Shut down all interrupts, send serious complaint
|
||||
to command port. FAST Blink on main LED.
|
||||
Arguments:
|
||||
Arguments:
|
||||
pxTask - pointer to task handle
|
||||
pcTaskName - pointer to task name
|
||||
Results:
|
||||
Results:
|
||||
<none>
|
||||
Notes:
|
||||
Notes:
|
||||
This routine will never return.
|
||||
This routine is referenced in the task.c file of FreeRTOS as an extern.
|
||||
\*---------------------------------------------------------------------------*/
|
||||
\*---------------------------------------------------------------------------*/
|
||||
extern "C"
|
||||
void vApplicationMallocFailedHook( void ) __attribute__((weak));
|
||||
void vApplicationMallocFailedHook(void) __attribute__((weak));
|
||||
|
||||
void vApplicationMallocFailedHook( void )
|
||||
{
|
||||
void vApplicationMallocFailedHook(void) {
|
||||
prvSetMainLedOn(); // Main LED on.
|
||||
|
||||
for(;;)
|
||||
{
|
||||
|
||||
for (;;) {
|
||||
sleep_ms(50);
|
||||
prvBlinkMainLed(); // Main LED fast blink.
|
||||
}
|
||||
@@ -293,16 +277,14 @@ void vApplicationMallocFailedHook( void )
|
||||
#if ( configCHECK_FOR_STACK_OVERFLOW >= 1 )
|
||||
|
||||
extern "C"
|
||||
void vApplicationStackOverflowHook( TaskHandle_t xTask,
|
||||
char * pcTaskName ) __attribute__((weak));
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask,
|
||||
char * pcTaskName) __attribute__((weak));
|
||||
|
||||
void vApplicationStackOverflowHook( TaskHandle_t xTask __attribute__((unused)),
|
||||
char * pcTaskName __attribute__((unused)) )
|
||||
{
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask __attribute__((unused)),
|
||||
char * pcTaskName __attribute__((unused))) {
|
||||
prvSetMainLedOn(); // Main LED on.
|
||||
|
||||
for(;;)
|
||||
{
|
||||
for (;;) {
|
||||
sleep_ms(2000);
|
||||
prvBlinkMainLed(); // Main LED slow blink.
|
||||
}
|
||||
@@ -314,14 +296,13 @@ void vApplicationStackOverflowHook( TaskHandle_t xTask __attribute__((unused)),
|
||||
#if ( configSUPPORT_STATIC_ALLOCATION >= 1 )
|
||||
|
||||
extern "C"
|
||||
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
|
||||
StackType_t ** ppxIdleTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize ) __attribute__((weak));
|
||||
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
|
||||
StackType_t ** ppxIdleTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize) __attribute__((weak));
|
||||
|
||||
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
|
||||
StackType_t ** ppxIdleTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize )
|
||||
{
|
||||
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
|
||||
StackType_t ** ppxIdleTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize) {
|
||||
static StaticTask_t xIdleTaskTCB;
|
||||
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
|
||||
|
||||
@@ -333,14 +314,13 @@ void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
|
||||
#if ( configUSE_TIMERS >= 1 )
|
||||
|
||||
extern "C"
|
||||
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
|
||||
StackType_t ** ppxTimerTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize ) __attribute__((weak));
|
||||
void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,
|
||||
StackType_t ** ppxTimerTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize) __attribute__((weak));
|
||||
|
||||
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
|
||||
StackType_t ** ppxTimerTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize )
|
||||
{
|
||||
void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,
|
||||
StackType_t ** ppxTimerTaskStackBuffer,
|
||||
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize) {
|
||||
static StaticTask_t xTimerTaskTCB;
|
||||
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
|
||||
|
||||
@@ -354,8 +334,8 @@ void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
|
||||
#endif /* configSUPPORT_STATIC_ALLOCATION >= 1 */
|
||||
|
||||
/**
|
||||
* configASSERT default implementation
|
||||
*/
|
||||
configASSERT default implementation
|
||||
*/
|
||||
#if configDEFAULT_ASSERT == 1
|
||||
|
||||
extern "C"
|
||||
@@ -364,8 +344,7 @@ void vApplicationAssertHook() {
|
||||
taskDISABLE_INTERRUPTS(); // Disable task interrupts
|
||||
|
||||
prvSetMainLedOn(); // Main LED on.
|
||||
for(;;)
|
||||
{
|
||||
for (;;) {
|
||||
sleep_ms(100);
|
||||
prvBlinkMainLed(); // Led off.
|
||||
|
||||
@@ -382,19 +361,10 @@ void vApplicationAssertHook() {
|
||||
#endif
|
||||
|
||||
|
||||
static void __usb_irq() {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
vTaskNotifyGiveFromISR( __usbTask, &xHigherPriorityTaskWoken );
|
||||
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||
}
|
||||
|
||||
static void __usb(void *param)
|
||||
{
|
||||
static void __usb(void *param) {
|
||||
(void) param;
|
||||
|
||||
tusb_init();
|
||||
irq_set_exclusive_handler(USB_TASK_IRQ, __usb_irq);
|
||||
irq_set_enabled(USB_TASK_IRQ, true);
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
@@ -412,8 +382,7 @@ static void __usb(void *param)
|
||||
extern void __SetupDescHIDReport();
|
||||
extern void __SetupUSBDescriptor();
|
||||
|
||||
void __USBStart()
|
||||
{
|
||||
void __USBStart() {
|
||||
mutex_init(&__usb_mutex);
|
||||
|
||||
__SetupDescHIDReport();
|
||||
@@ -421,5 +390,5 @@ void __USBStart()
|
||||
|
||||
// Make highest prio and locked to core 0
|
||||
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
|
||||
vTaskCoreAffinitySet( __usbTask, 1 << 0 );
|
||||
vTaskCoreAffinitySet(__usbTask, 1 << 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
Authorization.ino
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
http.setInsecure();
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
// configure traged server and url
|
||||
|
||||
|
||||
http.begin("https://guest:guest@jigsaw.w3.org/HTTP/Basic/");
|
||||
|
||||
/*
|
||||
// or
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/");
|
||||
http.setAuthorization("guest", "guest");
|
||||
|
||||
// or
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/");
|
||||
http.setAuthorization("Z3Vlc3Q6Z3Vlc3Q=");
|
||||
*/
|
||||
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
BasicHTTPClient.ino
|
||||
|
||||
Created on: 24.05.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
if (http.begin("http://httpbin.org")) { // HTTP
|
||||
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
Serial.printf("[HTTP} Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
BasicHTTPSClient-Hard.ino
|
||||
|
||||
Demonstrates the manual way of making a WiFiClient and passing it in to the HTTPClient
|
||||
|
||||
Created on: 20.08.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure(); // Not safe against MITM attacks
|
||||
|
||||
HTTPClient https;
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
if (https.begin(client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = https.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("[HTTPS] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
BasicHTTPSClient.ino
|
||||
|
||||
Created on: 20.08.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
const char *jigsaw_cert = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFKTCCBM+gAwIBAgIQAbTKhAICxb7iDJbE6qU/NzAKBggqhkjOPQQDAjBKMQsw
|
||||
CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
|
||||
Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjIwMzE3MDAwMDAwWhcNMjMwMzE2
|
||||
MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
|
||||
A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
|
||||
MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
|
||||
zj0DAQcDQgAEYnkGDyrIltjRnxoVdy/xgndo+WGMOASzs2hHeCjbJ1KplKJc/ciK
|
||||
XCWq/4+pTzSiVgTFhRmCdLcU1Fa05YFNQaOCA2owggNmMB8GA1UdIwQYMBaAFKXO
|
||||
N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBRIzOWGCDBB/PMrMucSrjIKqlgE
|
||||
uDAvBgNVHREEKDAmghVzbmkuY2xvdWRmbGFyZXNzbC5jb22CDWppZ3Nhdy53My5v
|
||||
cmcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vQ2xv
|
||||
dWRmbGFyZUluY0VDQ0NBLTMuY3JsMDegNaAzhjFodHRwOi8vY3JsNC5kaWdpY2Vy
|
||||
dC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EM
|
||||
AQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB2
|
||||
BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
|
||||
LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Ns
|
||||
b3VkZmxhcmVJbmNFQ0NDQS0zLmNydDAMBgNVHRMBAf8EAjAAMIIBfwYKKwYBBAHW
|
||||
eQIEAgSCAW8EggFrAWkAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9
|
||||
bgAAAX+aFPh6AAAEAwBGMEQCICivjuh2ywUYvVpTKHo65JEheR8dFq8QvBgEiXfw
|
||||
m6q6AiAkxAgz77oboGQGetNmab45+peY+nAGOfyW9vi9S1gMaAB3ADXPGRu/sWxX
|
||||
vw+tTG1Cy7u2JyAmUeo/4SrvqAPDO9ZMAAABf5oU+GEAAAQDAEgwRgIhANKeTNMy
|
||||
GqUsCo7ph7YMWzrhMuDeyP8xPSiCtFzKcn/eAiEAyv5lgCUQ6K14V13zYfL99wZD
|
||||
LFcIP/KZ1y7nuPAksTAAdwCzc3cH4YRQ+GOG1gWp3BEJSnktsWcMC4fc8AMOeTal
|
||||
mgAAAX+aFPiWAAAEAwBIMEYCIQD6535jWw776D4vjyupP2fBw26CBMpVT5++k4rR
|
||||
xqeOXwIhAIbEaEKkEq6JtpWWfVpTyDkMpMfTuiqYVe6REy2XsmEhMAoGCCqGSM49
|
||||
BAMCA0gAMEUCIH3r/puXZcX1bfUoBq2njuHe0bxWtvzDaz5k6WLYrazTAiEA+ePL
|
||||
N6K5xrmaof185pVCxACPLc/BoKyUwMeC8iXCm00=
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
HTTPClient https;
|
||||
switch (cnt) {
|
||||
case 0:
|
||||
Serial.println("[HTTPS] using insecure SSL, not validating certificate");
|
||||
https.setInsecure(); // Note this is unsafe against MITM attacks
|
||||
cnt++;
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("[HTTPS] using secure SSL, validating certificate");
|
||||
https.setCACert(jigsaw_cert);
|
||||
cnt++;
|
||||
break;
|
||||
default:
|
||||
Serial.println("[HTTPS] not setting any SSL verification settings, will fail");
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
if (https.begin("https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = https.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("[HTTPS] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
ChunkedClient.ino
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
if (http.begin("http://anglesharp.azurewebsites.net/Chunked")) {
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
Serial.printf("[HTTP] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait forever...");
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
This sketch shows how to handle HTTP Digest Authorization.
|
||||
|
||||
Written by Parham Alvani and Sajjad Rahnama, 2018-01-07.
|
||||
|
||||
This example is released into public domain,
|
||||
or, at your option, CC0 licensed.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "NOBABIES"
|
||||
#define STAPSK "ElephantsAreGreat"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssidPassword = STAPSK;
|
||||
|
||||
const char* username = "admin";
|
||||
const char* password = "admin";
|
||||
|
||||
const char* server = "http://httpbin.org";
|
||||
const char* uri = "/digest-auth/auth/admin/admin/MD5";
|
||||
|
||||
String exractParam(String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1) {
|
||||
return "";
|
||||
}
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
String getCNonce(const int len) {
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s = "";
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter) {
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String realm = exractParam(authReq, "realm=\"", '"');
|
||||
String nonce = exractParam(authReq, "nonce=\"", '"');
|
||||
String cNonce = getCNonce(8);
|
||||
|
||||
char nc[9];
|
||||
snprintf(nc, sizeof(nc), "%08x", counter);
|
||||
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.add(username + ":" + realm + ":" + password); // md5 of the user:realm:user
|
||||
md5.calculate();
|
||||
String h1 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(method + ":" + uri);
|
||||
md5.calculate();
|
||||
String h2 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(h1 + ":" + nonce + ":" + String(nc) + ":" + cNonce + ":" + "auth" + ":" + h2);
|
||||
md5.calculate();
|
||||
String response = md5.toString();
|
||||
|
||||
String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\"";
|
||||
Serial.println(authorization);
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, ssidPassword);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
srand(rp2040.getCycleCount());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
|
||||
// configure target server and url
|
||||
http.begin(String(server) + String(uri));
|
||||
|
||||
|
||||
const char* keys[] = { "WWW-Authenticate" };
|
||||
http.collectHeaders(keys, 1);
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode > 0) {
|
||||
String authReq = http.header("WWW-Authenticate");
|
||||
Serial.println(authReq);
|
||||
|
||||
String authorization = getDigestAuth(authReq, String(username), String(password), "GET", String(uri), 1);
|
||||
|
||||
http.end();
|
||||
http.begin(String(server) + String(uri));
|
||||
|
||||
http.addHeader("Authorization", authorization);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
PostHTTPClient.ino
|
||||
|
||||
Created on: 21.11.2016
|
||||
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
WiFi.begin(STASSID, STAPSK);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected! IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
http.setInsecure();
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
// configure target server and url
|
||||
http.begin("https://httpbin.org/post");
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
|
||||
Serial.print("[HTTP] POST...\n");
|
||||
// start connection and send HTTP header and body
|
||||
int httpCode = http.POST("{\"hello\":\"world\"}");
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
const String& payload = http.getString();
|
||||
Serial.println("received payload:\n<<");
|
||||
Serial.println(payload);
|
||||
Serial.println(">>");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
reuseConnectionV2.ino
|
||||
|
||||
Created on: 22.11.2015
|
||||
|
||||
This example reuses the http connection and also restores the connection if the connection is lost
|
||||
*/
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println("Connecting to WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(STASSID, STAPSK);
|
||||
|
||||
// wait for WiFi connection
|
||||
while ((WiFiMulti.run() != WL_CONNECTED)) {
|
||||
Serial.write('.');
|
||||
delay(500);
|
||||
}
|
||||
Serial.println(" connected to WiFi");
|
||||
|
||||
// allow reuse (if server supports it)
|
||||
http.setReuse(true);
|
||||
http.setInsecure();
|
||||
|
||||
http.begin("https://jigsaw.w3.org/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
int pass = 0;
|
||||
|
||||
void loop() {
|
||||
// First 10 loop()s, retrieve the URL
|
||||
if (pass < 10) {
|
||||
pass++;
|
||||
Serial.printf("Reuse connection example, GET url for the %d time\n", pass);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
http.writeToStream(&Serial);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
// Something went wrong with the connection, try to reconnect
|
||||
http.end();
|
||||
http.begin("https://jigsaw.w3.org/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
if (pass == 10) {
|
||||
http.end();
|
||||
Serial.println("Done testing");
|
||||
} else {
|
||||
Serial.println("\n\n\nWait 5 second...\n");
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
StreamHTTPClient.ino
|
||||
|
||||
Created on: 24.05.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
|
||||
// configure server and url
|
||||
const char *fp = "41:FA:FD:B6:96:5F:33:09:F4:ED:09:28:BF:66:4D:5B:A2:88:03:65";
|
||||
|
||||
HTTPClient https;
|
||||
https.setFingerprint(fp);
|
||||
|
||||
if (https.begin("https://www.trustedfirmware.org/projects/mbed-tls")) {
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
|
||||
// get length of document (is -1 when Server sends no Content-Length header)
|
||||
int len = https.getSize();
|
||||
|
||||
// create buffer for read
|
||||
static uint8_t buff[128] = { 0 };
|
||||
|
||||
// read all data from server
|
||||
while (https.connected() && (len > 0 || len == -1)) {
|
||||
// get available data size
|
||||
size_t size = https.getStreamPtr()->available();
|
||||
|
||||
if (size) {
|
||||
// read up to 128 byte
|
||||
int c = https.getStreamPtr()->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
|
||||
|
||||
// write it to Serial
|
||||
Serial.write(buff, c);
|
||||
|
||||
if (len > 0) {
|
||||
len -= c;
|
||||
}
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("[HTTPS] connection closed or file end.\n");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before the next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For HTTPClient
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
HTTPClient KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
t_http_codes KEYWORD1 DATA_TYPE
|
||||
transferEncoding_t KEYWORD1 DATA_TYPE
|
||||
TransportTraits KEYWORD1 DATA_TYPE
|
||||
TransportTraitsPtr KEYWORD1 DATA_TYPE
|
||||
StreamString KEYWORD1 DATA_TYPE
|
||||
HTTPClient KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
connected KEYWORD2
|
||||
setReuse KEYWORD2
|
||||
setUserAgent KEYWORD2
|
||||
setAuthorization KEYWORD2
|
||||
setTimeout KEYWORD2
|
||||
useHTTP10 KEYWORD2
|
||||
GET KEYWORD2
|
||||
POST KEYWORD2
|
||||
PUT KEYWORD2
|
||||
PATCH KEYWORD2
|
||||
sendRequest KEYWORD2
|
||||
addHeader KEYWORD2
|
||||
collectHeaders KEYWORD2
|
||||
header KEYWORD2
|
||||
headerName KEYWORD2
|
||||
headers KEYWORD2
|
||||
hasHeader KEYWORD2
|
||||
getSize KEYWORD2
|
||||
getStream KEYWORD2
|
||||
getStreamPtr KEYWORD2
|
||||
writeToStream KEYWORD2
|
||||
getString KEYWORD2
|
||||
errorToString KEYWORD2
|
||||
|
||||
setSession KEYWORD2
|
||||
setInsecure KEYWORD2
|
||||
setKnownKey KEYWORD2
|
||||
setFingerprint KEYWORD2
|
||||
allowSelfSignedCerts KEYWORD2
|
||||
setTrustAnchors KEYWORD2
|
||||
setX509Time KEYWORD2
|
||||
setClientRSACert KEYWORD2
|
||||
setClientECCert KEYWORD2
|
||||
setBufferSizes KEYWORD2
|
||||
setCertStore KEYWORD2
|
||||
setCiphers KEYWORD2
|
||||
setCiphersLessSecure KEYWORD2
|
||||
setSSLVersion KEYWORD2
|
||||
setCACert KEYWORD2
|
||||
setCertificate KEYWORD2
|
||||
setPrivateKey KEYWORD2
|
||||
loadCACert KEYWORD2
|
||||
loadCertificate KEYWORD2
|
||||
loadPrivateKey KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTPCLIENT_DEFAULT_TCP_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_CONNECTION_REFUSED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_SEND_HEADER_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_SEND_PAYLOAD_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NOT_CONNECTED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_CONNECTION_LOST LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NO_STREAM LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NO_HTTP_SERVER LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_TOO_LESS_RAM LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_ENCODING LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_STREAM_WRITE LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_READ_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_TCP_BUFFER_SIZE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CONTINUE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PROCESSING LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_OK LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CREATED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_ACCEPTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NO_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_RESET_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PARTIAL_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MULTI_STATUS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_ALREADY_REPORTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_IM_USED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MULTIPLE_CHOICES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MOVED_PERMANENTLY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SEE_OTHER LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_MODIFIED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_USE_PROXY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_TEMPORARY_REDIRECT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PERMANENT_REDIRECT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_BAD_REQUEST LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNAUTHORIZED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PAYMENT_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FORBIDDEN LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_ACCEPTABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_REQUEST_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CONFLICT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_GONE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LENGTH_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PRECONDITION_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_URI_TOO_LONG LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_EXPECTATION_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MISDIRECTED_REQUEST LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LOCKED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FAILED_DEPENDENCY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UPGRADE_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PRECONDITION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_TOO_MANY_REQUESTS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_IMPLEMENTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_BAD_GATEWAY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_GATEWAY_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LOOP_DETECTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_EXTENDED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_TE_IDENTITY LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_TE_CHUNKED LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPClient
|
||||
version=1.2
|
||||
author=Markus Sattler
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=http Client for ESP8266, portes to the Pico
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico/blob/master/libraries/HTTPClient
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
HTTPClient.h
|
||||
|
||||
Modified 2022 by Earle F. Philhower, III
|
||||
|
||||
Created on: 02.11.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266HTTPClient for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified by Jeroen Döll, June 2018
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <StreamString.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_CLIENT
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#define DEBUG_HTTPCLIENT(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__ )
|
||||
#ifndef DEBUG_HTTPCLIENT
|
||||
#define DEBUG_HTTPCLIENT(...) do { (void)0; } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_FAILED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
constexpr int HTTPC_ERROR_CONNECTION_REFUSED __attribute__((deprecated)) = HTTPC_ERROR_CONNECTION_FAILED;
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
typedef enum {
|
||||
HTTP_CODE_CONTINUE = 100,
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
HTTP_CODE_PROCESSING = 102,
|
||||
HTTP_CODE_OK = 200,
|
||||
HTTP_CODE_CREATED = 201,
|
||||
HTTP_CODE_ACCEPTED = 202,
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
HTTP_CODE_NO_CONTENT = 204,
|
||||
HTTP_CODE_RESET_CONTENT = 205,
|
||||
HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
HTTP_CODE_MULTI_STATUS = 207,
|
||||
HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
HTTP_CODE_IM_USED = 226,
|
||||
HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
HTTP_CODE_FOUND = 302,
|
||||
HTTP_CODE_SEE_OTHER = 303,
|
||||
HTTP_CODE_NOT_MODIFIED = 304,
|
||||
HTTP_CODE_USE_PROXY = 305,
|
||||
HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
HTTP_CODE_BAD_REQUEST = 400,
|
||||
HTTP_CODE_UNAUTHORIZED = 401,
|
||||
HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
HTTP_CODE_FORBIDDEN = 403,
|
||||
HTTP_CODE_NOT_FOUND = 404,
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
HTTP_CODE_CONFLICT = 409,
|
||||
HTTP_CODE_GONE = 410,
|
||||
HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
HTTP_CODE_URI_TOO_LONG = 414,
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
HTTP_CODE_LOCKED = 423,
|
||||
HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
HTTP_CODE_BAD_GATEWAY = 502,
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
HTTP_CODE_LOOP_DETECTED = 508,
|
||||
HTTP_CODE_NOT_EXTENDED = 510,
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
} t_http_codes;
|
||||
|
||||
typedef enum {
|
||||
HTTPC_TE_IDENTITY,
|
||||
HTTPC_TE_CHUNKED
|
||||
} transferEncoding_t;
|
||||
|
||||
/**
|
||||
redirection follow mode.
|
||||
+ `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
+ `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
GET or HEAD methods will be redirected (using the same method),
|
||||
since the RFC requires end-user confirmation in other cases.
|
||||
+ `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
regardless of a used method. New request will use the same method,
|
||||
and they will include the same body data and the same headers.
|
||||
In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
typedef enum {
|
||||
HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
} followRedirects_t;
|
||||
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient() = default;
|
||||
~HTTPClient() = default;
|
||||
HTTPClient(HTTPClient&&) = default;
|
||||
HTTPClient& operator=(HTTPClient&&) = default;
|
||||
|
||||
// The easier way
|
||||
bool begin(String url);
|
||||
bool begin(String host, uint16_t port, String uri = "/", bool https = false);
|
||||
bool begin(String url, const uint8_t httpsFingerprint[20]) {
|
||||
setFingerprint(httpsFingerprint);
|
||||
return begin(url);
|
||||
}
|
||||
bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) {
|
||||
setFingerprint(httpsFingerprint);
|
||||
return begin(host, port, uri);
|
||||
}
|
||||
|
||||
// Let's do it the hard way, too
|
||||
bool begin(WiFiClient &client, const String& url);
|
||||
bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false);
|
||||
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setAuthorization(String auth);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String& url); // handy for handling redirects
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int DELETE();
|
||||
int POST(const uint8_t* payload, size_t size);
|
||||
int POST(const String& payload);
|
||||
int PUT(const uint8_t* payload, size_t size);
|
||||
int PUT(const String& payload);
|
||||
int PATCH(const uint8_t* payload, size_t size);
|
||||
int PATCH(const String& payload);
|
||||
int sendRequest(const char* type, const String& payload);
|
||||
int sendRequest(const char* type, const uint8_t* payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char* type, Stream * stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char* name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char* name); // check if header exists
|
||||
|
||||
|
||||
int getSize(void);
|
||||
const String& getLocation(void); // Location header from redirect if 3XX
|
||||
|
||||
WiFiClient& getStream(void);
|
||||
WiFiClient* getStreamPtr(void);
|
||||
int writeToPrint(Print* print);
|
||||
int writeToStream(Stream* stream);
|
||||
const String& getString(void);
|
||||
static String errorToString(int error);
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// HTTPS support, mirrors the WiFiClientSecure interface
|
||||
// Could possibly use a virtual interface class between the two, but for now it is more
|
||||
// straightforward to simply feed calls through manually here.
|
||||
void setSession(Session *session) {
|
||||
_tls()->setSession(session);
|
||||
}
|
||||
void setInsecure() {
|
||||
_tls()->setInsecure();
|
||||
}
|
||||
void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) {
|
||||
_tls()->setKnownKey(pk, usages);
|
||||
}
|
||||
bool setFingerprint(const uint8_t fingerprint[20]) {
|
||||
return _tls()->setFingerprint(fingerprint);
|
||||
}
|
||||
bool setFingerprint(const char *fpStr) {
|
||||
return _tls()->setFingerprint(fpStr);
|
||||
}
|
||||
void allowSelfSignedCerts() {
|
||||
_tls()->allowSelfSignedCerts();
|
||||
}
|
||||
void setTrustAnchors(const X509List *ta) {
|
||||
_tls()->setTrustAnchors(ta);
|
||||
}
|
||||
void setX509Time(time_t now) {
|
||||
_tls()->setX509Time(now);
|
||||
}
|
||||
void setClientRSACert(const X509List *cert, const PrivateKey *sk) {
|
||||
_tls()->setClientRSACert(cert, sk);
|
||||
}
|
||||
void setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
||||
_tls()->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
||||
}
|
||||
void setBufferSizes(int recv, int xmit) {
|
||||
_tls()->setBufferSizes(recv, xmit);
|
||||
}
|
||||
void setCertStore(CertStoreBase *certStore) {
|
||||
_tls()->setCertStore(certStore);
|
||||
}
|
||||
bool setCiphers(const uint16_t *cipherAry, int cipherCount) {
|
||||
return _tls()->setCiphers(cipherAry, cipherCount);
|
||||
}
|
||||
bool setCiphers(const std::vector<uint16_t>& list) {
|
||||
return _tls()->setCiphers(list);
|
||||
}
|
||||
bool setCiphersLessSecure() {
|
||||
return _tls()->setCiphersLessSecure();
|
||||
}
|
||||
bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12) {
|
||||
return _tls()->setSSLVersion(min, max);
|
||||
}
|
||||
void setCACert(const char *rootCA) {
|
||||
_tls()->setCACert(rootCA);
|
||||
}
|
||||
void setCertificate(const char *client_ca) {
|
||||
_tls()->setCertificate(client_ca);
|
||||
}
|
||||
void setPrivateKey(const char *private_key) {
|
||||
_tls()->setPrivateKey(private_key);
|
||||
}
|
||||
bool loadCACert(Stream& stream, size_t size) {
|
||||
return _tls()->loadCACert(stream, size);
|
||||
}
|
||||
bool loadCertificate(Stream& stream, size_t size) {
|
||||
return _tls()->loadCertificate(stream, size);
|
||||
}
|
||||
bool loadPrivateKey(Stream& stream, size_t size) {
|
||||
return _tls()->loadPrivateKey(stream, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
// HTTPS helpers
|
||||
WiFiClientSecure *_tls() {
|
||||
if (!_clientMade) {
|
||||
_clientMade = new WiFiClientSecure();
|
||||
_clientGiven = false;
|
||||
}
|
||||
_clientTLS = true;
|
||||
return (WiFiClientSecure*)_clientMade;
|
||||
}
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(const String& url, const char* expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char * type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
WiFiClient *_clientMade = nullptr;
|
||||
bool _clientTLS = false;
|
||||
|
||||
std::unique_ptr<WiFiClient> _clientIn;
|
||||
bool _clientGiven = false;
|
||||
|
||||
WiFiClient *_client() {
|
||||
if (_clientGiven) {
|
||||
return _clientIn.get();
|
||||
} else {
|
||||
return _clientMade;
|
||||
}
|
||||
}
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _base64Authorization;
|
||||
|
||||
static const String defaultUserAgent;
|
||||
String _userAgent = defaultUserAgent;
|
||||
|
||||
/// Response handling
|
||||
std::unique_ptr<RequestArgument[]> _currentHeaders;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
std::unique_ptr<StreamString> _payload;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
base64.cpp
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "libb64/cencode.h"
|
||||
}
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
convert input data to base64
|
||||
@param data const uint8_t
|
||||
@param length size_t
|
||||
@return String
|
||||
*/
|
||||
String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
|
||||
String base64;
|
||||
|
||||
// base64 needs more size then the source data, use cencode.h macros
|
||||
size_t size = ((doNewLines ? base64_encode_expected_len(length)
|
||||
: base64_encode_expected_len_nonewlines(length)) + 1);
|
||||
|
||||
if (base64.reserve(size)) {
|
||||
|
||||
base64_encodestate _state;
|
||||
if (doNewLines) {
|
||||
base64_init_encodestate(&_state);
|
||||
} else {
|
||||
base64_init_encodestate_nonewlines(&_state);
|
||||
}
|
||||
|
||||
constexpr size_t BUFSIZE = 48;
|
||||
char buf[BUFSIZE + 1 /* newline */ + 1 /* NUL */];
|
||||
for (size_t len = 0; len < length; len += BUFSIZE * 3 / 4) {
|
||||
size_t blocklen = base64_encode_block((const char*) data + len,
|
||||
std::min(BUFSIZE * 3 / 4, length - len), buf, &_state);
|
||||
buf[blocklen] = '\0';
|
||||
base64 += buf;
|
||||
}
|
||||
if (base64_encode_blockend(buf, &_state)) {
|
||||
base64 += buf;
|
||||
}
|
||||
} else {
|
||||
base64 = F("-FAIL-");
|
||||
}
|
||||
|
||||
return base64;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
base64.h
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/String.h>
|
||||
|
||||
class base64 {
|
||||
public:
|
||||
// NOTE: The default behaviour of backend (lib64)
|
||||
// is to add a newline every 72 (encoded) characters output.
|
||||
// This may 'break' longer uris and json variables
|
||||
static String encode(const uint8_t * data, size_t length, bool doNewLines);
|
||||
static inline String encode(const String& text, bool doNewLines) {
|
||||
return encode((const uint8_t *) text.c_str(), text.length(), doNewLines);
|
||||
}
|
||||
|
||||
// esp32 compat:
|
||||
|
||||
static inline String encode(const uint8_t * data, size_t length) {
|
||||
return encode(data, length, false);
|
||||
}
|
||||
|
||||
static inline String encode(const String& text) {
|
||||
return encode(text, false);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
httpUpdate.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
#define UPDATE_URL "http://192.168.1.8/xfer/file.bin"
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void update_started() {
|
||||
Serial.println("CALLBACK: HTTP update process started");
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
Serial.println("CALLBACK: HTTP update process finished");
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
// Add optional callback notifiers
|
||||
httpUpdate.onStart(update_started);
|
||||
httpUpdate.onEnd(update_finished);
|
||||
httpUpdate.onProgress(update_progress);
|
||||
httpUpdate.onError(update_error);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(UPDATE_URL);
|
||||
// Or:
|
||||
// t_httpUpdate_return ret = httpUpdate.update("server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break;
|
||||
|
||||
case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
httpUpdateSecure.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
#define UPDATE_URL "https://www.ziplabel.com/file.bin"
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void update_started() {
|
||||
Serial.println("CALLBACK: HTTP update process started");
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
Serial.println("CALLBACK: HTTP update process finished");
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
// Add optional callback notifiers
|
||||
httpUpdate.onStart(update_started);
|
||||
httpUpdate.onEnd(update_finished);
|
||||
httpUpdate.onProgress(update_progress);
|
||||
httpUpdate.onError(update_error);
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, UPDATE_URL);
|
||||
// Or:
|
||||
// t_httpUpdate_return ret = httpUpdate.update("server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break;
|
||||
|
||||
case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESP8266httpUpdate
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
HTTPUpdate KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HTTPUpdateResult KEYWORD1 DATA_TYPE
|
||||
ESPhttpUpdate KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
rebootOnUpdate KEYWORD2
|
||||
update KEYWORD2
|
||||
updateSpiffs KEYWORD2
|
||||
getLastError KEYWORD2
|
||||
getLastErrorString KEYWORD2
|
||||
setAuthorization KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTP_UE_TOO_LESS_SPACE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_NOT_REPORT_SIZE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FILE_NOT_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FORBIDDEN LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_WRONG_HTTP_CODE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FAULTY_MD5 LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_VERIFY_HEADER_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_FOR_WRONG_FLASH LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_UNAUTHORIZED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_NO_UPDATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_OK LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPUpdate
|
||||
version=1.3
|
||||
author=Markus Sattler
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Http Update for ESP8266, ported to Pico
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
Executable
+392
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
|
||||
@file HTTPUpdate.cpp
|
||||
@date 21.06.2015
|
||||
@author Markus Sattler
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the Http Updater.
|
||||
|
||||
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 "HTTPUpdate.h"
|
||||
#include <StreamString.h>
|
||||
|
||||
extern uint8_t _FS_start;
|
||||
extern uint8_t _FS_end;
|
||||
|
||||
HTTPUpdate::HTTPUpdate(void)
|
||||
: _httpClientTimeout(8000) {
|
||||
}
|
||||
|
||||
HTTPUpdate::HTTPUpdate(int httpClientTimeout)
|
||||
: _httpClientTimeout(httpClientTimeout) {
|
||||
}
|
||||
|
||||
HTTPUpdate::~HTTPUpdate(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
set the Authorization for the http request
|
||||
@param user const String&
|
||||
@param password const String&
|
||||
*/
|
||||
void HTTPUpdate::setAuthorization(const String &user, const String &password) {
|
||||
_user = user;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
set the Authorization for the http request
|
||||
@param auth const String& base64
|
||||
*/
|
||||
void HTTPUpdate::setAuthorization(const String &auth) {
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, url);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateFS(WiFiClient& client, const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, url);
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
|
||||
const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, host, port, uri);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(host, port, uri);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateFS(const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
/**
|
||||
return error code as int
|
||||
@return int error code
|
||||
*/
|
||||
int HTTPUpdate::getLastError(void) {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
return error code as String
|
||||
@return String error
|
||||
*/
|
||||
String HTTPUpdate::getLastErrorString(void) {
|
||||
|
||||
if (_lastError == 0) {
|
||||
return String(); // no error
|
||||
}
|
||||
|
||||
// error from Update class
|
||||
if (_lastError > 0) {
|
||||
StreamString error;
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
return String(F("Update error: ")) + error;
|
||||
}
|
||||
|
||||
// error from http client
|
||||
if (_lastError > -100) {
|
||||
return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError);
|
||||
}
|
||||
|
||||
switch (_lastError) {
|
||||
case HTTP_UE_TOO_LESS_SPACE:
|
||||
return F("Not Enough space");
|
||||
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
|
||||
return F("Server Did Not Report Size");
|
||||
case HTTP_UE_SERVER_FILE_NOT_FOUND:
|
||||
return F("File Not Found (404)");
|
||||
case HTTP_UE_SERVER_FORBIDDEN:
|
||||
return F("Forbidden (403)");
|
||||
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
|
||||
return F("Wrong HTTP Code");
|
||||
case HTTP_UE_SERVER_FAULTY_MD5:
|
||||
return F("Wrong MD5");
|
||||
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
|
||||
return F("Verify Bin Header Failed");
|
||||
case HTTP_UE_BIN_FOR_WRONG_FLASH:
|
||||
return F("New Binary Does Not Fit Flash Size");
|
||||
case HTTP_UE_SERVER_UNAUTHORIZED:
|
||||
return F("Unauthorized (401)");
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@param http HTTPClient
|
||||
@param currentVersion const char
|
||||
@return HTTPUpdateResult
|
||||
*/
|
||||
HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) {
|
||||
|
||||
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
|
||||
|
||||
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
|
||||
http.useHTTP10(true);
|
||||
http.setTimeout(_httpClientTimeout);
|
||||
http.setFollowRedirects(_followRedirects);
|
||||
http.setUserAgent(F("Pico-HTTP-Update"));
|
||||
http.addHeader(F("x-Pico-Chip-ID"), String(rp2040.getChipID()));
|
||||
http.addHeader(F("x-Pico-STA-MAC"), WiFi.macAddress());
|
||||
http.addHeader(F("x-Pico-AP-MAC"), WiFi.softAPmacAddress());
|
||||
|
||||
if (spiffs) {
|
||||
http.addHeader(F("x-Pico-Mode"), F("spiffs"));
|
||||
} else {
|
||||
http.addHeader(F("x-Pico-Mode"), F("sketch"));
|
||||
}
|
||||
|
||||
if (currentVersion && currentVersion[0] != 0x00) {
|
||||
http.addHeader(F("x-Pico-Version"), currentVersion);
|
||||
}
|
||||
|
||||
if (_user != "" && _password != "") {
|
||||
http.setAuthorization(_user.c_str(), _password.c_str());
|
||||
}
|
||||
|
||||
if (_auth != "") {
|
||||
http.setAuthorization(_auth.c_str());
|
||||
}
|
||||
|
||||
const char * headerkeys[] = { "x-MD5" };
|
||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
|
||||
|
||||
// track these headers
|
||||
http.collectHeaders(headerkeys, headerkeyssize);
|
||||
|
||||
|
||||
int code = http.GET();
|
||||
int len = http.getSize();
|
||||
|
||||
if (code <= 0) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
|
||||
_setLastError(code);
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len);
|
||||
|
||||
String md5;
|
||||
if (_md5Sum.length()) {
|
||||
md5 = _md5Sum;
|
||||
} else if (http.hasHeader("x-MD5")) {
|
||||
md5 = http.header("x-MD5");
|
||||
}
|
||||
if (md5.length()) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", md5.c_str());
|
||||
}
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] info:\n");
|
||||
|
||||
if (currentVersion && currentVersion[0] != 0x00) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str());
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case HTTP_CODE_OK: ///< OK (Start Update)
|
||||
if (len > 0) {
|
||||
bool startUpdate = true;
|
||||
if (spiffs) {
|
||||
size_t spiffsSize = ((size_t)&_FS_end - (size_t)&_FS_start);
|
||||
if (len > (int) spiffsSize) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startUpdate) {
|
||||
_setLastError(HTTP_UE_TOO_LESS_SPACE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
} else {
|
||||
// Warn main app we're starting up...
|
||||
if (_cbStart) {
|
||||
_cbStart();
|
||||
}
|
||||
|
||||
WiFiClient * tcp = http.getStreamPtr();
|
||||
if (!tcp) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] WiFiClient connection unexpectedly absent\n");
|
||||
_setLastError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if (_closeConnectionsOnUpdate) {
|
||||
WiFiUDP::stopAll();
|
||||
WiFiClient::stopAllExcept(tcp);
|
||||
}
|
||||
|
||||
delay(100);
|
||||
|
||||
int command;
|
||||
|
||||
if (spiffs) {
|
||||
command = U_FS;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate filesystem...\n");
|
||||
} else {
|
||||
command = U_FLASH;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n");
|
||||
}
|
||||
|
||||
if (runUpdate(*tcp, len, md5, command)) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
|
||||
http.end();
|
||||
// Warn main app we're all done
|
||||
if (_cbEnd) {
|
||||
_cbEnd();
|
||||
}
|
||||
|
||||
#ifdef ATOMIC_FS_UPDATE
|
||||
if (_rebootOnUpdate) {
|
||||
#else
|
||||
if (_rebootOnUpdate && !spiffs) {
|
||||
#endif
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_setLastError(HTTP_UE_SERVER_NOT_REPORT_SIZE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length was 0 or wasn't set by Server?!\n");
|
||||
}
|
||||
break;
|
||||
case HTTP_CODE_NOT_MODIFIED:
|
||||
///< Not Modified (No updates)
|
||||
ret = HTTP_UPDATE_NO_UPDATES;
|
||||
break;
|
||||
case HTTP_CODE_NOT_FOUND:
|
||||
_setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_FORBIDDEN:
|
||||
_setLastError(HTTP_UE_SERVER_FORBIDDEN);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_UNAUTHORIZED:
|
||||
_setLastError(HTTP_UE_SERVER_UNAUTHORIZED);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
default:
|
||||
_setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
|
||||
//http.writeToStream(&Serial1);
|
||||
break;
|
||||
}
|
||||
|
||||
http.end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
write Update to flash
|
||||
@param in Stream&
|
||||
@param size uint32_t
|
||||
@param md5 String
|
||||
@return true if Update ok
|
||||
*/
|
||||
bool HTTPUpdate::runUpdate(Stream& in, uint32_t size, const String& md5, int command) {
|
||||
|
||||
StreamString error;
|
||||
|
||||
if (_cbProgress) {
|
||||
Update.onProgress(_cbProgress);
|
||||
}
|
||||
|
||||
if (!Update.begin(size, command)) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(0, size);
|
||||
}
|
||||
|
||||
if (md5.length()) {
|
||||
if (!Update.setMD5(md5.c_str())) {
|
||||
_setLastError(HTTP_UE_SERVER_FAULTY_MD5);
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Update.writeStream(in) != size) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(size, size);
|
||||
}
|
||||
|
||||
if (!Update.end()) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
|
||||
@file ESP8266HTTPUpdate.h
|
||||
@date 21.06.2015
|
||||
@author Markus Sattler
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 Http Updater.
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_UPDATE
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_HTTP_UPDATE
|
||||
#define DEBUG_HTTP_UPDATE(...) do { (void)0; } while(0)
|
||||
#endif
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
//TODO - in v3.0.0 make this an enum
|
||||
constexpr int HTTP_UE_TOO_LESS_SPACE = (-100);
|
||||
constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101);
|
||||
constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102);
|
||||
constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103);
|
||||
constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104);
|
||||
constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105);
|
||||
constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106);
|
||||
constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107);
|
||||
constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108);
|
||||
|
||||
enum HTTPUpdateResult {
|
||||
HTTP_UPDATE_FAILED,
|
||||
HTTP_UPDATE_NO_UPDATES,
|
||||
HTTP_UPDATE_OK
|
||||
};
|
||||
|
||||
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
|
||||
|
||||
using HTTPUpdateStartCB = std::function<void()>;
|
||||
using HTTPUpdateEndCB = std::function<void()>;
|
||||
using HTTPUpdateErrorCB = std::function<void(int)>;
|
||||
using HTTPUpdateProgressCB = std::function<void(int, int)>;
|
||||
|
||||
class HTTPUpdate {
|
||||
public:
|
||||
HTTPUpdate(void);
|
||||
HTTPUpdate(int httpClientTimeout);
|
||||
~HTTPUpdate(void);
|
||||
|
||||
void rebootOnUpdate(bool reboot) {
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
/**
|
||||
set true to follow redirects.
|
||||
@param follow
|
||||
@deprecated Please use `setFollowRedirects(followRedirects_t follow)`
|
||||
*/
|
||||
void followRedirects(bool follow) __attribute__((deprecated)) {
|
||||
_followRedirects = follow ? HTTPC_STRICT_FOLLOW_REDIRECTS : HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
}
|
||||
/**
|
||||
set redirect follow mode. See `followRedirects_t` enum for available modes.
|
||||
@param follow
|
||||
*/
|
||||
void setFollowRedirects(followRedirects_t follow) {
|
||||
_followRedirects = follow;
|
||||
}
|
||||
|
||||
void closeConnectionsOnUpdate(bool sever) {
|
||||
_closeConnectionsOnUpdate = sever;
|
||||
}
|
||||
|
||||
void setMD5sum(const String &md5Sum) {
|
||||
_md5Sum = md5Sum;
|
||||
}
|
||||
|
||||
void setAuthorization(const String& user, const String& password);
|
||||
void setAuthorization(const String& auth);
|
||||
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
||||
const String& currentVersion = "");
|
||||
t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
t_httpUpdate_return update(const String& url, const String& currentVersion = "");
|
||||
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", const String& currentVersion = "");
|
||||
t_httpUpdate_return updateFS(const String& url, const String& currentVersion = "");
|
||||
|
||||
// Notification callbacks
|
||||
void onStart(HTTPUpdateStartCB cbOnStart) {
|
||||
_cbStart = cbOnStart;
|
||||
}
|
||||
void onEnd(HTTPUpdateEndCB cbOnEnd) {
|
||||
_cbEnd = cbOnEnd;
|
||||
}
|
||||
void onError(HTTPUpdateErrorCB cbOnError) {
|
||||
_cbError = cbOnError;
|
||||
}
|
||||
void onProgress(HTTPUpdateProgressCB cbOnProgress) {
|
||||
_cbProgress = cbOnProgress;
|
||||
}
|
||||
|
||||
int getLastError(void);
|
||||
String getLastErrorString(void);
|
||||
|
||||
protected:
|
||||
t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
|
||||
bool runUpdate(Stream& in, uint32_t size, const String& md5, int command = U_FLASH);
|
||||
|
||||
// Set the error and potentially use a CB to notify the application
|
||||
void _setLastError(int err) {
|
||||
_lastError = err;
|
||||
if (_cbError) {
|
||||
_cbError(err);
|
||||
}
|
||||
}
|
||||
int _lastError;
|
||||
bool _rebootOnUpdate = true;
|
||||
bool _closeConnectionsOnUpdate = true;
|
||||
String _user;
|
||||
String _password;
|
||||
String _auth;
|
||||
String _md5Sum;
|
||||
private:
|
||||
int _httpClientTimeout;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
|
||||
// Callbacks
|
||||
HTTPUpdateStartCB _cbStart;
|
||||
HTTPUpdateEndCB _cbEnd;
|
||||
HTTPUpdateErrorCB _cbError;
|
||||
HTTPUpdateProgressCB _cbProgress;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
extern HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
SecureBearSSLUpdater - SSL encrypted, password-protected firmware update
|
||||
|
||||
This example starts a HTTPS server on the Pico to allow firmware updates
|
||||
to be performed. All communication, including the username and password,
|
||||
is encrypted via SSL. Be sure to update the SSID and PASSWORD before running
|
||||
to allow connection to your WiFi network.
|
||||
|
||||
To upload through terminal you can use:
|
||||
curl -u admin:admin -F "image=@firmware.bin" picow-webupdate.local/firmware
|
||||
|
||||
Adapted by Earle F. Philhower, III, from the SecureWebUpdater.ino example.
|
||||
This example is released into the public domain.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServerSecure.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* update_path = "/firmware";
|
||||
const char* update_username = "admin";
|
||||
const char* update_password = "admin";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServerSecure httpServer(443);
|
||||
HTTPUpdateServerSecure httpUpdater;
|
||||
|
||||
static const char serverCert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx
|
||||
EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w
|
||||
HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX
|
||||
DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh
|
||||
dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y
|
||||
X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj
|
||||
oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI
|
||||
t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO
|
||||
S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy
|
||||
+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM
|
||||
E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb
|
||||
fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC
|
||||
JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA
|
||||
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg==
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf
|
||||
zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+
|
||||
ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh
|
||||
BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8
|
||||
djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T
|
||||
yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M
|
||||
q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr
|
||||
eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN
|
||||
d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn
|
||||
geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y
|
||||
84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx
|
||||
/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim
|
||||
RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu
|
||||
DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg
|
||||
rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW
|
||||
YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK
|
||||
iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X
|
||||
jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ
|
||||
zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV
|
||||
kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt
|
||||
/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO
|
||||
j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg
|
||||
gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
)EOF";
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpServer.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
|
||||
httpUpdater.setup(&httpServer, update_path, update_username, update_password);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("https", "tcp", 443);
|
||||
Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in "
|
||||
"your browser and login with username '%s' and password "
|
||||
"'%s'\n",
|
||||
host, update_path, update_username, update_password);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -F "image=@firmware.bin" picow-webupdate.local/update
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer httpServer(80);
|
||||
HTTPUpdateServer httpUpdater;
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -u admin:admin -F "image=@firmware.bin" picow-webupdate.local/firmware
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* update_path = "/firmware";
|
||||
const char* update_username = "admin";
|
||||
const char* update_password = "admin";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer httpServer(80);
|
||||
HTTPUpdateServer httpUpdater;
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer, update_path, update_username, update_password);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
Serial.printf("HTTPUpdateServer ready! Open http://%s.local%s in your browser and login with username '%s' and password '%s'\n", host, update_path, update_username, update_password);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For HTTPUpdateServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HTTPUpdateServer KEYWORD1
|
||||
HTTPUpdateServerSecure KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setup KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPUpdateServer
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov, Miguel Angel Ajo
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Simple HTTP Update server based on the Pico WebServer
|
||||
paragraph=The library accepts HTTP post requests to the /update url, and updates the Pico firmware.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=false
|
||||
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
#include <WebServer.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <LittleFS.h>
|
||||
#include "StreamString.h"
|
||||
#include "HTTPUpdateServer.h"
|
||||
|
||||
extern uint8_t _FS_start;
|
||||
extern uint8_t _FS_end;
|
||||
|
||||
|
||||
static const char serverIndex[] PROGMEM =
|
||||
R"(<!DOCTYPE html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'/>
|
||||
</head>
|
||||
<body>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
Firmware:<br>
|
||||
<input type='file' accept='.bin,.bin.gz' name='firmware'>
|
||||
<input type='submit' value='Update Firmware'>
|
||||
</form>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
FileSystem:<br>
|
||||
<input type='file' accept='.bin,.bin.gz' name='filesystem'>
|
||||
<input type='submit' value='Update FileSystem'>
|
||||
</form>
|
||||
</body>
|
||||
</html>)";
|
||||
static const char successResponse[] PROGMEM =
|
||||
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
HTTPUpdateServerTemplate<ServerType, ServerPort>::HTTPUpdateServerTemplate(bool serial_debug) {
|
||||
_serial_output = serial_debug;
|
||||
_server = NULL;
|
||||
_username = "";
|
||||
_password = "";
|
||||
_authenticated = false;
|
||||
}
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
void HTTPUpdateServerTemplate<ServerType, ServerPort>::setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path, const String& username, const String& password) {
|
||||
_server = server;
|
||||
_username = username;
|
||||
_password = password;
|
||||
|
||||
// handler for the /update form page
|
||||
_server->on(path.c_str(), HTTP_GET, [&]() {
|
||||
if (_username != "" && _password != "" && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||
return _server->requestAuthentication();
|
||||
}
|
||||
_server->send_P(200, PSTR("text/html"), serverIndex);
|
||||
});
|
||||
|
||||
// handler for the /update form page - preflight options
|
||||
_server->on(path.c_str(), HTTP_OPTIONS, [&]() {
|
||||
_server->sendHeader("Access-Control-Allow-Headers", "*");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
_server->send(200, F("text/html"), String(F("y")));
|
||||
}, [&]() {
|
||||
_authenticated = (_username == "" || _password == "" || _server->authenticate(_username.c_str(), _password.c_str()));
|
||||
if (!_authenticated) {
|
||||
if (_serial_output) {
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// handler for the /update form POST (once file upload finishes)
|
||||
_server->on(path.c_str(), HTTP_POST, [&]() {
|
||||
_server->sendHeader("Access-Control-Allow-Headers", "*");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
if (!_authenticated) {
|
||||
return _server->requestAuthentication();
|
||||
}
|
||||
if (Update.hasError()) {
|
||||
_server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError);
|
||||
} else {
|
||||
_server->client().setNoDelay(true);
|
||||
_server->send_P(200, PSTR("text/html"), successResponse);
|
||||
delay(100);
|
||||
_server->client().stop();
|
||||
rp2040.restart();
|
||||
}
|
||||
}, [&]() {
|
||||
// handler for the file upload, gets the sketch bytes, and writes
|
||||
// them through the Update object
|
||||
HTTPUpload& upload = _server->upload();
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
_updaterError = "";
|
||||
|
||||
_authenticated = (_username == "" || _password == "" || _server->authenticate(_username.c_str(), _password.c_str()));
|
||||
if (!_authenticated) {
|
||||
if (_serial_output) {
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_serial_output) {
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
}
|
||||
if (upload.name == "filesystem") {
|
||||
size_t fsSize = ((size_t)&_FS_end - (size_t)&_FS_start);
|
||||
LittleFS.end();
|
||||
if (!Update.begin(fsSize, U_FS)) { //start with max available size
|
||||
if (_serial_output) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FSInfo64 i;
|
||||
LittleFS.begin();
|
||||
LittleFS.info64(i);
|
||||
uint32_t maxSketchSpace = i.totalBytes - i.usedBytes;
|
||||
if (!Update.begin(maxSketchSpace, U_FLASH)) { //start with max available size
|
||||
_setUpdaterError();
|
||||
}
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) {
|
||||
if (_serial_output) {
|
||||
Serial.printf(".");
|
||||
}
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
_setUpdaterError();
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) {
|
||||
if (Update.end(true)) { //true to set the size to the current progress
|
||||
if (_serial_output) {
|
||||
Serial.printf("Update Success: %zu\nRebooting...\n", upload.totalSize);
|
||||
}
|
||||
} else {
|
||||
_setUpdaterError();
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) {
|
||||
Update.end();
|
||||
if (_serial_output) {
|
||||
Serial.println("Update was aborted");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
void HTTPUpdateServerTemplate<ServerType, ServerPort>::_setUpdaterError() {
|
||||
if (_serial_output) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
StreamString str;
|
||||
Update.printError(str);
|
||||
_updaterError = str.c_str();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <WebServer.h>
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
class HTTPUpdateServerTemplate {
|
||||
public:
|
||||
HTTPUpdateServerTemplate(bool serial_debug = false);
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server) {
|
||||
setup(server, "", "");
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path) {
|
||||
setup(server, path, "", "");
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& username, const String& password) {
|
||||
setup(server, "/update", username, password);
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path, const String& username, const String& password);
|
||||
|
||||
void updateCredentials(const String& username, const String& password) {
|
||||
_username = username;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _setUpdaterError();
|
||||
|
||||
private:
|
||||
bool _serial_output;
|
||||
WebServerTemplate<ServerType, ServerPort> *_server;
|
||||
String _username;
|
||||
String _password;
|
||||
bool _authenticated;
|
||||
String _updaterError;
|
||||
};
|
||||
|
||||
#include "HTTPUpdateServer-impl.h"
|
||||
|
||||
using HTTPUpdateServer = HTTPUpdateServerTemplate<WiFiServer, 80>;
|
||||
using HTTPUpdateServerSecure = HTTPUpdateServerTemplate<WiFiServerSecure, 443>;
|
||||
@@ -78,7 +78,7 @@ bool I2S::setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample)
|
||||
bool I2S::setFrequency(int newFreq) {
|
||||
_freq = newFreq;
|
||||
if (_running) {
|
||||
float bitClk = _freq * _bps * 2.0;
|
||||
float bitClk = _freq * _bps * 2.0 /* channels */ * 2.0 /* edges per clock */;
|
||||
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
return write((uint32_t)s);
|
||||
}
|
||||
|
||||
// Write 32 bit value to port, user responsbile for packing/alignment, etc.
|
||||
// Write 32 bit value to port, user responsible for packing/alignment, etc.
|
||||
size_t write(int32_t val, bool sync);
|
||||
|
||||
// Write sample to I2S port, will block until completed
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
size_t write24(int32_t l, int32_t r); // Note that 24b must have values left-aligned (i.e. 0xABCDEF00)
|
||||
size_t write32(int32_t l, int32_t r);
|
||||
|
||||
// Read 32 bit value to port, user responsbile for packing/alignment, etc.
|
||||
// Read 32 bit value to port, user responsible for packing/alignment, etc.
|
||||
size_t read(int32_t *val, bool sync);
|
||||
|
||||
// Read samples from I2S port, will block until data available
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
ESP8266 Multicast DNS
|
||||
=====================
|
||||
|
||||
A port of CC3000 Multicast DNS library (version 1.1)
|
||||
|
||||
This is a simple implementation of multicast DNS query support for an
|
||||
Arduino running on ESP8266 chip. Only support for resolving address
|
||||
queries is currently implemented.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- ESP8266WiFi library
|
||||
- MDNS support in your operating system/client machines:
|
||||
- For Mac OSX support is built in through Bonjour already.
|
||||
- For Linux, install `Avahi <http://avahi.org/>`__.
|
||||
- For Windows, install
|
||||
`Bonjour <http://www.apple.com/support/bonjour/>`__.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
1. Download this repository as a zip (button on the right) and follow
|
||||
`these instructions to install into
|
||||
Arduino <http://arduino.cc/en/Guide/Libraries>`__.
|
||||
2. Include the ESP8266mDNS library in the sketch.
|
||||
3. Call MDNS.begin method in the sketch's setup and provide a domain
|
||||
name (without the '.local' suffix, i.e. just provide 'foo' to resolve
|
||||
'foo.local'). Optionally provide the IP address to advertise and time
|
||||
to live (in seconds) for the DNS record -- the default is 1 hour.
|
||||
4. To advertise DNS-SD services, call MDNS.addService(service, proto,
|
||||
port), where service and proto are strings with service and protocol
|
||||
name (e.g. "http", "tcp"), and port is an integer port number for
|
||||
this service (e.g. 80).
|
||||
|
||||
See the included MDNS + HTTP server sketch for a full example.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) ESP8266 port (c)
|
||||
2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,25 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
LEAmDNS KEYWORD1
|
||||
MDNSResponder KEYWORD1
|
||||
MDNS KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
update KEYWORD2
|
||||
addService KEYWORD2
|
||||
enableArduino KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name=LEAmDNS
|
||||
version=1.2
|
||||
author=multiple, see files
|
||||
maintainer=LaborEtArs
|
||||
sentence=Creates a mDNS responder.
|
||||
paragraph=Creates a mDNS responder to ensure host domain uniqueness in local networks and to allow for mDNS service discovery and announcement.
|
||||
category=Communication
|
||||
url=https://github.com/LaborEtArs/ESP8266mDNS
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
#include <ESP8266mDNS.h>
|
||||
|
||||
/*
|
||||
MDNS responder global instance
|
||||
|
||||
Class type that is instantiated depends on the type mapping in ESP8266mDNS.h
|
||||
*/
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
MDNSResponder MDNS;
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
ESP8266mDNS.h - mDNSResponder for ESP8266 family
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
mDNS implementation, that supports many mDNS features like:
|
||||
- Presenting a DNS-SD service to interested observers, eg. a http server by presenting
|
||||
_http._tcp service
|
||||
- Support for multi-level compressed names in input; in output only a very simple one-leven
|
||||
full-name compression is implemented
|
||||
- Probing host and service domains for uniqueness in the local network
|
||||
- Tiebreaking while probing is supported in a very minimalistic way (the 'higher' IP address
|
||||
wins the tiebreak)
|
||||
- Announcing available services after successful probing
|
||||
- Using fixed service TXT items or
|
||||
- Using dynamic service TXT items for presented services (via callback)
|
||||
- Remove services (and un-announcing them to the observers by sending goodbye-messages)
|
||||
- Static queries for DNS-SD services (creating a fixed answer set after a certain timeout
|
||||
period)
|
||||
- Dynamic queries for DNS-SD services with cached and updated answers and user notifications
|
||||
- Support for multi-homed client host domains
|
||||
|
||||
See 'src/LEAmDNS.h' for implementation details, configuration and usage information.
|
||||
See 'examples/LEAmDNS/' for examples of the new features.
|
||||
|
||||
LEAmDNS is expected to be compatible with the original ESP8266mDNS implementation, and it can be
|
||||
used as a drop-in replacement in existing projects.
|
||||
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LEAmDNS.h" // LEA
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
// Maps the implementation to use to the global namespace type
|
||||
using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; // LEA
|
||||
|
||||
extern MDNSResponder MDNS;
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user