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,./ota/uzlib
|
||||
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
|
||||
|
||||
+2
-1
@@ -3,4 +3,5 @@ system
|
||||
tools/dist
|
||||
docs/_build
|
||||
ota/build
|
||||
tools/libpico/build
|
||||
tools/libpico/build
|
||||
platform.local.txt
|
||||
|
||||
@@ -34,3 +34,6 @@
|
||||
[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
|
||||
|
||||
@@ -25,6 +25,7 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* 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
|
||||
@@ -147,6 +148,8 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* 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())
|
||||
@@ -170,6 +173,7 @@ Here are some links to coverage and additional tutorials for using `arduino-pico
|
||||
* Pre-release Adafruit QT Py RP2040 - https://www.youtube.com/watch?v=sfC1msqXX0I
|
||||
* Adafruit Feather RP2040 running LCD + TMP117 - https://www.youtube.com/watch?v=fKDeqZiIwHg
|
||||
* Demonstration of Servos and I2C in Korean - https://cafe.naver.com/arduinoshield/1201
|
||||
* Home Assistant Pico W integration starter project using Arduino - https://github.com/daniloc/PicoW_HomeAssistant_Starter
|
||||
|
||||
# Contributing
|
||||
If you want to contribute or have bugfixes, drop me a note at <earlephilhower@yahoo.com> or open an issue/PR here.
|
||||
@@ -187,7 +191,10 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [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.
|
||||
* [LEAmDMS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT 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
|
||||
|
||||
+1868
-1
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
|
||||
|
||||
@@ -24,26 +24,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include "_freertos.h"
|
||||
|
||||
class CoreMutex {
|
||||
public:
|
||||
CoreMutex(mutex_t *mutex) {
|
||||
uint32_t owner;
|
||||
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
return;
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(mutex);
|
||||
__freertos_mutex_take(m);
|
||||
} else {
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
if (debugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
}
|
||||
_acquired = true;
|
||||
}
|
||||
|
||||
~CoreMutex() {
|
||||
if (_acquired) {
|
||||
mutex_exit(_mutex);
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(_mutex);
|
||||
__freertos_mutex_give(m);
|
||||
} else {
|
||||
mutex_exit(_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -32,20 +32,10 @@
|
||||
#include "ccount.pio.h"
|
||||
#include <malloc.h>
|
||||
|
||||
#include "_freertos.h"
|
||||
|
||||
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 */ };
|
||||
@@ -314,7 +304,10 @@ public:
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
watchdog_reboot(0, 0, 100);
|
||||
watchdog_reboot(0, 0, 10);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
inline void restart() {
|
||||
|
||||
@@ -71,8 +71,6 @@ static int __usb_task_irq;
|
||||
|
||||
#define EPNUM_HID 0x83
|
||||
|
||||
#define EPNUM_MIDI 0x01
|
||||
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
static tusb_desc_device_t usbd_desc_device = {
|
||||
@@ -91,7 +89,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMIDI) {
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick) {
|
||||
// Can use as-is, this is the default USB case
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
@@ -105,9 +103,6 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
if (__USBInstallJoystick) {
|
||||
usbd_desc_device.idProduct |= 0x0100;
|
||||
}
|
||||
if (__USBInstallMIDI) {
|
||||
usbd_desc_device.idProduct |= 0x2000;
|
||||
}
|
||||
// Set the device class to 0 to indicate multiple device classes
|
||||
usbd_desc_device.bDeviceClass = 0;
|
||||
usbd_desc_device.bDeviceSubClass = 0;
|
||||
@@ -228,7 +223,7 @@ void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMIDI ? 2 : 0);
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0);
|
||||
|
||||
uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
@@ -243,13 +238,7 @@ void __SetupUSBDescriptor() {
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
|
||||
};
|
||||
|
||||
uint8_t midi_itf = hid_itf + (hasHID ? 1 : 0);
|
||||
uint8_t midi_desc[TUD_MIDI_DESC_LEN] = {
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MIDI_DESCRIPTOR(midi_itf, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0) + (__USBInstallMIDI ? sizeof(midi_desc) : 0);
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0);
|
||||
|
||||
uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
@@ -271,9 +260,6 @@ void __SetupUSBDescriptor() {
|
||||
memcpy(ptr, hid_desc, sizeof(hid_desc));
|
||||
ptr += sizeof(hid_desc);
|
||||
}
|
||||
if (__USBInstallMIDI) {
|
||||
memcpy(ptr, midi_desc, sizeof(midi_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ extern void __USBInstallSerial() __attribute__((weak));
|
||||
extern void __USBInstallKeyboard() __attribute__((weak));
|
||||
extern void __USBInstallJoystick() __attribute__((weak));
|
||||
extern void __USBInstallMouse() __attribute__((weak));
|
||||
extern void __USBInstallMIDI() __attribute__((weak));
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
// have multiple cores updating the TUSB state in parallel
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 4
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "2.4.0"
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 1
|
||||
#define ARDUINO_PICO_VERSION_STR "2.5.1"
|
||||
|
||||
@@ -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,19 +74,19 @@ int SerialUSB::peek() {
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tud_cdc_connected() && tud_cdc_available()) {
|
||||
if (tud_cdc_available()) {
|
||||
return tud_cdc_read_char();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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,51 @@
|
||||
/*
|
||||
_freertos.cpp - Internal core definitions for FreeRTOS
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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 "_freertos.h"
|
||||
#include "pico/mutex.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
mutex_t *src;
|
||||
SemaphoreHandle_t dst;
|
||||
} FMMap;
|
||||
|
||||
static FMMap *_map = nullptr;
|
||||
extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
if (!_map) {
|
||||
_map = (FMMap *)calloc(sizeof(FMMap), 16);
|
||||
}
|
||||
// Pre-existing map
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (m == _map[i].src) {
|
||||
return _map[i].dst;
|
||||
}
|
||||
}
|
||||
// Make a new mutex
|
||||
auto fm = __freertos_mutex_create();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (_map[i].src == nullptr) {
|
||||
_map[i].src = m;
|
||||
_map[i].dst = fm;
|
||||
return fm;
|
||||
}
|
||||
}
|
||||
return nullptr; // Need to make space for more mutex maps!
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
_freertos.h - Internal core definitions for FreeRTOS
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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 "pico/mutex.h"
|
||||
|
||||
// Cannot include refs to FreeRTOS's actual semaphore calls because they
|
||||
// are implemented as macros, so we have a wrapper in our variant hook
|
||||
// to handle it.
|
||||
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
// FreeRTOS has been set up
|
||||
extern volatile bool __freeRTOSinitted;
|
||||
|
||||
extern "C" {
|
||||
#ifndef INC_FREERTOS_H
|
||||
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
|
||||
typedef struct QueueDefinition * QueueHandle_t;
|
||||
typedef QueueHandle_t SemaphoreHandle_t;
|
||||
#endif
|
||||
|
||||
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
|
||||
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
|
||||
|
||||
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
|
||||
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
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));
|
||||
#endif
|
||||
|
||||
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m);
|
||||
}
|
||||
|
||||
// Halt the FreeRTOS PendSV task switching magic
|
||||
extern "C" int __holdUpPendSV;
|
||||
@@ -110,6 +110,19 @@ class HardwareSPI
|
||||
virtual uint16_t transfer16(uint16_t data) = 0;
|
||||
virtual void transfer(void *buf, size_t count) = 0;
|
||||
|
||||
// New transfer API. If either send or recv == nullptr then ignore it
|
||||
virtual void transfer(const void *send, void *recv, size_t count) {
|
||||
const uint8_t *out = (const uint8_t *)send;
|
||||
uint8_t *in = (uint8_t *)recv;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
uint8_t t = out ? *(out++) : 0xff;
|
||||
t = transfer(t);
|
||||
if (in) {
|
||||
*(in++) = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction Functions
|
||||
virtual void usingInterrupt(int interruptNumber) = 0;
|
||||
virtual void notUsingInterrupt(int interruptNumber) = 0;
|
||||
|
||||
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
|
||||
+107
-8
@@ -25,6 +25,8 @@
|
||||
#include <pico/mutex.h>
|
||||
#include <sys/lock.h>
|
||||
|
||||
#include "_freertos.h"
|
||||
|
||||
// HACK ALERT
|
||||
// Pico-SDK defines mutex which can be at global scope, but when the auto_init_
|
||||
// macros are used they are defined as static. Newlib needs global access to
|
||||
@@ -42,12 +44,75 @@ auto_init_mutex(__lock___dd_hash_mutex);
|
||||
auto_init_mutex(__lock___arc4random_mutex);
|
||||
#undef static
|
||||
|
||||
// FreeRTOS hack - Allow Newlib to use FreeRTOS mutexes which preserve TASKID which
|
||||
// is needed to support multithread
|
||||
|
||||
SemaphoreHandle_t __lock___sinit_recursive_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___sfp_recursive_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___atexit_recursive_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___at_quick_exit_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___malloc_recursive_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___env_recursive_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___tz_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___dd_hash_mutex_freertos;
|
||||
SemaphoreHandle_t __lock___arc4random_mutex_freertos;
|
||||
|
||||
void __initFreeRTOSMutexes() {
|
||||
__lock___sinit_recursive_mutex_freertos = _freertos_recursive_mutex_create();
|
||||
__lock___sfp_recursive_mutex_freertos = _freertos_recursive_mutex_create();
|
||||
__lock___atexit_recursive_mutex_freertos = _freertos_recursive_mutex_create();
|
||||
__lock___at_quick_exit_mutex_freertos = __freertos_mutex_create();
|
||||
__lock___malloc_recursive_mutex_freertos = _freertos_recursive_mutex_create();
|
||||
__lock___env_recursive_mutex_freertos = _freertos_recursive_mutex_create();
|
||||
__lock___tz_mutex_freertos = __freertos_mutex_create();
|
||||
__lock___dd_hash_mutex_freertos = __freertos_mutex_create();
|
||||
__lock___arc4random_mutex_freertos = __freertos_mutex_create();
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t __getFreeRTOSMutex(_LOCK_T lock) {
|
||||
mutex_t *l = (mutex_t *)lock;
|
||||
if (l == &__lock___at_quick_exit_mutex) {
|
||||
return __lock___at_quick_exit_mutex_freertos;
|
||||
} else if (l == &__lock___tz_mutex) {
|
||||
return __lock___tz_mutex_freertos;
|
||||
} else if (l == &__lock___dd_hash_mutex) {
|
||||
return __lock___dd_hash_mutex_freertos;
|
||||
} else if (l == &__lock___arc4random_mutex) {
|
||||
return __lock___arc4random_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
|
||||
recursive_mutex_t *l = (recursive_mutex_t *)lock;
|
||||
if (l == &__lock___sinit_recursive_mutex) {
|
||||
return __lock___sinit_recursive_mutex_freertos;
|
||||
} else if (l == &__lock___sfp_recursive_mutex) {
|
||||
return __lock___sfp_recursive_mutex_freertos;
|
||||
} else if (l == &__lock___atexit_recursive_mutex) {
|
||||
return __lock___atexit_recursive_mutex_freertos;
|
||||
} else if (l == &__lock___malloc_recursive_mutex) {
|
||||
return __lock___malloc_recursive_mutex_freertos;
|
||||
} else if (l == &__lock___env_recursive_mutex) {
|
||||
return __lock___env_recursive_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void __retarget_lock_init(_LOCK_T *lock) {
|
||||
mutex_init((mutex_t*) lock);
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
mutex_init((mutex_t*) lock);
|
||||
}
|
||||
}
|
||||
|
||||
void __retarget_lock_init_recursive(_LOCK_T *lock) {
|
||||
recursive_mutex_init((recursive_mutex_t*) lock);
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
recursive_mutex_init((recursive_mutex_t*) lock);
|
||||
}
|
||||
}
|
||||
|
||||
void __retarget_lock_close(_LOCK_T lock) {
|
||||
@@ -59,25 +124,59 @@ void __retarget_lock_close_recursive(_LOCK_T lock) {
|
||||
}
|
||||
|
||||
void __retarget_lock_acquire(_LOCK_T lock) {
|
||||
mutex_enter_blocking((mutex_t*)lock);
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSMutex(lock);
|
||||
__freertos_mutex_take(mtx);
|
||||
} else {
|
||||
mutex_enter_blocking((mutex_t*)lock);
|
||||
}
|
||||
}
|
||||
|
||||
void __retarget_lock_acquire_recursive(_LOCK_T lock) {
|
||||
recursive_mutex_enter_blocking((recursive_mutex_t*)lock);
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSRecursiveMutex(lock);
|
||||
__freertos_recursive_mutex_take(mtx);
|
||||
} else {
|
||||
recursive_mutex_enter_blocking((recursive_mutex_t*)lock);
|
||||
}
|
||||
}
|
||||
|
||||
int __retarget_lock_try_acquire(_LOCK_T lock) {
|
||||
return mutex_try_enter((mutex_t *)lock, NULL);
|
||||
int ret;
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSMutex(lock);
|
||||
ret = __freertos_mutex_try_take(mtx);
|
||||
} else {
|
||||
ret = mutex_try_enter((mutex_t *)lock, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __retarget_lock_try_acquire_recursive(_LOCK_T lock) {
|
||||
return recursive_mutex_try_enter((recursive_mutex_t*)lock, NULL);
|
||||
int ret;
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSRecursiveMutex(lock);
|
||||
ret = __freertos_recursive_mutex_try_take(mtx);
|
||||
} else {
|
||||
ret = recursive_mutex_try_enter((recursive_mutex_t*)lock, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __retarget_lock_release(_LOCK_T lock) {
|
||||
mutex_exit((mutex_t*)lock);
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSMutex(lock);
|
||||
__freertos_mutex_give(mtx);
|
||||
} else {
|
||||
mutex_exit((mutex_t*)lock);
|
||||
}
|
||||
}
|
||||
|
||||
void __retarget_lock_release_recursive(_LOCK_T lock) {
|
||||
recursive_mutex_exit((recursive_mutex_t*)lock);
|
||||
if (__freeRTOSinitted) {
|
||||
auto mtx = __getFreeRTOSRecursiveMutex(lock);
|
||||
__freertos_recursive_mutex_give(mtx);
|
||||
} else {
|
||||
recursive_mutex_exit((recursive_mutex_t*)lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ extern void loop();
|
||||
extern void initFreeRTOS() __attribute__((weak));
|
||||
extern void startFreeRTOS() __attribute__((weak));
|
||||
bool __isFreeRTOS;
|
||||
volatile bool __freeRTOSinitted;
|
||||
|
||||
|
||||
// Weak empty variant initialization. May be redefined by variant files.
|
||||
void initVariant() __attribute__((weak));
|
||||
@@ -166,8 +168,6 @@ extern "C" void __register_impure_ptr(struct _reent *p) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: FreeRTOS should implement this based on thread ID (and each thread should have its own struct _reent
|
||||
extern "C" struct _reent *__wrap___getreent() {
|
||||
if (get_core_num() == 0) {
|
||||
return _impure_ptr;
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
|
||||
volatile bool __inLWIP = false;
|
||||
|
||||
// note same code
|
||||
|
||||
@@ -131,6 +131,7 @@ extern "C" int analogRead(pin_size_t pin) {
|
||||
}
|
||||
if (!adcInitted) {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
adc_gpio_init(pin);
|
||||
adc_select_input(pin - minPin);
|
||||
@@ -145,6 +146,7 @@ extern "C" float analogReadTemp() {
|
||||
}
|
||||
if (!adcInitted) {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
delay(1); // Allow things to settle. Without this, readings can be erratic
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.4.0'
|
||||
version = u'2.5.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.4.0'
|
||||
release = u'2.5.1'
|
||||
|
||||
# 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,8 @@ 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>
|
||||
|
||||
+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
|
||||
-------------------------------------
|
||||
|
||||
Executable → Regular
+130
-10
@@ -10,8 +10,9 @@ OTA (Over the Air) update is the process of uploading firmware to a Pico using a
|
||||
OTA may be done using:
|
||||
|
||||
- `Arduino IDE <#arduino-ide>`__
|
||||
- `Web Browser <#web-browser>`__ - Coming soon
|
||||
- `HTTP Server <#http-server>`__ - Coming soon
|
||||
- `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.
|
||||
|
||||
@@ -48,16 +49,16 @@ Check functionality provided with the `ArduinoOTA <https://github.com/earlephilh
|
||||
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 ESP side using `MD5 <https://en.wikipedia.org/wiki/MD5>`__ checksum.
|
||||
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 ESP, etc.
|
||||
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 ESP8266 will reject the upload.
|
||||
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.
|
||||
|
||||
@@ -134,7 +135,7 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
<PicoArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
|
||||
Compression
|
||||
-----------
|
||||
@@ -155,14 +156,14 @@ If signing is desired, sign the gzip compressed file *after* compression.
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
<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 ESP’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.
|
||||
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 ESP 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.
|
||||
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:
|
||||
|
||||
@@ -215,6 +216,125 @@ You will not be prompted for a reentering the same password next time. Arduino I
|
||||
|
||||
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
|
||||
----------------
|
||||
|
||||
@@ -233,7 +353,7 @@ A firmware file is uploaded via any method (Ethernet, WiFi, serial ZModem, etc.)
|
||||
|
||||
The ROM layout consists of:
|
||||
|
||||
... code:
|
||||
.. code:: cpp
|
||||
|
||||
[boot2.S] [OTA Bootloader] [0-pad] [OTA partition table] [Main sketch] [LittleFS filesystem] [EEPROM]
|
||||
|
||||
|
||||
+4
-4
@@ -33,8 +33,8 @@ values returned may not meet the most stringent random tests. **If your
|
||||
application needs absolute bulletproof random numbers, consider using
|
||||
dedicated external hardware.**
|
||||
|
||||
void reboot()
|
||||
~~~~~~~~~~~~~
|
||||
void rp2040.reboot()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Forces a hardware reboot of the Pico.
|
||||
|
||||
Memory Information
|
||||
@@ -47,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.
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
Arduino KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
@@ -30,9 +32,22 @@ 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.
@@ -1,3 +1,24 @@
|
||||
/*
|
||||
Arduino OTA.cpp - Simple Arduino IDE OTA handler
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Taken from the ESP8266 core libraries, (c) various authors.
|
||||
|
||||
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 <functional>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ArduinoOTA.h"
|
||||
@@ -243,6 +264,19 @@ void ArduinoOTAClass::_onRx() {
|
||||
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");
|
||||
@@ -261,15 +295,6 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
if (!LittleFS.begin()) {
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append("nofilesystem", 6);
|
||||
_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);
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
/*
|
||||
Arduino OTA.h - Simple Arduino IDE OTA handler
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Taken from the ESP8266 core libraries, (c) various authors.
|
||||
|
||||
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 <WiFi.h>
|
||||
|
||||
@@ -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,145 @@
|
||||
#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,126 @@
|
||||
/** 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,20 @@
|
||||
/** 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,456 @@
|
||||
/*
|
||||
DNSServer.cpp - Simple DNS server for the Pico
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Taken from the ESP8266 core libraries, (c) various authors.
|
||||
|
||||
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 "WiFi.h"
|
||||
#include "DNSServer.h"
|
||||
#include <lwip/def.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#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,170 @@
|
||||
/*
|
||||
DNSServer.h - Simple DNS server for the Pico
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Taken from the ESP8266 core libraries, (c) various authors.
|
||||
|
||||
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 <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);
|
||||
};
|
||||
@@ -19,8 +19,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef EEPROM_h
|
||||
#define EEPROM_h
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -62,6 +61,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;
|
||||
}
|
||||
@@ -81,6 +84,3 @@ protected:
|
||||
};
|
||||
|
||||
extern EEPROMClass EEPROM;
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
Submodule libraries/ESP8266SdFat updated: 43a65b42d5...255180be14
@@ -0,0 +1,319 @@
|
||||
// FreeRTOS system call/mutex stress test
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <semphr.h>
|
||||
#define DELAY 1
|
||||
#define SERIAL_DEBUG Serial1
|
||||
#define STACK_SIZE 512
|
||||
#define CORE_0 (1 << 0)
|
||||
#define CORE_1 (1 << 1)
|
||||
|
||||
void semphrTakeConditional(bool useMutexOn, SemaphoreHandle_t xSemaphore);
|
||||
void semphrGiveConditional(bool useMutexOn, SemaphoreHandle_t xSemaphore);
|
||||
|
||||
/*
|
||||
I want to keep the possibility of using different and independent
|
||||
mutexes for each of the functions that operate on the heap.
|
||||
|
||||
If you want to enable the use of these mutexes remove the defines
|
||||
on lines 30 and 31 and enable the their initialization in setup()
|
||||
*/
|
||||
SemaphoreHandle_t xSemaphoreMalloc = NULL;
|
||||
// SemaphoreHandle_t xSemaphoreRealloc = NULL;
|
||||
// SemaphoreHandle_t xSemaphoreFree = NULL;
|
||||
|
||||
/*
|
||||
A lazy way to use the same mutex for malloc, realloc and free
|
||||
in order to bring us back to the same situation as the MCVE
|
||||
posted here: https://github.com/earlephilhower/arduino-pico/issues/795#issuecomment-1227122082
|
||||
*/
|
||||
#define xSemaphoreRealloc xSemaphoreMalloc
|
||||
#define xSemaphoreFree xSemaphoreMalloc
|
||||
|
||||
const bool useMutexOnMalloc = false;
|
||||
const bool useMutexOnRealloc = false;
|
||||
const bool useMutexOnFree = false;
|
||||
|
||||
/*
|
||||
Enabling this, a realloc will be performed and the string "_realloc"
|
||||
will be concateneted to *tmp
|
||||
*/
|
||||
const bool tryRealloc = true;
|
||||
|
||||
TaskHandle_t loop2Handle = NULL;
|
||||
TaskHandle_t loop3Handle = NULL;
|
||||
TaskHandle_t loop4Handle = NULL;
|
||||
TaskHandle_t loop5Handle = NULL;
|
||||
TaskHandle_t loop6Handle = NULL;
|
||||
TaskHandle_t loop7Handle = NULL;
|
||||
|
||||
void loop2(void *pvPramaters);
|
||||
void loop3(void *pvPramaters);
|
||||
void loop4(void *pvPramaters);
|
||||
void loop5(void *pvPramaters);
|
||||
void loop6(void *pvPramaters);
|
||||
void loop7(void *pvPramaters);
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
xSemaphoreMalloc = xSemaphoreCreateMutex();
|
||||
// xSemaphoreRealloc = xSemaphoreCreateMutex();
|
||||
// xSemaphoreFree = xSemaphoreCreateMutex();
|
||||
|
||||
xTaskCreate(loop2, "loop2", STACK_SIZE, NULL, 1, &loop2Handle);
|
||||
vTaskCoreAffinitySet(loop2Handle, CORE_0);
|
||||
xTaskCreate(loop3, "loop3", STACK_SIZE, NULL, 1, &loop3Handle);
|
||||
vTaskCoreAffinitySet(loop3Handle, CORE_1);
|
||||
xTaskCreate(loop4, "loop4", STACK_SIZE, NULL, 1, &loop4Handle);
|
||||
vTaskCoreAffinitySet(loop4Handle, CORE_0);
|
||||
xTaskCreate(loop5, "loop5", STACK_SIZE, NULL, 1, &loop5Handle);
|
||||
vTaskCoreAffinitySet(loop5Handle, CORE_1);
|
||||
// xTaskCreate(loop6, "loop6", STACK_SIZE, NULL, 1, &loop6Handle);
|
||||
// vTaskCoreAffinitySet(loop6Handle, CORE_0);
|
||||
// xTaskCreate(loop7, "loop7", STACK_SIZE, NULL, 1, &loop7Handle);
|
||||
// vTaskCoreAffinitySet(loop7Handle, CORE_1);
|
||||
}
|
||||
static int _loop[8];
|
||||
|
||||
void loop()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
_loop[0]++;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(500);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(500);
|
||||
for (int i=0; i<8; i++) Serial.printf("%d ", _loop[i]);
|
||||
Serial.println("");
|
||||
}
|
||||
}
|
||||
|
||||
void loop1()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
_loop[1]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "foo");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop2(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[2]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "bar");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop3(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[3]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "yeah");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop4(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[4]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "baz");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop5(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[5]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "asd");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop6(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[6]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "lol");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void loop7(void *pvPramaters)
|
||||
{
|
||||
(void) pvPramaters;
|
||||
while (1)
|
||||
{
|
||||
_loop[7]++;
|
||||
char *tmp;
|
||||
|
||||
semphrTakeConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
tmp = (char *)malloc(10 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnMalloc, xSemaphoreMalloc);
|
||||
|
||||
strcpy(tmp, "yay");
|
||||
|
||||
if (tryRealloc)
|
||||
{
|
||||
semphrTakeConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
tmp = (char *)realloc(tmp, 20 * sizeof(char));
|
||||
semphrGiveConditional(useMutexOnRealloc, xSemaphoreRealloc);
|
||||
strcat(tmp, "_realloc");
|
||||
}
|
||||
|
||||
semphrTakeConditional(useMutexOnFree, xSemaphoreFree);
|
||||
free(tmp);
|
||||
semphrGiveConditional(useMutexOnFree, xSemaphoreFree);
|
||||
|
||||
delay(DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void semphrTakeConditional(bool useMutexOn, SemaphoreHandle_t xSemaphore)
|
||||
{
|
||||
if (useMutexOn)
|
||||
{
|
||||
xSemaphoreTake(xSemaphore, TickType_t(portMAX_DELAY));
|
||||
}
|
||||
}
|
||||
|
||||
void semphrGiveConditional(bool useMutexOn, SemaphoreHandle_t xSemaphore)
|
||||
{
|
||||
if (useMutexOn)
|
||||
{
|
||||
xSemaphoreGive(xSemaphore);
|
||||
}
|
||||
}
|
||||
Submodule libraries/FreeRTOS/lib/FreeRTOS-Kernel updated: 0b55ee70ad...b5942ddd07
@@ -25,24 +25,28 @@
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "timers.h"
|
||||
#include "semphr.h"
|
||||
|
||||
/* Arduino Core includes */
|
||||
#include <Arduino.h>
|
||||
#include <RP2040USB.h>
|
||||
#include "tusb.h"
|
||||
|
||||
|
||||
/* Raspberry PI Pico includes */
|
||||
#include <pico.h>
|
||||
#include <pico/time.h>
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "timers.h"
|
||||
#include "_freertos.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
extern void __initFreeRTOSMutexes();
|
||||
void initFreeRTOS(void) {
|
||||
__initFreeRTOSMutexes();
|
||||
}
|
||||
|
||||
extern void setup() __attribute__((weak));
|
||||
@@ -107,7 +111,7 @@ extern "C" void yield() {
|
||||
extern mutex_t __usb_mutex;
|
||||
static TaskHandle_t __usbTask;
|
||||
static void __usb(void *param);
|
||||
|
||||
extern volatile bool __freeRTOSinitted;
|
||||
void startFreeRTOS(void) {
|
||||
|
||||
TaskHandle_t c0;
|
||||
@@ -121,6 +125,7 @@ void startFreeRTOS(void) {
|
||||
}
|
||||
|
||||
// Initialise and run the freeRTOS scheduler. Execution should never return here.
|
||||
__freeRTOSinitted = true;
|
||||
vTaskStartScheduler();
|
||||
|
||||
while (true) {
|
||||
@@ -392,3 +397,42 @@ void __USBStart() {
|
||||
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
|
||||
vTaskCoreAffinitySet(__usbTask, 1 << 0);
|
||||
}
|
||||
|
||||
|
||||
// Interfaces for the main core to use FreeRTOS mutexes
|
||||
extern "C" {
|
||||
|
||||
SemaphoreHandle_t __freertos_mutex_create() {
|
||||
return xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
SemaphoreHandle_t _freertos_recursive_mutex_create() {
|
||||
return xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTake(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTake(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGive(mtx);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTakeRecursive(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGiveRecursive(mtx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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,182 @@
|
||||
/*
|
||||
HTTPUpdateServer - Support simple web based OTA
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Ported from the ESP8266 Arduino core, (c) copyright multiple authors
|
||||
|
||||
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 <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,65 @@
|
||||
/*
|
||||
HTTPUpdateServer - Support simple web based OTA
|
||||
Modified 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Ported from the ESP8266 Arduino core, (c) copyright multiple authors
|
||||
|
||||
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 <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>;
|
||||
@@ -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
|
||||
|
||||
@@ -1100,7 +1100,7 @@ protected:
|
||||
};
|
||||
|
||||
public:
|
||||
uint16_t m_u16ID; // Query ID (used only in lagacy queries)
|
||||
uint16_t m_u16ID; // Query ID (used only in legacy queries)
|
||||
stcMDNS_RRQuestion* m_pQuestions; // A list of queries
|
||||
uint8_t m_u8HostReplyMask; // Flags for reply components/answers
|
||||
bool m_bLegacyQuery; // Flag: Legacy query
|
||||
|
||||
@@ -1980,7 +1980,7 @@ uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader&
|
||||
#ifdef MDNS_IP6_SUPPORT
|
||||
// TODO
|
||||
#endif
|
||||
} // Address qeuest
|
||||
} // Address quest
|
||||
|
||||
stcMDNS_RRDomain hostDomain;
|
||||
if ((_buildDomainForHost(m_pcHostname, hostDomain))
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MDNS_PRIV_H
|
||||
#define MDNS_PRIV_H
|
||||
#pragma once
|
||||
|
||||
namespace esp8266 {
|
||||
|
||||
@@ -186,5 +185,3 @@ namespace MDNSImplementation {
|
||||
|
||||
// Include the main header, so the submodlues only need to include this header
|
||||
#include "LEAmDNS.h"
|
||||
|
||||
#endif // MDNS_PRIV_H
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MDNS_LWIPDEFS_H
|
||||
#define MDNS_LWIPDEFS_H
|
||||
#pragma once
|
||||
|
||||
#include <lwip/prot/dns.h> // DNS_RRTYPE_xxx, DNS_MQUERY_PORT
|
||||
|
||||
#endif // MDNS_LWIPDEFS_H
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <SDFS.h>
|
||||
|
||||
// Choose the filesystem to test
|
||||
// WARNING: The filesystem will be formatted at the start of the test!
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __LITTLEFS_H
|
||||
#define __LITTLEFS_H
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <FS.h>
|
||||
@@ -681,6 +679,3 @@ protected:
|
||||
extern FS LittleFS;
|
||||
using littlefs_impl::LittleFSConfig;
|
||||
#endif // ARDUINO
|
||||
|
||||
|
||||
#endif // !defined(__LITTLEFS_H)
|
||||
|
||||
@@ -52,7 +52,7 @@ void loop() {
|
||||
|
||||
// Print samples to the serial monitor or plotter
|
||||
for (int i = 0; i < samplesRead; i++) {
|
||||
if(channels == 2) {
|
||||
if (channels == 2) {
|
||||
Serial.print("L:");
|
||||
Serial.print(sampleBuffer[i]);
|
||||
Serial.print(" R:");
|
||||
@@ -67,9 +67,9 @@ void loop() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to process the data from the PDM microphone.
|
||||
* NOTE: This callback is executed as part of an ISR.
|
||||
* Therefore using `Serial` to print messages inside this function isn't supported.
|
||||
Callback function to process the data from the PDM microphone.
|
||||
NOTE: This callback is executed as part of an ISR.
|
||||
Therefore using `Serial` to print messages inside this function isn't supported.
|
||||
* */
|
||||
void onPDMdata() {
|
||||
// Query the number of available bytes
|
||||
|
||||
+43
-45
@@ -1,74 +1,72 @@
|
||||
/*
|
||||
Copyright (c) 2019 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2019 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is 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.
|
||||
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
|
||||
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 _PDM_H_INCLUDED
|
||||
#define _PDM_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
//#include <pinDefinitions.h>
|
||||
|
||||
#include "utility/PDMDoubleBuffer.h"
|
||||
|
||||
class PDMClass
|
||||
{
|
||||
class PDMClass {
|
||||
public:
|
||||
PDMClass(int dinPin, int clkPin, int pwrPin);
|
||||
virtual ~PDMClass();
|
||||
PDMClass(int dinPin, int clkPin, int pwrPin);
|
||||
virtual ~PDMClass();
|
||||
|
||||
int begin(int channels, int sampleRate);
|
||||
void end();
|
||||
int begin(int channels, int sampleRate);
|
||||
void end();
|
||||
|
||||
virtual int available();
|
||||
virtual int read(void* buffer, size_t size);
|
||||
virtual int available();
|
||||
virtual int read(void* buffer, size_t size);
|
||||
|
||||
void onReceive(void(*)(void));
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
//PORTENTA_H7 min -12 max 51
|
||||
//NANO 33 BLE SENSe min 0 max 80
|
||||
void setGain(int gain);
|
||||
void setBufferSize(int bufferSize);
|
||||
size_t getBufferSize();
|
||||
//PORTENTA_H7 min -12 max 51
|
||||
//NANO 33 BLE SENSe min 0 max 80
|
||||
void setGain(int gain);
|
||||
void setBufferSize(int bufferSize);
|
||||
size_t getBufferSize();
|
||||
|
||||
// private:
|
||||
void IrqHandler(bool halftranfer);
|
||||
// private:
|
||||
void IrqHandler(bool halftranfer);
|
||||
|
||||
private:
|
||||
int _dinPin;
|
||||
int _clkPin;
|
||||
int _pwrPin;
|
||||
int _dinPin;
|
||||
int _clkPin;
|
||||
int _pwrPin;
|
||||
|
||||
int _channels;
|
||||
int _samplerate;
|
||||
int _channels;
|
||||
int _samplerate;
|
||||
|
||||
int _gain;
|
||||
int _init;
|
||||
int _gain;
|
||||
int _init;
|
||||
|
||||
// Hardware peripherals used
|
||||
uint _dmaChannel;
|
||||
PIO _pio;
|
||||
int _smIdx;
|
||||
int _pgmOffset;
|
||||
// Hardware peripherals used
|
||||
uint _dmaChannel;
|
||||
PIO _pio;
|
||||
int _smIdx;
|
||||
int _pgmOffset;
|
||||
|
||||
PDMDoubleBuffer _doubleBuffer;
|
||||
PDMDoubleBuffer _doubleBuffer;
|
||||
|
||||
void (*_onReceive)(void);
|
||||
void (*_onReceive)(void);
|
||||
};
|
||||
|
||||
#ifdef PIN_PDM_DIN
|
||||
extern PDMClass PDM;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
/**
|
||||
*******************************************************************************
|
||||
* @file OpenPDMFilter.c
|
||||
* @author CL
|
||||
* @version V1.0.0
|
||||
* @date 9-September-2015
|
||||
* @brief Open PDM audio software decoding Library.
|
||||
* This Library is used to decode and reconstruct the audio signal
|
||||
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
@file OpenPDMFilter.c
|
||||
@author CL
|
||||
@version V1.0.0
|
||||
@date 9-September-2015
|
||||
@brief Open PDM audio software decoding Library.
|
||||
This Library is used to decode and reconstruct the audio signal
|
||||
produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
*******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
@attention
|
||||
|
||||
<h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*******************************************************************************
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
@@ -48,266 +48,255 @@ int32_t lut[256][DECIMATION_MAX / 8][SINCN];
|
||||
/* Functions -----------------------------------------------------------------*/
|
||||
|
||||
#ifdef USE_LUT
|
||||
int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn];
|
||||
int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn) {
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn];
|
||||
}
|
||||
int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn];
|
||||
int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn) {
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn];
|
||||
}
|
||||
int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn] +
|
||||
lut[data[8]][8][sincn] +
|
||||
lut[data[9]][9][sincn] +
|
||||
lut[data[10]][10][sincn] +
|
||||
lut[data[11]][11][sincn] +
|
||||
lut[data[12]][12][sincn] +
|
||||
lut[data[13]][13][sincn] +
|
||||
lut[data[14]][14][sincn] +
|
||||
lut[data[15]][15][sincn];
|
||||
int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn) {
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn] +
|
||||
lut[data[8]][8][sincn] +
|
||||
lut[data[9]][9][sincn] +
|
||||
lut[data[10]][10][sincn] +
|
||||
lut[data[11]][11][sincn] +
|
||||
lut[data[12]][12][sincn] +
|
||||
lut[data[13]][13][sincn] +
|
||||
lut[data[14]][14][sincn] +
|
||||
lut[data[15]][15][sincn];
|
||||
}
|
||||
int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn] +
|
||||
lut[data[16]][8][sincn] +
|
||||
lut[data[18]][9][sincn] +
|
||||
lut[data[20]][10][sincn] +
|
||||
lut[data[22]][11][sincn] +
|
||||
lut[data[24]][12][sincn] +
|
||||
lut[data[26]][13][sincn] +
|
||||
lut[data[28]][14][sincn] +
|
||||
lut[data[30]][15][sincn];
|
||||
int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn) {
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn] +
|
||||
lut[data[16]][8][sincn] +
|
||||
lut[data[18]][9][sincn] +
|
||||
lut[data[20]][10][sincn] +
|
||||
lut[data[22]][11][sincn] +
|
||||
lut[data[24]][12][sincn] +
|
||||
lut[data[26]][13][sincn] +
|
||||
lut[data[28]][14][sincn] +
|
||||
lut[data[30]][15][sincn];
|
||||
}
|
||||
int32_t (* filter_tables_64[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64};
|
||||
int32_t (* filter_tables_128[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128};
|
||||
int32_t (* filter_tables_64[2])(uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64};
|
||||
int32_t (* filter_tables_128[2])(uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128};
|
||||
#else
|
||||
int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param)
|
||||
{
|
||||
uint8_t c, i;
|
||||
uint16_t data_index = 0;
|
||||
uint32_t *coef_p = &coef[sincn][0];
|
||||
int32_t F = 0;
|
||||
uint8_t decimation = param->Decimation;
|
||||
uint8_t channels = param->In_MicChannels;
|
||||
int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param) {
|
||||
uint8_t c, i;
|
||||
uint16_t data_index = 0;
|
||||
uint32_t *coef_p = &coef[sincn][0];
|
||||
int32_t F = 0;
|
||||
uint8_t decimation = param->Decimation;
|
||||
uint8_t channels = param->In_MicChannels;
|
||||
|
||||
for (i = 0; i < decimation; i += 8) {
|
||||
c = data[data_index];
|
||||
F += ((c >> 7) ) * coef_p[i ] +
|
||||
((c >> 6) & 0x01) * coef_p[i + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[i + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[i + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[i + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[i + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[i + 6] +
|
||||
((c ) & 0x01) * coef_p[i + 7];
|
||||
data_index += channels;
|
||||
}
|
||||
return F;
|
||||
for (i = 0; i < decimation; i += 8) {
|
||||
c = data[data_index];
|
||||
F += ((c >> 7)) * coef_p[i ] +
|
||||
((c >> 6) & 0x01) * coef_p[i + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[i + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[i + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[i + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[i + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[i + 6] +
|
||||
((c) & 0x01) * coef_p[i + 7];
|
||||
data_index += channels;
|
||||
}
|
||||
return F;
|
||||
}
|
||||
#endif
|
||||
|
||||
void convolve(uint32_t Signal[/* SignalLen */], unsigned short SignalLen,
|
||||
uint32_t Kernel[/* KernelLen */], unsigned short KernelLen,
|
||||
uint32_t Result[/* SignalLen + KernelLen - 1 */])
|
||||
{
|
||||
uint16_t n;
|
||||
uint32_t Result[/* SignalLen + KernelLen - 1 */]) {
|
||||
uint16_t n;
|
||||
|
||||
for (n = 0; n < SignalLen + KernelLen - 1; n++)
|
||||
{
|
||||
unsigned short kmin, kmax, k;
|
||||
|
||||
Result[n] = 0;
|
||||
|
||||
kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
|
||||
kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
|
||||
|
||||
for (k = kmin; k <= kmax; k++) {
|
||||
Result[n] += Signal[k] * Kernel[n - k];
|
||||
for (n = 0; n < SignalLen + KernelLen - 1; n++) {
|
||||
unsigned short kmin, kmax, k;
|
||||
|
||||
Result[n] = 0;
|
||||
|
||||
kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
|
||||
kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
|
||||
|
||||
for (k = kmin; k <= kmax; k++) {
|
||||
Result[n] += Signal[k] * Kernel[n - k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint16_t i, j;
|
||||
int64_t sum = 0;
|
||||
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param) {
|
||||
uint16_t i, j;
|
||||
int64_t sum = 0;
|
||||
|
||||
uint8_t decimation = Param->Decimation;
|
||||
uint8_t decimation = Param->Decimation;
|
||||
|
||||
for (i = 0; i < SINCN; i++) {
|
||||
Param->Coef[i] = 0;
|
||||
Param->bit[i] = 0;
|
||||
}
|
||||
for (i = 0; i < decimation; i++) {
|
||||
sinc1[i] = 1;
|
||||
}
|
||||
|
||||
Param->OldOut = Param->OldIn = Param->OldZ = 0;
|
||||
Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t) (Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0);
|
||||
Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t) (Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0);
|
||||
|
||||
Param->FilterLen = decimation * SINCN;
|
||||
sinc[0] = 0;
|
||||
sinc[decimation * SINCN - 1] = 0;
|
||||
convolve(sinc1, decimation, sinc1, decimation, sinc2);
|
||||
convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]);
|
||||
for(j = 0; j < SINCN; j++) {
|
||||
for (i = 0; i < SINCN; i++) {
|
||||
Param->Coef[i] = 0;
|
||||
Param->bit[i] = 0;
|
||||
}
|
||||
for (i = 0; i < decimation; i++) {
|
||||
coef[j][i] = sinc[j * decimation + i];
|
||||
sum += sinc[j * decimation + i];
|
||||
sinc1[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub_const = sum >> 1;
|
||||
div_const = sub_const * Param->MaxVolume / 32768 / Param->filterGain;
|
||||
div_const = (div_const == 0 ? 1 : div_const);
|
||||
Param->OldOut = Param->OldIn = Param->OldZ = 0;
|
||||
Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t)(Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0);
|
||||
Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t)(Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0);
|
||||
|
||||
Param->FilterLen = decimation * SINCN;
|
||||
sinc[0] = 0;
|
||||
sinc[decimation * SINCN - 1] = 0;
|
||||
convolve(sinc1, decimation, sinc1, decimation, sinc2);
|
||||
convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]);
|
||||
for (j = 0; j < SINCN; j++) {
|
||||
for (i = 0; i < decimation; i++) {
|
||||
coef[j][i] = sinc[j * decimation + i];
|
||||
sum += sinc[j * decimation + i];
|
||||
}
|
||||
}
|
||||
|
||||
sub_const = sum >> 1;
|
||||
div_const = sub_const * Param->MaxVolume / 32768 / Param->filterGain;
|
||||
div_const = (div_const == 0 ? 1 : div_const);
|
||||
|
||||
#ifdef USE_LUT
|
||||
/* Look-Up Table. */
|
||||
uint16_t c, d, s;
|
||||
for (s = 0; s < SINCN; s++)
|
||||
{
|
||||
uint32_t *coef_p = &coef[s][0];
|
||||
for (c = 0; c < 256; c++)
|
||||
for (d = 0; d < decimation / 8; d++)
|
||||
lut[c][d][s] = ((c >> 7) ) * coef_p[d * 8 ] +
|
||||
((c >> 6) & 0x01) * coef_p[d * 8 + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[d * 8 + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[d * 8 + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[d * 8 + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[d * 8 + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[d * 8 + 6] +
|
||||
((c ) & 0x01) * coef_p[d * 8 + 7];
|
||||
}
|
||||
/* Look-Up Table. */
|
||||
uint16_t c, d, s;
|
||||
for (s = 0; s < SINCN; s++) {
|
||||
uint32_t *coef_p = &coef[s][0];
|
||||
for (c = 0; c < 256; c++)
|
||||
for (d = 0; d < decimation / 8; d++)
|
||||
lut[c][d][s] = ((c >> 7)) * coef_p[d * 8 ] +
|
||||
((c >> 6) & 0x01) * coef_p[d * 8 + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[d * 8 + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[d * 8 + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[d * 8 + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[d * 8 + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[d * 8 + 6] +
|
||||
((c) & 0x01) * coef_p[d * 8 + 7];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_64(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
void Open_PDM_Filter_64(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param) {
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
|
||||
#ifdef USE_LUT
|
||||
uint8_t j = channels - 1;
|
||||
uint8_t j = channels - 1;
|
||||
#endif
|
||||
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
#ifdef USE_LUT
|
||||
Z0 = filter_tables_64[j](data, 0);
|
||||
Z1 = filter_tables_64[j](data, 1);
|
||||
Z2 = filter_tables_64[j](data, 2);
|
||||
Z0 = filter_tables_64[j](data, 0);
|
||||
Z1 = filter_tables_64[j](data, 1);
|
||||
Z2 = filter_tables_64[j](data, 2);
|
||||
#else
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
#endif
|
||||
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_128(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
void Open_PDM_Filter_128(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param) {
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
|
||||
#ifdef USE_LUT
|
||||
uint8_t j = channels - 1;
|
||||
uint8_t j = channels - 1;
|
||||
#endif
|
||||
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
#ifdef USE_LUT
|
||||
Z0 = filter_tables_128[j](data, 0);
|
||||
Z1 = filter_tables_128[j](data, 1);
|
||||
Z2 = filter_tables_128[j](data, 2);
|
||||
Z0 = filter_tables_128[j](data, 0);
|
||||
Z1 = filter_tables_128[j](data, 1);
|
||||
Z2 = filter_tables_128[j](data, 2);
|
||||
#else
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
#endif
|
||||
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
/**
|
||||
*******************************************************************************
|
||||
* @file OpenPDMFilter.h
|
||||
* @author CL
|
||||
* @version V1.0.0
|
||||
* @date 9-September-2015
|
||||
* @brief Header file for Open PDM audio software decoding Library.
|
||||
* This Library is used to decode and reconstruct the audio signal
|
||||
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
@file OpenPDMFilter.h
|
||||
@author CL
|
||||
@version V1.0.0
|
||||
@date 9-September-2015
|
||||
@brief Header file for Open PDM audio software decoding Library.
|
||||
This Library is used to decode and reconstruct the audio signal
|
||||
produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
*******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
@attention
|
||||
|
||||
<h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*******************************************************************************
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
@@ -33,7 +33,7 @@
|
||||
#define __OPENPDMFILTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
@@ -45,10 +45,10 @@
|
||||
/* Definitions ---------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Enable to use a Look-Up Table to improve performances while using more FLASH
|
||||
* and RAM memory.
|
||||
* Note: Without Look-Up Table up to stereo@16KHz configuration is supported.
|
||||
*/
|
||||
Enable to use a Look-Up Table to improve performances while using more FLASH
|
||||
and RAM memory.
|
||||
Note: Without Look-Up Table up to stereo@16KHz configuration is supported.
|
||||
*/
|
||||
#define USE_LUT
|
||||
|
||||
#define SINCN 3
|
||||
@@ -63,24 +63,24 @@
|
||||
/* Types ---------------------------------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
/* Public */
|
||||
float LP_HZ;
|
||||
float HP_HZ;
|
||||
uint16_t Fs;
|
||||
unsigned int nSamples;
|
||||
uint8_t In_MicChannels;
|
||||
uint8_t Out_MicChannels;
|
||||
uint8_t Decimation;
|
||||
uint8_t MaxVolume;
|
||||
/* Private */
|
||||
uint32_t Coef[SINCN];
|
||||
uint16_t FilterLen;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
uint16_t LP_ALFA;
|
||||
uint16_t HP_ALFA;
|
||||
uint16_t bit[5];
|
||||
uint16_t byte;
|
||||
uint16_t filterGain;
|
||||
/* Public */
|
||||
float LP_HZ;
|
||||
float HP_HZ;
|
||||
uint16_t Fs;
|
||||
unsigned int nSamples;
|
||||
uint8_t In_MicChannels;
|
||||
uint8_t Out_MicChannels;
|
||||
uint8_t Decimation;
|
||||
uint8_t MaxVolume;
|
||||
/* Private */
|
||||
uint32_t Coef[SINCN];
|
||||
uint16_t FilterLen;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
uint16_t LP_ALFA;
|
||||
uint16_t HP_ALFA;
|
||||
uint16_t bit[5];
|
||||
uint16_t byte;
|
||||
uint16_t filterGain;
|
||||
} TPDMFilter_InitStruct;
|
||||
|
||||
|
||||
|
||||
+141
-152
@@ -9,7 +9,7 @@ extern "C" {
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/clocks.h"
|
||||
}
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "pdm.pio.h"
|
||||
static PIOProgram _pdmPgm(&pdm_pio_program);
|
||||
|
||||
@@ -18,7 +18,7 @@ static PIOProgram _pdmPgm(&pdm_pio_program);
|
||||
uint8_t rawBuffer0[RAW_BUFFER_SIZE];
|
||||
uint8_t rawBuffer1[RAW_BUFFER_SIZE];
|
||||
uint8_t* rawBuffer[2] = {rawBuffer0, rawBuffer1};
|
||||
volatile int rawBufferIndex = 0;
|
||||
volatile int rawBufferIndex = 0;
|
||||
|
||||
int decimation = 64;
|
||||
|
||||
@@ -30,179 +30,168 @@ int16_t* volatile finalBuffer;
|
||||
TPDMFilter_InitStruct filter;
|
||||
|
||||
extern "C" {
|
||||
__attribute__((__used__)) void dmaHandler(void)
|
||||
{
|
||||
PDM.IrqHandler(true);
|
||||
}
|
||||
__attribute__((__used__)) void dmaHandler(void) {
|
||||
PDM.IrqHandler(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PDMClass::PDMClass(int dinPin, int clkPin, int pwrPin) :
|
||||
_dinPin(dinPin),
|
||||
_clkPin(clkPin),
|
||||
_pwrPin(pwrPin),
|
||||
_onReceive(NULL),
|
||||
_gain(-1),
|
||||
_channels(-1),
|
||||
_samplerate(-1),
|
||||
_init(-1),
|
||||
_dmaChannel(0),
|
||||
_pio(nullptr),
|
||||
_smIdx(-1),
|
||||
_pgmOffset(-1)
|
||||
{
|
||||
_dinPin(dinPin),
|
||||
_clkPin(clkPin),
|
||||
_pwrPin(pwrPin),
|
||||
_onReceive(NULL),
|
||||
_gain(-1),
|
||||
_channels(-1),
|
||||
_samplerate(-1),
|
||||
_init(-1),
|
||||
_dmaChannel(0),
|
||||
_pio(nullptr),
|
||||
_smIdx(-1),
|
||||
_pgmOffset(-1) {
|
||||
}
|
||||
|
||||
PDMClass::~PDMClass()
|
||||
{
|
||||
PDMClass::~PDMClass() {
|
||||
}
|
||||
|
||||
int PDMClass::begin(int channels, int sampleRate)
|
||||
{
|
||||
//_channels = channels; // only one channel available
|
||||
int PDMClass::begin(int channels, int sampleRate) {
|
||||
//_channels = channels; // only one channel available
|
||||
|
||||
// clear the final buffers
|
||||
_doubleBuffer.reset();
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
int finalBufferLength = _doubleBuffer.availableForWrite() / sizeof(int16_t);
|
||||
_doubleBuffer.swap(0);
|
||||
// clear the final buffers
|
||||
_doubleBuffer.reset();
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
int finalBufferLength = _doubleBuffer.availableForWrite() / sizeof(int16_t);
|
||||
_doubleBuffer.swap(0);
|
||||
|
||||
int rawBufferLength = RAW_BUFFER_SIZE / (decimation / 8);
|
||||
// Saturate number of samples. Remaining bytes are dropped.
|
||||
if (rawBufferLength > finalBufferLength) {
|
||||
rawBufferLength = finalBufferLength;
|
||||
}
|
||||
int rawBufferLength = RAW_BUFFER_SIZE / (decimation / 8);
|
||||
// Saturate number of samples. Remaining bytes are dropped.
|
||||
if (rawBufferLength > finalBufferLength) {
|
||||
rawBufferLength = finalBufferLength;
|
||||
}
|
||||
|
||||
/* Initialize Open PDM library */
|
||||
filter.Fs = sampleRate;
|
||||
filter.nSamples = rawBufferLength;
|
||||
filter.LP_HZ = sampleRate/2;
|
||||
filter.HP_HZ = 10;
|
||||
filter.In_MicChannels = 1;
|
||||
filter.Out_MicChannels = 1;
|
||||
filter.Decimation = decimation;
|
||||
if(_gain == -1) {
|
||||
_gain = FILTER_GAIN;
|
||||
}
|
||||
filter.filterGain = _gain;
|
||||
Open_PDM_Filter_Init(&filter);
|
||||
|
||||
// Configure PIO state machine
|
||||
float clkDiv = (float)clock_get_hz(clk_sys) / sampleRate / decimation / 2;
|
||||
|
||||
if (!_pdmPgm.prepare(&_pio, &_smIdx, &_pgmOffset)) {
|
||||
// ERROR, no free slots
|
||||
return -1;
|
||||
}
|
||||
pdm_pio_program_init(_pio, _smIdx, _pgmOffset, _clkPin, _dinPin, clkDiv);
|
||||
|
||||
// Wait for microphone
|
||||
delay(100);
|
||||
|
||||
// Configure DMA for transferring PIO rx buffer to raw buffers
|
||||
_dmaChannel = dma_claim_unused_channel(false);
|
||||
dma_channel_config c = dma_channel_get_default_config(_dmaChannel);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
channel_config_set_dreq(&c, pio_get_dreq(_pio, _smIdx, false));
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
|
||||
// Clear DMA interrupts
|
||||
dma_hw->ints0 = 1u << _dmaChannel;
|
||||
// Enable DMA interrupts
|
||||
dma_channel_set_irq0_enabled(_dmaChannel, true);
|
||||
// Share but allocate a high priority to the interrupt
|
||||
irq_add_shared_handler(DMA_IRQ_0, dmaHandler, 0);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
dma_channel_configure(_dmaChannel, &c,
|
||||
rawBuffer[rawBufferIndex], // Destinatinon pointer
|
||||
&_pio->rxf[_smIdx], // Source pointer
|
||||
RAW_BUFFER_SIZE, // Number of transfers
|
||||
true // Start immediately
|
||||
);
|
||||
|
||||
_init = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PDMClass::end()
|
||||
{
|
||||
dma_channel_abort(_dmaChannel);
|
||||
pinMode(_clkPin, INPUT);
|
||||
}
|
||||
|
||||
int PDMClass::available()
|
||||
{
|
||||
//NVIC_DisableIRQ(DMA_IRQ_0n);
|
||||
//uint32_t interrupts = save_and_disable_interrupts();
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
size_t avail = _doubleBuffer.available();
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
//restore_interrupts(interrupts);
|
||||
//NVIC_EnableIRQ(DMA_IRQ_0n);
|
||||
return avail;
|
||||
}
|
||||
|
||||
int PDMClass::read(void* buffer, size_t size)
|
||||
{
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
int read = _doubleBuffer.read(buffer, size);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
return read;
|
||||
}
|
||||
|
||||
void PDMClass::onReceive(void(*function)(void))
|
||||
{
|
||||
_onReceive = function;
|
||||
}
|
||||
|
||||
void PDMClass::setGain(int gain)
|
||||
{
|
||||
_gain = gain;
|
||||
if(_init == 1) {
|
||||
/* Initialize Open PDM library */
|
||||
filter.Fs = sampleRate;
|
||||
filter.nSamples = rawBufferLength;
|
||||
filter.LP_HZ = sampleRate / 2;
|
||||
filter.HP_HZ = 10;
|
||||
filter.In_MicChannels = 1;
|
||||
filter.Out_MicChannels = 1;
|
||||
filter.Decimation = decimation;
|
||||
if (_gain == -1) {
|
||||
_gain = FILTER_GAIN;
|
||||
}
|
||||
filter.filterGain = _gain;
|
||||
Open_PDM_Filter_Init(&filter);
|
||||
}
|
||||
|
||||
// Configure PIO state machine
|
||||
float clkDiv = (float)clock_get_hz(clk_sys) / sampleRate / decimation / 2;
|
||||
|
||||
if (!_pdmPgm.prepare(&_pio, &_smIdx, &_pgmOffset)) {
|
||||
// ERROR, no free slots
|
||||
return -1;
|
||||
}
|
||||
pdm_pio_program_init(_pio, _smIdx, _pgmOffset, _clkPin, _dinPin, clkDiv);
|
||||
|
||||
// Wait for microphone
|
||||
delay(100);
|
||||
|
||||
// Configure DMA for transferring PIO rx buffer to raw buffers
|
||||
_dmaChannel = dma_claim_unused_channel(false);
|
||||
dma_channel_config c = dma_channel_get_default_config(_dmaChannel);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
channel_config_set_dreq(&c, pio_get_dreq(_pio, _smIdx, false));
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
|
||||
// Clear DMA interrupts
|
||||
dma_hw->ints0 = 1u << _dmaChannel;
|
||||
// Enable DMA interrupts
|
||||
dma_channel_set_irq0_enabled(_dmaChannel, true);
|
||||
// Share but allocate a high priority to the interrupt
|
||||
irq_add_shared_handler(DMA_IRQ_0, dmaHandler, 0);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
dma_channel_configure(_dmaChannel, &c,
|
||||
rawBuffer[rawBufferIndex], // Destinatinon pointer
|
||||
&_pio->rxf[_smIdx], // Source pointer
|
||||
RAW_BUFFER_SIZE, // Number of transfers
|
||||
true // Start immediately
|
||||
);
|
||||
|
||||
_init = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PDMClass::setBufferSize(int bufferSize)
|
||||
{
|
||||
_doubleBuffer.setSize(bufferSize);
|
||||
void PDMClass::end() {
|
||||
dma_channel_abort(_dmaChannel);
|
||||
pinMode(_clkPin, INPUT);
|
||||
}
|
||||
|
||||
void PDMClass::IrqHandler(bool halftranfer)
|
||||
{
|
||||
static int cutSamples = 100;
|
||||
int PDMClass::available() {
|
||||
//NVIC_DisableIRQ(DMA_IRQ_0n);
|
||||
//uint32_t interrupts = save_and_disable_interrupts();
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
size_t avail = _doubleBuffer.available();
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
//restore_interrupts(interrupts);
|
||||
//NVIC_EnableIRQ(DMA_IRQ_0n);
|
||||
return avail;
|
||||
}
|
||||
|
||||
// Clear the interrupt request.
|
||||
dma_hw->ints0 = 1u << _dmaChannel;
|
||||
// Restart dma pointing to the other buffer
|
||||
int shadowIndex = rawBufferIndex ^ 1;
|
||||
dma_channel_set_write_addr(_dmaChannel, rawBuffer[shadowIndex], true);
|
||||
int PDMClass::read(void* buffer, size_t size) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
int read = _doubleBuffer.read(buffer, size);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
return read;
|
||||
}
|
||||
|
||||
if (_doubleBuffer.available()) {
|
||||
// buffer overflow, stop
|
||||
return end();
|
||||
}
|
||||
void PDMClass::onReceive(void(*function)(void)) {
|
||||
_onReceive = function;
|
||||
}
|
||||
|
||||
// fill final buffer with PCM samples
|
||||
Open_PDM_Filter_64(rawBuffer[rawBufferIndex], finalBuffer, 1, &filter);
|
||||
void PDMClass::setGain(int gain) {
|
||||
_gain = gain;
|
||||
if (_init == 1) {
|
||||
filter.filterGain = _gain;
|
||||
Open_PDM_Filter_Init(&filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (cutSamples) {
|
||||
memset(finalBuffer, 0, cutSamples);
|
||||
cutSamples = 0;
|
||||
}
|
||||
void PDMClass::setBufferSize(int bufferSize) {
|
||||
_doubleBuffer.setSize(bufferSize);
|
||||
}
|
||||
|
||||
// swap final buffer and raw buffers' indexes
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
_doubleBuffer.swap(filter.nSamples * sizeof(int16_t));
|
||||
rawBufferIndex = shadowIndex;
|
||||
void PDMClass::IrqHandler(bool halftranfer) {
|
||||
static int cutSamples = 100;
|
||||
|
||||
if (_onReceive) {
|
||||
_onReceive();
|
||||
}
|
||||
// Clear the interrupt request.
|
||||
dma_hw->ints0 = 1u << _dmaChannel;
|
||||
// Restart dma pointing to the other buffer
|
||||
int shadowIndex = rawBufferIndex ^ 1;
|
||||
dma_channel_set_write_addr(_dmaChannel, rawBuffer[shadowIndex], true);
|
||||
|
||||
if (_doubleBuffer.available()) {
|
||||
// buffer overflow, stop
|
||||
return end();
|
||||
}
|
||||
|
||||
// fill final buffer with PCM samples
|
||||
Open_PDM_Filter_64(rawBuffer[rawBufferIndex], finalBuffer, 1, &filter);
|
||||
|
||||
if (cutSamples) {
|
||||
memset(finalBuffer, 0, cutSamples);
|
||||
cutSamples = 0;
|
||||
}
|
||||
|
||||
// swap final buffer and raw buffers' indexes
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
_doubleBuffer.swap(filter.nSamples * sizeof(int16_t));
|
||||
rawBufferIndex = shadowIndex;
|
||||
|
||||
if (_onReceive) {
|
||||
_onReceive();
|
||||
}
|
||||
}
|
||||
#ifdef PIN_PDM_DIN
|
||||
PDMClass PDM(PIN_PDM_DIN, PIN_PDM_CLK, -1);
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
#define pdm_pio_wrap 1
|
||||
|
||||
static const uint16_t pdm_pio_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x9040, // 0: push iffull noblock side 1
|
||||
0x4001, // 1: in pins, 1 side 0
|
||||
// .wrap
|
||||
// .wrap_target
|
||||
0x9040, // 0: push iffull noblock side 1
|
||||
0x4001, // 1: in pins, 1 side 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
@@ -36,21 +36,21 @@ static inline pio_sm_config pdm_pio_program_get_default_config(uint offset) {
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
static inline void pdm_pio_program_init(PIO pio, uint sm, uint offset, uint clkPin, uint dataPin, float clkDiv) {
|
||||
pio_sm_config c = pdm_pio_program_get_default_config(offset);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
//sm_config_set_in_shift(&c, false, true, 8);
|
||||
sm_config_set_in_shift(&c, false, false, 8);
|
||||
sm_config_set_in_pins(&c, dataPin);
|
||||
sm_config_set_sideset_pins(&c, clkPin);
|
||||
sm_config_set_clkdiv(&c, clkDiv);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << clkPin) );
|
||||
//pio_gpio_init(pio, dataPin);
|
||||
pio_gpio_init(pio, clkPin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
pio_sm_config c = pdm_pio_program_get_default_config(offset);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
//sm_config_set_in_shift(&c, false, true, 8);
|
||||
sm_config_set_in_shift(&c, false, false, 8);
|
||||
sm_config_set_in_pins(&c, dataPin);
|
||||
sm_config_set_sideset_pins(&c, clkPin);
|
||||
sm_config_set_clkdiv(&c, clkDiv);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << clkPin));
|
||||
//pio_gpio_init(pio, dataPin);
|
||||
pio_gpio_init(pio, clkPin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is 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.
|
||||
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
|
||||
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 <stdlib.h>
|
||||
@@ -22,118 +22,106 @@
|
||||
#include "PDMDoubleBuffer.h"
|
||||
|
||||
PDMDoubleBuffer::PDMDoubleBuffer() :
|
||||
_size(DEFAULT_PDM_BUFFER_SIZE)
|
||||
{
|
||||
reset();
|
||||
_size(DEFAULT_PDM_BUFFER_SIZE) {
|
||||
reset();
|
||||
}
|
||||
|
||||
PDMDoubleBuffer::~PDMDoubleBuffer()
|
||||
{
|
||||
PDMDoubleBuffer::~PDMDoubleBuffer() {
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::setSize(int size)
|
||||
{
|
||||
_size = size;
|
||||
reset();
|
||||
void PDMDoubleBuffer::setSize(int size) {
|
||||
_size = size;
|
||||
reset();
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::getSize()
|
||||
{
|
||||
return _size;
|
||||
size_t PDMDoubleBuffer::getSize() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::reset()
|
||||
{
|
||||
_buffer[0] = (uint8_t*)realloc(_buffer[0], _size);
|
||||
_buffer[1] = (uint8_t*)realloc(_buffer[1], _size);
|
||||
void PDMDoubleBuffer::reset() {
|
||||
_buffer[0] = (uint8_t*)realloc(_buffer[0], _size);
|
||||
_buffer[1] = (uint8_t*)realloc(_buffer[1], _size);
|
||||
|
||||
memset(_buffer[0], 0x00, _size);
|
||||
memset(_buffer[1], 0x00, _size);
|
||||
memset(_buffer[0], 0x00, _size);
|
||||
memset(_buffer[1], 0x00, _size);
|
||||
|
||||
_index = 0;
|
||||
_length[0] = 0;
|
||||
_length[1] = 0;
|
||||
_readOffset[0] = 0;
|
||||
_readOffset[1] = 0;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::availableForWrite()
|
||||
{
|
||||
return (_size - (_length[_index] - _readOffset[_index]));
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::write(const void *buffer, size_t size)
|
||||
{
|
||||
size_t space = availableForWrite();
|
||||
|
||||
if (size > space) {
|
||||
size = space;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&_buffer[_index][_length[_index]], buffer, size);
|
||||
|
||||
_length[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::read(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
_readOffset[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::peek(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void* PDMDoubleBuffer::data()
|
||||
{
|
||||
return (void*)_buffer[_index];
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::available()
|
||||
{
|
||||
return _length[_index] - _readOffset[_index];
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::swap(int length)
|
||||
{
|
||||
if (_index == 0) {
|
||||
_index = 1;
|
||||
} else {
|
||||
_index = 0;
|
||||
}
|
||||
_length[0] = 0;
|
||||
_length[1] = 0;
|
||||
_readOffset[0] = 0;
|
||||
_readOffset[1] = 0;
|
||||
}
|
||||
|
||||
_length[_index] = length;
|
||||
_readOffset[_index] = 0;
|
||||
size_t PDMDoubleBuffer::availableForWrite() {
|
||||
return (_size - (_length[_index] - _readOffset[_index]));
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::write(const void *buffer, size_t size) {
|
||||
size_t space = availableForWrite();
|
||||
|
||||
if (size > space) {
|
||||
size = space;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&_buffer[_index][_length[_index]], buffer, size);
|
||||
|
||||
_length[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::read(void *buffer, size_t size) {
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
_readOffset[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::peek(void *buffer, size_t size) {
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void* PDMDoubleBuffer::data() {
|
||||
return (void*)_buffer[_index];
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::available() {
|
||||
return _length[_index] - _readOffset[_index];
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::swap(int length) {
|
||||
if (_index == 0) {
|
||||
_index = 1;
|
||||
} else {
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
_length[_index] = length;
|
||||
_readOffset[_index] = 0;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is 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.
|
||||
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
|
||||
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 _PDM_DOUBLE_BUFFER_H_INCLUDED
|
||||
@@ -24,31 +24,30 @@
|
||||
|
||||
#define DEFAULT_PDM_BUFFER_SIZE 512
|
||||
|
||||
class PDMDoubleBuffer
|
||||
{
|
||||
class PDMDoubleBuffer {
|
||||
public:
|
||||
PDMDoubleBuffer();
|
||||
virtual ~PDMDoubleBuffer();
|
||||
PDMDoubleBuffer();
|
||||
virtual ~PDMDoubleBuffer();
|
||||
|
||||
void setSize(int size);
|
||||
size_t getSize();
|
||||
void setSize(int size);
|
||||
size_t getSize();
|
||||
|
||||
void reset();
|
||||
void reset();
|
||||
|
||||
size_t availableForWrite();
|
||||
size_t write(const void *buffer, size_t size);
|
||||
size_t read(void *buffer, size_t size);
|
||||
size_t peek(void *buffer, size_t size);
|
||||
void* data();
|
||||
size_t available();
|
||||
void swap(int length = 0);
|
||||
size_t availableForWrite();
|
||||
size_t write(const void *buffer, size_t size);
|
||||
size_t read(void *buffer, size_t size);
|
||||
size_t peek(void *buffer, size_t size);
|
||||
void* data();
|
||||
size_t available();
|
||||
void swap(int length = 0);
|
||||
|
||||
private:
|
||||
uint8_t* _buffer[2] __attribute__((aligned (16)));
|
||||
int _size;
|
||||
volatile int _length[2];
|
||||
volatile int _readOffset[2];
|
||||
volatile int _index;
|
||||
uint8_t* _buffer[2] __attribute__((aligned(16)));
|
||||
int _size;
|
||||
volatile int _length[2];
|
||||
volatile int _readOffset[2];
|
||||
volatile int _index;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
}
|
||||
_page = new OTACmdPage;
|
||||
memset(_page, 0, sizeof(*_page));
|
||||
memcpy(_page->sign, "Pico OTA Format\0", sizeof(_page->sign));
|
||||
memcpy(_page->sign, "Pico OTA", sizeof(_page->sign));
|
||||
_page->count = 0;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user