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
|
||||
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
|
||||
@@ -185,7 +185,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
submodules: 'true'
|
||||
- name: Initialize needed submodules
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../libraries/Adafruit_TinyUSB_Arduino
|
||||
git submodule update --init
|
||||
cd ../..
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -211,4 +218,8 @@ jobs:
|
||||
- name: Build Fade Example
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Fade/Fade.ino
|
||||
- name: Build TinyUSB Example
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DUSE_TINYUSB" libraries/Adafruit_TinyUSB_Arduino/examples/CDC/cdc_multi/cdc_multi.ino
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DUSE_TINYUSB" libraries/Adafruit_TinyUSB_Arduino/examples/CDC/cdc_multi/cdc_multi.ino
|
||||
- name: Build WiFi Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
|
||||
- name: Build Signed OTA Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.DS_Store
|
||||
system
|
||||
tools/dist
|
||||
docs/_build
|
||||
ota/build
|
||||
tools/libpico/build
|
||||
platform.local.txt
|
||||
|
||||
+13
-1
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/earlephilhower/ArduinoCore-API.git
|
||||
[submodule "pico-sdk"]
|
||||
path = pico-sdk
|
||||
url = https://github.com/raspberrypi/pico-sdk.git
|
||||
url = https://github.com/earlephilhower/pico-sdk.git
|
||||
[submodule "system/pyserial"]
|
||||
path = tools/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
@@ -19,9 +19,21 @@
|
||||
[submodule "libraries/Mouse"]
|
||||
path = libraries/Mouse
|
||||
url = https://github.com/earlephilhower/Mouse
|
||||
[submodule "libraries/Joystick"]
|
||||
path = libraries/Joystick
|
||||
url = https://github.com/benjaminaigner/Joystick
|
||||
[submodule "libraries/Adafruit_TinyUSB_Arduino"]
|
||||
path = libraries/Adafruit_TinyUSB_Arduino
|
||||
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
|
||||
[submodule "libraries/FreeRTOS/lib/FreeRTOS-Kernel"]
|
||||
path = libraries/FreeRTOS/lib/FreeRTOS-Kernel
|
||||
url = https://github.com/earlephilhower/FreeRTOS-Kernel.git
|
||||
[submodule "tools/libbearssl/bearssl"]
|
||||
path = tools/libbearssl/bearssl
|
||||
url = https://github.com/earlephilhower/bearssl-esp8266.git
|
||||
[submodule "ota/uzlib"]
|
||||
path = ota/uzlib
|
||||
url = https://github.com/pfalcon/uzlib.git
|
||||
[submodule "libraries/http_parser/lib/http-parser"]
|
||||
path = libraries/http-parser/lib/http-parser
|
||||
url = https://github.com/nodejs/http-parser.git
|
||||
|
||||
@@ -11,6 +11,7 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
|
||||
# Supported Boards
|
||||
* Raspberry Pi Pico
|
||||
* Raspberry Pi Pico W
|
||||
* Adafruit Feather RP2040
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
* Adafruit KB2040
|
||||
@@ -23,11 +24,15 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* Cytron Maker Nano RP2040
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
* ExtremeElectronics RC2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 LTE
|
||||
* Invector Labs Challenger RP2040 LoRa
|
||||
* Invector Labs Challenger RP2040 SubGHz
|
||||
* Invector Labs Challenger RP2040 SD/RTC
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Shake RP2040
|
||||
* Seeed XIAO RP2040
|
||||
@@ -142,6 +147,10 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
* Filesystems (LittleFS and SD/SDFS)
|
||||
* Multicore support (setup1() and loop1())
|
||||
* FreeRTOS SMP support
|
||||
@@ -175,8 +184,16 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [Arduino-Pico](https://github.com/earlephilhower/arduino-pico) core files are licensed under the LGPL.
|
||||
* [LittleFS](https://github.com/ARMmbed/littlefs) library written by ARM Limited and released under the [BSD 3-clause license](https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md).
|
||||
* [UF2CONV.PY](https://github.com/microsoft/uf2) is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Some filesystem code taken from the [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) and licensed under the LGPL.
|
||||
* Networking and filesystem code taken from the [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) and licensed under the LGPL.
|
||||
* DHCP server for AP host mode from the [Micropython Project](https://micropython.org), distributed under the MIT License.
|
||||
* [FreeRTOS](https://freertos.org) is Copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [lwIP](https://savannah.nongnu.org/projects/lwip/) is (c) the Swedish Institute of Computer Science and licenced under the BSD license.
|
||||
* [BearSSL](https://bearssl.org) library written by Thomas Pornin, is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
* [UZLib](https://github.com/pfalcon/uzlib) is copyright (c) 2003 Joergen Ibsen and distributed under the zlib license.
|
||||
* [LEAmDNS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [http-parser](https://github.com/nodejs/http-parser) is copyright Joyent, Inc. and other Node contributors.
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others
|
||||
|
||||
-Earle F. Philhower, III
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
+4056
-1113
File diff suppressed because it is too large
Load Diff
+10
-4
@@ -18,8 +18,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -91,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
|
||||
@@ -112,5 +120,3 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // Arduino_h
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
class CoreMutex {
|
||||
public:
|
||||
CoreMutex(mutex_t *mutex) {
|
||||
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
|
||||
uint32_t owner;
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
if (debugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
|
||||
+5
-7
@@ -18,8 +18,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef FS_H
|
||||
#define FS_H
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
@@ -51,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;
|
||||
@@ -91,9 +92,8 @@ public:
|
||||
uint8_t obuf[256];
|
||||
size_t doneLen = 0;
|
||||
size_t sentLen;
|
||||
int i;
|
||||
|
||||
while (src.available() > sizeof(obuf)) {
|
||||
while ((size_t)src.available() > sizeof(obuf)) {
|
||||
src.read(obuf, sizeof(obuf));
|
||||
sentLen = write(obuf, sizeof(obuf));
|
||||
doneLen = doneLen + sentLen;
|
||||
@@ -264,5 +264,3 @@ using fs::SeekEnd;
|
||||
using fs::FSInfo;
|
||||
using fs::FSConfig;
|
||||
#endif //FS_NO_GLOBALS
|
||||
|
||||
#endif //FS_H
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
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 FSIMPL_H
|
||||
#define FSIMPL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -149,5 +149,3 @@ protected:
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#endif //FSIMPL_H
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
WiFiMutex.h - Ensure the timer-driven sys_check_timeouts doesn't
|
||||
get executed while we're in the user-level TCP stack.
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Implements the API defined by the Arduino WiFiNINA library,
|
||||
copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" volatile bool __inLWIP;
|
||||
|
||||
class LWIPMutex {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
__inLWIP = true;
|
||||
_ref++;
|
||||
}
|
||||
~LWIPMutex() {
|
||||
if (0 == --_ref) {
|
||||
__inLWIP = false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
static int _ref;
|
||||
};
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
PolledTimeout.h - Encapsulation of a polled Timeout
|
||||
|
||||
Copyright (c) 2018 Daniel Salazar. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::is_unsigned
|
||||
|
||||
#define IRAM_ATTR
|
||||
|
||||
namespace esp8266 {
|
||||
|
||||
|
||||
namespace polledTimeout {
|
||||
|
||||
namespace YieldPolicy {
|
||||
|
||||
struct DoNothing {
|
||||
static void execute() {}
|
||||
};
|
||||
|
||||
struct YieldOrSkip {
|
||||
static void execute() {} //{esp_yield();}
|
||||
};
|
||||
|
||||
template <unsigned long delayMs>
|
||||
struct YieldAndDelayMs {
|
||||
static void execute() {
|
||||
delay(delayMs);
|
||||
}
|
||||
};
|
||||
|
||||
} //YieldPolicy
|
||||
|
||||
namespace TimePolicy {
|
||||
|
||||
struct TimeSourceMillis {
|
||||
// time policy in milli-seconds based on millis()
|
||||
|
||||
using timeType = decltype(millis());
|
||||
static timeType time() {
|
||||
return millis();
|
||||
}
|
||||
static constexpr timeType ticksPerSecond = 1000;
|
||||
static constexpr timeType ticksPerSecondMax = 1000;
|
||||
};
|
||||
|
||||
struct TimeSourceCycles {
|
||||
// time policy based on esp_get_cycle_count()
|
||||
// this particular time measurement is intended to be called very often
|
||||
// (every loop, every yield)
|
||||
|
||||
using timeType = decltype(rp2040.getCycleCount());
|
||||
static timeType time() {
|
||||
return rp2040.getCycleCount();
|
||||
}
|
||||
static constexpr timeType ticksPerSecond = F_CPU;
|
||||
static constexpr timeType ticksPerSecondMax = F_CPU;
|
||||
};
|
||||
|
||||
template <typename TimeSourceType, unsigned long long second_th>
|
||||
// "second_th" units of timeType for one second
|
||||
struct TimeUnit {
|
||||
using timeType = typename TimeSourceType::timeType;
|
||||
|
||||
#if __GNUC__ < 5
|
||||
// gcc-4.8 cannot compile the constexpr-only version of this function
|
||||
// using #defines instead luckily works
|
||||
static constexpr timeType computeRangeCompensation() {
|
||||
#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond)
|
||||
#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick)
|
||||
|
||||
return ({
|
||||
fractional == 0 ?
|
||||
1 : // no need for compensation
|
||||
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
|
||||
});
|
||||
|
||||
#undef number_of_secondTh_in_one_tick
|
||||
#undef fractional
|
||||
}
|
||||
#else
|
||||
static constexpr timeType computeRangeCompensation() {
|
||||
return ({
|
||||
constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond;
|
||||
constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick;
|
||||
fractional == 0 ?
|
||||
1 : // no need for compensation
|
||||
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
static constexpr timeType ticksPerSecond = TimeSourceType::ticksPerSecond;
|
||||
static constexpr timeType ticksPerSecondMax = TimeSourceType::ticksPerSecondMax;
|
||||
static constexpr timeType rangeCompensate = computeRangeCompensation();
|
||||
static constexpr timeType user2UnitMultiplierMax = (ticksPerSecondMax * rangeCompensate) / second_th;
|
||||
static constexpr timeType user2UnitMultiplier = (ticksPerSecond * rangeCompensate) / second_th;
|
||||
static constexpr timeType user2UnitDivider = rangeCompensate;
|
||||
// std::numeric_limits<timeType>::max() is reserved
|
||||
static constexpr timeType timeMax = (std::numeric_limits<timeType>::max() - 1) / user2UnitMultiplierMax;
|
||||
|
||||
static timeType toTimeTypeUnit(const timeType userUnit) {
|
||||
return (userUnit * user2UnitMultiplier) / user2UnitDivider;
|
||||
}
|
||||
static timeType toUserUnit(const timeType internalUnit) {
|
||||
return (internalUnit * user2UnitDivider) / user2UnitMultiplier;
|
||||
}
|
||||
static timeType time() {
|
||||
return TimeSourceType::time();
|
||||
}
|
||||
};
|
||||
|
||||
using TimeMillis = TimeUnit< TimeSourceMillis, 1000 >;
|
||||
using TimeFastMillis = TimeUnit< TimeSourceCycles, 1000 >;
|
||||
using TimeFastMicros = TimeUnit< TimeSourceCycles, 1000000 >;
|
||||
using TimeFastNanos = TimeUnit< TimeSourceCycles, 1000000000 >;
|
||||
|
||||
} //TimePolicy
|
||||
|
||||
template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing, typename TimePolicyT = TimePolicy::TimeMillis>
|
||||
class timeoutTemplate {
|
||||
public:
|
||||
using timeType = typename TimePolicyT::timeType;
|
||||
static_assert(std::is_unsigned<timeType>::value == true, "timeType must be unsigned");
|
||||
|
||||
static constexpr timeType alwaysExpired = 0;
|
||||
static constexpr timeType neverExpires = std::numeric_limits<timeType>::max();
|
||||
static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug
|
||||
|
||||
timeoutTemplate(const timeType userTimeout) {
|
||||
reset(userTimeout);
|
||||
}
|
||||
|
||||
IRAM_ATTR // fast
|
||||
bool expired() {
|
||||
YieldPolicyT::execute(); //in case of DoNothing: gets optimized away
|
||||
if (PeriodicT) { //in case of false: gets optimized away
|
||||
return expiredRetrigger();
|
||||
}
|
||||
return expiredOneShot();
|
||||
}
|
||||
|
||||
IRAM_ATTR // fast
|
||||
operator bool() {
|
||||
return expired();
|
||||
}
|
||||
|
||||
bool canExpire() const {
|
||||
return !_neverExpires;
|
||||
}
|
||||
|
||||
bool canWait() const {
|
||||
return _timeout != alwaysExpired;
|
||||
}
|
||||
|
||||
// Resets, will trigger after this new timeout.
|
||||
IRAM_ATTR // called from ISR
|
||||
void reset(const timeType newUserTimeout) {
|
||||
reset();
|
||||
_timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout);
|
||||
_neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax());
|
||||
}
|
||||
|
||||
// Resets, will trigger after the timeout previously set.
|
||||
IRAM_ATTR // called from ISR
|
||||
void reset() {
|
||||
_start = TimePolicyT::time();
|
||||
}
|
||||
|
||||
// Resets to just expired so that on next poll the check will immediately trigger for the user,
|
||||
// also change timeout (after next immediate trigger).
|
||||
IRAM_ATTR // called from ISR
|
||||
void resetAndSetExpired(const timeType newUserTimeout) {
|
||||
reset(newUserTimeout);
|
||||
_start -= _timeout;
|
||||
}
|
||||
|
||||
// Resets to just expired so that on next poll the check will immediately trigger for the user.
|
||||
IRAM_ATTR // called from ISR
|
||||
void resetAndSetExpired() {
|
||||
reset();
|
||||
_start -= _timeout;
|
||||
}
|
||||
|
||||
void resetToNeverExpires() {
|
||||
_timeout = alwaysExpired + 1; // because canWait() has precedence
|
||||
_neverExpires = true;
|
||||
}
|
||||
|
||||
timeType getTimeout() const {
|
||||
return TimePolicyT::toUserUnit(_timeout);
|
||||
}
|
||||
|
||||
static constexpr timeType timeMax() {
|
||||
return TimePolicyT::timeMax;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
IRAM_ATTR // fast
|
||||
bool checkExpired(const timeType internalUnit) const {
|
||||
// canWait() is not checked here
|
||||
// returns "can expire" and "time expired"
|
||||
return (!_neverExpires) && ((internalUnit - _start) >= _timeout);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
IRAM_ATTR // fast
|
||||
bool expiredRetrigger() {
|
||||
if (!canWait()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
timeType current = TimePolicyT::time();
|
||||
if (checkExpired(current)) {
|
||||
unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout)
|
||||
_start += n * _timeout;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IRAM_ATTR // fast
|
||||
bool expiredOneShot() const {
|
||||
// returns "always expired" or "has expired"
|
||||
return !canWait() || checkExpired(TimePolicyT::time());
|
||||
}
|
||||
|
||||
timeType _timeout;
|
||||
timeType _start;
|
||||
bool _neverExpires;
|
||||
};
|
||||
|
||||
// legacy type names, deprecated (unit is milliseconds)
|
||||
|
||||
using oneShot = polledTimeout::timeoutTemplate<false> /*__attribute__((deprecated("use oneShotMs")))*/;
|
||||
using periodic = polledTimeout::timeoutTemplate<true> /*__attribute__((deprecated("use periodicMs")))*/;
|
||||
|
||||
// standard versions (based on millis())
|
||||
// timeMax() is 49.7 days ((2^32)-2 ms)
|
||||
|
||||
using oneShotMs = polledTimeout::timeoutTemplate<false>;
|
||||
using periodicMs = polledTimeout::timeoutTemplate<true>;
|
||||
|
||||
// Time policy based on esp_get_cycle_count(), and intended to be called very often:
|
||||
// "Fast" versions sacrifices time range for improved precision and reduced execution time (by 86%)
|
||||
// (cpu cycles for ::expired(): 372 (millis()) vs 52 (esp_get_cycle_count()))
|
||||
// timeMax() values:
|
||||
// Ms: max is 26843 ms (26.8 s)
|
||||
// Us: max is 26843545 us (26.8 s)
|
||||
// Ns: max is 1073741823 ns ( 1.07 s)
|
||||
// (time policy based on esp_get_cycle_count() is intended to be called very often)
|
||||
|
||||
using oneShotFastMs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
|
||||
using periodicFastMs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
|
||||
using oneShotFastUs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
|
||||
using periodicFastUs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
|
||||
using oneShotFastNs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
|
||||
using periodicFastNs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
|
||||
|
||||
} //polledTimeout
|
||||
|
||||
|
||||
/* A 1-shot timeout that auto-yields when in CONT can be built as follows:
|
||||
using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>;
|
||||
|
||||
Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file.
|
||||
*/
|
||||
|
||||
}//esp8266
|
||||
@@ -21,16 +21,31 @@
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <hardware/exception.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/util/queue.h>
|
||||
#include "CoreMutex.h"
|
||||
#include "ccount.pio.h"
|
||||
|
||||
#include <malloc.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 */ };
|
||||
@@ -85,6 +100,11 @@ public:
|
||||
if (!_multicore) {
|
||||
return;
|
||||
}
|
||||
__holdUpPendSV = 1;
|
||||
if (__isFreeRTOS) {
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
vTaskSuspendAll();
|
||||
}
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
@@ -97,6 +117,12 @@ public:
|
||||
}
|
||||
mutex_exit(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
if (__isFreeRTOS) {
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
}
|
||||
__holdUpPendSV = 0;
|
||||
|
||||
// Other core will exit busy-loop and return to operation
|
||||
// once __otherCoreIdled == false.
|
||||
}
|
||||
@@ -141,6 +167,9 @@ extern RP2040 rp2040;
|
||||
extern "C" void main1();
|
||||
class PIOProgram;
|
||||
|
||||
extern "C" char __StackLimit;
|
||||
extern "C" char __bss_end__;
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
// TODO - Add unload/destructor
|
||||
class PIOProgram {
|
||||
@@ -257,6 +286,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
inline int getFreeHeap() {
|
||||
return getTotalHeap() - getUsedHeap();
|
||||
}
|
||||
|
||||
inline int getUsedHeap() {
|
||||
struct mallinfo m = mallinfo();
|
||||
return m.uordblks;
|
||||
}
|
||||
|
||||
inline int getTotalHeap() {
|
||||
return &__StackLimit - &__bss_end__;
|
||||
}
|
||||
|
||||
void idleOtherCore() {
|
||||
fifo.idleOtherCore();
|
||||
}
|
||||
@@ -271,9 +313,51 @@ public:
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
watchdog_reboot(0, 0, 10);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
inline void restart() {
|
||||
reboot();
|
||||
}
|
||||
|
||||
const char *getChipID() {
|
||||
static char id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1] = { 0 };
|
||||
if (!id[0]) {
|
||||
pico_get_unique_board_id_string(id, sizeof(id));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// Multicore comms FIFO
|
||||
_MFIFO fifo;
|
||||
|
||||
|
||||
// TODO - Not so great HW random generator. 32-bits wide. Cryptographers somewhere are crying
|
||||
uint32_t hwrand32() {
|
||||
// Try and whiten the HW ROSC bit
|
||||
uint32_t r = 0;
|
||||
for (int k = 0; k < 32; k++) {
|
||||
unsigned long int b;
|
||||
do {
|
||||
b = rosc_hw->randombit & 1;
|
||||
if (b != (rosc_hw->randombit & 1)) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
r <<= 1;
|
||||
r |= b;
|
||||
}
|
||||
// Stir using the cycle count LSBs. In any WiFi use case this will be a random # since the connection time is not cycle-accurate
|
||||
uint64_t rr = (((uint64_t)~r) << 32LL) | r;
|
||||
rr >>= rp2040.getCycleCount() & 32LL;
|
||||
|
||||
return (uint32_t)rr;
|
||||
}
|
||||
|
||||
private:
|
||||
static void _SystickHandler() {
|
||||
rp2040._epoch += 1LL << 24;
|
||||
|
||||
+81
-35
@@ -40,7 +40,7 @@ mutex_t __usb_mutex;
|
||||
|
||||
// USB processing will be a periodic timer task
|
||||
#define USB_TASK_INTERVAL 1000
|
||||
#define USB_TASK_IRQ 31
|
||||
static int __usb_task_irq;
|
||||
|
||||
// USB VID/PID (note that PID can change depending on the add'l interfaces)
|
||||
#define USBD_VID (0x2E8A) // Raspberry Pi
|
||||
@@ -91,7 +91,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallMIDI) {
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMIDI) {
|
||||
// Can use as-is, this is the default USB case
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
@@ -102,6 +102,9 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
if (__USBInstallMouse) {
|
||||
usbd_desc_device.idProduct |= 0x4000;
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
usbd_desc_device.idProduct |= 0x0100;
|
||||
}
|
||||
if (__USBInstallMIDI) {
|
||||
usbd_desc_device.idProduct |= 0x2000;
|
||||
}
|
||||
@@ -120,6 +123,17 @@ int __USBGetMouseReportID() {
|
||||
return __USBInstallKeyboard ? 2 : 1;
|
||||
}
|
||||
|
||||
int __USBGetJoystickReportID() {
|
||||
int i = 1;
|
||||
if (__USBInstallKeyboard) {
|
||||
i++;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int __hid_report_len = 0;
|
||||
static uint8_t *__hid_report = nullptr;
|
||||
|
||||
@@ -131,37 +145,68 @@ static uint8_t *GetDescHIDReport(int *len) {
|
||||
}
|
||||
|
||||
void __SetupDescHIDReport() {
|
||||
if (__USBInstallKeyboard && __USBInstallMouse) {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)),
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2))
|
||||
};
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
} else if (__USBInstallKeyboard && ! __USBInstallMouse) {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1))
|
||||
};
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
} else if (! __USBInstallKeyboard && __USBInstallMouse) {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1))
|
||||
};
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
} else {
|
||||
//allocate memory for the HID report descriptors. We don't use them, but need the size here.
|
||||
uint8_t desc_hid_report_mouse[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)) };
|
||||
int size = 0;
|
||||
|
||||
//accumulate the size of all used HID report descriptors
|
||||
if (__USBInstallKeyboard) {
|
||||
size += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
size += sizeof(desc_hid_report_mouse);
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
size += sizeof(desc_hid_report_joystick);
|
||||
}
|
||||
|
||||
//no HID used at all
|
||||
if (size == 0) {
|
||||
__hid_report = nullptr;
|
||||
__hid_report_len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//allocate the "real" HID report descriptor
|
||||
__hid_report = (uint8_t *)malloc(size);
|
||||
if (__hid_report) {
|
||||
__hid_report_len = size;
|
||||
|
||||
//now copy the descriptors
|
||||
|
||||
//1.) keyboard descriptor, if requested
|
||||
if (__USBInstallKeyboard) {
|
||||
memcpy(__hid_report, desc_hid_report_keyboard, sizeof(desc_hid_report_keyboard));
|
||||
}
|
||||
|
||||
//2.) mouse descriptor, if necessary. Additional offset & new array is necessary if there is a keyboard.
|
||||
if (__USBInstallMouse) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (__USBInstallKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2)) };
|
||||
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(__hid_report, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
|
||||
}
|
||||
}
|
||||
|
||||
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
|
||||
if (__USBInstallJoystick) {
|
||||
uint8_t reportid = 1;
|
||||
int offset = 0;
|
||||
if (__USBInstallKeyboard) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_mouse);
|
||||
}
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
|
||||
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +226,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
|
||||
void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse;
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMIDI ? 2 : 0);
|
||||
|
||||
@@ -284,7 +329,7 @@ static void usb_irq() {
|
||||
}
|
||||
|
||||
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
||||
irq_set_pending(USB_TASK_IRQ);
|
||||
irq_set_pending(__usb_task_irq);
|
||||
return USB_TASK_INTERVAL;
|
||||
}
|
||||
|
||||
@@ -303,8 +348,9 @@ void __USBStart() {
|
||||
|
||||
tusb_init();
|
||||
|
||||
irq_set_exclusive_handler(USB_TASK_IRQ, usb_irq);
|
||||
irq_set_enabled(USB_TASK_IRQ, true);
|
||||
__usb_task_irq = user_irq_claim_unused(true);
|
||||
irq_set_exclusive_handler(__usb_task_irq, usb_irq);
|
||||
irq_set_enabled(__usb_task_irq, true);
|
||||
|
||||
add_alarm_in_us(USB_TASK_INTERVAL, timer_task, NULL, true);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// Weak function definitions for each type of endpoint
|
||||
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));
|
||||
|
||||
@@ -34,6 +35,7 @@ extern mutex_t __usb_mutex;
|
||||
// HID report ID inquiry (report ID will vary depending on the number/type of other HID)
|
||||
int __USBGetKeyboardReportID();
|
||||
int __USBGetMouseReportID();
|
||||
int __USBGetJoystickReportID();
|
||||
|
||||
// Called by main() to init the USB HW/SW.
|
||||
void __USBStart();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 2
|
||||
#define ARDUINO_PICO_REVISION 2
|
||||
#define ARDUINO_PICO_VERSION_STR "2.2.2"
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "2.5.0"
|
||||
|
||||
@@ -292,7 +292,7 @@ int SerialUART::available() {
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
return (_writer - _reader) % _fifoSize;
|
||||
return (_fifoSize + _writer - _reader) % _fifoSize;
|
||||
}
|
||||
|
||||
int SerialUART::availableForWrite() {
|
||||
|
||||
@@ -64,7 +64,7 @@ void SerialUSB::end() {
|
||||
}
|
||||
|
||||
int SerialUSB::peek() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ int SerialUSB::peek() {
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ int SerialUSB::read() {
|
||||
}
|
||||
|
||||
int SerialUSB::available() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ int SerialUSB::available() {
|
||||
}
|
||||
|
||||
int SerialUSB::availableForWrite() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ int SerialUSB::availableForWrite() {
|
||||
}
|
||||
|
||||
void SerialUSB::flush() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ size_t SerialUSB::write(uint8_t c) {
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
}
|
||||
|
||||
SerialUSB::operator bool() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __SERIALUSB_H__
|
||||
#define __SERIALUSB_H__
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "api/HardwareSerial.h"
|
||||
@@ -54,5 +53,3 @@ extern SerialUSB Serial;
|
||||
namespace arduino {
|
||||
extern void serialEventRun(void) __attribute__((weak));
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
StreamString.h
|
||||
|
||||
Copyright (c) 2020 D. Gauchard. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __STREAMSTRING_H
|
||||
#define __STREAMSTRING_H
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include "Stream.h"
|
||||
#include "api/String.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// S2Stream points to a String and makes it a Stream
|
||||
// (it is also the helper for StreamString)
|
||||
|
||||
class S2Stream: public Stream {
|
||||
public:
|
||||
S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { }
|
||||
|
||||
S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { }
|
||||
|
||||
virtual int available() override {
|
||||
return string->length();
|
||||
}
|
||||
|
||||
virtual int availableForWrite() override {
|
||||
return std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
|
||||
virtual int read() override {
|
||||
if (peekPointer < 0) {
|
||||
// consume chars
|
||||
if (string->length()) {
|
||||
char c = string->charAt(0);
|
||||
string->remove(0, 1);
|
||||
return c;
|
||||
}
|
||||
} else if (peekPointer < (int)string->length()) {
|
||||
// return pointed and move pointer
|
||||
return string->charAt(peekPointer++);
|
||||
}
|
||||
|
||||
// everything is read
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t data) override {
|
||||
return string->concat((char)data);
|
||||
}
|
||||
|
||||
// virtual int read(uint8_t* buffer, size_t len) override
|
||||
// {
|
||||
// if (peekPointer < 0)
|
||||
// {
|
||||
// // string will be consumed
|
||||
// size_t l = std::min(len, (size_t)string->length());
|
||||
// memcpy(buffer, string->c_str(), l);
|
||||
// string->remove(0, l);
|
||||
// return l;
|
||||
// }
|
||||
//
|
||||
// if (peekPointer >= (int)string->length())
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// // only the pointer is moved
|
||||
// size_t l = std::min(len, (size_t)(string->length() - peekPointer));
|
||||
// memcpy(buffer, string->c_str() + peekPointer, l);
|
||||
// peekPointer += l;
|
||||
// return l;
|
||||
// }
|
||||
|
||||
virtual size_t write(const uint8_t* buffer, size_t len) override {
|
||||
return string->concat((const char*)buffer, len) ? len : 0;
|
||||
}
|
||||
|
||||
virtual int peek() override {
|
||||
if (peekPointer < 0) {
|
||||
if (string->length()) {
|
||||
return string->charAt(0);
|
||||
}
|
||||
} else if (peekPointer < (int)string->length()) {
|
||||
return string->charAt(peekPointer);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual void flush() override {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual bool inputCanTimeout() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool outputCanTimeout() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Stream's peekBufferAPI
|
||||
|
||||
virtual bool hasPeekBufferAPI() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual size_t peekAvailable() {
|
||||
if (peekPointer < 0) {
|
||||
return string->length();
|
||||
}
|
||||
return string->length() - peekPointer;
|
||||
}
|
||||
|
||||
virtual const char* peekBuffer() override {
|
||||
if (peekPointer < 0) {
|
||||
return string->c_str();
|
||||
}
|
||||
if (peekPointer < (int)string->length()) {
|
||||
return string->c_str() + peekPointer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void peekConsume(size_t consume) override {
|
||||
if (peekPointer < 0) {
|
||||
// string is really consumed
|
||||
string->remove(0, consume);
|
||||
} else {
|
||||
// only the pointer is moved
|
||||
peekPointer = std::min((size_t)string->length(), peekPointer + consume);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ssize_t streamRemaining() override {
|
||||
return peekPointer < 0 ? string->length() : string->length() - peekPointer;
|
||||
}
|
||||
|
||||
// calling setConsume() will consume bytes as the stream is read
|
||||
// (enabled by default)
|
||||
void setConsume() {
|
||||
peekPointer = -1;
|
||||
}
|
||||
#endif
|
||||
// Reading this stream will mark the string as read without consuming
|
||||
// (not enabled by default)
|
||||
// Calling resetPointer() resets the read state and allows rereading.
|
||||
void resetPointer(int pointer = 0) {
|
||||
peekPointer = pointer;
|
||||
}
|
||||
|
||||
protected:
|
||||
String* string;
|
||||
int peekPointer; // -1:String is consumed / >=0:resettable pointer
|
||||
};
|
||||
|
||||
// StreamString is a S2Stream holding the String
|
||||
|
||||
class StreamString: public String, public S2Stream {
|
||||
protected:
|
||||
void resetpp() {
|
||||
if (peekPointer > 0) {
|
||||
peekPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
StreamString(StreamString&& bro) : String(bro), S2Stream(this) { }
|
||||
StreamString(const StreamString& bro) : String(bro), S2Stream(this) { }
|
||||
|
||||
// duplicate String constructors and operator=:
|
||||
|
||||
StreamString(const char* text = nullptr) : String(text), S2Stream(this) { }
|
||||
StreamString(const String& string) : String(string), S2Stream(this) { }
|
||||
StreamString(const __FlashStringHelper* str) : String(str), S2Stream(this) { }
|
||||
StreamString(String&& string) : String(string), S2Stream(this) { }
|
||||
|
||||
explicit StreamString(char c) : String(c), S2Stream(this) { }
|
||||
explicit StreamString(unsigned char c, unsigned char base = 10) :
|
||||
String(c, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(int i, unsigned char base = 10) : String(i, base), S2Stream(this) { }
|
||||
explicit StreamString(unsigned int i, unsigned char base = 10) : String(i, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(long l, unsigned char base = 10) : String(l, base), S2Stream(this) { }
|
||||
explicit StreamString(unsigned long l, unsigned char base = 10) :
|
||||
String(l, base), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(float f, unsigned char decimalPlaces = 2) :
|
||||
String(f, decimalPlaces), S2Stream(this) {
|
||||
}
|
||||
explicit StreamString(double d, unsigned char decimalPlaces = 2) :
|
||||
String(d, decimalPlaces), S2Stream(this) {
|
||||
}
|
||||
|
||||
StreamString& operator=(const StreamString& rhs) {
|
||||
String::operator=(rhs);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const String& rhs) {
|
||||
String::operator=(rhs);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const char* cstr) {
|
||||
String::operator=(cstr);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(const __FlashStringHelper* str) {
|
||||
String::operator=(str);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamString& operator=(String&& rval) {
|
||||
String::operator=(rval);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __STREAMSTRING_H
|
||||
+187
-66
@@ -1,55 +1,75 @@
|
||||
/*
|
||||
IPAddress.cpp - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
IPAddress.cpp - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. 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 "IPAddress.h"
|
||||
#include "Print.h"
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#include <Print.h>
|
||||
|
||||
using namespace arduino;
|
||||
|
||||
IPAddress::IPAddress()
|
||||
IPAddress::IPAddress(const IPAddress& from)
|
||||
{
|
||||
_address.dword = 0;
|
||||
ip_addr_copy(_ip, from._ip);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
|
||||
{
|
||||
_address.bytes[0] = first_octet;
|
||||
_address.bytes[1] = second_octet;
|
||||
_address.bytes[2] = third_octet;
|
||||
_address.bytes[3] = fourth_octet;
|
||||
IPAddress::IPAddress() {
|
||||
_ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint32_t address)
|
||||
{
|
||||
_address.dword = address;
|
||||
bool IPAddress::isSet () const {
|
||||
return !ip_addr_isany(&_ip) && ((*this) != IPADDR_NONE);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
||||
setV4();
|
||||
(*this)[0] = first_octet;
|
||||
(*this)[1] = second_octet;
|
||||
(*this)[2] = third_octet;
|
||||
(*this)[3] = fourth_octet;
|
||||
}
|
||||
|
||||
bool IPAddress::fromString(const char *address)
|
||||
{
|
||||
// TODO: add support for "a", "a.b", "a.b.c" formats
|
||||
void IPAddress::ctor32(uint32_t address) {
|
||||
setV4();
|
||||
v4() = address;
|
||||
}
|
||||
|
||||
int16_t acc = -1; // Accumulator
|
||||
IPAddress::IPAddress(const uint8_t *address) {
|
||||
setV4();
|
||||
(*this)[0] = address[0];
|
||||
(*this)[1] = address[1];
|
||||
(*this)[2] = address[2];
|
||||
(*this)[3] = address[3];
|
||||
}
|
||||
|
||||
bool IPAddress::fromString(const char *address) {
|
||||
if (!fromString4(address)) {
|
||||
#if LWIP_IPV6
|
||||
return fromString6(address);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPAddress::fromString4(const char *address) {
|
||||
// TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats
|
||||
|
||||
uint16_t acc = 0; // Accumulator
|
||||
uint8_t dots = 0;
|
||||
|
||||
while (*address)
|
||||
@@ -57,7 +77,7 @@ bool IPAddress::fromString(const char *address)
|
||||
char c = *address++;
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
|
||||
acc = acc * 10 + (c - '0');
|
||||
if (acc > 255) {
|
||||
// Value out of [0..255] range
|
||||
return false;
|
||||
@@ -69,12 +89,8 @@ bool IPAddress::fromString(const char *address)
|
||||
// Too much dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
if (acc < 0) {
|
||||
/* No value between dots, e.g. '1..' */
|
||||
return false;
|
||||
}
|
||||
_address.bytes[dots++] = acc;
|
||||
acc = -1;
|
||||
(*this)[dots++] = acc;
|
||||
acc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,41 +103,146 @@ bool IPAddress::fromString(const char *address)
|
||||
// Too few dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
if (acc < 0) {
|
||||
/* No value between dots, e.g. '1..' */
|
||||
return false;
|
||||
}
|
||||
_address.bytes[3] = acc;
|
||||
(*this)[3] = acc;
|
||||
|
||||
setV4();
|
||||
return true;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
IPAddress& IPAddress::operator=(const uint8_t *address) {
|
||||
setV4();
|
||||
v4() = *reinterpret_cast<const uint32_t*>(address);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(uint32_t address)
|
||||
{
|
||||
_address.dword = address;
|
||||
IPAddress& IPAddress::operator=(uint32_t address) {
|
||||
setV4();
|
||||
v4() = address;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const uint8_t* addr) const
|
||||
{
|
||||
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||
bool IPAddress::operator==(const uint8_t* addr) const {
|
||||
return isV4() && v4() == *reinterpret_cast<const uint32_t*>(addr);
|
||||
}
|
||||
|
||||
size_t IPAddress::printTo(Print& p) const
|
||||
size_t IPAddress::printTo(Print& p) const {
|
||||
String s = toString();
|
||||
return p.print(s);
|
||||
}
|
||||
|
||||
String IPAddress::toString() const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (int i =0; i < 3; i++)
|
||||
{
|
||||
n += p.print(_address.bytes[i], DEC);
|
||||
n += p.print('.');
|
||||
String s;
|
||||
|
||||
if (!isSet())
|
||||
return "(IP unset)";
|
||||
|
||||
#if LWIP_IPV6
|
||||
if (isV6()) {
|
||||
int count0 = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint16_t bit = PP_NTOHS(raw6()[i]);
|
||||
if (bit || count0 < 0) {
|
||||
char buff[64];
|
||||
snprintf(buff, 64, "%x", bit);
|
||||
buff[63] = 0;
|
||||
s += buff;
|
||||
if (count0 > 0)
|
||||
// no more hiding 0
|
||||
count0 = -8;
|
||||
} else
|
||||
count0++;
|
||||
if ((i != 7 && count0 < 2) || count0 == 7)
|
||||
s += ':';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
n += p.print(_address.bytes[3], DEC);
|
||||
return n;
|
||||
#endif
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
char buff[16];
|
||||
snprintf(buff, 16, "%d", (*this)[i]);
|
||||
buff[15] = 0;
|
||||
s += buff;
|
||||
if (i != 3)
|
||||
s += '.';
|
||||
}
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
const IPAddress arduino::INADDR_NONE(0,0,0,0);
|
||||
bool IPAddress::isValid(const String& arg) {
|
||||
return IPAddress().fromString(arg);
|
||||
}
|
||||
|
||||
bool IPAddress::isValid(const char* arg) {
|
||||
return IPAddress().fromString(arg);
|
||||
}
|
||||
|
||||
namespace arduino {
|
||||
const IPAddress INADDR_ANY; // generic "0.0.0.0" for IPv4 & IPv6
|
||||
const IPAddress INADDR_NONE(255,255,255,255);
|
||||
};
|
||||
|
||||
void IPAddress::clear() {
|
||||
(*this) = INADDR_ANY;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
||||
#if LWIP_IPV6
|
||||
|
||||
bool IPAddress::fromString6(const char *address) {
|
||||
// TODO: test test test
|
||||
|
||||
uint32_t acc = 0; // Accumulator
|
||||
int dots = 0, doubledots = -1;
|
||||
|
||||
while (*address)
|
||||
{
|
||||
char c = tolower(*address++);
|
||||
if (isalnum(c)) {
|
||||
if (c >= 'a')
|
||||
c -= 'a' - '0' - 10;
|
||||
acc = acc * 16 + (c - '0');
|
||||
if (acc > 0xffff)
|
||||
// Value out of range
|
||||
return false;
|
||||
}
|
||||
else if (c == ':') {
|
||||
if (*address == ':') {
|
||||
if (doubledots >= 0)
|
||||
// :: allowed once
|
||||
return false;
|
||||
// remember location
|
||||
doubledots = dots + !!acc;
|
||||
address++;
|
||||
}
|
||||
if (dots == 7)
|
||||
// too many separators
|
||||
return false;
|
||||
raw6()[dots++] = PP_HTONS(acc);
|
||||
acc = 0;
|
||||
}
|
||||
else
|
||||
// Invalid char
|
||||
return false;
|
||||
}
|
||||
|
||||
if (doubledots == -1 && dots != 7)
|
||||
// Too few separators
|
||||
return false;
|
||||
raw6()[dots++] = PP_HTONS(acc);
|
||||
|
||||
if (doubledots != -1) {
|
||||
for (int i = dots - doubledots - 1; i >= 0; i--)
|
||||
raw6()[8 - dots + doubledots + i] = raw6()[doubledots + i];
|
||||
for (int i = doubledots; i < 8 - dots + doubledots; i++)
|
||||
raw6()[i] = 0;
|
||||
}
|
||||
|
||||
setV6();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+207
-56
@@ -1,85 +1,236 @@
|
||||
/*
|
||||
IPAddress.h - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
IPAddress.h - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Printable.h"
|
||||
#include "String.h"
|
||||
#include <api/String.h>
|
||||
#include <Printable.h>
|
||||
|
||||
// forward declartions of global name space friend classes
|
||||
#include <lwip/init.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include <lwip/ip4_addr.h>
|
||||
|
||||
|
||||
// forward declarations of global name space friend classes
|
||||
class EthernetClass;
|
||||
class DhcpClass;
|
||||
class DNSClient;
|
||||
|
||||
|
||||
namespace arduino {
|
||||
|
||||
// to display a netif id with printf:
|
||||
#define NETIFID_STR "%c%c%u"
|
||||
#define NETIFID_VAL(netif) \
|
||||
((netif)? (netif)->name[0]: '-'), \
|
||||
((netif)? (netif)->name[1]: '-'), \
|
||||
((netif)? netif_get_index(netif): 42)
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
// IPv6 update:
|
||||
// IPAddress is now a decorator class for lwIP's ip_addr_t
|
||||
// fully backward compatible with legacy IPv4-only Arduino's
|
||||
// with unchanged footprint when IPv6 is disabled
|
||||
|
||||
class IPAddress : public Printable {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[4]; // IPv4 address
|
||||
uint32_t dword;
|
||||
} _address;
|
||||
class IPAddress: public Printable {
|
||||
private:
|
||||
#if !LWIP_IPV6
|
||||
// Ugly hack to allow Arduino Ethernet library to twiddle internal bits.
|
||||
// This can only work in IPv4-only mode, of course.
|
||||
union {
|
||||
ip_addr_t _ip;
|
||||
struct {
|
||||
uint8_t bytes[4];
|
||||
} _address;
|
||||
};
|
||||
static_assert(sizeof(_ip) == sizeof(_address), "IP_ADDR_T size != _ADDRESS size");
|
||||
#else
|
||||
ip_addr_t _ip;
|
||||
#endif
|
||||
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t* raw_address() { return _address.bytes; };
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t* raw_address() {
|
||||
return reinterpret_cast<uint8_t*>(&v4());
|
||||
}
|
||||
const uint8_t* raw_address() const {
|
||||
return reinterpret_cast<const uint8_t*>(&v4());
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
IPAddress();
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(uint32_t address);
|
||||
IPAddress(const uint8_t *address);
|
||||
void ctor32 (uint32_t);
|
||||
|
||||
bool fromString(const char *address);
|
||||
bool fromString(const String &address) { return fromString(address.c_str()); }
|
||||
public:
|
||||
// Constructors
|
||||
IPAddress();
|
||||
IPAddress(const IPAddress& from);
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(uint32_t address) { ctor32(address); }
|
||||
// IPAddress(unsigned long address) { ctor32(address); }
|
||||
IPAddress(int address) { ctor32(address); }
|
||||
IPAddress(const uint8_t *address);
|
||||
|
||||
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||
// to a four-byte uint8_t array is expected
|
||||
operator uint32_t() const { return _address.dword; };
|
||||
bool operator==(const IPAddress& addr) const { return _address.dword == addr._address.dword; };
|
||||
bool operator!=(const IPAddress& addr) const { return _address.dword != addr._address.dword; };
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
bool fromString(const char *address);
|
||||
bool fromString(const String &address) { return fromString(address.c_str()); }
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const { return _address.bytes[index]; };
|
||||
uint8_t& operator[](int index) { return _address.bytes[index]; };
|
||||
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||
// to a four-byte uint8_t array is expected
|
||||
operator uint32_t() const { return isV4()? v4(): (uint32_t)0; }
|
||||
operator uint32_t() { return isV4()? v4(): (uint32_t)0; }
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||
IPAddress& operator=(const uint8_t *address);
|
||||
IPAddress& operator=(uint32_t address);
|
||||
bool isSet () const;
|
||||
operator bool () const { return isSet(); } // <-
|
||||
operator bool () { return isSet(); } // <- both are needed
|
||||
|
||||
virtual size_t printTo(Print& p) const;
|
||||
// generic IPv4 wrapper to uint32-view like arduino loves to see it
|
||||
const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const)
|
||||
uint32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
||||
|
||||
bool operator==(const IPAddress& addr) const {
|
||||
return ip_addr_cmp(&_ip, &addr._ip);
|
||||
}
|
||||
bool operator!=(const IPAddress& addr) const {
|
||||
return !ip_addr_cmp(&_ip, &addr._ip);
|
||||
}
|
||||
bool operator==(uint32_t addr) const {
|
||||
return isV4() && v4() == addr;
|
||||
}
|
||||
// bool operator==(unsigned long addr) const {
|
||||
// return isV4() && v4() == (uint32_t)addr;
|
||||
// }
|
||||
bool operator!=(uint32_t addr) const {
|
||||
return !(isV4() && v4() == addr);
|
||||
}
|
||||
// bool operator!=(unsigned long addr) const {
|
||||
// return isV4() && v4() != (uint32_t)addr;
|
||||
// }
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
|
||||
int operator>>(int n) const {
|
||||
return isV4()? v4() >> n: 0;
|
||||
}
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const {
|
||||
return isV4()? *(raw_address() + index): 0;
|
||||
}
|
||||
uint8_t& operator[](int index) {
|
||||
setV4();
|
||||
return *(raw_address() + index);
|
||||
}
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||
IPAddress& operator=(const uint8_t *address);
|
||||
IPAddress& operator=(uint32_t address);
|
||||
IPAddress& operator=(const IPAddress&) = default;
|
||||
|
||||
virtual size_t printTo(Print& p) const;
|
||||
String toString() const;
|
||||
|
||||
void clear();
|
||||
|
||||
/*
|
||||
check if input string(arg) is a valid IPV4 address or not.
|
||||
return true on valid.
|
||||
return false on invalid.
|
||||
*/
|
||||
static bool isValid(const String& arg);
|
||||
static bool isValid(const char* arg);
|
||||
|
||||
friend class WiFiClass;
|
||||
friend class EthernetClass;
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
friend class DhcpClass;
|
||||
friend class DNSClient;
|
||||
|
||||
friend ::EthernetClass;
|
||||
friend ::DhcpClass;
|
||||
friend ::DNSClient;
|
||||
|
||||
/*
|
||||
lwIP address compatibility
|
||||
*/
|
||||
//IPAddress(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; }
|
||||
//IPAddress(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; }
|
||||
|
||||
//IPAddress& operator=(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; return *this; }
|
||||
//IPAddress& operator=(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; return *this; }
|
||||
|
||||
operator ip_addr_t () const { return _ip; }
|
||||
operator const ip_addr_t*() const { return &_ip; }
|
||||
operator ip_addr_t*() { return &_ip; }
|
||||
|
||||
bool isV4() const { return IP_IS_V4_VAL(_ip); }
|
||||
void setV4() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); }
|
||||
|
||||
bool isLocal () const { return ip_addr_islinklocal(&_ip); }
|
||||
|
||||
|
||||
IPAddress(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); }
|
||||
IPAddress(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); }
|
||||
|
||||
IPAddress& operator=(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); return *this; }
|
||||
IPAddress& operator=(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); return *this; }
|
||||
|
||||
#if LWIP_IPV6
|
||||
uint16_t* raw6()
|
||||
{
|
||||
setV6();
|
||||
return reinterpret_cast<uint16_t*>(ip_2_ip6(&_ip));
|
||||
}
|
||||
|
||||
const uint16_t* raw6() const
|
||||
{
|
||||
return isV6()? reinterpret_cast<const uint16_t*>(ip_2_ip6(&_ip)): nullptr;
|
||||
}
|
||||
|
||||
// when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous
|
||||
// required otherwise
|
||||
operator const ip4_addr_t*() const { return isV4()? ip_2_ip4(&_ip): nullptr; }
|
||||
|
||||
bool isV6() const { return IP_IS_V6_VAL(_ip); }
|
||||
void setV6() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); }
|
||||
|
||||
protected:
|
||||
bool fromString6(const char *address);
|
||||
|
||||
#else
|
||||
|
||||
// allow portable code when IPv6 is not enabled
|
||||
|
||||
uint16_t* raw6() { return nullptr; }
|
||||
const uint16_t* raw6() const { return nullptr; }
|
||||
bool isV6() const { return false; }
|
||||
void setV6() { }
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool fromString4(const char *address);
|
||||
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
|
||||
friend ::EthernetClass;
|
||||
friend ::DhcpClass;
|
||||
friend ::DNSClient;
|
||||
};
|
||||
|
||||
extern const IPAddress INADDR_ANY;
|
||||
extern const IPAddress INADDR_NONE;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
@@ -22,15 +22,19 @@
|
||||
#include "RP2040USB.h"
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/multicore.h>
|
||||
#include "LWIPMutex.h"
|
||||
#include <reent.h>
|
||||
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
|
||||
int LWIPMutex::_ref = 0;
|
||||
|
||||
extern void setup();
|
||||
extern void loop();
|
||||
|
||||
@@ -126,7 +130,6 @@ extern "C" int main() {
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
rp2040.fifo.begin(2);
|
||||
multicore_launch_core1(main1);
|
||||
} else {
|
||||
rp2040.fifo.begin(1);
|
||||
}
|
||||
@@ -135,6 +138,10 @@ extern "C" int main() {
|
||||
#endif
|
||||
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
delay(1); // Needed to make Picoprobe upload start 2nd core
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
|
||||
@@ -108,6 +108,13 @@ extern "C" int settimeofday(const struct timeval *tv, const struct timezone *tz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For NTP
|
||||
extern "C" void __setSystemTime(unsigned long long sec, unsigned long usec) {
|
||||
uint64_t now_us = to_us_since_boot(get_absolute_time());
|
||||
uint64_t newnow_us = sec * 1000000LL + usec;
|
||||
__timedelta_us = newnow_us - now_us;
|
||||
}
|
||||
|
||||
extern "C" int _isatty(int file) {
|
||||
(void) file;
|
||||
errno = ENOSYS;
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Taken from the Pico-SDK v1.4.0 and hacked by EFP3 to allow overriding the LWIP setup
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/sem.h"
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
#include "cyw43_stats.h"
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include "lwip/timeouts.h"
|
||||
|
||||
#ifndef CYW43_PIN_WL_HOST_WAKE
|
||||
#define CYW43_PIN_WL_HOST_WAKE 24
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_PIN_WL_REG_ON
|
||||
#define CYW43_PIN_WL_REG_ON 23
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_COUNT
|
||||
#define CYW43_WL_GPIO_COUNT 3
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_LED_PIN
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
volatile bool __inLWIP = false;
|
||||
|
||||
// note same code
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && !NO_SYS
|
||||
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
|
||||
#endif
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && MEM_LIBC_MALLOC
|
||||
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
#endif
|
||||
// todo right now we are now always doing a cyw43_dispatch along with a lwip one when hopping cores in low_prio_irq_schedule_dispatch
|
||||
|
||||
#ifndef CYW43_SLEEP_CHECK_MS
|
||||
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
|
||||
#endif
|
||||
static alarm_id_t periodic_alarm = -1;
|
||||
|
||||
static inline uint recursive_mutex_enter_count(recursive_mutex_t *mutex) {
|
||||
return mutex->enter_count;
|
||||
}
|
||||
|
||||
static inline lock_owner_id_t recursive_mutex_owner(recursive_mutex_t *mutex) {
|
||||
return mutex->owner;
|
||||
}
|
||||
|
||||
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
|
||||
|
||||
enum {
|
||||
CYW43_DISPATCH_SLOT_CYW43 = 0,
|
||||
CYW43_DISPATCH_SLOT_ADAPTER,
|
||||
CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
};
|
||||
#ifndef CYW43_DISPATCH_SLOT_COUNT
|
||||
#define CYW43_DISPATCH_SLOT_COUNT CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
#endif
|
||||
|
||||
typedef void (*low_prio_irq_dispatch_t)(void);
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f);
|
||||
|
||||
static uint8_t cyw43_core_num;
|
||||
#ifndef NDEBUG
|
||||
static bool in_low_priority_irq;
|
||||
#endif
|
||||
static uint8_t low_priority_irq_num;
|
||||
static bool low_priority_irq_missed;
|
||||
static low_prio_irq_dispatch_t low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_COUNT];
|
||||
static recursive_mutex_t cyw43_mutex;
|
||||
semaphore_t cyw43_irq_sem;
|
||||
|
||||
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
|
||||
static void periodic_worker(void) {
|
||||
#if CYW43_USE_STATS && LWIP_SYS_CHECK_MS
|
||||
static uint32_t counter;
|
||||
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
|
||||
cyw43_dump_stats();
|
||||
}
|
||||
#endif
|
||||
|
||||
CYW43_STAT_INC(LWIP_RUN_COUNT);
|
||||
//#if CYW43_LWIP
|
||||
if (!__inLWIP) {
|
||||
sys_check_timeouts();
|
||||
}
|
||||
//#endif
|
||||
if (cyw43_poll) {
|
||||
if (cyw43_sleep > 0) {
|
||||
if (--cyw43_sleep == 0) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular callback to get lwip to check for timeouts
|
||||
static int64_t periodic_alarm_handler(__unused alarm_id_t id, __unused void *user_data) {
|
||||
// Do lwip processing in low priority pendsv interrupt
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_ADAPTER, periodic_worker);
|
||||
return CYW43_SLEEP_CHECK_MS * 1000;
|
||||
}
|
||||
|
||||
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
|
||||
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
|
||||
if (!__get_current_exception()) {
|
||||
sem_acquire_timeout_us(&cyw43_irq_sem, timeout_us);
|
||||
}
|
||||
}
|
||||
|
||||
// GPIO interrupt handler to tell us there's cyw43 has work to do
|
||||
static void gpio_irq_handler(void) {
|
||||
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
|
||||
if (events & GPIO_IRQ_LEVEL_HIGH) {
|
||||
// As we use a high level interrupt, it will go off forever until it's serviced
|
||||
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
|
||||
// which is called at the end of cyw43_poll_func
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
// also clear the force bit which we use to progratically cause this handler to fire (on the right core)
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
CYW43_STAT_INC(IRQ_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// Low priority interrupt handler to perform background processing
|
||||
static void low_priority_irq_handler(void) {
|
||||
assert(cyw43_core_num == get_core_num());
|
||||
if (recursive_mutex_try_enter(&cyw43_mutex, NULL)) {
|
||||
if (recursive_mutex_enter_count(&cyw43_mutex) != 1) {
|
||||
low_priority_irq_missed = true;
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_RUN_COUNT);
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = true;
|
||||
#endif
|
||||
for (size_t i = 0; i < count_of(low_priority_irq_dispatch_slots); i++) {
|
||||
if (low_priority_irq_dispatch_slots[i] != NULL) {
|
||||
low_prio_irq_dispatch_t f = low_priority_irq_dispatch_slots[i];
|
||||
low_priority_irq_dispatch_slots[i] = NULL;
|
||||
f();
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = false;
|
||||
#endif
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
low_priority_irq_missed = true;
|
||||
}
|
||||
sem_release(&cyw43_irq_sem);
|
||||
}
|
||||
|
||||
static bool low_prio_irq_init(uint8_t priority) {
|
||||
assert(get_core_num() == cyw43_core_num);
|
||||
int irq = user_irq_claim_unused(false);
|
||||
if (irq < 0) {
|
||||
return false;
|
||||
}
|
||||
low_priority_irq_num = (uint8_t) irq;
|
||||
irq_set_exclusive_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
irq_set_enabled(low_priority_irq_num, true);
|
||||
irq_set_priority(low_priority_irq_num, priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void low_prio_irq_deinit(void) {
|
||||
if (low_priority_irq_num > 0) {
|
||||
irq_set_enabled(low_priority_irq_num, false);
|
||||
irq_remove_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
user_irq_unclaim(low_priority_irq_num);
|
||||
low_priority_irq_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
cyw43_core_num = get_core_num();
|
||||
recursive_mutex_init(&cyw43_mutex);
|
||||
cyw43_init(&cyw43_state);
|
||||
sem_init(&cyw43_irq_sem, 0, 1);
|
||||
|
||||
// Start regular lwip callback to handle timeouts
|
||||
periodic_alarm = add_alarm_in_us(CYW43_SLEEP_CHECK_MS * 1000, periodic_alarm_handler, NULL, true);
|
||||
if (periodic_alarm < 0) {
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
irq_set_enabled(IO_IRQ_BANK0, true);
|
||||
|
||||
lwip_init();
|
||||
|
||||
// start low priority handler (no background work is done before this)
|
||||
bool ok = low_prio_irq_init(PICO_LOWEST_IRQ_PRIORITY);
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
return PICO_OK;
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
if (periodic_alarm >= 0) {
|
||||
cancel_alarm(periodic_alarm);
|
||||
periodic_alarm = -1;
|
||||
}
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
|
||||
low_prio_irq_deinit();
|
||||
}
|
||||
|
||||
void cyw43_post_poll_hook(void) {
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
}
|
||||
|
||||
// This is called in the gpio and low_prio_irq interrupts and on either core
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f) {
|
||||
assert(slot < count_of(low_priority_irq_dispatch_slots));
|
||||
low_priority_irq_dispatch_slots[slot] = f;
|
||||
if (cyw43_core_num == get_core_num()) {
|
||||
//on same core, can dispatch directly
|
||||
irq_set_pending(low_priority_irq_num);
|
||||
} else {
|
||||
// on wrong core, so force via GPIO IRQ which itself calls this method for the CYW43 slot.
|
||||
// since the CYW43 slot always uses the same function, this is fine with the addition of an
|
||||
// extra (but harmless) CYW43 slot call when another SLOT is invoked.
|
||||
// We could do better, but would have to track why the IRQ was called.
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = cyw43_core_num ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_set_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, func);
|
||||
}
|
||||
|
||||
// Prevent background processing in pensv and access by the other core
|
||||
// These methods are called in pensv context and on either core
|
||||
// They can be called recursively
|
||||
void cyw43_thread_enter(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
recursive_mutex_enter_blocking(&cyw43_mutex);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void cyw43_thread_lock_check(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
if (recursive_mutex_enter_count(&cyw43_mutex) < 1 || recursive_mutex_owner(&cyw43_mutex) != lock_get_caller_owner_id()) {
|
||||
panic("cyw43_thread_lock_check failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Re-enable background processing
|
||||
void cyw43_thread_exit(void) {
|
||||
// Run low_prio_irq if needed
|
||||
if (1 == recursive_mutex_enter_count(&cyw43_mutex)) {
|
||||
// note the outer release of the mutex is not via cyw43_exit in the low_priority_irq case (it is a direct mutex exit)
|
||||
assert(!in_low_priority_irq);
|
||||
// if (low_priority_irq_missed) {
|
||||
// low_priority_irq_missed = false;
|
||||
if (low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_CYW43]) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
// }
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void cyw43_delay_until(absolute_time_t until) {
|
||||
// sleep can be called in IRQs, so there's not much we can do there
|
||||
if (__get_current_exception()) {
|
||||
busy_wait_until(until);
|
||||
} else {
|
||||
sleep_until(until);
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_delay_ms(uint32_t ms) {
|
||||
cyw43_delay_until(make_timeout_time_ms(ms));
|
||||
}
|
||||
|
||||
void cyw43_delay_us(uint32_t us) {
|
||||
cyw43_delay_until(make_timeout_time_us(us));
|
||||
}
|
||||
|
||||
void cyw43_arch_poll() {
|
||||
// should not be necessary
|
||||
// if (cyw43_poll) {
|
||||
// low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
// }
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf);
|
||||
void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
|
||||
(void) cb_data;
|
||||
(void) itf;
|
||||
(void) len;
|
||||
(void) buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -19,8 +19,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef STDLIB_NONISO_H
|
||||
#define STDLIB_NONISO_H
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -54,6 +53,3 @@ const char* strrstr(const char*__restrict p_pcString,
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
|
||||
static PinMode _pm[30];
|
||||
|
||||
extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) __attribute__((weak, alias("__pinMode")));
|
||||
extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
switch (ulMode) {
|
||||
case INPUT:
|
||||
gpio_init(ulPin);
|
||||
@@ -76,7 +77,8 @@ extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
_pm[ulPin] = ulMode;
|
||||
}
|
||||
|
||||
extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) __attribute__((weak, alias("__digitalWrite")));
|
||||
extern "C" void __digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
if (ulPin > 29) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
|
||||
return;
|
||||
@@ -99,7 +101,8 @@ extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" PinStatus digitalRead(pin_size_t ulPin) {
|
||||
extern "C" PinStatus digitalRead(pin_size_t ulPin) __attribute__((weak, alias("__digitalRead")));
|
||||
extern "C" PinStatus __digitalRead(pin_size_t ulPin) {
|
||||
if (ulPin > 29) {
|
||||
DEBUGCORE("ERROR: Illegal pin in digitalRead (%d)\n", ulPin);
|
||||
return LOW;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
:orphan:
|
||||
|
||||
WiFiClientSecure Class
|
||||
======================
|
||||
|
||||
`BearSSL::WiFiClientSecure` is the object which actually handles TLS encrypted WiFi connections to a remote server or client. It extends `WiFiClient` and so can be used with minimal changes to code that does unsecured communications.
|
||||
|
||||
Validating X509 Certificates (Am I talking to the server I think I'm talking to?)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Prior to connecting to a server, the `BearSSL::WiFiClientSecure` needs to be told how to verify the identity of the other machine. **By default BearSSL will not validate any connections and will refuse to connect to any server.**
|
||||
|
||||
There are multiple modes to tell BearSSL how to verify the identity of the remote server. See the `BearSSL_Validation` example for real uses of the following methods:
|
||||
|
||||
setInsecure()
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Don't verify any X509 certificates. There is no guarantee that the server connected to is the one you think it is in this case.
|
||||
|
||||
setKnownKey(const BearSSL::PublicKey \*pk)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Assume the server is using the specific public key. This does not verify the identity of the server or the X509 certificate it sends, it simply assumes that its public key is the one given. If the server updates its public key at a later point then connections will fail.
|
||||
|
||||
setFingerprint(const uint8_t fp[20]) / setFingerprint(const char \*fpStr)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Verify the SHA1 fingerprint of the certificate returned matches this one. If the server certificate changes, it will fail. If an array of 20 bytes are sent in, it is assumed they are the binary SHA1 values. If a `char*` string is passed in, it is parsed as a series of human-readable hex values separated by spaces or colons (e.g. `setFingerprint("00:01:02:03:...:1f");`)
|
||||
|
||||
This fingerprint is calculated on the raw X509 certificate served by the server. In very rare cases, these certificates have certain encodings which should be normalized before taking a fingerprint (but in order to preserve memory BearSSL does not do this normalization since it would need RAM for an entire copy of the cert), and the fingerprint BearSSL calculates will not match the fingerprint OpenSSL calculates. In this case, you can enable SSL debugging and get a dump of BearSSL's calculated fingerprint and use that one in your code, or use full certificate validation. See the `original issue and debug here <https://github.com/esp8266/Arduino/issues/6209>`__.
|
||||
|
||||
setTrustAnchors(BearSSL::X509List \*ta)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use the passed-in certificate(s) as a trust anchor, accepting remote certificates signed by any of these. If you have many trust anchors it may make sense to use a `BearSSL::CertStore` because it will only require RAM for a single trust anchor (while the `setTrustAnchors` call requires memory for all certificates in the list).
|
||||
|
||||
setX509Time(time_t now)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For `setTrustAnchors` and `CertStore` , the current time (set via SNTP) is used to verify the certificate against the list, so SNTP must be enabled and functioning before the connection is attempted. If you cannot use SNTP for some reason, you can manually set the "present time" that BearSSL will use to validate a certificate with this call where `now` is standard UNIX time.
|
||||
|
||||
Client Certificates (Proving I'm who I say I am to the server)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TLS servers can request that a client identify themselves with an X509 certificate signed by a trust anchor it honors (i.e. a global TA or a private CA). This is commonly done for applications like MQTT. By default the client doesn't send a certificate, and in cases where a certificate is required the server will disconnect and no connection will be possible.
|
||||
|
||||
setClientRSACert / setClientECCert
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sets a client certificate to send to a TLS server that requests one. It should be called before `connect()` to add a certificate to the client in case the server requests it. Note that certificates include both a certificate and a private key. Both should be provided to you by your certificate generator. Elliptic Curve (EC) keys require additional information, as shown in the prototype.
|
||||
|
||||
MFLN or Maximum Fragment Length Negotiation (Saving RAM)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Because TLS was developed on systems with many megabytes of memory, they require by default a 16KB buffer for receive and transmit. That's enormous for the ESP8266, which has only around 40KB total heap available.
|
||||
|
||||
We can (and do) minimize the transmission buffer down to slightly more than 512 bytes to save memory, since BearSSL can internally ensure transmissions larger than that are broken up into smaller chunks that do fit. But that still leaves the 16KB receive buffer requirement since we cannot in general guarantee the TLS peer will send in smaller chunks.
|
||||
|
||||
TLS 1.2 added MFLN, which lets a client negotiate smaller buffers with a server and reduce the memory requirements on the ESP8266. Unfortunately, BearSSL needs to know the buffer sizes before it begins connection, so applications that want to use smaller buffers need to check the remote server's support before `connect()` .
|
||||
|
||||
probeMaxFragmentLength(host, port, len)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Use one of these calls **before** connection to determine if a specific fragment length is supported (len must be a power of two from 512 to 4096, per the specification). This does **not** initiate a SSL connection, it simply opens a TCP port and performs a trial handshake to check support.
|
||||
|
||||
setBufferSizes(int recv, int xmit)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have verified (or know beforehand) that MFLN is supported you can use this call to set the size of memory buffers allocated by the connection object. This must be called **before** `connect()` or it will be ignored.
|
||||
|
||||
In certain applications where the TLS server does not support MFLN (not many do as of this writing as it is relatively new to OpenSSL), but you control both the ESP8266 and the server to which it is communicating, you may still be able to `setBufferSizes()` smaller if you guarantee no chunk of data will overflow those buffers.
|
||||
|
||||
bool getMFLNStatus()
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After a successful connection, this method returns whether or not MFLN negotiation succeeded or not. If it did not succeed, and you reduced the receive buffer with `setBufferSizes` then you may experience reception errors if the server attempts to send messages larger than your receive buffer.
|
||||
|
||||
Sessions (Resuming connections fast)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
setSession(BearSSL::Session &sess)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you are connecting to a server repeatedly in a fixed time period (usually 30 or 60 minutes, but normally configurable at the server), a TLS session can be used to cache crypto settings and speed up connections significantly.
|
||||
|
||||
Errors
|
||||
~~~~~~
|
||||
|
||||
BearSSL can fail in many more unique and interesting ways. Use these calls to get more information when something fails.
|
||||
|
||||
getLastSSLError(char \*dest = NULL, size_t len = 0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Returns the last BearSSL error code encountered and optionally set a user-allocated buffer to a human-readable form of the error. To only get the last error integer code, just call without any parameters (`int errCode = getLastSSLError();`).
|
||||
|
||||
Limiting Ciphers (New connections faster)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There is very rarely reason to use these calls, but they are available.
|
||||
|
||||
setCiphers()
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Takes an array (in PROGMEM is valid) or a std::vector of 16-bit BearSSL cipher identifiers and restricts BearSSL to only use them. If the server requires a different cipher, then connection will fail. Generally this is not useful except in cases where you want to connect to servers using a specific cipher. See the BearSSL headers for more information on the supported ciphers.
|
||||
|
||||
setCiphersLessSecure()
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Helper function which essentially limits BearSSL to less secure ciphers than it would natively choose, but they may be helpful and faster if your server depended on specific crypto options.
|
||||
|
||||
Limiting TLS(SSL) Versions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, BearSSL will connect with TLS 1.0, TLS 1.1, or TLS 1.2 protocols (depending on the request of the remote side). If you want to limit to a subset, use the following call:
|
||||
|
||||
setSSLVersion(uint32_t min, uint32_t max)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Valid values for min and max are `BR_TLS10`, `BR_TLS11`, `BR_TLS12`. Min and max may be set to the same value if only a single TLS version is desired.
|
||||
|
||||
|
||||
ESP32 Compatibility
|
||||
===================
|
||||
Simple ESP32 ``WiFiClientSecure`` compatibility is built-in, allow for some sketches to run without any modification.
|
||||
The following methods are implemented:
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void setCACert(const char *rootCA);
|
||||
void setCertificate(const char *client_ca);
|
||||
void setPrivateKey(const char *private_key);
|
||||
bool loadCACert(Stream& stream, size_t size);
|
||||
bool loadCertificate(Stream& stream, size_t size);
|
||||
bool loadPrivateKey(Stream& stream, size_t size);
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout);
|
||||
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
|
||||
|
||||
Note that the SSL backend is very different between Arduino-Pico and ESP32-Arduino (BearSSL vs. mbedTLS). This means
|
||||
that, for instance, the SSL connection will check valid dates of certificates (and hence require system time to be
|
||||
set on the Pico, which is automatically done in this case).
|
||||
|
||||
TLS-Pre Shared Keys (PSK) is not supported by BearSSL, and hence not implemented here. Neither is ALPN.
|
||||
|
||||
For more advanced control, it is recommended to port to the native Pico calls which allows much more flexibility and control.
|
||||
@@ -0,0 +1,64 @@
|
||||
:orphan:
|
||||
|
||||
WiFiServerSecure Class
|
||||
----------------------
|
||||
|
||||
Implements a TLS encrypted server with optional client certificate validation. See `Server Class <server-class.rst>`__ for general information and `BearSSL Secure Client Class <bearssl-client-secure-class.rst>`__ for basic server and BearSSL concepts.
|
||||
|
||||
setBufferSizes(int recv, int xmit)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Similar to the `BearSSL::WiFiClientSecure` method, sets the receive and transmit buffer sizes. Note that servers cannot request a buffer size from the client, so if these are shrunk and the client tries to send a chunk larger than the receive buffer, it will always fail. Needs to be called before `begin()`
|
||||
|
||||
Setting Server Certificates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TLS servers require a certificate identifying itself and containing its public key, and a private key they will use to encrypt information with. The application author is responsible for generating this certificate and key, either using a self-signed generator or using a commercial certification authority. **Do not re-use the certificates included in the examples provided.**
|
||||
|
||||
This example command will generate a RSA 2048-bit key and certificate:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days 4096
|
||||
|
||||
Again, it is up to the application author to generate this certificate and key and keep the private key safe and **private.**
|
||||
|
||||
setRSACert(const BearSSL::X509List \*chain, const BearSSL::PrivateKey \*sk)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sets a RSA certificate and key to be used by the server when connections are received. Needs to be called before `begin()`
|
||||
|
||||
setECCert(const BearSSL::X509List \*chain, unsigned cert_issuer_key_type, const BearSSL::PrivateKey \*sk)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sets an elliptic curve certificate and key for the server. Needs to be called before `begin()`.
|
||||
|
||||
Client sessions (Resuming connections fast)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The TLS handshake process takes a long time because of all the back and forth between the client and the server. You can shorten it by caching the clients' sessions which will skip a few steps in the TLS handshake. In order for this to work, your client also needs to cache the session. `BearSSL::WiFiClientSecure <bearssl-client-secure-class.rst#sessions-resuming-connections-fast>`__ can do that as well as modern web browsers.
|
||||
|
||||
Here are the kind of performance improvements that you'll be able to see for TLS handshakes with an ESP8266 with it's clock set at 160MHz on a network with fairly low latency:
|
||||
|
||||
* With an EC key of 256 bits, a request taking ~360ms without caching takes ~60ms with caching.
|
||||
* With an RSA key of 2048 bits, a request taking ~1850ms without caching takes ~70ms with caching.
|
||||
|
||||
setCache(BearSSL::ServerSessions \*cache)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sets the cache for the server's sessions. When choosing the size of the cache, remember that each client session takes 100 bytes. If you setup a cache for 10 sessions, it will take 1000 bytes. Needs to be called before `begin()`
|
||||
|
||||
When creating the cache, you can use any of the 2 available constructors:
|
||||
|
||||
* `BearSSL::ServerSessions(ServerSession *sessions, uint32_t size)`: Creates a cache with the given buffer and number of sessions.
|
||||
* `BearSSL::ServerSessions(uint32_t size)`: Dynamically allocates a cache for the given number of sessions.
|
||||
|
||||
Requiring Client Certificates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TLS servers can request the client to identify itself by transmitting a certificate during handshake. If the client cannot transmit the certificate, the connection will be dropped by the server.
|
||||
|
||||
setClientTrustAnchor(const BearSSL::X509List \*client_CA_ta)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sets the trust anchor (normally a self-signing CA) that all received certificates will be verified against. Needs to be called before `begin()`.
|
||||
@@ -0,0 +1,113 @@
|
||||
:orphan:
|
||||
|
||||
BearSSL WiFi Classes
|
||||
--------------------
|
||||
|
||||
Methods and properties described in this section are specific to the Raspberry Pi Pico W and the ESP8266. They are not covered in `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ documentation. Before they are fully documented please refer to information below.
|
||||
|
||||
The `BearSSL <https://bearssl.org>`__ library (with modifications for ESP8266 compatibility and to use ROM tables whenever possible) is used to perform all cryptography and TLS operations. The main ported repo is available `on GitHub <https://github.com/earlephilhower/bearssl-esp8266>`__.
|
||||
|
||||
CPU Requirements
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
SSL operations take significant CPU cycles to run, so it will connect significantly slower than unprotected connections on the Pico, but the actual data transfer rates once connected are similar.
|
||||
|
||||
See the section on `sessions <#sessions-resuming-connections-fast>`__ and `limiting cryptographic negotiation <#limiting-ciphers-new-connections-faster>`__ for ways of ensuring faster modes are used.
|
||||
|
||||
Memory Requirements
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
BearSSL doesn't perform memory allocations at runtime, but it does require allocation of memory at the beginning of a connection. There are two memory chunks required:
|
||||
. A per-application secondary stack
|
||||
. A per-connection TLS receive/transmit buffer plus overhead
|
||||
|
||||
The per-application secondary stack is approximately 7KB in size and is used for temporary variables during BearSSL processing. Only one stack is required, and it will be allocated whenever any `BearSSL::WiFiClientSecure` or `BearSSL::WiFiServerSecure` are instantiated. So, in the case of a global client or server, the memory will be allocated before `setup()` is called.
|
||||
|
||||
The per-connection buffers are approximately 22KB in size, but in certain circumstances it can be reduced dramatically by using MFLN or limiting message sizes. See the `MLFN section <#mfln-or-maximum-fragment-length-negotiation-saving-ram>`__ below for more information.
|
||||
|
||||
Object Lifetimes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
There are many configuration options that require passing in a pointer to an object (i.e. a pointer to a private key, or a certificate list). In order to preserve memory, BearSSL does NOT copy the objects passed in via these pointers and as such any pointer passed in to BearSSL needs to be preserved for the life of the client object. For example, the following code is **in error**:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
BearSSL::WiFiClientSecure client;
|
||||
const char x509CA PROGMEM = ".......";
|
||||
void setup() {
|
||||
BearSSL::X509List x509(x509CA);
|
||||
client.setTrustAnchor(&x509);
|
||||
}
|
||||
void loop() {
|
||||
client.connect("192.168.1.1", 443);
|
||||
}
|
||||
|
||||
Because the pointer to the local object `x509` no longer is valid after setup(), expect to crash in the main `loop()` where it is accessed by the `client` object.
|
||||
|
||||
As a rule, either keep your objects global, use `new` to create them, or ensure that all objects needed live inside the same scope as the client.
|
||||
|
||||
TLS and HTTPS Basics
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following discussion is only intended to give a rough idea of TLS/HTTPS(which is just HTTP over a TLS connection) and the components an application needs to manage to make a TLS connection. For more detailed information, please check the relevant `RFC 5246 <https://tools.ietf.org/search/rfc5246>`__ and others.
|
||||
|
||||
TLS can be broken into two stages: verifying the identities of server (and potentially client), and then encrypting blocks of data bidirectionally. Verifying the identity of the other partner is handled via keys encoded in X509 certificates, optionally signed by a series of other entities.
|
||||
|
||||
|
||||
Public and Private Keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Cryptographic keys are required for many of the BearSSL functions. Both public and private keys are supported, with either Elliptic Curve or RSA key support.
|
||||
|
||||
To generate a public or private key from an existing PEM (ASCII format) or DER (binary format), the simplest method is to use the constructor:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
BearSSL::PublicKey(const char *pemString)
|
||||
... or ...
|
||||
BearSSL::PublicKey(const uint8_t *derArray, size_t derLen)
|
||||
|
||||
Note that `PROGMEM` strings and arrays are natively supported by these constructors and no special `*_P` modes are required. There are additional functions to identify the key type and access the underlying BearSSL proprietary types, but they are not needed by user applications.
|
||||
|
||||
TLS Sessions
|
||||
~~~~~~~~~~~~
|
||||
|
||||
TLS supports the notion of a session (completely independent and different from HTTP sessions) which allow clients to reconnect to a server without having to renegotiate encryption settings or validate X509 certificates. This can save significant time (3-4 seconds in the case of EC keys) and can help save power by allowing the ESP8266 to sleep for a long time, reconnect and transmit some samples using the SSL session, and then jump back to sleep quicker.
|
||||
|
||||
`BearSSL::Session` is an opaque class. Use the `BearSSL::WiFiClientSecure.setSession(&BearSSLSession)` method to apply it before the first `BearSSL::WiFiClientSecure.connect()` and it will be updated with session parameters during the operation of the connection. After the connection has had `.close()` called on it, serialize the `BearSSL::Session` object to stable storage (EEPROM, RTC RAM, etc.) and restore it before trying to reconnect. See the `BearSSL_Sessions` example for a detailed example.
|
||||
|
||||
`Sessions <#sessions-resuming-connections-fast>`__ contains additional information on the sessions API.
|
||||
|
||||
X.509 Certificate(s)
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
X509 certificates are used to identify peers in TLS connections. Normally only the server identifies itself, but the client can also supply an X509 certificate if desired (this is often done in MQTT applications). The certificate contains many fields, but the most interesting in our applications are the name, the public key, and potentially a chain of signing that leads back to a trusted authority (like a global internet CA or a company-wide private certificate authority).
|
||||
|
||||
Any call that takes an X509 certificate can also take a list of X509 certificates, so there is no special `X509` class, simply `BearSSL::X509List` (which may only contain a single certificate).
|
||||
|
||||
Generating a certificate to be used to validate using the constructor
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
BearSSL::X509List(const char *pemX509);
|
||||
...or...
|
||||
BearSSL::X509List(const uint8_t *derCert, size_t derLen);
|
||||
|
||||
If you need to add additional certificates (unlikely in normal operation), the `::append()` operation can be used.
|
||||
|
||||
|
||||
Certificate Stores
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The web browser you're using to read this document keeps a list of 100s of certification authorities (CAs) worldwide that it trusts to attest to the identity of websites.
|
||||
|
||||
In many cases your application will know the specific CA it needs to validate web or MQTT servers against (often just a single, self-signing CA private to your institution). Simply load your private CA in a `BearSSL::X509List` and use that as your trust anchor.
|
||||
|
||||
However, there are cases where you will not know beforehand which CA you will need (i.e. a user enters a website through a keypad), and you need to keep the list of CAs just like your web browser. In those cases, you need to generate a certificate bundle on the PC while compiling your application, upload the `certs.ar` bundle to LittleFS or SD when uploading your application binary, and pass it to a `BearSSL::CertStore()` in order to validate TLS peers.
|
||||
|
||||
See the `BearSSL_CertStore` example for full details.
|
||||
|
||||
Supported Crypto
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Please see the `BearSSL website <https://bearssl.org>`__ for detailed cryptographic information. In general, TLS 1.2, TLS 1.1, and TLS 1.0 are supported with RSA and Elliptic Curve keys and a very rich set of hashing and symmetric encryption codes. Please note that Elliptic Curve (EC) key operations take a significant amount of time.
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'1.0.0'
|
||||
version = u'2.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'1.0.0'
|
||||
release = u'2.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+1
-1
@@ -55,4 +55,4 @@ Returns the length of the EEPROM (i.e. the value specified in
|
||||
|
||||
EEPROM Examples
|
||||
---------------
|
||||
Three EEPROM `examples<https://github.com/earlephilhower/arduino-pico/tree/master/libraries/EEPROM>`_ are included.
|
||||
Three EEPROM `examples <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/EEPROM>`_ are included.
|
||||
|
||||
+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.
|
||||
+4
-4
@@ -53,7 +53,7 @@ the word to fill when no data is available to send to the I2S hardware.
|
||||
Call before ``I2S::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the word clock frequency, but does not start the I2S device if not
|
||||
already running. May be called after ``I2S::begin()`` to change the
|
||||
sample rate on-the-fly.
|
||||
@@ -158,17 +158,17 @@ Reads a left and right 8-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
bool read16(int16_t \*l, int16_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 16-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
bool read24(int32_t \*l, int32_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 24-bit sample and returns ``true`` on success. See note below
|
||||
about 24-bit mode. Will block until data is available.
|
||||
|
||||
bool read32(int32_t \*l, int32_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 32-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
|
||||
+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.
|
||||
|
||||
|
||||
+16
-1
@@ -7,7 +7,7 @@ Arduino-Pico. Arduino-Pico is a community port of the RP2040
|
||||
to make it easier and more fun to use and program the Raspberry Pi
|
||||
Pico / RP2040 based boards.
|
||||
|
||||
This Arduino core uses a custom toolset with GCC 10.2 and Newlib 4.0.0
|
||||
This Arduino core uses a custom toolset with GCC 10.3 and Newlib 4.0.0
|
||||
and doesn't require any system-installed prerequisites.
|
||||
|
||||
For the latest version, always check https://github.com/earlephilhower/arduino-pico
|
||||
@@ -24,6 +24,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
|
||||
Pin (Re)Assignment <pins>
|
||||
|
||||
RP2040 Helper <rp2040>
|
||||
|
||||
Analog I/O <analog>
|
||||
Digital I/O <digital>
|
||||
EEPROM <eeprom>
|
||||
@@ -39,6 +41,19 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
WiFi (Pico-W Support) <wifi>
|
||||
WiFiClient <wificlient>
|
||||
WiFiServer <wifiserver>
|
||||
WiFiUDP <wifiudp>
|
||||
NTP client <wifintp>
|
||||
BearSSL Encrypted TLS <bearssl>
|
||||
WiFiClientSecure (TLS/SSL/HTTPS) <bearssl-client-secure-class>
|
||||
WiFiServerSecure (TLS/SSL/HTTPS) <bearssl-server-secure-class>
|
||||
|
||||
HTTP/HTTPS Client <httpclient>
|
||||
|
||||
Over-the-Air (OTA) Updates <ota>
|
||||
|
||||
Ported/Optimized Libraries <libraries>
|
||||
Using Pico-SDK <sdk>
|
||||
|
||||
|
||||
+22
-2
@@ -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
|
||||
-------------------------------------
|
||||
@@ -65,6 +65,26 @@ Them hit the upload button and your sketch should upload and run.
|
||||
In some cases the Pico will encounter a hard hang and its USB port will not respond to the auto-reset request. Should this happen, just
|
||||
follow the initial procedure of holding the BOOTSEL button down while plugging in the Pico to enter the ROM bootloader.
|
||||
|
||||
Unable to Upload First Sketch
|
||||
-----------------------------
|
||||
If the Arduino IDE has never seen a serial port from a working device (Pico, AVR, or any other serial port), you
|
||||
may not be able to install using the prior directions because the ``Tools->Port`` menu will be grayed out and
|
||||
empty. In this case, a special workaround identified by @fjansson in `this issue report <https://github.com/earlephilhower/arduino-pico/issues/688>`_ .
|
||||
|
||||
To allow subsequent uploads to automatically work, you will need to manually install a UF2 binary onto the Raspberry Pi Pico one time to
|
||||
allow it to present a Serial port for the Arduino IDE to detect and save.
|
||||
|
||||
Perform the following steps to program a dummy sketch:
|
||||
|
||||
1. Open a new, empty sketch (doesn't work with a read-only example sketch).
|
||||
2. Compile it by pressing the "Verify" checkmark button.
|
||||
3. Select the ``Sketch -> Export Compiled Binary`` menu. Select a location e.g. the desktop. A folder is created there, containing a file ending in .uf2
|
||||
4. Copy this file to the Pico's drive, by drag and drop in the Explorer.
|
||||
|
||||
The Pico restarts and now now has a serial port. Now the Port menu in Arduino is not gray anymore, select the port there.
|
||||
After this, normal uploading from the Arduino editor should work.
|
||||
|
||||
|
||||
Windows 7 Driver Notes
|
||||
----------------------
|
||||
|
||||
@@ -85,7 +105,7 @@ When running MalwareBytes antivirus (or others) the scanner may lock the compile
|
||||
|
||||
Symptoms include:
|
||||
|
||||
*Access denied during update in the boards manager - affects the .exe files, because MalwareBytes has locked them.
|
||||
* Access denied during update in the boards manager - affects the .exe files, because MalwareBytes has locked them.
|
||||
* Access denied during compilation, to one of the .exe files - same reason.
|
||||
* Can't delete the .exe files - they're locked by MalwareBytes.
|
||||
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ uint32_t rp2040.fifo.pop()
|
||||
|
||||
Reads a value from this core's FIFO. Blocks until one is available.
|
||||
|
||||
bool rp2040.fifo.pop_nb(uint32_t *dest)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool rp2040.fifo.pop_nb(uint32_t \*dest)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reads a value from this core's FIFO and places it in dest. Will return
|
||||
``true`` if successful, or ``false`` if the pop would block.
|
||||
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
OTA Updates
|
||||
===========
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
OTA (Over the Air) update is the process of uploading firmware to a Pico using a Wi-Fi, Ethernet, or other connection rather than a serial port. This is especially useful for WiFi enabled Picos, like the Pico W, because it lets systems be updated remotely, without needing physical access.
|
||||
|
||||
OTA may be done using:
|
||||
|
||||
- `Arduino IDE <#arduino-ide>`__
|
||||
- `Web Browser <#web-browser>`__
|
||||
- `HTTP Server <#http-server>`__
|
||||
- Any other method (ZModen receive over a UART port, etc.) by using the ``Updater`` object in your sketch
|
||||
|
||||
The Arduino IDE option is intended primarily for the software development phase. The other two options would be more useful after deployment, to provide the module with application updates either manually with a web browser, or automatically using an HTTP server.
|
||||
|
||||
In any case, the first firmware upload has to be done over a serial port. If the OTA routines are correctly implemented in the sketch, then all subsequent uploads may be done over the air.
|
||||
|
||||
By default, there is no imposed security for the OTA process. It is up to the developer to ensure that updates are allowed only from legitimate / trusted sources. Once the update is complete, the module restarts, and the new code is executed. The developer should ensure that the application running on the module is shut down and restarted in a safe manner. Chapters below provide additional information regarding security and safety of OTA updates.
|
||||
|
||||
OTA Requirements
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
OTA requires a LittleFS partition to store firmware upgrade files. Make sure that you configure the sketch with a filesystem large enough to handle whatever size firmware binary you expect. Updates may be compressed, minimizing the total space needed.
|
||||
|
||||
Power Fail Safety
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The update commands are all stored in flash, so a power cycle during update (except if the OTA bootloader is being changed) should not brick the device because when power is restored the OTA bootloader will begin the process from scratch once again.
|
||||
|
||||
|
||||
Security Disclaimer
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
No guarantees as to the level of security provided for your application by the following methods is implied. Please refer to the GNU LGPL license associated for this project for full disclaimers. If you do find security weaknesses, please don't hesitate to contact the maintainers or supply pull requests with fixes. The MD5 verification and password protection schemes are already known to supply a very weak level of security.
|
||||
|
||||
Basic Security
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The module has to be exposed wirelessly to get it updated with a new sketch. That poses a risk of the module being violently hacked and programmed with some other code. To reduce the likelihood of being hacked, consider protecting your uploads with a password, selecting certain OTA port, etc.
|
||||
|
||||
Check functionality provided with the `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ library that may improve security:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setPort(uint16_t port);
|
||||
void setHostname(const char* hostname);
|
||||
void setPassword(const char* password);
|
||||
|
||||
Certain basic protection is already built in and does not require any additional coding by the developer. `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ and espota.py use `Digest-MD5 <https://en.wikipedia.org/wiki/Digest_access_authentication>`__ to authenticate uploads. Integrity of transferred data is verified on the Pico side using `MD5 <https://en.wikipedia.org/wiki/MD5>`__ checksum.
|
||||
|
||||
Make your own risk analysis and, depending on the application, decide what library functions to implement. If required, consider implementation of other means of protection from being hacked, like exposing modules for uploads only according to a specific schedule, triggering OTA only when the user presses a dedicated “Update” button wired to the Pico, etc.
|
||||
|
||||
Advanced Security - Signed Updates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While the above password-based security will dissuade casual hacking attempts, it is not highly secure. For applications where a higher level of security is needed, cryptographically signed OTA updates can be required. This uses SHA256 hashing in place of MD5 (which is known to be cryptographically broken) and RSA-2048 bit level public-key encryption to guarantee that only the holder of a cryptographic private key can produce signed updates accepted by the OTA update mechanisms.
|
||||
|
||||
Signed updates are updates whose compiled binaries are signed with a private key (held by the developer) and verified with a public key (stored in the application and available for all to see). The signing process computes a hash of the binary code, encrypts the hash with the developer's private key, and appends this encrypted hash (also called a signature) to the binary that is uploaded (via OTA, web, or HTTP server). If the code is modified or replaced in any way by anyone except the holder of the developer's private key, the signature will not match and the Pico will reject the upload.
|
||||
|
||||
Cryptographic signing only protects against tampering with binaries delivered via OTA. If someone has physical access, they will always be able to flash the device over the serial port. Signing also does not encrypt anything but the hash (so that it can't be modified), so this does not protect code inside the device: if a user has physical access they can read out your program.
|
||||
|
||||
**Securing your private key is paramount. The same private/public key pair that was used with the original upload must also be used to sign later binaries. Loss of the private key associated with a binary means that you will not be able to OTA-update any of your devices in the field. Alternatively, if someone else copies the private key, then they will be able to use it to sign binaries which will be accepted by the Pico.**
|
||||
|
||||
Signed Binary Format
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The format of a signed binary is compatible with the standard binary format, and can be uploaded to a non-signed Pico via serial or OTA without any conditions. Note, however, that once an unsigned OTA app is overwritten by this signed version, further updates will require signing.
|
||||
|
||||
As shown below, the signed hash is appended to the unsigned binary, followed by the total length of the signed hash (i.e., if the signed hash was 64 bytes, then this uint32 data segment will contain 64). This format allows for extensibility (such as adding a CA-based validation scheme allowing multiple signing keys all based on a trust anchor). Pull requests are always welcome. (currently it uses SHA256 with RSASSA-PKCS1-V1_5-SIGN signature scheme from RSA PKCS #1 v1.5)
|
||||
|
||||
.. code:: bash
|
||||
|
||||
NORMAL-BINARY <SIGNATURE> <uint32 LENGTH-OF-SIGNATURE>
|
||||
|
||||
Signed Binary Prerequisites
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
OpenSSL is required to run the standard signing steps, and should be available on any UNIX-like or Windows system. As usual, the latest stable version of OpenSSL is recommended.
|
||||
|
||||
Signing requires the generation of an RSA-2048 key (other bit lengths are supported as well, but 2048 is a good selection today) using any appropriate tool. The following shell commands will generate a new public/private key pair. Run them in the sketch directory:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
openssl genrsa -out private.key 2048
|
||||
openssl rsa -in private.key -outform PEM -pubout -out public.key
|
||||
|
||||
Automatic Signing
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable sigining verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
|
||||
To enable this mode, just include `private.key` and `public.key` in the sketch `.ino` directory. The IDE will call a helper script (`tools/signing.py`) before the build begins to create a header to enable key validation using the given public key, and to actually do the signing after the build process, generating a `sketch.bin.signed` file. When OTA is enabled (ArduinoOTA, Web, or HTTP), the binary will automatically only accept signed updates.
|
||||
|
||||
When the signing process starts, the message:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Enabling binary signing
|
||||
|
||||
will appear in the IDE window before a compile is launched. At the completion of the build, the signed binary file well be displayed in the IDE build window as:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Signed binary: /full/path/to/sketch.bin.signed
|
||||
|
||||
If you receive either of the following messages in the IDE window, the signing was not completed and you will need to verify the `public.key` and `private.key`:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Not enabling binary signing
|
||||
... or ...
|
||||
Not signing the generated binary
|
||||
|
||||
Manual Signing of Binaries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Users may also manually sign executables and require the OTA process to verify their signature. In the main code, before enabling any update methods, add the following declarations and function call:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
<in globals>
|
||||
BearSSL::PublicKey signPubKey( ... key contents ... );
|
||||
BearSSL::HashSHA256 hash;
|
||||
BearSSL::SigningVerifier sign( &signPubKey );
|
||||
...
|
||||
<in setup()>
|
||||
Update.installSignature( &hash, &sign );
|
||||
|
||||
The above snippet creates a BearSSL public key and a SHA256 hash verifier, and tells the Update object to use them to validate any updates it receives from any method.
|
||||
|
||||
Compile the sketch normally and, once a `.bin` file is available, sign it using the signer script:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<PicoArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
|
||||
|
||||
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin # Maximum compression, output sketch.bin.gz
|
||||
<Upload the resultant sketch.bin.gz>
|
||||
|
||||
If signing is desired, sign the gzip compressed file *after* compression.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin
|
||||
<PicoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
|
||||
Safety
|
||||
~~~~~~
|
||||
|
||||
The OTA process consumes some of the Pico’s resources and bandwidth during upload. Then, the module is restarted and a new sketch executed. Analyse and test how this affects the functionality of the existing and new sketches.
|
||||
|
||||
If the Pico is in a remote location and controlling some equipment, you should devote additional attention to what happens if operation of this equipment is suddenly interrupted by the update process. Therefore, decide how to put this equipment into a safe state before starting the update. For instance, your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve is left open, the garden may be flooded.
|
||||
|
||||
The following functions are provided with the `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ library and intended to handle functionality of your application during specific stages of OTA, or on an OTA error:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void onStart(OTA_CALLBACK(fn));
|
||||
void onEnd(OTA_CALLBACK(fn));
|
||||
void onProgress(OTA_CALLBACK_PROGRESS(fn));
|
||||
void onError(OTA_CALLBACK_ERROR (fn));
|
||||
|
||||
Uploading from the Arduino IDE
|
||||
------------------------------
|
||||
|
||||
Uploading modules wirelessly from Arduino IDE is intended for the following typical scenarios:
|
||||
|
||||
- During firmware development as a quicker alternative to loading over a serial port,
|
||||
|
||||
- For updating a small number of modules,
|
||||
|
||||
- Only if modules are accessible on the same network as the computer with the Arduino IDE.
|
||||
|
||||
- For all IDE uploads,m the Pico W and the computer must be connected to the same network.
|
||||
|
||||
To upload wirelessly from the IDE:
|
||||
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriare calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
|
||||
2. Upload using standard USB connection the first time.
|
||||
|
||||
3. The ``Tools->Port`` should now list ``pico-######`` under the ``Network Ports``. Select it (you won't be able to use the serial monitor, of course).
|
||||
|
||||
4. Try another upload. It should display the OTA process in place of the serial port upload.
|
||||
|
||||
Password Protection
|
||||
-------------------
|
||||
|
||||
Protecting your OTA uploads with password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
ArduinoOTA.setPassword((const char *)"123");
|
||||
|
||||
Where ``123`` is a sample password that you should replace with your own.
|
||||
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This is including original simple ``123`` OTA password. Then attempt to upload sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see prompt for password.
|
||||
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in upload log.
|
||||
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
|
||||
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
|
||||
|
||||
|
||||
|
||||
|
||||
Web Browser
|
||||
-----------
|
||||
|
||||
Updates described in this chapter are done with a web browser that can be useful in the following typical scenarios:
|
||||
|
||||
- after application deployment if loading directly from Arduino IDE is inconvenient or not possible,
|
||||
- after deployment if user is unable to expose module for OTA from external update server,
|
||||
- to provide updates after deployment to small quantity of modules when setting an update server is not practicable.
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- The Pico and the computer must be connected to the same network, or the IP of the Pico should be known if on a different network.
|
||||
|
||||
Implementation Overview
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Updates with a web browser are implemented using ``HTTPUpdateServer`` class together with ``WebServer`` and ``LEAmDNS`` classes. The following code is required to get it work:
|
||||
|
||||
setup()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
loop()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
httpServer.handleClient();
|
||||
|
||||
In case OTA update fails dead after entering modifications in your sketch, you can always recover module by loading it over a serial port. Then diagnose the issue with sketch using Serial Monitor. Once the issue is fixed try OTA again.
|
||||
|
||||
|
||||
HTTP Server
|
||||
-----------
|
||||
|
||||
``HTTPUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet.
|
||||
|
||||
Note that by default this class closes all other connections except the one used by the update, this is because the update method blocks. This means that if there's another application receiving data then TCP packets will build up in the buffer leading to out of memory errors causing the OTA update to fail. There's also a limited number of receive buffers available and all may be used up by other applications.
|
||||
|
||||
There are some cases where you know that you won't be receiving any data but would still like to send progress updates.
|
||||
It's possible to disable the default behaviour (and keep connections open) by calling closeConnectionsOnUpdate(false).
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- web server
|
||||
|
||||
Arduino code
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Simple updater downloads the file every time the function is called.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
HTTPUpdate.update(client, "192.168.0.2", 80, "/arduino.bin");
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
|
||||
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
t_httpUpdate_return ret = HTTPUpdate.update(client, "192.168.0.2", 80, "/pico/update/arduino.php", "optional current version string here");
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.println("[update] Update failed.");
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
|
||||
break;
|
||||
}
|
||||
|
||||
TLS updater
|
||||
^^^^^^^^^^^
|
||||
|
||||
Please read and try the examples provided with the library.
|
||||
|
||||
Server request handling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
For the simple updater the server only needs to deliver the binary file for update.
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
For advanced update management a script (such as a PHP script) can run on the server side. It will receive the following headers which it may use to choose a specific firmware file to serve:
|
||||
|
||||
::
|
||||
[User-Agent] => Pico-HTTP-Update
|
||||
[x-Pico-STA-MAC] => 18:FE:AA:AA:AA:AA
|
||||
[x-Pico-AP-MAC] => 1A:FE:AA:AA:AA:AA
|
||||
[x-Pico-Version] => DOOR-7-g14f53a19
|
||||
[x-Pico-Mode] => sketch
|
||||
|
||||
|
||||
Stream Interface
|
||||
----------------
|
||||
|
||||
The Stream Interface is the base for all other update modes like OTA, HTTP Server / client. Given a Stream-class variable `streamVar` providing `byteCount` bytes of firmware, it can store the firmware as follows:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Update.begin(firmwareLengthInBytes);
|
||||
Update.writeStream(streamVar);
|
||||
Update.end();
|
||||
|
||||
OTA Bootloader and Memory Map
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A firmware file is uploaded via any method (Ethernet, WiFi, serial ZModem, etc.) and stored on the LittleFS filesystem as a normal file. The Updater class (or the underlying PicoOTA) will make a special "OTA command" file on the filesystem, which will be read by the OTA bootloader. On a reboot, this OTA bootloader will check for an upgrade file, verify its contents, and then perform the requested update and reboot. If no upgrade file is present, the OTA bootloader simply jumps to the main sketch.
|
||||
|
||||
The ROM layout consists of:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
[boot2.S] [OTA Bootloader] [0-pad] [OTA partition table] [Main sketch] [LittleFS filesystem] [EEPROM]
|
||||
|
||||
@@ -222,6 +222,18 @@ default Pico SDK USB stack. To change it, add
|
||||
Note that the special "No USB" setting is also supported, through the
|
||||
shortcut-define ``PIO_FRAMEWORK_ARDUINO_NO_USB``.
|
||||
|
||||
IP Stack
|
||||
---------
|
||||
|
||||
The lwIP stack can be configured to support only IPv4 (default) or additionally IPv6. To activate IPv6 support, add
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; IPv6
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6
|
||||
|
||||
to the ``platformio.ini``.
|
||||
|
||||
|
||||
Selecting a different core version
|
||||
----------------------------------
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
RP2040 Helper Class
|
||||
===================
|
||||
|
||||
Some of the core functionality of the RP2040 chip powering the Raspberry Pi
|
||||
Pico is exposed in the RP2040 class variable ``rp2040``.
|
||||
|
||||
Core Internals
|
||||
--------------
|
||||
|
||||
int rp2040.f_cpu()
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Returns the current frequency of the core clock. This is read at runtime,
|
||||
versus the constant ``F_CPU`` macro that is also available. This is useful
|
||||
in cases where your code changes the core clock (i.e. low power modes, etc.)
|
||||
|
||||
uint32_t rp2040.getCycleCount()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a 32-bit cycle count from then the core started running. Because it
|
||||
is only 32-bits, and the Pico runs at 133MHz, this value can loop around
|
||||
in a matter of seconds.
|
||||
|
||||
uint64_t rp2040.getCycleCount64()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a 64-bit cycle count from then the core started running. This value
|
||||
should never loop around in normal mode (at 133MHz it would take over 4,000
|
||||
years to overflow).
|
||||
|
||||
uint32_t rp2040.hwrand32()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a 32-bit value derived from the CPU cycle counter and the ROSC
|
||||
oscillator. Because the ROSC bit is not a true random number generator, the
|
||||
values returned may not meet the most stringent random tests. **If your
|
||||
application needs absolute bulletproof random numbers, consider using
|
||||
dedicated external hardware.**
|
||||
|
||||
void rp2040.reboot()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Forces a hardware reboot of the Pico.
|
||||
|
||||
Memory Information
|
||||
------------------
|
||||
|
||||
int rp2040.getFreeHeap()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of bytes free for heap allocation (i.e. malloc, new). Note
|
||||
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()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of bytes allocated out of the heap.
|
||||
|
||||
int rp2040.getTotalHeap()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the total heap that was available to this program at compile time (i.e.
|
||||
the Pico RAM size minus things like the ``.data`` and ``.bss`` sections and other
|
||||
overhead).
|
||||
@@ -28,6 +28,7 @@ The size of the receive FIFO may also be adjusted from the default 32 bytes by
|
||||
using the ``setFIFOSize`` call prior to calling ``begin()``
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Serial1.setFIFOSize(128);
|
||||
Serial1.begin(baud);
|
||||
|
||||
@@ -38,6 +39,7 @@ For applications where an IRQ driven serial port is not appropriate, use
|
||||
``setPollingMode(true)`` before calling ``begin()``
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Serial1.setPollingMode(true);
|
||||
Serial1.begin(300)
|
||||
|
||||
|
||||
+3
-2
@@ -12,8 +12,9 @@ serial port, ``Serial`` as well as supporting automatic reset-to-upload
|
||||
from the IDE.
|
||||
|
||||
The Arduino-Pico core includes ported versions of the basic Arduino
|
||||
``Keyboard`` and ``Mouse`` libraries. These libraries allow you to
|
||||
emulate a keyboard or mouse with the Pico in your sketches.
|
||||
``Keyboard``, ``Mouse`` and ``Joystick`` libraries. These libraries
|
||||
allow you to emulate a keyboard, a gamepad or mouse (or all together)
|
||||
with the Pico in your sketches.
|
||||
|
||||
See the examples and Arduino Reference at
|
||||
https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
WiFi (Raspberry Pi Pico W) Support
|
||||
==================================
|
||||
|
||||
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.
|
||||
|
||||
Supported Features
|
||||
------------------
|
||||
|
||||
* WiFi connection (Open, WPA/WPA2)
|
||||
|
||||
* Static IP or dynamic DHCP supported
|
||||
|
||||
* Station Mode (STA, connects to an existing network)
|
||||
|
||||
* Access Point Mode (AP, creates its own wireless network) with 4 clients
|
||||
|
||||
* WiFi Scanning and Reporting
|
||||
|
||||
* See the ``ScanNetworks.ino`` example to better understand the process.
|
||||
|
||||
|
||||
Important Information
|
||||
---------------------
|
||||
|
||||
Please note that WiFi on the Pico W is a work-in-progress and there are some important caveats:
|
||||
|
||||
* Adding WiFi increases flash usage by over 220KB
|
||||
|
||||
* There is a 220KB binary firmware blob for the WiFi chip (CYW43-series) which the Pico W uses, even to control the onboard LED.
|
||||
|
||||
* Adding WiFi increases RAM usage by ~40KB.
|
||||
|
||||
* LWIP, the TCP/IP driver, requires preallocated buffers to allow it to run in non-polling mode (i.e. packets can be sent and received in the background without the application needing to explicitly do anything).
|
||||
|
||||
* The WiFi driver is a little limited as of now, but fully functional for sending and receiving data
|
||||
|
||||
* Extensible Authentication Protocol (EAP) is not supported
|
||||
|
||||
* Combined STA/AP mode is not supported
|
||||
|
||||
* Certain WiFi status values (RSSI, BSSID, etc.) are not available.
|
||||
|
||||
* Multicore is supported, but only one core may run ``WiFi`` code.
|
||||
|
||||
* FreeRTOS is not yet supported due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
|
||||
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
|
||||
|
||||
Special Thanks
|
||||
--------------
|
||||
|
||||
Special thanks to:
|
||||
|
||||
* @todbot for donating one of his Pico W boards to the effort
|
||||
|
||||
* @d-a-v for much patient explanation about LWIP internals
|
||||
|
||||
* The whole ESP8266 Arduino team for their network classes
|
||||
|
||||
* Adafruit Industries for their kind donation
|
||||
@@ -0,0 +1,110 @@
|
||||
.. _HeaderTag:
|
||||
|
||||
WiFiClient
|
||||
==========
|
||||
|
||||
Methods documented for `Client <https://www.arduino.cc/en/Reference/WiFiClientConstructor>`__ in `Arduino <https://github.com/arduino/Arduino>`__
|
||||
|
||||
1. `WiFiClient() <https://www.arduino.cc/en/Reference/WiFiClient>`__
|
||||
2. `connected() <https://www.arduino.cc/en/Reference/WiFiClientConnected>`__
|
||||
3. `connect() <https://www.arduino.cc/en/Reference/WiFiClientConnect>`__
|
||||
4. `write() <https://www.arduino.cc/en/Reference/WiFiClientWrite>`__
|
||||
5. `print() <https://www.arduino.cc/en/Reference/WiFiClientPrint>`__
|
||||
6. `println() <https://www.arduino.cc/en/Reference/WiFiClientPrintln>`__
|
||||
7. `available() <https://www.arduino.cc/en/Reference/WiFiClientAvailable>`__
|
||||
8. `read() <https://www.arduino.cc/en/Reference/WiFiClientRead>`__
|
||||
9. `flush() <https://www.arduino.cc/en/Reference/WiFiClientFlush>`__
|
||||
10. `stop() <https://www.arduino.cc/en/Reference/WiFIClientStop>`__
|
||||
|
||||
Methods and properties described further down are specific to ESP8266. They are not covered in `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ documentation. Before they are fully documented please refer to information below.
|
||||
|
||||
flush and stop
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
``flush(timeoutMs)`` and ``stop(timeoutMs)`` both have now an optional argument: ``timeout`` in millisecond, and both return a boolean.
|
||||
|
||||
Default input value 0 means that effective value is left at the discretion of the implementer.
|
||||
|
||||
``flush()`` returning ``true`` indicates that output data have effectively been sent, and ``false`` that a timeout has occurred.
|
||||
|
||||
``stop()`` returns ``false`` in case of an issue when closing the client (for instance a timed-out ``flush``). Depending on implementation, its parameter can be passed to ``flush()``.
|
||||
|
||||
setNoDelay
|
||||
~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
setNoDelay(nodelay)
|
||||
|
||||
With ``nodelay`` set to ``true``, this function will to disable `Nagle algorithm <https://en.wikipedia.org/wiki/Nagle%27s_algorithm>`__.
|
||||
|
||||
This algorithm is intended to reduce TCP/IP traffic of small packets sent over the network by combining a number of small outgoing messages, and sending them all at once. The downside of such approach is effectively delaying individual messages until a big enough packet is assembled.
|
||||
|
||||
*Example:*
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
client.setNoDelay(true);
|
||||
|
||||
getNoDelay
|
||||
~~~~~~~~~~
|
||||
|
||||
Returns whether NoDelay is enabled or not for the current connection.
|
||||
|
||||
setSync
|
||||
~~~~~~~
|
||||
|
||||
This is an experimental API that will set the client in synchronized mode.
|
||||
In this mode, every ``write()`` is flushed. It means that after a call to
|
||||
``write()``, data are ensured to be received where they went sent to (that is
|
||||
``flush`` semantic).
|
||||
|
||||
When set to ``true`` in ``WiFiClient`` implementation,
|
||||
|
||||
- It slows down transfers, and implicitly disable the Nagle algorithm.
|
||||
|
||||
- It also allows to avoid a temporary copy of data that otherwise consumes
|
||||
at most ``TCP_SND_BUF`` = (2 * ``MSS``) bytes per connection,
|
||||
|
||||
getSync
|
||||
~~~~~~~
|
||||
|
||||
Returns whether Sync is enabled or not for the current connection.
|
||||
|
||||
setDefaultNoDelay and setDefaultSync
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These set the default value for both ``setSync`` and ``setNoDelay`` for
|
||||
every future instance of ``WiFiClient`` (including those coming from
|
||||
``WiFiServer.available()`` by default).
|
||||
|
||||
Default values are false for both ``NoDelay`` and ``Sync``.
|
||||
|
||||
This means that Nagle is enabled by default *for all new connections*.
|
||||
|
||||
getDefaultNoDelay and getDefaultSync
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Return the values to be used as default for NoDelay and Sync for all future connections.
|
||||
|
||||
Other Function Calls
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
uint8_t status ()
|
||||
virtual size_t write (const uint8_t *buf, size_t size)
|
||||
size_t write_P (PGM_P buf, size_t size)
|
||||
size_t write (Stream &stream)
|
||||
size_t write (Stream &stream, size_t unitSize) __attribute__((deprecated))
|
||||
virtual int read (uint8_t *buf, size_t size)
|
||||
virtual int peek ()
|
||||
virtual size_t peekBytes (uint8_t *buffer, size_t length)
|
||||
size_t peekBytes (char *buffer, size_t length)
|
||||
virtual operator bool ()
|
||||
IPAddress remoteIP ()
|
||||
uint16_t remotePort ()
|
||||
IPAddress localIP ()
|
||||
uint16_t localPort ()
|
||||
|
||||
Documentation for the above functions is not yet available.
|
||||
@@ -0,0 +1,76 @@
|
||||
Network Time Protocol (NTP)
|
||||
===========================
|
||||
|
||||
NTP allows the Pico to set its internal clock using the internet, and is
|
||||
required for secure connections because the certificates used have valid
|
||||
date stamps.
|
||||
|
||||
After ``WiFi.begin()`` use ``NTP.begin(s1)`` or ``NTP,begin(s1, s2)`` to
|
||||
use one or two NTP servers (common ones are ``pool.ntp.org`` and
|
||||
``time.nist.gov``) .
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
WiFi.begin("ssid", "pass");
|
||||
NTP.begin("pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Either names or ``IPAddress`` may be used to identify the NTP server to
|
||||
use.
|
||||
|
||||
It may take seconds to minutes for the system time to be updated by NTP,
|
||||
depending on the server. It is often useful to check that ``time(NULL)``
|
||||
returns a sane value before continuing a sketch:
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void setClock() {
|
||||
NTP.begin("pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Serial.print("Waiting for NTP time sync: ");
|
||||
time_t now = time(nullptr);
|
||||
while (now < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
now = time(nullptr);
|
||||
}
|
||||
Serial.println("");
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Serial.print("Current time: ");
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
bool NTP.waitSet(uint32_t timeout)
|
||||
----------------------------------
|
||||
This call will wait up to timeout milliseconds for the time to be set, and returns
|
||||
success or failure. It will also begin NTP with a default "pool.ntp.org" server if
|
||||
it is not already running. Using this method, the above code becomes:
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void setClock() {
|
||||
NTP.begin("pool.ntp.org", "time.nist.gov");
|
||||
NTP.waitSet();
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Serial.print("Current time: ");
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
bool NTP.waitSet(void (\*cb)(), uint32_t timeout)
|
||||
-------------------------------------------------
|
||||
Allows for a callback that will be called every 1/10th of a second while waiting for
|
||||
NTP sync. For example, using lambdas you can simply print "."s:"
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void setClock() {
|
||||
NTP.begin("pool.ntp.org", "time.nist.gov");
|
||||
NTP.waitSet([]() { Serial.print("."); });
|
||||
time_t now = time(nullptr);
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Serial.print("Current time: ");
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
:orphan:
|
||||
|
||||
Server Class
|
||||
------------
|
||||
|
||||
Methods documented for the `Server Class <https://www.arduino.cc/en/Reference/WiFiServerConstructor>`__ in `Arduino <https://github.com/arduino/Arduino>`__
|
||||
|
||||
1. `WiFiServer() <https://www.arduino.cc/en/Reference/WiFiServer>`__
|
||||
2. `begin() <https://www.arduino.cc/en/Reference/WiFiServerBegin>`__
|
||||
3. `available() <https://www.arduino.cc/en/Reference/WiFiServerAvailable>`__
|
||||
4. `write() <https://www.arduino.cc/en/Reference/WiFiServerWrite>`__
|
||||
5. `print() <https://www.arduino.cc/en/Reference/WiFiServerPrint>`__
|
||||
6. `println() <https://www.arduino.cc/en/Reference/WiFiServerPrintln>`__
|
||||
|
||||
In ESP8266WiFi library the ``ArduinoWiFiServer`` class implements ``available`` and the write-to-all-clients functionality as described in the Arduino WiFi library reference. The PageServer example shows how ``available`` and the write-to-all-clients works.
|
||||
|
||||
For most use cases the basic WiFiServer class of the ESP8266WiFi library is suitable.
|
||||
|
||||
Methods and properties described further down are specific to ESP8266. They are not covered in `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ documentation. Before they are fully documented please refer to information below.
|
||||
|
||||
accept
|
||||
~~~~~~
|
||||
|
||||
Method ``accept()`` returns a waiting client connection. `accept() is documented <https://www.arduino.cc/en/Reference/EthernetServerAccept>`__ for the Arduino Ethernet library.
|
||||
|
||||
available
|
||||
~~~~~~~~~
|
||||
see ``accept``
|
||||
|
||||
``available`` in the ESP8266WiFi library's WiFiServer class doesn't work as documented for the Arduino WiFi library. It works the same way as ``accept``.
|
||||
|
||||
write (write to all clients) not supported
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please note that the ``write`` method on the ``WiFiServer`` object is not implemented and returns failure always. Use the returned
|
||||
``WiFiClient`` object from the ``WiFiServer::accept()`` method to communicate with individual clients. If you need to send
|
||||
the exact same packets to a series of clients, your application must maintain a list of connected clients and iterate over them manually.
|
||||
|
||||
setNoDelay
|
||||
~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
setNoDelay(nodelay)
|
||||
|
||||
With ``nodelay`` set to ``true``, this function will to disable `Nagle algorithm <https://en.wikipedia.org/wiki/Nagle%27s_algorithm>`__.
|
||||
|
||||
This algorithm is intended to reduce TCP/IP traffic of small packets sent over the network by combining a number of small outgoing messages, and sending them all at once. The downside of such approach is effectively delaying individual messages until a big enough packet is assembled.
|
||||
|
||||
*Example:*
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
server.begin();
|
||||
server.setNoDelay(true);
|
||||
|
||||
By default, ``nodelay`` value will depends on global ``WiFiClient::getDefaultNoDelay()`` (currently false by default).
|
||||
|
||||
However, a call to ``wiFiServer.setNoDelay()`` will override ``NoDelay`` for all new ``WiFiClient`` provided by the calling instance (``wiFiServer``).
|
||||
|
||||
Other Function Calls
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
bool hasClient ()
|
||||
size_t hasClientData ()
|
||||
bool hasMaxPendingClients ()
|
||||
bool getNoDelay ()
|
||||
virtual size_t write (const uint8_t *buf, size_t size)
|
||||
uint8_t status ()
|
||||
void close ()
|
||||
void stop ()
|
||||
|
||||
Documentation for the above functions is not yet prepared.
|
||||
@@ -0,0 +1,19 @@
|
||||
:orphan:
|
||||
|
||||
UDP Class
|
||||
---------
|
||||
|
||||
Methods documented for `WiFiUDP Class <https://www.arduino.cc/en/Reference/WiFiUDPConstructor>`__ in `Arduino <https://github.com/arduino/Arduino>`__
|
||||
|
||||
1. `begin() <https://www.arduino.cc/en/Reference/WiFiUDPBegin>`__
|
||||
2. `available() <https://www.arduino.cc/en/Reference/WiFiUDPAvailable>`__
|
||||
3. `beginPacket() <https://www.arduino.cc/en/Reference/WiFiUDPBeginPacket>`__
|
||||
4. `endPacket() <https://www.arduino.cc/en/Reference/WiFiUDPEndPacket>`__
|
||||
5. `write() <https://www.arduino.cc/en/Reference/WiFiUDPWrite>`__
|
||||
6. `parsePacket() <https://www.arduino.cc/en/Reference/WiFiUDPParsePacket>`__
|
||||
7. `peek() <https://www.arduino.cc/en/Reference/WiFiUDPPeek>`__
|
||||
8. `read() <https://www.arduino.cc/en/Reference/WiFiUDPRead>`__
|
||||
9. `flush() <https://www.arduino.cc/en/Reference/WiFiUDPFlush>`__
|
||||
10. `stop() <https://www.arduino.cc/en/Reference/WiFIUDPStop>`__
|
||||
11. `remoteIP() <https://www.arduino.cc/en/Reference/WiFiUDPRemoteIP>`__
|
||||
12. `remotePort() <https://www.arduino.cc/en/Reference/WiFiUDPRemotePort>`__
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_H__
|
||||
#define BR_BEARSSL_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** \mainpage BearSSL API
|
||||
*
|
||||
* # API Layout
|
||||
*
|
||||
* The functions and structures defined by the BearSSL API are located
|
||||
* in various header files:
|
||||
*
|
||||
* | Header file | Elements |
|
||||
* | :-------------- | :------------------------------------------------ |
|
||||
* | bearssl_hash.h | Hash functions |
|
||||
* | bearssl_hmac.h | HMAC |
|
||||
* | bearssl_kdf.h | Key Derivation Functions |
|
||||
* | bearssl_rand.h | Pseudorandom byte generators |
|
||||
* | bearssl_prf.h | PRF implementations (for SSL/TLS) |
|
||||
* | bearssl_block.h | Symmetric encryption |
|
||||
* | bearssl_aead.h | AEAD algorithms (combined encryption + MAC) |
|
||||
* | bearssl_rsa.h | RSA encryption and signatures |
|
||||
* | bearssl_ec.h | Elliptic curves support (including ECDSA) |
|
||||
* | bearssl_ssl.h | SSL/TLS engine interface |
|
||||
* | bearssl_x509.h | X.509 certificate decoding and validation |
|
||||
* | bearssl_pem.h | Base64/PEM decoding support functions |
|
||||
*
|
||||
* Applications using BearSSL are supposed to simply include `bearssl.h`
|
||||
* as follows:
|
||||
*
|
||||
* #include <bearssl.h>
|
||||
*
|
||||
* The `bearssl.h` file itself includes all the other header files. It is
|
||||
* possible to include specific header files, but it has no practical
|
||||
* advantage for the application. The API is separated into separate
|
||||
* header files only for documentation convenience.
|
||||
*
|
||||
*
|
||||
* # Conventions
|
||||
*
|
||||
* ## MUST and SHALL
|
||||
*
|
||||
* In all descriptions, the usual "MUST", "SHALL", "MAY",... terminology
|
||||
* is used. Failure to meet requirements expressed with a "MUST" or
|
||||
* "SHALL" implies undefined behaviour, which means that segmentation
|
||||
* faults, buffer overflows, and other similar adverse events, may occur.
|
||||
*
|
||||
* In general, BearSSL is not very forgiving of programming errors, and
|
||||
* does not include much failsafes or error reporting when the problem
|
||||
* does not arise from external transient conditions, and can be fixed
|
||||
* only in the application code. This is done so in order to make the
|
||||
* total code footprint lighter.
|
||||
*
|
||||
*
|
||||
* ## `NULL` values
|
||||
*
|
||||
* Function parameters with a pointer type shall not be `NULL` unless
|
||||
* explicitly authorised by the documentation. As an exception, when
|
||||
* the pointer aims at a sequence of bytes and is accompanied with
|
||||
* a length parameter, and the length is zero (meaning that there is
|
||||
* no byte at all to retrieve), then the pointer may be `NULL` even if
|
||||
* not explicitly allowed.
|
||||
*
|
||||
*
|
||||
* ## Memory Allocation
|
||||
*
|
||||
* BearSSL does not perform dynamic memory allocation. This implies that
|
||||
* for any functionality that requires a non-transient state, the caller
|
||||
* is responsible for allocating the relevant context structure. Such
|
||||
* allocation can be done in any appropriate area, including static data
|
||||
* segments, the heap, and the stack, provided that proper alignment is
|
||||
* respected. The header files define these context structures
|
||||
* (including size and contents), so the C compiler should handle
|
||||
* alignment automatically.
|
||||
*
|
||||
* Since there is no dynamic resource allocation, there is also nothing to
|
||||
* release. When the calling code is done with a BearSSL feature, it
|
||||
* may simple release the context structures it allocated itself, with
|
||||
* no "close function" to call. If the context structures were allocated
|
||||
* on the stack (as local variables), then even that release operation is
|
||||
* implicit.
|
||||
*
|
||||
*
|
||||
* ## Structure Contents
|
||||
*
|
||||
* Except when explicitly indicated, structure contents are opaque: they
|
||||
* are included in the header files so that calling code may know the
|
||||
* structure sizes and alignment requirements, but callers SHALL NOT
|
||||
* access individual fields directly. For fields that are supposed to
|
||||
* be read from or written to, the API defines accessor functions (the
|
||||
* simplest of these accessor functions are defined as `static inline`
|
||||
* functions, and the C compiler will optimise them away).
|
||||
*
|
||||
*
|
||||
* # API Usage
|
||||
*
|
||||
* BearSSL usage for running a SSL/TLS client or server is described
|
||||
* on the [BearSSL Web site](https://www.bearssl.org/api1.html). The
|
||||
* BearSSL source archive also comes with sample code.
|
||||
*/
|
||||
|
||||
#include "bearssl_hash.h"
|
||||
#include "bearssl_hmac.h"
|
||||
#include "bearssl_kdf.h"
|
||||
#include "bearssl_rand.h"
|
||||
#include "bearssl_prf.h"
|
||||
#include "bearssl_block.h"
|
||||
#include "bearssl_aead.h"
|
||||
#include "bearssl_rsa.h"
|
||||
#include "bearssl_ec.h"
|
||||
#include "bearssl_ssl.h"
|
||||
#include "bearssl_x509.h"
|
||||
#include "bearssl_pem.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \brief Type for a configuration option.
|
||||
*
|
||||
* A "configuration option" is a value that is selected when the BearSSL
|
||||
* library itself is compiled. Most options are boolean; their value is
|
||||
* then either 1 (option is enabled) or 0 (option is disabled). Some
|
||||
* values have other integer values. Option names correspond to macro
|
||||
* names. Some of the options can be explicitly set in the internal
|
||||
* `"config.h"` file.
|
||||
*/
|
||||
typedef struct {
|
||||
/** \brief Configurable option name. */
|
||||
const char *name;
|
||||
/** \brief Configurable option value. */
|
||||
long value;
|
||||
} br_config_option;
|
||||
|
||||
/** \brief Get configuration report.
|
||||
*
|
||||
* This function returns compiled configuration options, each as a
|
||||
* 'long' value. Names match internal macro names, in particular those
|
||||
* that can be set in the `"config.h"` inner file. For boolean options,
|
||||
* the numerical value is 1 if enabled, 0 if disabled. For maximum
|
||||
* key sizes, values are expressed in bits.
|
||||
*
|
||||
* The returned array is terminated by an entry whose `name` is `NULL`.
|
||||
*
|
||||
* \return the configuration report.
|
||||
*/
|
||||
const br_config_option *br_get_config(void);
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
/** \brief Version feature: support for time callback. */
|
||||
#define BR_FEATURE_X509_TIME_CALLBACK 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_EC_H__
|
||||
#define BR_BEARSSL_EC_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bearssl_rand.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_ec.h
|
||||
*
|
||||
* # Elliptic Curves
|
||||
*
|
||||
* This file documents the EC implementations provided with BearSSL, and
|
||||
* ECDSA.
|
||||
*
|
||||
* ## Elliptic Curve API
|
||||
*
|
||||
* Only "named curves" are supported. Each EC implementation supports
|
||||
* one or several named curves, identified by symbolic identifiers.
|
||||
* These identifiers are small integers, that correspond to the values
|
||||
* registered by the
|
||||
* [IANA](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8).
|
||||
*
|
||||
* Since all currently defined elliptic curve identifiers are in the 0..31
|
||||
* range, it is convenient to encode support of some curves in a 32-bit
|
||||
* word, such that bit x corresponds to curve of identifier x.
|
||||
*
|
||||
* An EC implementation is incarnated by a `br_ec_impl` instance, that
|
||||
* offers the following fields:
|
||||
*
|
||||
* - `supported_curves`
|
||||
*
|
||||
* A 32-bit word that documents the identifiers of the curves supported
|
||||
* by this implementation.
|
||||
*
|
||||
* - `generator()`
|
||||
*
|
||||
* Callback method that returns a pointer to the conventional generator
|
||||
* point for that curve.
|
||||
*
|
||||
* - `order()`
|
||||
*
|
||||
* Callback method that returns a pointer to the subgroup order for
|
||||
* that curve. That value uses unsigned big-endian encoding.
|
||||
*
|
||||
* - `xoff()`
|
||||
*
|
||||
* Callback method that returns the offset and length of the X
|
||||
* coordinate in an encoded point.
|
||||
*
|
||||
* - `mul()`
|
||||
*
|
||||
* Multiply a curve point with an integer.
|
||||
*
|
||||
* - `mulgen()`
|
||||
*
|
||||
* Multiply the curve generator with an integer. This may be faster
|
||||
* than the generic `mul()`.
|
||||
*
|
||||
* - `muladd()`
|
||||
*
|
||||
* Multiply two curve points by two integers, and return the sum of
|
||||
* the two products.
|
||||
*
|
||||
* All curve points are represented in uncompressed format. The `mul()`
|
||||
* and `muladd()` methods take care to validate that the provided points
|
||||
* are really part of the relevant curve subgroup.
|
||||
*
|
||||
* For all point multiplication functions, the following holds:
|
||||
*
|
||||
* - Functions validate that the provided points are valid members
|
||||
* of the relevant curve subgroup. An error is reported if that is
|
||||
* not the case.
|
||||
*
|
||||
* - Processing is constant-time, even if the point operands are not
|
||||
* valid. This holds for both the source and resulting points, and
|
||||
* the multipliers (integers). Only the byte length of the provided
|
||||
* multiplier arrays (not their actual value length in bits) may
|
||||
* leak through timing-based side channels.
|
||||
*
|
||||
* - The multipliers (integers) MUST be lower than the subgroup order.
|
||||
* If this property is not met, then the result is indeterminate,
|
||||
* but an error value is not necessarily returned.
|
||||
*
|
||||
*
|
||||
* ## ECDSA
|
||||
*
|
||||
* ECDSA signatures have two standard formats, called "raw" and "asn1".
|
||||
* Internally, such a signature is a pair of modular integers `(r,s)`.
|
||||
* The "raw" format is the concatenation of the unsigned big-endian
|
||||
* encodings of these two integers, possibly left-padded with zeros so
|
||||
* that they have the same encoded length. The "asn1" format is the
|
||||
* DER encoding of an ASN.1 structure that contains the two integer
|
||||
* values:
|
||||
*
|
||||
* ECDSASignature ::= SEQUENCE {
|
||||
* r INTEGER,
|
||||
* s INTEGER
|
||||
* }
|
||||
*
|
||||
* In general, in all of X.509 and SSL/TLS, the "asn1" format is used.
|
||||
* BearSSL offers ECDSA implementations for both formats; conversion
|
||||
* functions between the two formats are also provided. Conversion of a
|
||||
* "raw" format signature into "asn1" may enlarge a signature by no more
|
||||
* than 9 bytes for all supported curves; conversely, conversion of an
|
||||
* "asn1" signature to "raw" may expand the signature but the "raw"
|
||||
* length will never be more than twice the length of the "asn1" length
|
||||
* (and usually it will be shorter).
|
||||
*
|
||||
* Note that for a given signature, the "raw" format is not fully
|
||||
* deterministic, in that it does not enforce a minimal common length.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Standard curve ID. These ID are equal to the assigned numerical
|
||||
* identifiers assigned to these curves for TLS:
|
||||
* http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
|
||||
*/
|
||||
|
||||
/** \brief Identifier for named curve sect163k1. */
|
||||
#define BR_EC_sect163k1 1
|
||||
|
||||
/** \brief Identifier for named curve sect163r1. */
|
||||
#define BR_EC_sect163r1 2
|
||||
|
||||
/** \brief Identifier for named curve sect163r2. */
|
||||
#define BR_EC_sect163r2 3
|
||||
|
||||
/** \brief Identifier for named curve sect193r1. */
|
||||
#define BR_EC_sect193r1 4
|
||||
|
||||
/** \brief Identifier for named curve sect193r2. */
|
||||
#define BR_EC_sect193r2 5
|
||||
|
||||
/** \brief Identifier for named curve sect233k1. */
|
||||
#define BR_EC_sect233k1 6
|
||||
|
||||
/** \brief Identifier for named curve sect233r1. */
|
||||
#define BR_EC_sect233r1 7
|
||||
|
||||
/** \brief Identifier for named curve sect239k1. */
|
||||
#define BR_EC_sect239k1 8
|
||||
|
||||
/** \brief Identifier for named curve sect283k1. */
|
||||
#define BR_EC_sect283k1 9
|
||||
|
||||
/** \brief Identifier for named curve sect283r1. */
|
||||
#define BR_EC_sect283r1 10
|
||||
|
||||
/** \brief Identifier for named curve sect409k1. */
|
||||
#define BR_EC_sect409k1 11
|
||||
|
||||
/** \brief Identifier for named curve sect409r1. */
|
||||
#define BR_EC_sect409r1 12
|
||||
|
||||
/** \brief Identifier for named curve sect571k1. */
|
||||
#define BR_EC_sect571k1 13
|
||||
|
||||
/** \brief Identifier for named curve sect571r1. */
|
||||
#define BR_EC_sect571r1 14
|
||||
|
||||
/** \brief Identifier for named curve secp160k1. */
|
||||
#define BR_EC_secp160k1 15
|
||||
|
||||
/** \brief Identifier for named curve secp160r1. */
|
||||
#define BR_EC_secp160r1 16
|
||||
|
||||
/** \brief Identifier for named curve secp160r2. */
|
||||
#define BR_EC_secp160r2 17
|
||||
|
||||
/** \brief Identifier for named curve secp192k1. */
|
||||
#define BR_EC_secp192k1 18
|
||||
|
||||
/** \brief Identifier for named curve secp192r1. */
|
||||
#define BR_EC_secp192r1 19
|
||||
|
||||
/** \brief Identifier for named curve secp224k1. */
|
||||
#define BR_EC_secp224k1 20
|
||||
|
||||
/** \brief Identifier for named curve secp224r1. */
|
||||
#define BR_EC_secp224r1 21
|
||||
|
||||
/** \brief Identifier for named curve secp256k1. */
|
||||
#define BR_EC_secp256k1 22
|
||||
|
||||
/** \brief Identifier for named curve secp256r1. */
|
||||
#define BR_EC_secp256r1 23
|
||||
|
||||
/** \brief Identifier for named curve secp384r1. */
|
||||
#define BR_EC_secp384r1 24
|
||||
|
||||
/** \brief Identifier for named curve secp521r1. */
|
||||
#define BR_EC_secp521r1 25
|
||||
|
||||
/** \brief Identifier for named curve brainpoolP256r1. */
|
||||
#define BR_EC_brainpoolP256r1 26
|
||||
|
||||
/** \brief Identifier for named curve brainpoolP384r1. */
|
||||
#define BR_EC_brainpoolP384r1 27
|
||||
|
||||
/** \brief Identifier for named curve brainpoolP512r1. */
|
||||
#define BR_EC_brainpoolP512r1 28
|
||||
|
||||
/** \brief Identifier for named curve Curve25519. */
|
||||
#define BR_EC_curve25519 29
|
||||
|
||||
/** \brief Identifier for named curve Curve448. */
|
||||
#define BR_EC_curve448 30
|
||||
|
||||
/**
|
||||
* \brief Structure for an EC public key.
|
||||
*/
|
||||
typedef struct {
|
||||
/** \brief Identifier for the curve used by this key. */
|
||||
int curve;
|
||||
/** \brief Public curve point (uncompressed format). */
|
||||
unsigned char *q;
|
||||
/** \brief Length of public curve point (in bytes). */
|
||||
size_t qlen;
|
||||
} br_ec_public_key;
|
||||
|
||||
/**
|
||||
* \brief Structure for an EC private key.
|
||||
*
|
||||
* The private key is an integer modulo the curve subgroup order. The
|
||||
* encoding below tolerates extra leading zeros. In general, it is
|
||||
* recommended that the private key has the same length as the curve
|
||||
* subgroup order.
|
||||
*/
|
||||
typedef struct {
|
||||
/** \brief Identifier for the curve used by this key. */
|
||||
int curve;
|
||||
/** \brief Private key (integer, unsigned big-endian encoding). */
|
||||
unsigned char *x;
|
||||
/** \brief Private key length (in bytes). */
|
||||
size_t xlen;
|
||||
} br_ec_private_key;
|
||||
|
||||
/**
|
||||
* \brief Type for an EC implementation.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* \brief Supported curves.
|
||||
*
|
||||
* This word is a bitfield: bit `x` is set if the curve of ID `x`
|
||||
* is supported. E.g. an implementation supporting both NIST P-256
|
||||
* (secp256r1, ID 23) and NIST P-384 (secp384r1, ID 24) will have
|
||||
* value `0x01800000` in this field.
|
||||
*/
|
||||
uint32_t supported_curves;
|
||||
|
||||
/**
|
||||
* \brief Get the conventional generator.
|
||||
*
|
||||
* This function returns the conventional generator (encoded
|
||||
* curve point) for the specified curve. This function MUST NOT
|
||||
* be called if the curve is not supported.
|
||||
*
|
||||
* \param curve curve identifier.
|
||||
* \param len receiver for the encoded generator length (in bytes).
|
||||
* \return the encoded generator.
|
||||
*/
|
||||
const unsigned char *(*generator)(int curve, size_t *len);
|
||||
|
||||
/**
|
||||
* \brief Get the subgroup order.
|
||||
*
|
||||
* This function returns the order of the subgroup generated by
|
||||
* the conventional generator, for the specified curve. Unsigned
|
||||
* big-endian encoding is used. This function MUST NOT be called
|
||||
* if the curve is not supported.
|
||||
*
|
||||
* \param curve curve identifier.
|
||||
* \param len receiver for the encoded order length (in bytes).
|
||||
* \return the encoded order.
|
||||
*/
|
||||
const unsigned char *(*order)(int curve, size_t *len);
|
||||
|
||||
/**
|
||||
* \brief Get the offset and length for the X coordinate.
|
||||
*
|
||||
* This function returns the offset and length (in bytes) of
|
||||
* the X coordinate in an encoded non-zero point.
|
||||
*
|
||||
* \param curve curve identifier.
|
||||
* \param len receiver for the X coordinate length (in bytes).
|
||||
* \return the offset for the X coordinate (in bytes).
|
||||
*/
|
||||
size_t (*xoff)(int curve, size_t *len);
|
||||
|
||||
/**
|
||||
* \brief Multiply a curve point by an integer.
|
||||
*
|
||||
* The source point is provided in array `G` (of size `Glen` bytes);
|
||||
* the multiplication result is written over it. The multiplier
|
||||
* `x` (of size `xlen` bytes) uses unsigned big-endian encoding.
|
||||
*
|
||||
* Rules:
|
||||
*
|
||||
* - The specified curve MUST be supported.
|
||||
*
|
||||
* - The source point must be a valid point on the relevant curve
|
||||
* subgroup (and not the "point at infinity" either). If this is
|
||||
* not the case, then this function returns an error (0).
|
||||
*
|
||||
* - The multiplier integer MUST be non-zero and less than the
|
||||
* curve subgroup order. If this property does not hold, then
|
||||
* the result is indeterminate and an error code is not
|
||||
* guaranteed.
|
||||
*
|
||||
* Returned value is 1 on success, 0 on error. On error, the
|
||||
* contents of `G` are indeterminate.
|
||||
*
|
||||
* \param G point to multiply.
|
||||
* \param Glen length of the encoded point (in bytes).
|
||||
* \param x multiplier (unsigned big-endian).
|
||||
* \param xlen multiplier length (in bytes).
|
||||
* \param curve curve identifier.
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t (*mul)(unsigned char *G, size_t Glen,
|
||||
const unsigned char *x, size_t xlen, int curve);
|
||||
|
||||
/**
|
||||
* \brief Multiply the generator by an integer.
|
||||
*
|
||||
* The multiplier MUST be non-zero and less than the curve
|
||||
* subgroup order. Results are indeterminate if this property
|
||||
* does not hold.
|
||||
*
|
||||
* \param R output buffer for the point.
|
||||
* \param x multiplier (unsigned big-endian).
|
||||
* \param xlen multiplier length (in bytes).
|
||||
* \param curve curve identifier.
|
||||
* \return encoded result point length (in bytes).
|
||||
*/
|
||||
size_t (*mulgen)(unsigned char *R,
|
||||
const unsigned char *x, size_t xlen, int curve);
|
||||
|
||||
/**
|
||||
* \brief Multiply two points by two integers and add the
|
||||
* results.
|
||||
*
|
||||
* The point `x*A + y*B` is computed and written back in the `A`
|
||||
* array.
|
||||
*
|
||||
* Rules:
|
||||
*
|
||||
* - The specified curve MUST be supported.
|
||||
*
|
||||
* - The source points (`A` and `B`) must be valid points on
|
||||
* the relevant curve subgroup (and not the "point at
|
||||
* infinity" either). If this is not the case, then this
|
||||
* function returns an error (0).
|
||||
*
|
||||
* - If the `B` pointer is `NULL`, then the conventional
|
||||
* subgroup generator is used. With some implementations,
|
||||
* this may be faster than providing a pointer to the
|
||||
* generator.
|
||||
*
|
||||
* - The multiplier integers (`x` and `y`) MUST be non-zero
|
||||
* and less than the curve subgroup order. If either integer
|
||||
* is zero, then an error is reported, but if one of them is
|
||||
* not lower than the subgroup order, then the result is
|
||||
* indeterminate and an error code is not guaranteed.
|
||||
*
|
||||
* - If the final result is the point at infinity, then an
|
||||
* error is returned.
|
||||
*
|
||||
* Returned value is 1 on success, 0 on error. On error, the
|
||||
* contents of `A` are indeterminate.
|
||||
*
|
||||
* \param A first point to multiply.
|
||||
* \param B second point to multiply (`NULL` for the generator).
|
||||
* \param len common length of the encoded points (in bytes).
|
||||
* \param x multiplier for `A` (unsigned big-endian).
|
||||
* \param xlen length of multiplier for `A` (in bytes).
|
||||
* \param y multiplier for `A` (unsigned big-endian).
|
||||
* \param ylen length of multiplier for `A` (in bytes).
|
||||
* \param curve curve identifier.
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
|
||||
const unsigned char *x, size_t xlen,
|
||||
const unsigned char *y, size_t ylen, int curve);
|
||||
} br_ec_impl;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "i31".
|
||||
*
|
||||
* This implementation internally uses generic code for modular integers,
|
||||
* with a representation as sequences of 31-bit words. It supports secp256r1,
|
||||
* secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_prime_i31;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "i15".
|
||||
*
|
||||
* This implementation internally uses generic code for modular integers,
|
||||
* with a representation as sequences of 15-bit words. It supports secp256r1,
|
||||
* secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_prime_i15;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m15" for P-256.
|
||||
*
|
||||
* This implementation uses specialised code for curve secp256r1 (also
|
||||
* known as NIST P-256), with optional Karatsuba decomposition, and fast
|
||||
* modular reduction thanks to the field modulus special format. Only
|
||||
* 32-bit multiplications are used (with 32-bit results, not 64-bit).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_p256_m15;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m31" for P-256.
|
||||
*
|
||||
* This implementation uses specialised code for curve secp256r1 (also
|
||||
* known as NIST P-256), relying on multiplications of 31-bit values
|
||||
* (MUL31).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_p256_m31;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m62" (specialised code) for P-256.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 64 bits, with a 128-bit result. This implementation is
|
||||
* defined only on platforms that offer the 64x64->128 multiplication
|
||||
* support; use `br_ec_p256_m62_get()` to dynamically obtain a pointer
|
||||
* to that implementation.
|
||||
*/
|
||||
extern const br_ec_impl br_ec_p256_m62;
|
||||
|
||||
/**
|
||||
* \brief Get the "m62" implementation of P-256, if available.
|
||||
*
|
||||
* \return the implementation, or 0.
|
||||
*/
|
||||
const br_ec_impl *br_ec_p256_m62_get(void);
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m64" (specialised code) for P-256.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 64 bits, with a 128-bit result. This implementation is
|
||||
* defined only on platforms that offer the 64x64->128 multiplication
|
||||
* support; use `br_ec_p256_m64_get()` to dynamically obtain a pointer
|
||||
* to that implementation.
|
||||
*/
|
||||
extern const br_ec_impl br_ec_p256_m64;
|
||||
|
||||
/**
|
||||
* \brief Get the "m64" implementation of P-256, if available.
|
||||
*
|
||||
* \return the implementation, or 0.
|
||||
*/
|
||||
const br_ec_impl *br_ec_p256_m64_get(void);
|
||||
|
||||
/**
|
||||
* \brief EC implementation "i15" (generic code) for Curve25519.
|
||||
*
|
||||
* This implementation uses the generic code for modular integers (with
|
||||
* 15-bit words) to support Curve25519. Due to the specificities of the
|
||||
* curve definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_i15;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "i31" (generic code) for Curve25519.
|
||||
*
|
||||
* This implementation uses the generic code for modular integers (with
|
||||
* 31-bit words) to support Curve25519. Due to the specificities of the
|
||||
* curve definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_i31;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m15" (specialised code) for Curve25519.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 15 bits. Due to the specificities of the curve
|
||||
* definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_m15;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m31" (specialised code) for Curve25519.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 31 bits. Due to the specificities of the curve
|
||||
* definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_m31;
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m62" (specialised code) for Curve25519.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 62 bits, with a 124-bit result. This implementation is
|
||||
* defined only on platforms that offer the 64x64->128 multiplication
|
||||
* support; use `br_ec_c25519_m62_get()` to dynamically obtain a pointer
|
||||
* to that implementation. Due to the specificities of the curve
|
||||
* definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_m62;
|
||||
|
||||
/**
|
||||
* \brief Get the "m62" implementation of Curve25519, if available.
|
||||
*
|
||||
* \return the implementation, or 0.
|
||||
*/
|
||||
const br_ec_impl *br_ec_c25519_m62_get(void);
|
||||
|
||||
/**
|
||||
* \brief EC implementation "m64" (specialised code) for Curve25519.
|
||||
*
|
||||
* This implementation uses custom code relying on multiplication of
|
||||
* integers up to 64 bits, with a 128-bit result. This implementation is
|
||||
* defined only on platforms that offer the 64x64->128 multiplication
|
||||
* support; use `br_ec_c25519_m64_get()` to dynamically obtain a pointer
|
||||
* to that implementation. Due to the specificities of the curve
|
||||
* definition, the following applies:
|
||||
*
|
||||
* - `muladd()` is not implemented (the function returns 0 systematically).
|
||||
* - `order()` returns 2^255-1, since the point multiplication algorithm
|
||||
* accepts any 32-bit integer as input (it clears the top bit and low
|
||||
* three bits systematically).
|
||||
*/
|
||||
extern const br_ec_impl br_ec_c25519_m64;
|
||||
|
||||
/**
|
||||
* \brief Get the "m64" implementation of Curve25519, if available.
|
||||
*
|
||||
* \return the implementation, or 0.
|
||||
*/
|
||||
const br_ec_impl *br_ec_c25519_m64_get(void);
|
||||
|
||||
/**
|
||||
* \brief Aggregate EC implementation "m15".
|
||||
*
|
||||
* This implementation is a wrapper for:
|
||||
*
|
||||
* - `br_ec_c25519_m15` for Curve25519
|
||||
* - `br_ec_p256_m15` for NIST P-256
|
||||
* - `br_ec_prime_i15` for other curves (NIST P-384 and NIST-P512)
|
||||
*/
|
||||
extern const br_ec_impl br_ec_all_m15;
|
||||
|
||||
/**
|
||||
* \brief Aggregate EC implementation "m31".
|
||||
*
|
||||
* This implementation is a wrapper for:
|
||||
*
|
||||
* - `br_ec_c25519_m31` for Curve25519
|
||||
* - `br_ec_p256_m31` for NIST P-256
|
||||
* - `br_ec_prime_i31` for other curves (NIST P-384 and NIST-P512)
|
||||
*/
|
||||
extern const br_ec_impl br_ec_all_m31;
|
||||
|
||||
/**
|
||||
* \brief Get the "default" EC implementation for the current system.
|
||||
*
|
||||
* This returns a pointer to the preferred implementation on the
|
||||
* current system.
|
||||
*
|
||||
* \return the default EC implementation.
|
||||
*/
|
||||
const br_ec_impl *br_ec_get_default(void);
|
||||
|
||||
/**
|
||||
* \brief Convert a signature from "raw" to "asn1".
|
||||
*
|
||||
* Conversion is done "in place" and the new length is returned.
|
||||
* Conversion may enlarge the signature, but by no more than 9 bytes at
|
||||
* most. On error, 0 is returned (error conditions include an odd raw
|
||||
* signature length, or an oversized integer).
|
||||
*
|
||||
* \param sig signature to convert.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return the new signature length, or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_raw_to_asn1(void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief Convert a signature from "asn1" to "raw".
|
||||
*
|
||||
* Conversion is done "in place" and the new length is returned.
|
||||
* Conversion may enlarge the signature, but the new signature length
|
||||
* will be less than twice the source length at most. On error, 0 is
|
||||
* returned (error conditions include an invalid ASN.1 structure or an
|
||||
* oversized integer).
|
||||
*
|
||||
* \param sig signature to convert.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return the new signature length, or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_asn1_to_raw(void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief Type for an ECDSA signer function.
|
||||
*
|
||||
* A pointer to the EC implementation is provided. The hash value is
|
||||
* assumed to have the length inferred from the designated hash function
|
||||
* class.
|
||||
*
|
||||
* Signature is written in the buffer pointed to by `sig`, and the length
|
||||
* (in bytes) is returned. On error, nothing is written in the buffer,
|
||||
* and 0 is returned. This function returns 0 if the specified curve is
|
||||
* not supported by the provided EC implementation.
|
||||
*
|
||||
* The signature format is either "raw" or "asn1", depending on the
|
||||
* implementation; maximum length is predictable from the implemented
|
||||
* curve:
|
||||
*
|
||||
* | curve | raw | asn1 |
|
||||
* | :--------- | --: | ---: |
|
||||
* | NIST P-256 | 64 | 72 |
|
||||
* | NIST P-384 | 96 | 104 |
|
||||
* | NIST P-521 | 132 | 139 |
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hf hash function used to process the data.
|
||||
* \param hash_value signed data (hashed).
|
||||
* \param sk EC private key.
|
||||
* \param sig destination buffer.
|
||||
* \return the signature length (in bytes), or 0 on error.
|
||||
*/
|
||||
typedef size_t (*br_ecdsa_sign)(const br_ec_impl *impl,
|
||||
const br_hash_class *hf, const void *hash_value,
|
||||
const br_ec_private_key *sk, void *sig);
|
||||
|
||||
/**
|
||||
* \brief Type for an ECDSA signature verification function.
|
||||
*
|
||||
* A pointer to the EC implementation is provided. The hashed value,
|
||||
* computed over the purportedly signed data, is also provided with
|
||||
* its length.
|
||||
*
|
||||
* The signature format is either "raw" or "asn1", depending on the
|
||||
* implementation.
|
||||
*
|
||||
* Returned value is 1 on success (valid signature), 0 on error. This
|
||||
* function returns 0 if the specified curve is not supported by the
|
||||
* provided EC implementation.
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hash signed data (hashed).
|
||||
* \param hash_len hash value length (in bytes).
|
||||
* \param pk EC public key.
|
||||
* \param sig signature.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
typedef uint32_t (*br_ecdsa_vrfy)(const br_ec_impl *impl,
|
||||
const void *hash, size_t hash_len,
|
||||
const br_ec_public_key *pk, const void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature generator, "i31" implementation, "asn1" format.
|
||||
*
|
||||
* \see br_ecdsa_sign()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hf hash function used to process the data.
|
||||
* \param hash_value signed data (hashed).
|
||||
* \param sk EC private key.
|
||||
* \param sig destination buffer.
|
||||
* \return the signature length (in bytes), or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_i31_sign_asn1(const br_ec_impl *impl,
|
||||
const br_hash_class *hf, const void *hash_value,
|
||||
const br_ec_private_key *sk, void *sig);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature generator, "i31" implementation, "raw" format.
|
||||
*
|
||||
* \see br_ecdsa_sign()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hf hash function used to process the data.
|
||||
* \param hash_value signed data (hashed).
|
||||
* \param sk EC private key.
|
||||
* \param sig destination buffer.
|
||||
* \return the signature length (in bytes), or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_i31_sign_raw(const br_ec_impl *impl,
|
||||
const br_hash_class *hf, const void *hash_value,
|
||||
const br_ec_private_key *sk, void *sig);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature verifier, "i31" implementation, "asn1" format.
|
||||
*
|
||||
* \see br_ecdsa_vrfy()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hash signed data (hashed).
|
||||
* \param hash_len hash value length (in bytes).
|
||||
* \param pk EC public key.
|
||||
* \param sig signature.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl,
|
||||
const void *hash, size_t hash_len,
|
||||
const br_ec_public_key *pk, const void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature verifier, "i31" implementation, "raw" format.
|
||||
*
|
||||
* \see br_ecdsa_vrfy()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hash signed data (hashed).
|
||||
* \param hash_len hash value length (in bytes).
|
||||
* \param pk EC public key.
|
||||
* \param sig signature.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl,
|
||||
const void *hash, size_t hash_len,
|
||||
const br_ec_public_key *pk, const void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature generator, "i15" implementation, "asn1" format.
|
||||
*
|
||||
* \see br_ecdsa_sign()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hf hash function used to process the data.
|
||||
* \param hash_value signed data (hashed).
|
||||
* \param sk EC private key.
|
||||
* \param sig destination buffer.
|
||||
* \return the signature length (in bytes), or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_i15_sign_asn1(const br_ec_impl *impl,
|
||||
const br_hash_class *hf, const void *hash_value,
|
||||
const br_ec_private_key *sk, void *sig);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature generator, "i15" implementation, "raw" format.
|
||||
*
|
||||
* \see br_ecdsa_sign()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hf hash function used to process the data.
|
||||
* \param hash_value signed data (hashed).
|
||||
* \param sk EC private key.
|
||||
* \param sig destination buffer.
|
||||
* \return the signature length (in bytes), or 0 on error.
|
||||
*/
|
||||
size_t br_ecdsa_i15_sign_raw(const br_ec_impl *impl,
|
||||
const br_hash_class *hf, const void *hash_value,
|
||||
const br_ec_private_key *sk, void *sig);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature verifier, "i15" implementation, "asn1" format.
|
||||
*
|
||||
* \see br_ecdsa_vrfy()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hash signed data (hashed).
|
||||
* \param hash_len hash value length (in bytes).
|
||||
* \param pk EC public key.
|
||||
* \param sig signature.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl,
|
||||
const void *hash, size_t hash_len,
|
||||
const br_ec_public_key *pk, const void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief ECDSA signature verifier, "i15" implementation, "raw" format.
|
||||
*
|
||||
* \see br_ecdsa_vrfy()
|
||||
*
|
||||
* \param impl EC implementation to use.
|
||||
* \param hash signed data (hashed).
|
||||
* \param hash_len hash value length (in bytes).
|
||||
* \param pk EC public key.
|
||||
* \param sig signature.
|
||||
* \param sig_len signature length (in bytes).
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
uint32_t br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl,
|
||||
const void *hash, size_t hash_len,
|
||||
const br_ec_public_key *pk, const void *sig, size_t sig_len);
|
||||
|
||||
/**
|
||||
* \brief Get "default" ECDSA implementation (signer, asn1 format).
|
||||
*
|
||||
* This returns the preferred implementation of ECDSA signature generation
|
||||
* ("asn1" output format) on the current system.
|
||||
*
|
||||
* \return the default implementation.
|
||||
*/
|
||||
br_ecdsa_sign br_ecdsa_sign_asn1_get_default(void);
|
||||
|
||||
/**
|
||||
* \brief Get "default" ECDSA implementation (signer, raw format).
|
||||
*
|
||||
* This returns the preferred implementation of ECDSA signature generation
|
||||
* ("raw" output format) on the current system.
|
||||
*
|
||||
* \return the default implementation.
|
||||
*/
|
||||
br_ecdsa_sign br_ecdsa_sign_raw_get_default(void);
|
||||
|
||||
/**
|
||||
* \brief Get "default" ECDSA implementation (verifier, asn1 format).
|
||||
*
|
||||
* This returns the preferred implementation of ECDSA signature verification
|
||||
* ("asn1" output format) on the current system.
|
||||
*
|
||||
* \return the default implementation.
|
||||
*/
|
||||
br_ecdsa_vrfy br_ecdsa_vrfy_asn1_get_default(void);
|
||||
|
||||
/**
|
||||
* \brief Get "default" ECDSA implementation (verifier, raw format).
|
||||
*
|
||||
* This returns the preferred implementation of ECDSA signature verification
|
||||
* ("raw" output format) on the current system.
|
||||
*
|
||||
* \return the default implementation.
|
||||
*/
|
||||
br_ecdsa_vrfy br_ecdsa_vrfy_raw_get_default(void);
|
||||
|
||||
/**
|
||||
* \brief Maximum size for EC private key element buffer.
|
||||
*
|
||||
* This is the largest number of bytes that `br_ec_keygen()` may need or
|
||||
* ever return.
|
||||
*/
|
||||
#define BR_EC_KBUF_PRIV_MAX_SIZE 72
|
||||
|
||||
/**
|
||||
* \brief Maximum size for EC public key element buffer.
|
||||
*
|
||||
* This is the largest number of bytes that `br_ec_compute_public()` may
|
||||
* need or ever return.
|
||||
*/
|
||||
#define BR_EC_KBUF_PUB_MAX_SIZE 145
|
||||
|
||||
/**
|
||||
* \brief Generate a new EC private key.
|
||||
*
|
||||
* If the specified `curve` is not supported by the elliptic curve
|
||||
* implementation (`impl`), then this function returns zero.
|
||||
*
|
||||
* The `sk` structure fields are set to the new private key data. In
|
||||
* particular, `sk.x` is made to point to the provided key buffer (`kbuf`),
|
||||
* in which the actual private key data is written. That buffer is assumed
|
||||
* to be large enough. The `BR_EC_KBUF_PRIV_MAX_SIZE` defines the maximum
|
||||
* size for all supported curves.
|
||||
*
|
||||
* The number of bytes used in `kbuf` is returned. If `kbuf` is `NULL`, then
|
||||
* the private key is not actually generated, and `sk` may also be `NULL`;
|
||||
* the minimum length for `kbuf` is still computed and returned.
|
||||
*
|
||||
* If `sk` is `NULL` but `kbuf` is not `NULL`, then the private key is
|
||||
* still generated and stored in `kbuf`.
|
||||
*
|
||||
* \param rng_ctx source PRNG context (already initialized).
|
||||
* \param impl the elliptic curve implementation.
|
||||
* \param sk the private key structure to fill, or `NULL`.
|
||||
* \param kbuf the key element buffer, or `NULL`.
|
||||
* \param curve the curve identifier.
|
||||
* \return the key data length (in bytes), or zero.
|
||||
*/
|
||||
size_t br_ec_keygen(const br_prng_class **rng_ctx,
|
||||
const br_ec_impl *impl, br_ec_private_key *sk,
|
||||
void *kbuf, int curve);
|
||||
|
||||
/**
|
||||
* \brief Compute EC public key from EC private key.
|
||||
*
|
||||
* This function uses the provided elliptic curve implementation (`impl`)
|
||||
* to compute the public key corresponding to the private key held in `sk`.
|
||||
* The public key point is written into `kbuf`, which is then linked from
|
||||
* the `*pk` structure. The size of the public key point, i.e. the number
|
||||
* of bytes used in `kbuf`, is returned.
|
||||
*
|
||||
* If `kbuf` is `NULL`, then the public key point is NOT computed, and
|
||||
* the public key structure `*pk` is unmodified (`pk` may be `NULL` in
|
||||
* that case). The size of the public key point is still returned.
|
||||
*
|
||||
* If `pk` is `NULL` but `kbuf` is not `NULL`, then the public key
|
||||
* point is computed and stored in `kbuf`, and its size is returned.
|
||||
*
|
||||
* If the curve used by the private key is not supported by the curve
|
||||
* implementation, then this function returns zero.
|
||||
*
|
||||
* The private key MUST be valid. An off-range private key value is not
|
||||
* necessarily detected, and leads to unpredictable results.
|
||||
*
|
||||
* \param impl the elliptic curve implementation.
|
||||
* \param pk the public key structure to fill (or `NULL`).
|
||||
* \param kbuf the public key point buffer (or `NULL`).
|
||||
* \param sk the source private key.
|
||||
* \return the public key point length (in bytes), or zero.
|
||||
*/
|
||||
size_t br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk,
|
||||
void *kbuf, const br_ec_private_key *sk);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
// Do not edit -- Automatically generated by tools/sdk/ssl/bearssl/Makefile
|
||||
#define BEARSSL_GIT f294aa0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_HMAC_H__
|
||||
#define BR_BEARSSL_HMAC_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bearssl_hash.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_hmac.h
|
||||
*
|
||||
* # HMAC
|
||||
*
|
||||
* HMAC is initialized with a key and an underlying hash function; it
|
||||
* then fills a "key context". That context contains the processed
|
||||
* key.
|
||||
*
|
||||
* With the key context, a HMAC context can be initialized to process
|
||||
* the input bytes and obtain the MAC output. The key context is not
|
||||
* modified during that process, and can be reused.
|
||||
*
|
||||
* IMPORTANT: HMAC shall be used only with functions that have the
|
||||
* following properties:
|
||||
*
|
||||
* - hash output size does not exceed 64 bytes;
|
||||
* - hash internal state size does not exceed 64 bytes;
|
||||
* - internal block length is a power of 2 between 16 and 256 bytes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief HMAC key context.
|
||||
*
|
||||
* The HMAC key context is initialised with a hash function implementation
|
||||
* and a secret key. Contents are opaque (callers should not access them
|
||||
* directly). The caller is responsible for allocating the context where
|
||||
* appropriate. Context initialisation and usage incurs no dynamic
|
||||
* allocation, so there is no release function.
|
||||
*/
|
||||
typedef struct {
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
const br_hash_class *dig_vtable;
|
||||
unsigned char ksi[64], kso[64];
|
||||
#endif
|
||||
} br_hmac_key_context;
|
||||
|
||||
/**
|
||||
* \brief HMAC key context initialisation.
|
||||
*
|
||||
* Initialise the key context with the provided key, using the hash function
|
||||
* identified by `digest_vtable`. This supports arbitrary key lengths.
|
||||
*
|
||||
* \param kc HMAC key context to initialise.
|
||||
* \param digest_vtable pointer to the hash function implementation vtable.
|
||||
* \param key pointer to the HMAC secret key.
|
||||
* \param key_len HMAC secret key length (in bytes).
|
||||
*/
|
||||
void br_hmac_key_init(br_hmac_key_context *kc,
|
||||
const br_hash_class *digest_vtable, const void *key, size_t key_len);
|
||||
|
||||
/*
|
||||
* \brief Get the underlying hash function.
|
||||
*
|
||||
* This function returns a pointer to the implementation vtable of the
|
||||
* hash function used for this HMAC key context.
|
||||
*
|
||||
* \param kc HMAC key context.
|
||||
* \return the hash function implementation.
|
||||
*/
|
||||
static inline const br_hash_class *br_hmac_key_get_digest(
|
||||
const br_hmac_key_context *kc)
|
||||
{
|
||||
return kc->dig_vtable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief HMAC computation context.
|
||||
*
|
||||
* The HMAC computation context maintains the state for a single HMAC
|
||||
* computation. It is modified as input bytes are injected. The context
|
||||
* is caller-allocated and has no release function since it does not
|
||||
* dynamically allocate external resources. Its contents are opaque.
|
||||
*/
|
||||
typedef struct {
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
br_hash_compat_context dig;
|
||||
unsigned char kso[64];
|
||||
size_t out_len;
|
||||
#endif
|
||||
} br_hmac_context;
|
||||
|
||||
/**
|
||||
* \brief HMAC computation initialisation.
|
||||
*
|
||||
* Initialise a HMAC context with a key context. The key context is
|
||||
* unmodified. Relevant data from the key context is immediately copied;
|
||||
* the key context can thus be independently reused, modified or released
|
||||
* without impacting this HMAC computation.
|
||||
*
|
||||
* An explicit output length can be specified; the actual output length
|
||||
* will be the minimum of that value and the natural HMAC output length.
|
||||
* If `out_len` is 0, then the natural HMAC output length is selected. The
|
||||
* "natural output length" is the output length of the underlying hash
|
||||
* function.
|
||||
*
|
||||
* \param ctx HMAC context to initialise.
|
||||
* \param kc HMAC key context (already initialised with the key).
|
||||
* \param out_len HMAC output length (0 to select "natural length").
|
||||
*/
|
||||
void br_hmac_init(br_hmac_context *ctx,
|
||||
const br_hmac_key_context *kc, size_t out_len);
|
||||
|
||||
/**
|
||||
* \brief Get the HMAC output size.
|
||||
*
|
||||
* The HMAC output size is the number of bytes that will actually be
|
||||
* produced with `br_hmac_out()` with the provided context. This function
|
||||
* MUST NOT be called on a non-initialised HMAC computation context.
|
||||
* The returned value is the minimum of the HMAC natural length (output
|
||||
* size of the underlying hash function) and the `out_len` parameter which
|
||||
* was used with the last `br_hmac_init()` call on that context (if the
|
||||
* initialisation `out_len` parameter was 0, then this function will
|
||||
* return the HMAC natural length).
|
||||
*
|
||||
* \param ctx the (already initialised) HMAC computation context.
|
||||
* \return the HMAC actual output size.
|
||||
*/
|
||||
static inline size_t
|
||||
br_hmac_size(br_hmac_context *ctx)
|
||||
{
|
||||
return ctx->out_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Get the underlying hash function.
|
||||
*
|
||||
* This function returns a pointer to the implementation vtable of the
|
||||
* hash function used for this HMAC context.
|
||||
*
|
||||
* \param hc HMAC context.
|
||||
* \return the hash function implementation.
|
||||
*/
|
||||
static inline const br_hash_class *br_hmac_get_digest(
|
||||
const br_hmac_context *hc)
|
||||
{
|
||||
return hc->dig.vtable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Inject some bytes in HMAC.
|
||||
*
|
||||
* The provided `len` bytes are injected as extra input in the HMAC
|
||||
* computation incarnated by the `ctx` HMAC context. It is acceptable
|
||||
* that `len` is zero, in which case `data` is ignored (and may be
|
||||
* `NULL`) and this function does nothing.
|
||||
*/
|
||||
void br_hmac_update(br_hmac_context *ctx, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* \brief Compute the HMAC output.
|
||||
*
|
||||
* The destination buffer MUST be large enough to accommodate the result;
|
||||
* its length is at most the "natural length" of HMAC (i.e. the output
|
||||
* length of the underlying hash function). The context is NOT modified;
|
||||
* further bytes may be processed. Thus, "partial HMAC" values can be
|
||||
* efficiently obtained.
|
||||
*
|
||||
* Returned value is the output length (in bytes).
|
||||
*
|
||||
* \param ctx HMAC computation context.
|
||||
* \param out destination buffer for the HMAC output.
|
||||
* \return the produced value length (in bytes).
|
||||
*/
|
||||
size_t br_hmac_out(const br_hmac_context *ctx, void *out);
|
||||
|
||||
/**
|
||||
* \brief Constant-time HMAC computation.
|
||||
*
|
||||
* This function compute the HMAC output in constant time. Some extra
|
||||
* input bytes are processed, then the output is computed. The extra
|
||||
* input consists in the `len` bytes pointed to by `data`. The `len`
|
||||
* parameter must lie between `min_len` and `max_len` (inclusive);
|
||||
* `max_len` bytes are actually read from `data`. Computing time (and
|
||||
* memory access pattern) will not depend upon the data byte contents or
|
||||
* the value of `len`.
|
||||
*
|
||||
* The output is written in the `out` buffer, that MUST be large enough
|
||||
* to receive it.
|
||||
*
|
||||
* The difference `max_len - min_len` MUST be less than 2<sup>30</sup>
|
||||
* (i.e. about one gigabyte).
|
||||
*
|
||||
* This function computes the output properly only if the underlying
|
||||
* hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256,
|
||||
* SHA-384 or SHA-512).
|
||||
*
|
||||
* The provided context is NOT modified.
|
||||
*
|
||||
* \param ctx the (already initialised) HMAC computation context.
|
||||
* \param data the extra input bytes.
|
||||
* \param len the extra input length (in bytes).
|
||||
* \param min_len minimum extra input length (in bytes).
|
||||
* \param max_len maximum extra input length (in bytes).
|
||||
* \param out destination buffer for the HMAC output.
|
||||
* \return the produced value length (in bytes).
|
||||
*/
|
||||
size_t br_hmac_outCT(const br_hmac_context *ctx,
|
||||
const void *data, size_t len, size_t min_len, size_t max_len,
|
||||
void *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_KDF_H__
|
||||
#define BR_BEARSSL_KDF_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bearssl_hash.h"
|
||||
#include "bearssl_hmac.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_kdf.h
|
||||
*
|
||||
* # Key Derivation Functions
|
||||
*
|
||||
* KDF are functions that takes a variable length input, and provide a
|
||||
* variable length output, meant to be used to derive subkeys from a
|
||||
* master key.
|
||||
*
|
||||
* ## HKDF
|
||||
*
|
||||
* HKDF is a KDF defined by [RFC 5869](https://tools.ietf.org/html/rfc5869).
|
||||
* It is based on HMAC, itself using an underlying hash function. Any
|
||||
* hash function can be used, as long as it is compatible with the rules
|
||||
* for the HMAC implementation (i.e. output size is 64 bytes or less, hash
|
||||
* internal state size is 64 bytes or less, and the internal block length is
|
||||
* a power of 2 between 16 and 256 bytes). HKDF has two phases:
|
||||
*
|
||||
* - HKDF-Extract: the input data in ingested, along with a "salt" value.
|
||||
*
|
||||
* - HKDF-Expand: the output is produced, from the result of processing
|
||||
* the input and salt, and using an extra non-secret parameter called
|
||||
* "info".
|
||||
*
|
||||
* The "salt" and "info" strings are non-secret and can be empty. Their role
|
||||
* is normally to bind the input and output, respectively, to conventional
|
||||
* identifiers that qualifu them within the used protocol or application.
|
||||
*
|
||||
* The implementation defined in this file uses the following functions:
|
||||
*
|
||||
* - `br_hkdf_init()`: initialize an HKDF context, with a hash function,
|
||||
* and the salt. This starts the HKDF-Extract process.
|
||||
*
|
||||
* - `br_hkdf_inject()`: inject more input bytes. This function may be
|
||||
* called repeatedly if the input data is provided by chunks.
|
||||
*
|
||||
* - `br_hkdf_flip()`: end the HKDF-Extract process, and start the
|
||||
* HKDF-Expand process.
|
||||
*
|
||||
* - `br_hkdf_produce()`: get the next bytes of output. This function
|
||||
* may be called several times to obtain the full output by chunks.
|
||||
* For correct HKDF processing, the same "info" string must be
|
||||
* provided for each call.
|
||||
*
|
||||
* Note that the HKDF total output size (the number of bytes that
|
||||
* HKDF-Expand is willing to produce) is limited: if the hash output size
|
||||
* is _n_ bytes, then the maximum output size is _255*n_.
|
||||
*
|
||||
* ## SHAKE
|
||||
*
|
||||
* SHAKE is defined in
|
||||
* [FIPS 202](https://csrc.nist.gov/publications/detail/fips/202/final)
|
||||
* under two versions: SHAKE128 and SHAKE256, offering an alleged
|
||||
* "security level" of 128 and 256 bits, respectively (SHAKE128 is
|
||||
* about 20 to 25% faster than SHAKE256). SHAKE internally relies on
|
||||
* the Keccak family of sponge functions, not on any externally provided
|
||||
* hash function. Contrary to HKDF, SHAKE does not have a concept of
|
||||
* either a "salt" or an "info" string. The API consists in four
|
||||
* functions:
|
||||
*
|
||||
* - `br_shake_init()`: initialize a SHAKE context for a given
|
||||
* security level.
|
||||
*
|
||||
* - `br_shake_inject()`: inject more input bytes. This function may be
|
||||
* called repeatedly if the input data is provided by chunks.
|
||||
*
|
||||
* - `br_shake_flip()`: end the data injection process, and start the
|
||||
* data production process.
|
||||
*
|
||||
* - `br_shake_produce()`: get the next bytes of output. This function
|
||||
* may be called several times to obtain the full output by chunks.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief HKDF context.
|
||||
*
|
||||
* The HKDF context is initialized with a hash function implementation
|
||||
* and a salt value. Contents are opaque (callers should not access them
|
||||
* directly). The caller is responsible for allocating the context where
|
||||
* appropriate. Context initialisation and usage incurs no dynamic
|
||||
* allocation, so there is no release function.
|
||||
*/
|
||||
typedef struct {
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
union {
|
||||
br_hmac_context hmac_ctx;
|
||||
br_hmac_key_context prk_ctx;
|
||||
} u;
|
||||
unsigned char buf[64];
|
||||
size_t ptr;
|
||||
size_t dig_len;
|
||||
unsigned chunk_num;
|
||||
#endif
|
||||
} br_hkdf_context;
|
||||
|
||||
/**
|
||||
* \brief HKDF context initialization.
|
||||
*
|
||||
* The underlying hash function and salt value are provided. Arbitrary
|
||||
* salt lengths can be used.
|
||||
*
|
||||
* HKDF makes a difference between a salt of length zero, and an
|
||||
* absent salt (the latter being equivalent to a salt consisting of
|
||||
* bytes of value zero, of the same length as the hash function output).
|
||||
* If `salt_len` is zero, then this function assumes that the salt is
|
||||
* present but of length zero. To specify an _absent_ salt, use
|
||||
* `BR_HKDF_NO_SALT` as `salt` parameter (`salt_len` is then ignored).
|
||||
*
|
||||
* \param hc HKDF context to initialise.
|
||||
* \param digest_vtable pointer to the hash function implementation vtable.
|
||||
* \param salt HKDF-Extract salt.
|
||||
* \param salt_len HKDF-Extract salt length (in bytes).
|
||||
*/
|
||||
void br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable,
|
||||
const void *salt, size_t salt_len);
|
||||
|
||||
/**
|
||||
* \brief The special "absent salt" value for HKDF.
|
||||
*/
|
||||
#define BR_HKDF_NO_SALT (&br_hkdf_no_salt)
|
||||
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
extern const unsigned char br_hkdf_no_salt;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief HKDF input injection (HKDF-Extract).
|
||||
*
|
||||
* This function injects some more input bytes ("key material") into
|
||||
* HKDF. This function may be called several times, after `br_hkdf_init()`
|
||||
* but before `br_hkdf_flip()`.
|
||||
*
|
||||
* \param hc HKDF context.
|
||||
* \param ikm extra input bytes.
|
||||
* \param ikm_len number of extra input bytes.
|
||||
*/
|
||||
void br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len);
|
||||
|
||||
/**
|
||||
* \brief HKDF switch to the HKDF-Expand phase.
|
||||
*
|
||||
* This call terminates the HKDF-Extract process (input injection), and
|
||||
* starts the HKDF-Expand process (output production).
|
||||
*
|
||||
* \param hc HKDF context.
|
||||
*/
|
||||
void br_hkdf_flip(br_hkdf_context *hc);
|
||||
|
||||
/**
|
||||
* \brief HKDF output production (HKDF-Expand).
|
||||
*
|
||||
* Produce more output bytes from the current state. This function may be
|
||||
* called several times, but only after `br_hkdf_flip()`.
|
||||
*
|
||||
* Returned value is the number of actually produced bytes. The total
|
||||
* output length is limited to 255 times the output length of the
|
||||
* underlying hash function.
|
||||
*
|
||||
* \param hc HKDF context.
|
||||
* \param info application specific information string.
|
||||
* \param info_len application specific information string length (in bytes).
|
||||
* \param out destination buffer for the HKDF output.
|
||||
* \param out_len the length of the requested output (in bytes).
|
||||
* \return the produced output length (in bytes).
|
||||
*/
|
||||
size_t br_hkdf_produce(br_hkdf_context *hc,
|
||||
const void *info, size_t info_len, void *out, size_t out_len);
|
||||
|
||||
/**
|
||||
* \brief SHAKE context.
|
||||
*
|
||||
* The HKDF context is initialized with a "security level". The internal
|
||||
* notion is called "capacity"; the capacity is twice the security level
|
||||
* (for instance, SHAKE128 has capacity 256).
|
||||
*
|
||||
* The caller is responsible for allocating the context where
|
||||
* appropriate. Context initialisation and usage incurs no dynamic
|
||||
* allocation, so there is no release function.
|
||||
*/
|
||||
typedef struct {
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
unsigned char dbuf[200];
|
||||
size_t dptr;
|
||||
size_t rate;
|
||||
uint64_t A[25];
|
||||
#endif
|
||||
} br_shake_context;
|
||||
|
||||
/**
|
||||
* \brief SHAKE context initialization.
|
||||
*
|
||||
* The context is initialized for the provided "security level".
|
||||
* Internally, this sets the "capacity" to twice the security level;
|
||||
* thus, for SHAKE128, the `security_level` parameter should be 128,
|
||||
* which corresponds to a 256-bit capacity.
|
||||
*
|
||||
* Allowed security levels are all multiples of 32, from 32 to 768,
|
||||
* inclusive. Larger security levels imply lower performance; levels
|
||||
* beyond 256 bits don't make much sense. Standard levels are 128
|
||||
* and 256 bits (for SHAKE128 and SHAKE256, respectively).
|
||||
*
|
||||
* \param sc SHAKE context to initialise.
|
||||
* \param security_level security level (in bits).
|
||||
*/
|
||||
void br_shake_init(br_shake_context *sc, int security_level);
|
||||
|
||||
/**
|
||||
* \brief SHAKE input injection.
|
||||
*
|
||||
* This function injects some more input bytes ("key material") into
|
||||
* SHAKE. This function may be called several times, after `br_shake_init()`
|
||||
* but before `br_shake_flip()`.
|
||||
*
|
||||
* \param sc SHAKE context.
|
||||
* \param data extra input bytes.
|
||||
* \param len number of extra input bytes.
|
||||
*/
|
||||
void br_shake_inject(br_shake_context *sc, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* \brief SHAKE switch to production phase.
|
||||
*
|
||||
* This call terminates the input injection process, and starts the
|
||||
* output production process.
|
||||
*
|
||||
* \param sc SHAKE context.
|
||||
*/
|
||||
void br_shake_flip(br_shake_context *hc);
|
||||
|
||||
/**
|
||||
* \brief SHAKE output production.
|
||||
*
|
||||
* Produce more output bytes from the current state. This function may be
|
||||
* called several times, but only after `br_shake_flip()`.
|
||||
*
|
||||
* There is no practical limit to the number of bytes that may be produced.
|
||||
*
|
||||
* \param sc SHAKE context.
|
||||
* \param out destination buffer for the SHAKE output.
|
||||
* \param len the length of the requested output (in bytes).
|
||||
*/
|
||||
void br_shake_produce(br_shake_context *sc, void *out, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_PEM_H__
|
||||
#define BR_BEARSSL_PEM_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_pem.h
|
||||
*
|
||||
* # PEM Support
|
||||
*
|
||||
* PEM is a traditional encoding layer use to store binary objects (in
|
||||
* particular X.509 certificates, and private keys) in text files. While
|
||||
* the acronym comes from an old, defunct standard ("Privacy Enhanced
|
||||
* Mail"), the format has been reused, with some variations, by many
|
||||
* systems, and is a _de facto_ standard, even though it is not, actually,
|
||||
* specified in all clarity anywhere.
|
||||
*
|
||||
* ## Format Details
|
||||
*
|
||||
* BearSSL contains a generic, streamed PEM decoder, which handles the
|
||||
* following format:
|
||||
*
|
||||
* - The input source (a sequence of bytes) is assumed to be the
|
||||
* encoding of a text file in an ASCII-compatible charset. This
|
||||
* includes ISO-8859-1, Windows-1252, and UTF-8 encodings. Each
|
||||
* line ends on a newline character (U+000A LINE FEED). The
|
||||
* U+000D CARRIAGE RETURN characters are ignored, so the code
|
||||
* accepts both Windows-style and Unix-style line endings.
|
||||
*
|
||||
* - Each object begins with a banner that occurs at the start of
|
||||
* a line; the first banner characters are "`-----BEGIN `" (five
|
||||
* dashes, the word "BEGIN", and a space). The banner matching is
|
||||
* not case-sensitive.
|
||||
*
|
||||
* - The _object name_ consists in the characters that follow the
|
||||
* banner start sequence, up to the end of the line, but without
|
||||
* trailing dashes (in "normal" PEM, there are five trailing
|
||||
* dashes, but this implementation is not picky about these dashes).
|
||||
* The BearSSL decoder normalises the name characters to uppercase
|
||||
* (for ASCII letters only) and accepts names up to 127 characters.
|
||||
*
|
||||
* - The object ends with a banner that again occurs at the start of
|
||||
* a line, and starts with "`-----END `" (again case-insensitive).
|
||||
*
|
||||
* - Between that start and end banner, only Base64 data shall occur.
|
||||
* Base64 converts each sequence of three bytes into four
|
||||
* characters; the four characters are ASCII letters, digits, "`+`"
|
||||
* or "`-`" signs, and one or two "`=`" signs may occur in the last
|
||||
* quartet. Whitespace is ignored (whitespace is any ASCII character
|
||||
* of code 32 or less, so control characters are whitespace) and
|
||||
* lines may have arbitrary length; the only restriction is that the
|
||||
* four characters of a quartet must appear on the same line (no
|
||||
* line break inside a quartet).
|
||||
*
|
||||
* - A single file may contain more than one PEM object. Bytes that
|
||||
* occur between objects are ignored.
|
||||
*
|
||||
*
|
||||
* ## PEM Decoder API
|
||||
*
|
||||
* The PEM decoder offers a state-machine API. The caller allocates a
|
||||
* decoder context, then injects source bytes. Source bytes are pushed
|
||||
* with `br_pem_decoder_push()`. The decoder stops accepting bytes when
|
||||
* it reaches an "event", which is either the start of an object, the
|
||||
* end of an object, or a decoding error within an object.
|
||||
*
|
||||
* The `br_pem_decoder_event()` function is used to obtain the current
|
||||
* event; it also clears it, thus allowing the decoder to accept more
|
||||
* bytes. When a object start event is raised, the decoder context
|
||||
* offers the found object name (normalised to ASCII uppercase).
|
||||
*
|
||||
* When an object is reached, the caller must set an appropriate callback
|
||||
* function, which will receive (by chunks) the decoded object data.
|
||||
*
|
||||
* Since the decoder context makes no dynamic allocation, it requires
|
||||
* no explicit deallocation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief PEM decoder context.
|
||||
*
|
||||
* Contents are opaque (they should not be accessed directly).
|
||||
*/
|
||||
typedef struct {
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
/* CPU for the T0 virtual machine. */
|
||||
struct {
|
||||
uint32_t *dp;
|
||||
uint32_t *rp;
|
||||
const unsigned char *ip;
|
||||
} cpu;
|
||||
uint32_t dp_stack[32];
|
||||
uint32_t rp_stack[32];
|
||||
int err;
|
||||
|
||||
const unsigned char *hbuf;
|
||||
size_t hlen;
|
||||
|
||||
void (*dest)(void *dest_ctx, const void *src, size_t len);
|
||||
void *dest_ctx;
|
||||
|
||||
unsigned char event;
|
||||
char name[128];
|
||||
unsigned char buf[255];
|
||||
size_t ptr;
|
||||
#endif
|
||||
} br_pem_decoder_context;
|
||||
|
||||
/**
|
||||
* \brief Initialise a PEM decoder structure.
|
||||
*
|
||||
* \param ctx decoder context to initialise.
|
||||
*/
|
||||
void br_pem_decoder_init(br_pem_decoder_context *ctx);
|
||||
|
||||
/**
|
||||
* \brief Push some bytes into the decoder.
|
||||
*
|
||||
* Returned value is the number of bytes actually consumed; this may be
|
||||
* less than the number of provided bytes if an event is raised. When an
|
||||
* event is raised, it must be read (with `br_pem_decoder_event()`);
|
||||
* until the event is read, this function will return 0.
|
||||
*
|
||||
* \param ctx decoder context.
|
||||
* \param data new data bytes.
|
||||
* \param len number of new data bytes.
|
||||
* \return the number of bytes actually received (may be less than `len`).
|
||||
*/
|
||||
size_t br_pem_decoder_push(br_pem_decoder_context *ctx,
|
||||
const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* \brief Set the receiver for decoded data.
|
||||
*
|
||||
* When an object is entered, the provided function (with opaque context
|
||||
* pointer) will be called repeatedly with successive chunks of decoded
|
||||
* data for that object. If `dest` is set to 0, then decoded data is
|
||||
* simply ignored. The receiver can be set at any time, but, in practice,
|
||||
* it should be called immediately after receiving a "start of object"
|
||||
* event.
|
||||
*
|
||||
* \param ctx decoder context.
|
||||
* \param dest callback for receiving decoded data.
|
||||
* \param dest_ctx opaque context pointer for the `dest` callback.
|
||||
*/
|
||||
static inline void
|
||||
br_pem_decoder_setdest(br_pem_decoder_context *ctx,
|
||||
void (*dest)(void *dest_ctx, const void *src, size_t len),
|
||||
void *dest_ctx)
|
||||
{
|
||||
ctx->dest = dest;
|
||||
ctx->dest_ctx = dest_ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the last event.
|
||||
*
|
||||
* If an event was raised, then this function returns the event value, and
|
||||
* also clears it, thereby allowing the decoder to proceed. If no event
|
||||
* was raised since the last call to `br_pem_decoder_event()`, then this
|
||||
* function returns 0.
|
||||
*
|
||||
* \param ctx decoder context.
|
||||
* \return the raised event, or 0.
|
||||
*/
|
||||
int br_pem_decoder_event(br_pem_decoder_context *ctx);
|
||||
|
||||
/**
|
||||
* \brief Event: start of object.
|
||||
*
|
||||
* This event is raised when the start of a new object has been detected.
|
||||
* The object name (normalised to uppercase) can be accessed with
|
||||
* `br_pem_decoder_name()`.
|
||||
*/
|
||||
#define BR_PEM_BEGIN_OBJ 1
|
||||
|
||||
/**
|
||||
* \brief Event: end of object.
|
||||
*
|
||||
* This event is raised when the end of the current object is reached
|
||||
* (normally, i.e. with no decoding error).
|
||||
*/
|
||||
#define BR_PEM_END_OBJ 2
|
||||
|
||||
/**
|
||||
* \brief Event: decoding error.
|
||||
*
|
||||
* This event is raised when decoding fails within an object.
|
||||
* This formally closes the current object and brings the decoder back
|
||||
* to the "out of any object" state. The offending line in the source
|
||||
* is consumed.
|
||||
*/
|
||||
#define BR_PEM_ERROR 3
|
||||
|
||||
/**
|
||||
* \brief Get the name of the encountered object.
|
||||
*
|
||||
* The encountered object name is defined only when the "start of object"
|
||||
* event is raised. That name is normalised to uppercase (for ASCII letters
|
||||
* only) and does not include trailing dashes.
|
||||
*
|
||||
* \param ctx decoder context.
|
||||
* \return the current object name.
|
||||
*/
|
||||
static inline const char *
|
||||
br_pem_decoder_name(br_pem_decoder_context *ctx)
|
||||
{
|
||||
return ctx->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Encode an object in PEM.
|
||||
*
|
||||
* This function encodes the provided binary object (`data`, of length `len`
|
||||
* bytes) into PEM. The `banner` text will be included in the header and
|
||||
* footer (e.g. use `"CERTIFICATE"` to get a `"BEGIN CERTIFICATE"` header).
|
||||
*
|
||||
* The length (in characters) of the PEM output is returned; that length
|
||||
* does NOT include the terminating zero, that this function nevertheless
|
||||
* adds. If using the returned value for allocation purposes, the allocated
|
||||
* buffer size MUST be at least one byte larger than the returned size.
|
||||
*
|
||||
* If `dest` is `NULL`, then the encoding does not happen; however, the
|
||||
* length of the encoded object is still computed and returned.
|
||||
*
|
||||
* The `data` pointer may be `NULL` only if `len` is zero (when encoding
|
||||
* an object of length zero, which is not very useful), or when `dest`
|
||||
* is `NULL` (in that case, source data bytes are ignored).
|
||||
*
|
||||
* Some `flags` can be specified to alter the encoding behaviour:
|
||||
*
|
||||
* - If `BR_PEM_LINE64` is set, then line-breaking will occur after
|
||||
* every 64 characters of output, instead of the default of 76.
|
||||
*
|
||||
* - If `BR_PEM_CRLF` is set, then end-of-line sequence will use
|
||||
* CR+LF instead of a single LF.
|
||||
*
|
||||
* The `data` and `dest` buffers may overlap, in which case the source
|
||||
* binary data is destroyed in the process. Note that the PEM-encoded output
|
||||
* is always larger than the source binary.
|
||||
*
|
||||
* \param dest the destination buffer (or `NULL`).
|
||||
* \param data the source buffer (can be `NULL` in some cases).
|
||||
* \param len the source length (in bytes).
|
||||
* \param banner the PEM banner expression.
|
||||
* \param flags the behavioural flags.
|
||||
* \return the PEM object length (in characters), EXCLUDING the final zero.
|
||||
*/
|
||||
size_t br_pem_encode(void *dest, const void *data, size_t len,
|
||||
const char *banner, unsigned flags);
|
||||
|
||||
/**
|
||||
* \brief PEM encoding flag: split lines at 64 characters.
|
||||
*/
|
||||
#define BR_PEM_LINE64 0x0001
|
||||
|
||||
/**
|
||||
* \brief PEM encoding flag: use CR+LF line endings.
|
||||
*/
|
||||
#define BR_PEM_CRLF 0x0002
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_PRF_H__
|
||||
#define BR_BEARSSL_PRF_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_prf.h
|
||||
*
|
||||
* # The TLS PRF
|
||||
*
|
||||
* The "PRF" is the pseudorandom function used internally during the
|
||||
* SSL/TLS handshake, notably to expand negotiated shared secrets into
|
||||
* the symmetric encryption keys that will be used to process the
|
||||
* application data.
|
||||
*
|
||||
* TLS 1.0 and 1.1 define a PRF that is based on both MD5 and SHA-1. This
|
||||
* is implemented by the `br_tls10_prf()` function.
|
||||
*
|
||||
* TLS 1.2 redefines the PRF, using an explicit hash function. The
|
||||
* `br_tls12_sha256_prf()` and `br_tls12_sha384_prf()` functions apply that
|
||||
* PRF with, respectively, SHA-256 and SHA-384. Most standard cipher suites
|
||||
* rely on the SHA-256 based PRF, but some use SHA-384.
|
||||
*
|
||||
* The PRF always uses as input three parameters: a "secret" (some
|
||||
* bytes), a "label" (ASCII string), and a "seed" (again some bytes). An
|
||||
* arbitrary output length can be produced. The "seed" is provided as an
|
||||
* arbitrary number of binary chunks, that gets internally concatenated.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Type for a seed chunk.
|
||||
*
|
||||
* Each chunk may have an arbitrary length, and may be empty (no byte at
|
||||
* all). If the chunk length is zero, then the pointer to the chunk data
|
||||
* may be `NULL`.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* \brief Pointer to the chunk data.
|
||||
*/
|
||||
const void *data;
|
||||
|
||||
/**
|
||||
* \brief Chunk length (in bytes).
|
||||
*/
|
||||
size_t len;
|
||||
} br_tls_prf_seed_chunk;
|
||||
|
||||
/**
|
||||
* \brief PRF implementation for TLS 1.0 and 1.1.
|
||||
*
|
||||
* This PRF is the one specified by TLS 1.0 and 1.1. It internally uses
|
||||
* MD5 and SHA-1.
|
||||
*
|
||||
* \param dst destination buffer.
|
||||
* \param len output length (in bytes).
|
||||
* \param secret secret value (key) for this computation.
|
||||
* \param secret_len length of "secret" (in bytes).
|
||||
* \param label PRF label (zero-terminated ASCII string).
|
||||
* \param seed_num number of seed chunks.
|
||||
* \param seed seed chnks for this computation (usually non-secret).
|
||||
*/
|
||||
void br_tls10_prf(void *dst, size_t len,
|
||||
const void *secret, size_t secret_len, const char *label,
|
||||
size_t seed_num, const br_tls_prf_seed_chunk *seed);
|
||||
|
||||
/**
|
||||
* \brief PRF implementation for TLS 1.2, with SHA-256.
|
||||
*
|
||||
* This PRF is the one specified by TLS 1.2, when the underlying hash
|
||||
* function is SHA-256.
|
||||
*
|
||||
* \param dst destination buffer.
|
||||
* \param len output length (in bytes).
|
||||
* \param secret secret value (key) for this computation.
|
||||
* \param secret_len length of "secret" (in bytes).
|
||||
* \param label PRF label (zero-terminated ASCII string).
|
||||
* \param seed_num number of seed chunks.
|
||||
* \param seed seed chnks for this computation (usually non-secret).
|
||||
*/
|
||||
void br_tls12_sha256_prf(void *dst, size_t len,
|
||||
const void *secret, size_t secret_len, const char *label,
|
||||
size_t seed_num, const br_tls_prf_seed_chunk *seed);
|
||||
|
||||
/**
|
||||
* \brief PRF implementation for TLS 1.2, with SHA-384.
|
||||
*
|
||||
* This PRF is the one specified by TLS 1.2, when the underlying hash
|
||||
* function is SHA-384.
|
||||
*
|
||||
* \param dst destination buffer.
|
||||
* \param len output length (in bytes).
|
||||
* \param secret secret value (key) for this computation.
|
||||
* \param secret_len length of "secret" (in bytes).
|
||||
* \param label PRF label (zero-terminated ASCII string).
|
||||
* \param seed_num number of seed chunks.
|
||||
* \param seed seed chnks for this computation (usually non-secret).
|
||||
*/
|
||||
void br_tls12_sha384_prf(void *dst, size_t len,
|
||||
const void *secret, size_t secret_len, const char *label,
|
||||
size_t seed_num, const br_tls_prf_seed_chunk *seed);
|
||||
|
||||
/**
|
||||
* brief A convenient type name for a PRF implementation.
|
||||
*
|
||||
* \param dst destination buffer.
|
||||
* \param len output length (in bytes).
|
||||
* \param secret secret value (key) for this computation.
|
||||
* \param secret_len length of "secret" (in bytes).
|
||||
* \param label PRF label (zero-terminated ASCII string).
|
||||
* \param seed_num number of seed chunks.
|
||||
* \param seed seed chnks for this computation (usually non-secret).
|
||||
*/
|
||||
typedef void (*br_tls_prf_impl)(void *dst, size_t len,
|
||||
const void *secret, size_t secret_len, const char *label,
|
||||
size_t seed_num, const br_tls_prf_seed_chunk *seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BR_BEARSSL_RAND_H__
|
||||
#define BR_BEARSSL_RAND_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bearssl_block.h"
|
||||
#include "bearssl_hash.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \file bearssl_rand.h
|
||||
*
|
||||
* # Pseudo-Random Generators
|
||||
*
|
||||
* A PRNG is a state-based engine that outputs pseudo-random bytes on
|
||||
* demand. It is initialized with an initial seed, and additional seed
|
||||
* bytes can be added afterwards. Bytes produced depend on the seeds and
|
||||
* also on the exact sequence of calls (including sizes requested for
|
||||
* each call).
|
||||
*
|
||||
*
|
||||
* ## Procedural and OOP API
|
||||
*
|
||||
* For the PRNG of name "`xxx`", two API are provided. The _procedural_
|
||||
* API defined a context structure `br_xxx_context` and three functions:
|
||||
*
|
||||
* - `br_xxx_init()`
|
||||
*
|
||||
* Initialise the context with an initial seed.
|
||||
*
|
||||
* - `br_xxx_generate()`
|
||||
*
|
||||
* Produce some pseudo-random bytes.
|
||||
*
|
||||
* - `br_xxx_update()`
|
||||
*
|
||||
* Inject some additional seed.
|
||||
*
|
||||
* The initialisation function sets the first context field (`vtable`)
|
||||
* to a pointer to the vtable that supports the OOP API. The OOP API
|
||||
* provides access to the same functions through function pointers,
|
||||
* named `init()`, `generate()` and `update()`.
|
||||
*
|
||||
* Note that the context initialisation method may accept additional
|
||||
* parameters, provided as a 'const void *' pointer at API level. These
|
||||
* additional parameters depend on the implemented PRNG.
|
||||
*
|
||||
*
|
||||
* ## HMAC_DRBG
|
||||
*
|
||||
* HMAC_DRBG is defined in [NIST SP 800-90A Revision
|
||||
* 1](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf).
|
||||
* It uses HMAC repeatedly, over some configurable underlying hash
|
||||
* function. In BearSSL, it is implemented under the "`hmac_drbg`" name.
|
||||
* The "extra parameters" pointer for context initialisation should be
|
||||
* set to a pointer to the vtable for the underlying hash function (e.g.
|
||||
* pointer to `br_sha256_vtable` to use HMAC_DRBG with SHA-256).
|
||||
*
|
||||
* According to the NIST standard, each request shall produce up to
|
||||
* 2<sup>19</sup> bits (i.e. 64 kB of data); moreover, the context shall
|
||||
* be reseeded at least once every 2<sup>48</sup> requests. This
|
||||
* implementation does not maintain the reseed counter (the threshold is
|
||||
* too high to be reached in practice) and does not object to producing
|
||||
* more than 64 kB in a single request; thus, the code cannot fail,
|
||||
* which corresponds to the fact that the API has no room for error
|
||||
* codes. However, this implies that requesting more than 64 kB in one
|
||||
* `generate()` request, or making more than 2<sup>48</sup> requests
|
||||
* without reseeding, is formally out of NIST specification. There is
|
||||
* no currently known security penalty for exceeding the NIST limits,
|
||||
* and, in any case, HMAC_DRBG usage in implementing SSL/TLS always
|
||||
* stays much below these thresholds.
|
||||
*
|
||||
*
|
||||
* ## AESCTR_DRBG
|
||||
*
|
||||
* AESCTR_DRBG is a custom PRNG based on AES-128 in CTR mode. This is
|
||||
* meant to be used only in situations where you are desperate for
|
||||
* speed, and have an hardware-optimized AES/CTR implementation. Whether
|
||||
* this will yield perceptible improvements depends on what you use the
|
||||
* pseudorandom bytes for, and how many you want; for instance, RSA key
|
||||
* pair generation uses a substantial amount of randomness, and using
|
||||
* AESCTR_DRBG instead of HMAC_DRBG yields a 15 to 20% increase in key
|
||||
* generation speed on a recent x86 CPU (Intel Core i7-6567U at 3.30 GHz).
|
||||
*
|
||||
* Internally, it uses CTR mode with successive counter values, starting
|
||||
* at zero (counter value expressed over 128 bits, big-endian convention).
|
||||
* The counter is not allowed to reach 32768; thus, every 32768*16 bytes
|
||||
* at most, the `update()` function is run (on an empty seed, if none is
|
||||
* provided). The `update()` function computes the new AES-128 key by
|
||||
* applying a custom hash function to the concatenation of a state-dependent
|
||||
* word (encryption of an all-one block with the current key) and the new
|
||||
* seed. The custom hash function uses Hirose's construction over AES-256;
|
||||
* see the comments in `aesctr_drbg.c` for details.
|
||||
*
|
||||
* This DRBG does not follow an existing standard, and thus should be
|
||||
* considered as inadequate for production use until it has been properly
|
||||
* analysed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Class type for PRNG implementations.
|
||||
*
|
||||
* A `br_prng_class` instance references the methods implementing a PRNG.
|
||||
* Constant instances of this structure are defined for each implemented
|
||||
* PRNG. Such instances are also called "vtables".
|
||||
*/
|
||||
typedef struct br_prng_class_ br_prng_class;
|
||||
struct br_prng_class_ {
|
||||
/**
|
||||
* \brief Size (in bytes) of the context structure appropriate for
|
||||
* running this PRNG.
|
||||
*/
|
||||
size_t context_size;
|
||||
|
||||
/**
|
||||
* \brief Initialisation method.
|
||||
*
|
||||
* The context to initialise is provided as a pointer to its
|
||||
* first field (the vtable pointer); this function sets that
|
||||
* first field to a pointer to the vtable.
|
||||
*
|
||||
* The extra parameters depend on the implementation; each
|
||||
* implementation defines what kind of extra parameters it
|
||||
* expects (if any).
|
||||
*
|
||||
* Requirements on the initial seed depend on the implemented
|
||||
* PRNG.
|
||||
*
|
||||
* \param ctx PRNG context to initialise.
|
||||
* \param params extra parameters for the PRNG.
|
||||
* \param seed initial seed.
|
||||
* \param seed_len initial seed length (in bytes).
|
||||
*/
|
||||
void (*init)(const br_prng_class **ctx, const void *params,
|
||||
const void *seed, size_t seed_len);
|
||||
|
||||
/**
|
||||
* \brief Random bytes generation.
|
||||
*
|
||||
* This method produces `len` pseudorandom bytes, in the `out`
|
||||
* buffer. The context is updated accordingly.
|
||||
*
|
||||
* \param ctx PRNG context.
|
||||
* \param out output buffer.
|
||||
* \param len number of pseudorandom bytes to produce.
|
||||
*/
|
||||
void (*generate)(const br_prng_class **ctx, void *out, size_t len);
|
||||
|
||||
/**
|
||||
* \brief Inject additional seed bytes.
|
||||
*
|
||||
* The provided seed bytes are added into the PRNG internal
|
||||
* entropy pool.
|
||||
*
|
||||
* \param ctx PRNG context.
|
||||
* \param seed additional seed.
|
||||
* \param seed_len additional seed length (in bytes).
|
||||
*/
|
||||
void (*update)(const br_prng_class **ctx,
|
||||
const void *seed, size_t seed_len);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Context for HMAC_DRBG.
|
||||
*
|
||||
* The context contents are opaque, except the first field, which
|
||||
* supports OOP.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* \brief Pointer to the vtable.
|
||||
*
|
||||
* This field is set with the initialisation method/function.
|
||||
*/
|
||||
const br_prng_class *vtable;
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
unsigned char K[64];
|
||||
unsigned char V[64];
|
||||
const br_hash_class *digest_class;
|
||||
#endif
|
||||
} br_hmac_drbg_context;
|
||||
|
||||
/**
|
||||
* \brief Statically allocated, constant vtable for HMAC_DRBG.
|
||||
*/
|
||||
extern const br_prng_class br_hmac_drbg_vtable;
|
||||
|
||||
/**
|
||||
* \brief HMAC_DRBG initialisation.
|
||||
*
|
||||
* The context to initialise is provided as a pointer to its first field
|
||||
* (the vtable pointer); this function sets that first field to a
|
||||
* pointer to the vtable.
|
||||
*
|
||||
* The `seed` value is what is called, in NIST terminology, the
|
||||
* concatenation of the "seed", "nonce" and "personalization string", in
|
||||
* that order.
|
||||
*
|
||||
* The `digest_class` parameter defines the underlying hash function.
|
||||
* Formally, the NIST standard specifies that the hash function shall
|
||||
* be only SHA-1 or one of the SHA-2 functions. This implementation also
|
||||
* works with any other implemented hash function (such as MD5), but
|
||||
* this is non-standard and therefore not recommended.
|
||||
*
|
||||
* \param ctx HMAC_DRBG context to initialise.
|
||||
* \param digest_class vtable for the underlying hash function.
|
||||
* \param seed initial seed.
|
||||
* \param seed_len initial seed length (in bytes).
|
||||
*/
|
||||
void br_hmac_drbg_init(br_hmac_drbg_context *ctx,
|
||||
const br_hash_class *digest_class, const void *seed, size_t seed_len);
|
||||
|
||||
/**
|
||||
* \brief Random bytes generation with HMAC_DRBG.
|
||||
*
|
||||
* This method produces `len` pseudorandom bytes, in the `out`
|
||||
* buffer. The context is updated accordingly. Formally, requesting
|
||||
* more than 65536 bytes in one request falls out of specification
|
||||
* limits (but it won't fail).
|
||||
*
|
||||
* \param ctx HMAC_DRBG context.
|
||||
* \param out output buffer.
|
||||
* \param len number of pseudorandom bytes to produce.
|
||||
*/
|
||||
void br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len);
|
||||
|
||||
/**
|
||||
* \brief Inject additional seed bytes in HMAC_DRBG.
|
||||
*
|
||||
* The provided seed bytes are added into the HMAC_DRBG internal
|
||||
* entropy pool. The process does not _replace_ existing entropy,
|
||||
* thus pushing non-random bytes (i.e. bytes which are known to the
|
||||
* attackers) does not degrade the overall quality of generated bytes.
|
||||
*
|
||||
* \param ctx HMAC_DRBG context.
|
||||
* \param seed additional seed.
|
||||
* \param seed_len additional seed length (in bytes).
|
||||
*/
|
||||
void br_hmac_drbg_update(br_hmac_drbg_context *ctx,
|
||||
const void *seed, size_t seed_len);
|
||||
|
||||
/**
|
||||
* \brief Get the hash function implementation used by a given instance of
|
||||
* HMAC_DRBG.
|
||||
*
|
||||
* This calls MUST NOT be performed on a context which was not
|
||||
* previously initialised.
|
||||
*
|
||||
* \param ctx HMAC_DRBG context.
|
||||
* \return the hash function vtable.
|
||||
*/
|
||||
static inline const br_hash_class *
|
||||
br_hmac_drbg_get_hash(const br_hmac_drbg_context *ctx)
|
||||
{
|
||||
return ctx->digest_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Type for a provider of entropy seeds.
|
||||
*
|
||||
* A "seeder" is a function that is able to obtain random values from
|
||||
* some source and inject them as entropy seed in a PRNG. A seeder
|
||||
* shall guarantee that the total entropy of the injected seed is large
|
||||
* enough to seed a PRNG for purposes of cryptographic key generation
|
||||
* (i.e. at least 128 bits).
|
||||
*
|
||||
* A seeder may report a failure to obtain adequate entropy. Seeders
|
||||
* shall endeavour to fix themselves transient errors by trying again;
|
||||
* thus, callers may consider reported errors as permanent.
|
||||
*
|
||||
* \param ctx PRNG context to seed.
|
||||
* \return 1 on success, 0 on error.
|
||||
*/
|
||||
typedef int (*br_prng_seeder)(const br_prng_class **ctx);
|
||||
|
||||
/**
|
||||
* \brief Get a seeder backed by the operating system or hardware.
|
||||
*
|
||||
* Get a seeder that feeds on RNG facilities provided by the current
|
||||
* operating system or hardware. If no such facility is known, then 0
|
||||
* is returned.
|
||||
*
|
||||
* If `name` is not `NULL`, then `*name` is set to a symbolic string
|
||||
* that identifies the seeder implementation. If no seeder is returned
|
||||
* and `name` is not `NULL`, then `*name` is set to a pointer to the
|
||||
* constant string `"none"`.
|
||||
*
|
||||
* \param name receiver for seeder name, or `NULL`.
|
||||
* \return the system seeder, if available, or 0.
|
||||
*/
|
||||
br_prng_seeder br_prng_seeder_system(const char **name);
|
||||
|
||||
/**
|
||||
* \brief Context for AESCTR_DRBG.
|
||||
*
|
||||
* The context contents are opaque, except the first field, which
|
||||
* supports OOP.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* \brief Pointer to the vtable.
|
||||
*
|
||||
* This field is set with the initialisation method/function.
|
||||
*/
|
||||
const br_prng_class *vtable;
|
||||
#ifndef BR_DOXYGEN_IGNORE
|
||||
br_aes_gen_ctr_keys sk;
|
||||
uint32_t cc;
|
||||
#endif
|
||||
} br_aesctr_drbg_context;
|
||||
|
||||
/**
|
||||
* \brief Statically allocated, constant vtable for AESCTR_DRBG.
|
||||
*/
|
||||
extern const br_prng_class br_aesctr_drbg_vtable;
|
||||
|
||||
/**
|
||||
* \brief AESCTR_DRBG initialisation.
|
||||
*
|
||||
* The context to initialise is provided as a pointer to its first field
|
||||
* (the vtable pointer); this function sets that first field to a
|
||||
* pointer to the vtable.
|
||||
*
|
||||
* The internal AES key is first set to the all-zero key; then, the
|
||||
* `br_aesctr_drbg_update()` function is called with the provided `seed`.
|
||||
* The call is performed even if the seed length (`seed_len`) is zero.
|
||||
*
|
||||
* The `aesctr` parameter defines the underlying AES/CTR implementation.
|
||||
*
|
||||
* \param ctx AESCTR_DRBG context to initialise.
|
||||
* \param aesctr vtable for the AES/CTR implementation.
|
||||
* \param seed initial seed (can be `NULL` if `seed_len` is zero).
|
||||
* \param seed_len initial seed length (in bytes).
|
||||
*/
|
||||
void br_aesctr_drbg_init(br_aesctr_drbg_context *ctx,
|
||||
const br_block_ctr_class *aesctr, const void *seed, size_t seed_len);
|
||||
|
||||
/**
|
||||
* \brief Random bytes generation with AESCTR_DRBG.
|
||||
*
|
||||
* This method produces `len` pseudorandom bytes, in the `out`
|
||||
* buffer. The context is updated accordingly.
|
||||
*
|
||||
* \param ctx AESCTR_DRBG context.
|
||||
* \param out output buffer.
|
||||
* \param len number of pseudorandom bytes to produce.
|
||||
*/
|
||||
void br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx,
|
||||
void *out, size_t len);
|
||||
|
||||
/**
|
||||
* \brief Inject additional seed bytes in AESCTR_DRBG.
|
||||
*
|
||||
* The provided seed bytes are added into the AESCTR_DRBG internal
|
||||
* entropy pool. The process does not _replace_ existing entropy,
|
||||
* thus pushing non-random bytes (i.e. bytes which are known to the
|
||||
* attackers) does not degrade the overall quality of generated bytes.
|
||||
*
|
||||
* \param ctx AESCTR_DRBG context.
|
||||
* \param seed additional seed.
|
||||
* \param seed_len additional seed length (in bytes).
|
||||
*/
|
||||
void br_aesctr_drbg_update(br_aesctr_drbg_context *ctx,
|
||||
const void *seed, size_t seed_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
||||
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
|
||||
#define _LWIPOPTS_EXAMPLE_COMMONH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
// Critical section protection
|
||||
extern void noInterrupts();
|
||||
extern void interrupts();
|
||||
#define SYS_ARCH_DECL_PROTECT int
|
||||
#define SYS_ARCH_PROTECT(lev) noInterrupts
|
||||
#define SYS_ARCH_UNPROTECT(lev) interrupts
|
||||
|
||||
// Common settings used in most of the pico_w examples
|
||||
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
|
||||
|
||||
#define NO_SYS 1
|
||||
#define LWIP_SOCKET 0
|
||||
#define MEM_LIBC_MALLOC 0
|
||||
|
||||
#define MEM_ALIGNMENT 4
|
||||
#define MEM_SIZE 16384
|
||||
#define MEMP_NUM_TCP_SEG 32
|
||||
#define MEMP_NUM_ARP_QUEUE 10
|
||||
#define PBUF_POOL_SIZE 24
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
#define TCP_WND (8 * TCP_MSS)
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_SND_BUF (8 * TCP_MSS)
|
||||
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
|
||||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
#define LWIP_NETIF_HOSTNAME 1
|
||||
#define LWIP_NETCONN 0
|
||||
#define MEM_STATS 0
|
||||
#define SYS_STATS 0
|
||||
#define MEMP_STATS 0
|
||||
#define LINK_STATS 0
|
||||
// #define ETH_PAD_SIZE 2
|
||||
#define LWIP_CHKSUM_ALGORITHM 3
|
||||
#define LWIP_DHCP 1
|
||||
#define LWIP_IPV4 1
|
||||
#define LWIP_TCP 1
|
||||
#define LWIP_UDP 1
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
#define LWIP_NETIF_TX_SINGLE_PBUF 1
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#endif
|
||||
|
||||
// NTP
|
||||
extern void __setSystemTime(unsigned long long sec, unsigned long us);
|
||||
#define SNTP_SET_SYSTEM_TIME_US(sec, us) __setSystemTime(sec, us)
|
||||
#define SNTP_MAX_SERVERS 2
|
||||
//#define SNTP_SERVER_ADDRESS "pool.ntp.org"
|
||||
#define SNTP_SERVER_DNS 1
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LWIP_DEBUG 1
|
||||
#define LWIP_STATS 1
|
||||
#define LWIP_STATS_DISPLAY 1
|
||||
#endif
|
||||
|
||||
#define ETHARP_DEBUG LWIP_DBG_OFF
|
||||
#define NETIF_DEBUG LWIP_DBG_OFF
|
||||
#define PBUF_DEBUG LWIP_DBG_OFF
|
||||
#define API_LIB_DEBUG LWIP_DBG_OFF
|
||||
#define API_MSG_DEBUG LWIP_DBG_OFF
|
||||
#define SOCKETS_DEBUG LWIP_DBG_OFF
|
||||
#define ICMP_DEBUG LWIP_DBG_OFF
|
||||
#define INET_DEBUG LWIP_DBG_OFF
|
||||
#define IP_DEBUG LWIP_DBG_OFF
|
||||
#define IP_REASS_DEBUG LWIP_DBG_OFF
|
||||
#define RAW_DEBUG LWIP_DBG_OFF
|
||||
#define MEM_DEBUG LWIP_DBG_OFF
|
||||
#define MEMP_DEBUG LWIP_DBG_OFF
|
||||
#define SYS_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_RTO_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_CWND_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_WND_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_FR_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
|
||||
#define TCP_RST_DEBUG LWIP_DBG_OFF
|
||||
#define UDP_DEBUG LWIP_DBG_OFF
|
||||
#define TCPIP_DEBUG LWIP_DBG_OFF
|
||||
#define PPP_DEBUG LWIP_DBG_OFF
|
||||
#define SLIP_DEBUG LWIP_DBG_OFF
|
||||
#define DHCP_DEBUG LWIP_DBG_OFF
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
#endif /* __LWIPOPTS_H__ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
ota_command.h - OTA stub that copies from LittleFS to flash
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define _OTA_WRITE 1
|
||||
#define _OTA_VERIFY 1
|
||||
|
||||
typedef struct {
|
||||
uint32_t command;
|
||||
union {
|
||||
struct {
|
||||
char filename[64];
|
||||
uint32_t fileOffset;
|
||||
uint32_t fileLength;
|
||||
uint32_t flashAddress; // Normally XIP_BASE
|
||||
} write;
|
||||
};
|
||||
} commandEntry;
|
||||
|
||||
// Must fit within 4K page
|
||||
typedef struct {
|
||||
uint8_t sign[8]; // "Pico OTA"
|
||||
|
||||
// List of operations
|
||||
uint32_t count;
|
||||
commandEntry cmd[8];
|
||||
|
||||
uint32_t crc32; // CRC32 over just the contents of this struct, up until just before this value
|
||||
} OTACmdPage;
|
||||
|
||||
#define _OTA_COMMAND_FILE "otacommand.bin"
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 1
|
||||
#define PICO_SDK_VERSION_MINOR 3
|
||||
#define PICO_SDK_VERSION_REVISION 2
|
||||
#define PICO_SDK_VERSION_STRING "1.3.2-develop"
|
||||
#define PICO_SDK_VERSION_MINOR 4
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "1.4.0"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#define CFG_TUSB_MCU OPT_MCU_RP2040
|
||||
#endif
|
||||
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||
#define CFG_TUSB_OS OPT_OS_PICO
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID (2)
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_MSC (0)
|
||||
#define CFG_TUD_MIDI (0)
|
||||
#define CFG_TUD_VENDOR (0)
|
||||
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE (64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
@@ -2,6 +2,8 @@
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
Arduino KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
@@ -23,14 +25,29 @@ pop KEYWORD2
|
||||
pop_nb KEYWORD2
|
||||
|
||||
rp2040 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
RP2040 KEYWORD2
|
||||
usToPIOCycles KEYWORD2
|
||||
f_cpu KEYWORD2
|
||||
getCycleCount KEYWORD2
|
||||
getCycleCount64 KEYWORD2
|
||||
|
||||
getFreeHeap KEYWORD2
|
||||
getUsedHeap KEYWORD2
|
||||
getTotalHeap KEYWORD2
|
||||
|
||||
idleOtherCore KEYWORD2
|
||||
resumeOtherCore KEYWORD2
|
||||
|
||||
restartCore1 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
|
||||
PIOProgram KEYWORD2
|
||||
prepare KEYWORD2
|
||||
SerialPIO KEYWORD2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+23
-1
@@ -55,6 +55,28 @@ SECTIONS
|
||||
ASSERT(__boot2_end__ - __boot2_start__ == 256,
|
||||
"ERROR: Pico second stage bootloader must be 256 bytes in size")
|
||||
|
||||
.ota : {
|
||||
/* Start image with OTA */
|
||||
KEEP (*(.OTA))
|
||||
/* Align to the last 16-bytes of the OTA region */
|
||||
/* If anyone has a better way of doing this, please submit a PR! */
|
||||
/* . = __flash_binary_start + 0x2ff0;
|
||||
LONG(__FS_START__)
|
||||
LONG(__FS_END__)
|
||||
LONG(__EEPROM_START__)
|
||||
LONG(__FLASH_LENGTH__)*/
|
||||
} > FLASH
|
||||
|
||||
.partition : {
|
||||
/* Align to the last 16-bytes of the OTA region */
|
||||
/* If anyone has a better way of doing this, please submit a PR! */
|
||||
. = __flash_binary_start + 0x2ff0;
|
||||
LONG(__FS_START__)
|
||||
LONG(__FS_END__)
|
||||
LONG(__EEPROM_START__)
|
||||
LONG(__FLASH_LENGTH__)
|
||||
} > FLASH
|
||||
|
||||
/* The second stage will always enter the image at the start of .text.
|
||||
The debugger will use the ELF entry point, which is the _entry_point
|
||||
symbol if present, otherwise defaults to start of .text.
|
||||
@@ -92,7 +114,7 @@ SECTIONS
|
||||
} > FLASH
|
||||
|
||||
.rodata : {
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata* .big_const*)
|
||||
. = ALIGN(4);
|
||||
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
|
||||
. = ALIGN(4);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-iwithprefixbefore/cores/rp2040/api/deprecated-avr-comp/
|
||||
-iwithprefixbefore/lib/pico_base/
|
||||
-iwithprefixbefore/include/pico_base/
|
||||
-iwithprefixbefore/pico-sdk/lib/tinyusb/src/
|
||||
-iwithprefixbefore/pico-sdk/src/boards/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_base/include
|
||||
@@ -40,9 +40,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_watchdog/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_xosc/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_bootrom/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_arch/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_double/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_lwip/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_multicore/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_printf/include
|
||||
@@ -50,3 +52,5 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_uart/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_unique_id/include
|
||||
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
|
||||
-iwithprefixbefore/pico-sdk/lib/lwip/src/include
|
||||
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: 7264c1492a...5be0b21c0a
@@ -0,0 +1,77 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
const char* host = "OTA-LEDS";
|
||||
|
||||
int led_pin = 13;
|
||||
#define N_DIMMERS 3
|
||||
int dimmer_pin[] = { 14, 5, 15 };
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/* switch on led */
|
||||
pinMode(led_pin, OUTPUT);
|
||||
digitalWrite(led_pin, LOW);
|
||||
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("Retrying connection...");
|
||||
}
|
||||
/* switch off led */
|
||||
digitalWrite(led_pin, HIGH);
|
||||
|
||||
/* configure dimmers, and OTA server events */
|
||||
analogWriteRange(1000);
|
||||
analogWrite(led_pin, 990);
|
||||
|
||||
for (int i = 0; i < N_DIMMERS; i++) {
|
||||
pinMode(dimmer_pin[i], OUTPUT);
|
||||
analogWrite(dimmer_pin[i], 50);
|
||||
}
|
||||
|
||||
ArduinoOTA.setHostname(host);
|
||||
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
|
||||
for (int i = 0; i < N_DIMMERS; i++) {
|
||||
analogWrite(dimmer_pin[i], 0);
|
||||
}
|
||||
analogWrite(led_pin, 0);
|
||||
});
|
||||
|
||||
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
|
||||
for (int i = 0; i < 30; i++) {
|
||||
analogWrite(led_pin, (i * 100) % 1001);
|
||||
delay(50);
|
||||
}
|
||||
});
|
||||
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
(void)error;
|
||||
rp2040.restart();
|
||||
});
|
||||
|
||||
/* setup the OTA server */
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// To be used with SignedOTA. The IDE will sign the binary
|
||||
// automatically and upload over WiFi
|
||||
|
||||
// Released to the public domain, Earle Philhower, 2022
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
srand(123);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int del = rand() % 100;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(del * 10);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(del * 10);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAu1Pt7yEk/xI+6cozLj5Bu4xV8gXDXcHS0rSJFfl4wBTk4UXp
|
||||
aJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibONx0VVoWmeqN2HBc3zkA1eqCksI0Q
|
||||
Uudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLoSjVTbsJmGuwx8RGMBXozpg/uL0hH
|
||||
flihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w3J2nNjtuPuVN5vcQkd8ncMexVfy9
|
||||
AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1WIo75bZHKZNFw/iXe2xoPpm74qri
|
||||
MNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2zQIDAQABAoIBAE5GpuDKb8Qp4qIc
|
||||
fMBxAVSWMn+cSuONj0O+bp4BDaTt1ioP5ZVukDQtt0ehLOEePFgf9LEc+1a6Ozy3
|
||||
EaJTTs4W2Ai8djE+xqa8SPRlPjOMluSzPUP3NRHuTpTXd3YiXksrZjP1U02+/Cos
|
||||
8ZIROtFvcPqSPso3MjMyitjrFFPqEtf1P+UiamjDrMSM72YX4W55kOkiCWCnAOmw
|
||||
mGTlXOIqDSTBb1lloKWJfpB3RdnNo2izkU1HMBn7hVi433NUBA22o+RZhDFSZdD4
|
||||
3kbkUqXd4p+vc/sh6muJtWS/COSIPFkLzdEYpBdt3XQ4FhlsRtILJaPWXa4OPjR6
|
||||
ZoOwMB0CgYEA6OHfIofQiu4+HlTDN5YdyTmtYEYcrtbaQUxuQSEa2mshBphHP8uT
|
||||
mYRVl2BzuprFmXZPz+FcjnPnfxqEehljvA3wMjA/PE+nQo9yyOC0N4ulXpkkqHdR
|
||||
f+4KZVR7D+hesGe+57OQmvTqYZSHEt/ubjC9wZ90UFonLjsa4zibbrsCgYEAzexn
|
||||
XDnThb3ffyBgvprP0IJjgMAEY0pXD++PKPQqPu9JMz68t7roYzkKFCFVOsaWpKxC
|
||||
vX9mvYjTBjLpWh+ltIAN+EFz6seIbeSJ0RNybsAXYwT/mFWGHx2tMtlW6DgBu3UD
|
||||
J2Yf76n0JaddBkfNMQI00Dl41+MU+AwwTB9fTBcCgYB2+f6Pm6d1cyYVROS/X1g0
|
||||
V9011FwPDwFOXwftCka31Ad5YQ71jsIHqk44GjTF3xCYyJMZ917cAGcCzr9jydjk
|
||||
WJKgcXm9DEy9ep//9Jzdy+BepgrObrcajriM8E424FaP9VDY+yojoICl/cXMZM9h
|
||||
SFGJvDcmXgiqW9PuxhrSxQKBgAMN2oqXoPd+1W3BQS4ShbqF9IvYTThbxebKmsj0
|
||||
thuw2NkVuR7Qetnd4rRhui3g/CL9GxBMb22oNdkFsEhR59dBfvOLpPh6dR+MIC8l
|
||||
prDV0IL7c/8CZbbYbdUvPAa9rejl12IiNZ8MWj6kuNB7CCQN8FKWR6CMEaeMJrs6
|
||||
S+OJAoGAbehNOUwEzmUKkfxf+279kBkgabcQ3NTaeSx0QOnI9KWHFGLYLQk9cMSu
|
||||
maQJ1TYpbIoP1njzJ4bI2tynhwEuSMEhh4afP6U5H10NJX4PqSd0Rqc1vSJYcszr
|
||||
5mUWil8FfbCBZ8jod2NQ55KYMVY5CphCqaK/s2bw2pvIR3uqJGg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Pt7yEk/xI+6cozLj5B
|
||||
u4xV8gXDXcHS0rSJFfl4wBTk4UXpaJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibO
|
||||
Nx0VVoWmeqN2HBc3zkA1eqCksI0QUudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLo
|
||||
SjVTbsJmGuwx8RGMBXozpg/uL0hHflihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w
|
||||
3J2nNjtuPuVN5vcQkd8ncMexVfy9AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1
|
||||
WIo75bZHKZNFw/iXe2xoPpm74qriMNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2
|
||||
zQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,91 @@
|
||||
// Simple Signed OTA example
|
||||
// Released to the public domain by Earle Philhower, Aug 2022
|
||||
//
|
||||
// Note that the actual code of this is the same as the BasicOTA. No user
|
||||
// code changes are needed, only the presence of public.key and private.key
|
||||
// in the sketch directory. The core will automatically sign any binaries
|
||||
// and include the necessary code to verify signatures. For more info
|
||||
// check the documentation
|
||||
//
|
||||
// After uploading this sketch, try uploading the SignedOTA-Blink sketch
|
||||
// All unsigned binaries, or binaries signed with a different private
|
||||
// key will fail to upload.
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAu1Pt7yEk/xI+6cozLj5Bu4xV8gXDXcHS0rSJFfl4wBTk4UXp
|
||||
aJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibONx0VVoWmeqN2HBc3zkA1eqCksI0Q
|
||||
Uudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLoSjVTbsJmGuwx8RGMBXozpg/uL0hH
|
||||
flihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w3J2nNjtuPuVN5vcQkd8ncMexVfy9
|
||||
AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1WIo75bZHKZNFw/iXe2xoPpm74qri
|
||||
MNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2zQIDAQABAoIBAE5GpuDKb8Qp4qIc
|
||||
fMBxAVSWMn+cSuONj0O+bp4BDaTt1ioP5ZVukDQtt0ehLOEePFgf9LEc+1a6Ozy3
|
||||
EaJTTs4W2Ai8djE+xqa8SPRlPjOMluSzPUP3NRHuTpTXd3YiXksrZjP1U02+/Cos
|
||||
8ZIROtFvcPqSPso3MjMyitjrFFPqEtf1P+UiamjDrMSM72YX4W55kOkiCWCnAOmw
|
||||
mGTlXOIqDSTBb1lloKWJfpB3RdnNo2izkU1HMBn7hVi433NUBA22o+RZhDFSZdD4
|
||||
3kbkUqXd4p+vc/sh6muJtWS/COSIPFkLzdEYpBdt3XQ4FhlsRtILJaPWXa4OPjR6
|
||||
ZoOwMB0CgYEA6OHfIofQiu4+HlTDN5YdyTmtYEYcrtbaQUxuQSEa2mshBphHP8uT
|
||||
mYRVl2BzuprFmXZPz+FcjnPnfxqEehljvA3wMjA/PE+nQo9yyOC0N4ulXpkkqHdR
|
||||
f+4KZVR7D+hesGe+57OQmvTqYZSHEt/ubjC9wZ90UFonLjsa4zibbrsCgYEAzexn
|
||||
XDnThb3ffyBgvprP0IJjgMAEY0pXD++PKPQqPu9JMz68t7roYzkKFCFVOsaWpKxC
|
||||
vX9mvYjTBjLpWh+ltIAN+EFz6seIbeSJ0RNybsAXYwT/mFWGHx2tMtlW6DgBu3UD
|
||||
J2Yf76n0JaddBkfNMQI00Dl41+MU+AwwTB9fTBcCgYB2+f6Pm6d1cyYVROS/X1g0
|
||||
V9011FwPDwFOXwftCka31Ad5YQ71jsIHqk44GjTF3xCYyJMZ917cAGcCzr9jydjk
|
||||
WJKgcXm9DEy9ep//9Jzdy+BepgrObrcajriM8E424FaP9VDY+yojoICl/cXMZM9h
|
||||
SFGJvDcmXgiqW9PuxhrSxQKBgAMN2oqXoPd+1W3BQS4ShbqF9IvYTThbxebKmsj0
|
||||
thuw2NkVuR7Qetnd4rRhui3g/CL9GxBMb22oNdkFsEhR59dBfvOLpPh6dR+MIC8l
|
||||
prDV0IL7c/8CZbbYbdUvPAa9rejl12IiNZ8MWj6kuNB7CCQN8FKWR6CMEaeMJrs6
|
||||
S+OJAoGAbehNOUwEzmUKkfxf+279kBkgabcQ3NTaeSx0QOnI9KWHFGLYLQk9cMSu
|
||||
maQJ1TYpbIoP1njzJ4bI2tynhwEuSMEhh4afP6U5H10NJX4PqSd0Rqc1vSJYcszr
|
||||
5mUWil8FfbCBZ8jod2NQ55KYMVY5CphCqaK/s2bw2pvIR3uqJGg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Pt7yEk/xI+6cozLj5B
|
||||
u4xV8gXDXcHS0rSJFfl4wBTk4UXpaJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibO
|
||||
Nx0VVoWmeqN2HBc3zkA1eqCksI0QUudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLo
|
||||
SjVTbsJmGuwx8RGMBXozpg/uL0hHflihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w
|
||||
3J2nNjtuPuVN5vcQkd8ncMexVfy9AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1
|
||||
WIo75bZHKZNFw/iXe2xoPpm74qriMNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2
|
||||
zQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ArduinoOTA KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setup KEYWORD2
|
||||
handle KEYWORD2
|
||||
onStart KEYWORD2
|
||||
onEnd KEYWORD2
|
||||
onError KEYWORD2
|
||||
onProgress KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name=ArduinoOTA
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov and Miguel Angel Ajo
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download.
|
||||
paragraph=With this library you can enable your sketch to be upgraded over network. Includes mdns announces to get discovered by the arduino IDE.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,402 @@
|
||||
#include <functional>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ArduinoOTA.h"
|
||||
#include "MD5Builder.h"
|
||||
#include <PicoOTA.h>
|
||||
#include <StreamString.h>
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "include/UdpContext.h"
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
#include <LEAmDNS.h>
|
||||
#endif
|
||||
|
||||
//#ifdef DEBUG_ESP_OTA
|
||||
//#ifdef DEBUG_ESP_PORT
|
||||
//#define OTA_DEBUG DEBUG_ESP_PORT
|
||||
//#endif
|
||||
//#endif
|
||||
#define OTA_DEBUG Serial
|
||||
|
||||
ArduinoOTAClass::ArduinoOTAClass() {
|
||||
}
|
||||
|
||||
ArduinoOTAClass::~ArduinoOTAClass() {
|
||||
if (_udp_ota) {
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onStart(THandlerFunction fn) {
|
||||
_start_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onEnd(THandlerFunction fn) {
|
||||
_end_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) {
|
||||
_progress_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::onError(THandlerFunction_Error fn) {
|
||||
_error_callback = fn;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPort(uint16_t port) {
|
||||
if (!_initialized && !_port && port) {
|
||||
_port = port;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setHostname(const char * hostname) {
|
||||
if (!_initialized && !_hostname.length() && hostname) {
|
||||
_hostname = hostname;
|
||||
}
|
||||
}
|
||||
|
||||
String ArduinoOTAClass::getHostname() {
|
||||
return _hostname;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPassword(const char * password) {
|
||||
if (!_initialized && !_password.length() && password) {
|
||||
MD5Builder passmd5;
|
||||
passmd5.begin();
|
||||
passmd5.add(password);
|
||||
passmd5.calculate();
|
||||
_password = passmd5.toString();
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setPasswordHash(const char * password) {
|
||||
if (!_initialized && !_password.length() && password) {
|
||||
_password = password;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setRebootOnSuccess(bool reboot) {
|
||||
_rebootOnSuccess = reboot;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
if (_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
_useMDNS = useMDNS;
|
||||
|
||||
if (!_hostname.length()) {
|
||||
char tmp[15];
|
||||
sprintf(tmp, "pico-%s", rp2040.getChipID());
|
||||
_hostname = tmp;
|
||||
}
|
||||
if (!_port) {
|
||||
_port = 2040;
|
||||
}
|
||||
|
||||
if (_udp_ota) {
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
}
|
||||
|
||||
_udp_ota = new UdpContext;
|
||||
_udp_ota->ref();
|
||||
|
||||
if (!_udp_ota->listen(IP_ADDR_ANY, _port)) {
|
||||
return;
|
||||
}
|
||||
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.begin(_hostname.c_str());
|
||||
|
||||
if (_password.length()) {
|
||||
MDNS.enableArduino(_port, true);
|
||||
} else {
|
||||
MDNS.enableArduino(_port);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_initialized = true;
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ArduinoOTAClass::parseInt() {
|
||||
char data[16];
|
||||
uint8_t index;
|
||||
char value;
|
||||
while (_udp_ota->peek() == ' ') {
|
||||
_udp_ota->read();
|
||||
}
|
||||
for (index = 0; index < sizeof(data); ++index) {
|
||||
value = _udp_ota->peek();
|
||||
if (value < '0' || value > '9') {
|
||||
data[index] = '\0';
|
||||
return atoi(data);
|
||||
}
|
||||
data[index] = _udp_ota->read();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ArduinoOTAClass::readStringUntil(char end) {
|
||||
String res;
|
||||
int value;
|
||||
while (true) {
|
||||
value = _udp_ota->read();
|
||||
if (value < 0 || value == '\0' || value == end) {
|
||||
return res;
|
||||
}
|
||||
res += static_cast<char>(value);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::_onRx() {
|
||||
if (!_udp_ota->next()) {
|
||||
return;
|
||||
}
|
||||
IPAddress ota_ip;
|
||||
|
||||
if (_state == OTA_IDLE) {
|
||||
int cmd = parseInt();
|
||||
if (cmd != U_FLASH && cmd != U_FS) {
|
||||
return;
|
||||
}
|
||||
_ota_ip = _udp_ota->getRemoteAddress();
|
||||
_cmd = cmd;
|
||||
_ota_port = parseInt();
|
||||
_ota_udp_port = _udp_ota->getRemotePort();
|
||||
_size = parseInt();
|
||||
_udp_ota->read();
|
||||
_md5 = readStringUntil('\n');
|
||||
_md5.trim();
|
||||
if (_md5.length() != 32) {
|
||||
return;
|
||||
}
|
||||
|
||||
ota_ip = _ota_ip;
|
||||
|
||||
if (_password.length()) {
|
||||
MD5Builder nonce_md5;
|
||||
nonce_md5.begin();
|
||||
nonce_md5.add(String(micros()));
|
||||
nonce_md5.calculate();
|
||||
_nonce = nonce_md5.toString();
|
||||
|
||||
char auth_req[38];
|
||||
sprintf(auth_req, "AUTH %s", _nonce.c_str());
|
||||
_udp_ota->append((const char *)auth_req, strlen(auth_req));
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
_state = OTA_WAITAUTH;
|
||||
return;
|
||||
} else {
|
||||
_state = OTA_RUNUPDATE;
|
||||
}
|
||||
} else if (_state == OTA_WAITAUTH) {
|
||||
int cmd = parseInt();
|
||||
if (cmd != U_AUTH) {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
_udp_ota->read();
|
||||
String cnonce = readStringUntil(' ');
|
||||
String response = readStringUntil('\n');
|
||||
if (cnonce.length() != 32 || response.length() != 32) {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
String challenge = _password + ':' + String(_nonce) + ':' + cnonce;
|
||||
MD5Builder _challengemd5;
|
||||
_challengemd5.begin();
|
||||
_challengemd5.add(challenge);
|
||||
_challengemd5.calculate();
|
||||
String result = _challengemd5.toString();
|
||||
|
||||
ota_ip = _ota_ip;
|
||||
// if(result.equalsConstantTime(response)) {
|
||||
if (result.equals(response)) {
|
||||
_state = OTA_RUNUPDATE;
|
||||
} else {
|
||||
_udp_ota->append("Authentication Failed", 21);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_AUTH_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
while (_udp_ota->next()) {
|
||||
_udp_ota->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::_runUpdate() {
|
||||
IPAddress ota_ip = _ota_ip;
|
||||
|
||||
if (!LittleFS.begin()) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("LittleFS Begin Error");
|
||||
#endif
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append("No Filesystem", 13);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Update.begin(_size, _cmd)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("Update Begin Error");
|
||||
#endif
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_BEGIN_ERROR);
|
||||
}
|
||||
|
||||
StreamString ss;
|
||||
Update.printError(ss);
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append(ss.c_str(), ss.length());
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
_udp_ota->append("OK", 2);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
|
||||
Update.setMD5(_md5.c_str());
|
||||
|
||||
if (_start_callback) {
|
||||
_start_callback();
|
||||
}
|
||||
if (_progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
|
||||
WiFiClient client;
|
||||
if (!client.connect(_ota_ip, _ota_port)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Connect Failed\n");
|
||||
#endif
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_CONNECT_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
// OTA sends little packets
|
||||
client.setNoDelay(true);
|
||||
|
||||
uint32_t written, total = 0;
|
||||
while (!Update.isFinished() && (client.connected() || client.available())) {
|
||||
int waited = 1000;
|
||||
while (!client.available() && waited--) {
|
||||
delay(1);
|
||||
}
|
||||
if (!waited) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Receive Failed\n");
|
||||
#endif
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_RECEIVE_ERROR);
|
||||
}
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
written = Update.write(client);
|
||||
if (written > 0) {
|
||||
client.print(written, DEC);
|
||||
total += written;
|
||||
if (_progress_callback) {
|
||||
_progress_callback(total, _size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Update.end()) {
|
||||
// Ensure last count packet has been sent out and not combined with the final OK
|
||||
client.flush();
|
||||
delay(1000);
|
||||
client.print("OK");
|
||||
client.flush();
|
||||
delay(1000);
|
||||
client.stop();
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Update Success\n");
|
||||
#endif
|
||||
if (_end_callback) {
|
||||
_end_callback();
|
||||
}
|
||||
if (_rebootOnSuccess) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Rebooting...\n");
|
||||
#endif
|
||||
LittleFS.end();
|
||||
//let serial/network finish tasks that might be given in _end_callback
|
||||
delay(100);
|
||||
rp2040.reboot();
|
||||
}
|
||||
} else {
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_END_ERROR);
|
||||
}
|
||||
Update.printError(client);
|
||||
#ifdef OTA_DEBUG
|
||||
Update.printError(OTA_DEBUG);
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::end() {
|
||||
_initialized = false;
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.end();
|
||||
}
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server stopped.\n");
|
||||
#endif
|
||||
}
|
||||
//this needs to be called in the loop()
|
||||
void ArduinoOTAClass::handle() {
|
||||
if (_state == OTA_RUNUPDATE) {
|
||||
_runUpdate();
|
||||
_state = OTA_IDLE;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if (_useMDNS) {
|
||||
MDNS.update(); //handle MDNS update as well, given that ArduinoOTA relies on it anyways
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ArduinoOTAClass::getCommand() {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA)
|
||||
ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <functional>
|
||||
#include <LittleFS.h>
|
||||
#include <Updater.h>
|
||||
|
||||
class UdpContext;
|
||||
|
||||
typedef enum {
|
||||
OTA_IDLE,
|
||||
OTA_WAITAUTH,
|
||||
OTA_RUNUPDATE
|
||||
} ota_state_t;
|
||||
|
||||
typedef enum {
|
||||
OTA_AUTH_ERROR,
|
||||
OTA_BEGIN_ERROR,
|
||||
OTA_CONNECT_ERROR,
|
||||
OTA_RECEIVE_ERROR,
|
||||
OTA_END_ERROR
|
||||
} ota_error_t;
|
||||
|
||||
|
||||
class ArduinoOTAClass {
|
||||
public:
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<void(ota_error_t)> THandlerFunction_Error;
|
||||
typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress;
|
||||
|
||||
ArduinoOTAClass();
|
||||
~ArduinoOTAClass();
|
||||
|
||||
//Sets the service port. Default 2040
|
||||
void setPort(uint16_t port);
|
||||
|
||||
//Sets the device hostname. Default pico-xxxxxx
|
||||
void setHostname(const char *hostname);
|
||||
String getHostname();
|
||||
|
||||
//Sets the password that will be required for OTA. Default NULL
|
||||
void setPassword(const char *password);
|
||||
|
||||
//Sets the password as above but in the form MD5(password). Default NULL
|
||||
void setPasswordHash(const char *password);
|
||||
|
||||
//Sets if the device should be rebooted after successful update. Default true
|
||||
void setRebootOnSuccess(bool reboot);
|
||||
|
||||
//This callback will be called when OTA connection has begun
|
||||
void onStart(THandlerFunction fn);
|
||||
|
||||
//This callback will be called when OTA has finished
|
||||
void onEnd(THandlerFunction fn);
|
||||
|
||||
//This callback will be called when OTA encountered Error
|
||||
void onError(THandlerFunction_Error fn);
|
||||
|
||||
//This callback will be called when OTA is receiving data
|
||||
void onProgress(THandlerFunction_Progress fn);
|
||||
|
||||
//Starts the ArduinoOTA service
|
||||
void begin(bool useMDNS = true);
|
||||
|
||||
//Ends the ArduinoOTA service
|
||||
void end();
|
||||
//Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
|
||||
void handle();
|
||||
|
||||
//Gets update command type after OTA has started. Either U_FLASH or U_FS
|
||||
int getCommand();
|
||||
|
||||
private:
|
||||
void _runUpdate(void);
|
||||
void _onRx(void);
|
||||
int parseInt(void);
|
||||
String readStringUntil(char end);
|
||||
|
||||
int _port = 0;
|
||||
String _password;
|
||||
String _hostname;
|
||||
String _nonce;
|
||||
UdpContext *_udp_ota = nullptr;
|
||||
bool _initialized = false;
|
||||
bool _rebootOnSuccess = true;
|
||||
bool _useMDNS = true;
|
||||
ota_state_t _state = OTA_IDLE;
|
||||
int _size = 0;
|
||||
int _cmd = 0;
|
||||
uint16_t _ota_port = 0;
|
||||
uint16_t _ota_udp_port = 0;
|
||||
IPAddress _ota_ip;
|
||||
String _md5;
|
||||
|
||||
THandlerFunction _start_callback = nullptr;
|
||||
THandlerFunction _end_callback = nullptr;
|
||||
THandlerFunction_Error _error_callback = nullptr;
|
||||
THandlerFunction_Progress _progress_callback = nullptr;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA)
|
||||
extern ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
String responseHTML = ""
|
||||
"<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Hello World!</h1><p>This is a captive portal example."
|
||||
" All requests will be redirected here.</p></body></html>";
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("DNSServer CaptivePortal example");
|
||||
|
||||
// if DNSServer is started with "*" for domain name, it will reply with
|
||||
// provided IP to all DNS request
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
// replay to all requests with same HTML
|
||||
webServer.onNotFound([]() {
|
||||
webServer.send(200, "text/html", responseHTML);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
/*
|
||||
This example serves a "hello world" on a WLAN and a SoftAP at the same time.
|
||||
The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.
|
||||
|
||||
Connect your computer or cell phone to wifi network pico_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
|
||||
Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from pico_ap and return to your regular WLAN.
|
||||
|
||||
Now the Pico is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://picow.local too.
|
||||
|
||||
This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
|
||||
*/
|
||||
|
||||
/* Set these to your desired softAP credentials. They are not configurable at runtime */
|
||||
#ifndef APSSID
|
||||
#define APSSID "pico_ap"
|
||||
#define APPSK "12345678"
|
||||
#endif
|
||||
|
||||
const char *softAP_ssid = APSSID;
|
||||
const char *softAP_password = APPSK;
|
||||
|
||||
/* hostname for mDNS. Should work at least on windows. Try http://picow.local */
|
||||
const char *myHostname = "picow";
|
||||
|
||||
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
|
||||
char ssid[33] = "";
|
||||
char password[65] = "";
|
||||
|
||||
// DNS server
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
|
||||
// Web server
|
||||
WebServer server(80);
|
||||
|
||||
/* Soft AP network parameters */
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
IPAddress netMsk(255, 255, 255, 0);
|
||||
|
||||
|
||||
/** Should I connect to WLAN asap? */
|
||||
boolean connect;
|
||||
|
||||
/** Last time I tried to connect to WLAN */
|
||||
unsigned long lastConnectTry = 0;
|
||||
|
||||
/** Current WLAN status */
|
||||
unsigned int status = WL_IDLE_STATUS;
|
||||
|
||||
void setup() {
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
/* You can remove the password parameter if you want the AP to be open. */
|
||||
WiFi.softAPConfig(apIP, apIP, netMsk);
|
||||
WiFi.softAP(softAP_ssid, softAP_password);
|
||||
delay(500); // Without delay I've seen the IP address blank
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
/* Setup the DNS server redirecting all the domains to the apIP */
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
|
||||
server.on("/", handleRoot);
|
||||
server.on("/wifi", handleWifi);
|
||||
server.on("/wifisave", handleWifiSave);
|
||||
server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin(); // Web server start
|
||||
Serial.println("HTTP server started");
|
||||
//loadCredentials(); // Load WLAN credentials from network
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
Serial.println("Connecting as wifi client...");
|
||||
WiFi.disconnect();
|
||||
WiFi.end();
|
||||
WiFi.begin(ssid, password);
|
||||
int connRes = WiFi.waitForConnectResult();
|
||||
Serial.print("connRes: ");
|
||||
Serial.println(connRes);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (connect) {
|
||||
Serial.println("Connect requested");
|
||||
connect = false;
|
||||
connectWifi();
|
||||
lastConnectTry = millis();
|
||||
}
|
||||
{
|
||||
unsigned int s = WiFi.status();
|
||||
if (s == 0 && millis() > (lastConnectTry + 60000)) {
|
||||
/* If WLAN disconnected and idle try to connect */
|
||||
/* Don't set retry time too low as retry interfere the softAP operation */
|
||||
connect = true;
|
||||
}
|
||||
if (status != s) { // WLAN status change
|
||||
Serial.print("Status: ");
|
||||
Serial.println(s);
|
||||
status = s;
|
||||
if (s == WL_CONNECTED) {
|
||||
/* Just connected to WLAN */
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Setup MDNS responder
|
||||
if (!MDNS.begin(myHostname)) {
|
||||
Serial.println("Error setting up MDNS responder!");
|
||||
} else {
|
||||
Serial.println("mDNS responder started");
|
||||
// Add service to MDNS-SD
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
} else if (s == WL_NO_SSID_AVAIL) {
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
||||
if (s == WL_CONNECTED) { MDNS.update(); }
|
||||
}
|
||||
// Do work:
|
||||
// DNS
|
||||
dnsServer.processNextRequest();
|
||||
// HTTP
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Load WLAN credentials from EEPROM */
|
||||
void loadCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.get(0, ssid);
|
||||
EEPROM.get(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1];
|
||||
EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.end();
|
||||
if (String(ok) != String("OK")) {
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
}
|
||||
Serial.println("Recovered credentials:");
|
||||
Serial.println(ssid);
|
||||
Serial.println(strlen(password) > 0 ? "********" : "<no password>");
|
||||
}
|
||||
|
||||
/** Store WLAN credentials to EEPROM */
|
||||
void saveCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.put(0, ssid);
|
||||
EEPROM.put(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1] = "OK";
|
||||
EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.commit();
|
||||
EEPROM.end();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/** Handle root or redirect to captive portal */
|
||||
void handleRoot() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||
return;
|
||||
}
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>HELLO WORLD!!</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += F("<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
"</body></html>");
|
||||
|
||||
server.send(200, "text/html", Page);
|
||||
}
|
||||
|
||||
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
|
||||
boolean captivePortal() {
|
||||
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
|
||||
Serial.println("Request redirected to captive portal");
|
||||
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Wifi config page handler */
|
||||
void handleWifi() {
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Wifi config</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += String(F("\r\n<br />"
|
||||
"<table><tr><th align='left'>SoftAP config</th></tr>"
|
||||
"<tr><td>SSID "))
|
||||
+ String(softAP_ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.softAPIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN config</th></tr>"
|
||||
"<tr><td>SSID ")
|
||||
+ String(ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.localIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
|
||||
Serial.println("scan start");
|
||||
int n = WiFi.scanNetworks();
|
||||
Serial.println("scan done");
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) { Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>"); }
|
||||
} else {
|
||||
Page += F("<tr><td>No WLAN found</td></tr>");
|
||||
}
|
||||
Page += F("</table>"
|
||||
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
|
||||
"<input type='text' placeholder='network' name='n'/>"
|
||||
"<br /><input type='password' placeholder='password' name='p'/>"
|
||||
"<br /><input type='submit' value='Connect/Disconnect'/></form>"
|
||||
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
|
||||
"</body></html>");
|
||||
server.send(200, "text/html", Page);
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
}
|
||||
|
||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
||||
void handleWifiSave() {
|
||||
Serial.println("wifi save");
|
||||
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
|
||||
server.arg("p").toCharArray(password, sizeof(password) - 1);
|
||||
server.sendHeader("Location", "wifi", true);
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
saveCredentials();
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
|
||||
return;
|
||||
}
|
||||
String message = F("File Not Found\n\n");
|
||||
message += F("URI: ");
|
||||
message += server.uri();
|
||||
message += F("\nMethod: ");
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += F("\nArguments: ");
|
||||
message += server.args();
|
||||
message += F("\n");
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); }
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/** Is this an IP? */
|
||||
boolean isIp(String str) {
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c != '.' && (c < '0' || c > '9')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** IP to String? */
|
||||
String toStringIp(IPAddress ip) {
|
||||
String res = "";
|
||||
for (int i = 0; i < 3; i++) { res += String((ip >> (8 * i)) & 0xFF) + "."; }
|
||||
res += String(((ip >> 8 * 3)) & 0xFF);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("picow", "12345678");
|
||||
|
||||
// modify TTL associated with the domain name (in seconds)
|
||||
// default is 60 seconds
|
||||
dnsServer.setTTL(300);
|
||||
// set which return code will be used for all other domains (e.g. sending
|
||||
// ServerFailure instead of NonExistentDomain will reduce number of queries
|
||||
// sent by clients)
|
||||
// default is DNSReplyCode::NonExistentDomain
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
|
||||
|
||||
// start DNS server for a specific domain name
|
||||
dnsServer.start(DNS_PORT, "www.example.com", apIP);
|
||||
|
||||
// simple HTTP server to see that DNS server is working
|
||||
webServer.onNotFound([]() {
|
||||
String message = "Hello World!\n\n";
|
||||
message += "URI: ";
|
||||
message += webServer.uri();
|
||||
|
||||
webServer.send(200, "text/plain", message);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For DNSServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
DNSServer KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
DNSReplyCode KEYWORD1 DATA_TYPE
|
||||
DNSHeader KEYWORD1 DATA_TYPE
|
||||
DNSServer KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
processNextRequest KEYWORD2
|
||||
setErrorReplyCode KEYWORD2
|
||||
setTTL KEYWORD2
|
||||
start KEYWORD2
|
||||
stop KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
DNS_QR_QUERY LITERAL1 RESERVED_WORD_2
|
||||
DNS_QR_RESPONSE LITERAL1 RESERVED_WORD_2
|
||||
DNS_OPCODE_QUERY LITERAL1 RESERVED_WORD_2
|
||||
MAX_DNSNAME_LENGTH LITERAL1 RESERVED_WORD_2
|
||||
NoError LITERAL1 RESERVED_WORD_2
|
||||
FormError LITERAL1 RESERVED_WORD_2
|
||||
ServerFailure LITERAL1 RESERVED_WORD_2
|
||||
NonExistentDomain LITERAL1 RESERVED_WORD_2
|
||||
NotImplemented LITERAL1 RESERVED_WORD_2
|
||||
Refused LITERAL1 RESERVED_WORD_2
|
||||
YXDomain LITERAL1 RESERVED_WORD_2
|
||||
YXRRSet LITERAL1 RESERVED_WORD_2
|
||||
NXRRSet LITERAL1 RESERVED_WORD_2
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user