1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-07 16:23:38 +03:00

Merge branch 'master' into wifi_mesh_update_2.2

This commit is contained in:
aerlon 2020-05-15 20:08:59 +02:00 committed by GitHub
commit 3f5495bb3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
101 changed files with 4963 additions and 1666 deletions

View File

@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip
# Quick links
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/2.6.3/)
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/2.7.1/)
- [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/)
- [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version))
@ -28,7 +28,7 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and
Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit).
- Install the current upstream Arduino IDE at the 1.8.7 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software).
- Install the current upstream Arduino IDE at the 1.8.9 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software).
- Start Arduino and open the Preferences window.
- Enter ```https://arduino.esp8266.com/stable/package_esp8266com_index.json``` into the *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
- Open Boards Manager from Tools > Board menu and install *esp8266* platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation).
@ -36,7 +36,7 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
#### Latest release [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/)
Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json`
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.6.3/](https://arduino-esp8266.readthedocs.io/en/2.6.3/)
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.7.1/](https://arduino-esp8266.readthedocs.io/en/2.7.1/)
### Using git version
[![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino)
@ -73,7 +73,7 @@ Documentation for latest development version: https://arduino-esp8266.readthedoc
### Issues and support ###
[ESP8266 Community Forum](https://www.esp8266.com/u/arduinoanswers) is a well-established community for questions and answers about Arduino for ESP8266. If you need help, have a "How do I..." type question, have a problem with a 3rd party library not hosted in this repo, or just want to discuss how to approach a problem, please ask there.
[ESP8266 Community Forum](https://www.esp8266.com/u/arduinoanswers) is a well-established community for questions and answers about Arduino for ESP8266. Stackoverflow is also an alternative. If you need help, have a "How do I..." type question, have a problem with a 3rd party library not hosted in this repo, or just want to discuss how to approach a problem, please ask there.
If you find the forum useful, please consider supporting it with a donation. <br />
[![Donate](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/webscr?cmd=_s-xclick&hosted_button_id=4M56YCWV6PX66)
@ -96,7 +96,7 @@ For minor fixes of code and documentation, please go ahead and submit a pull req
Check out the list of issues that are easy to fix — [easy issues pending](https://github.com/esp8266/Arduino/issues?q=is%3Aopen+is%3Aissue+label%3A%22level%3A+easy%22). Working on them is a great way to move the project forward.
Larger changes (rewriting parts of existing code from scratch, adding new functions to the core, adding new libraries) should generally be discussed by opening an issue first.
Larger changes (rewriting parts of existing code from scratch, adding new functions to the core, adding new libraries) should generally be discussed by opening an issue first. PRs with such changes require testing and approval.
Feature branches with lots of small commits (especially titled "oops", "fix typo", "forgot to add file", etc.) should be squashed before opening a pull request. At the same time, please refrain from putting multiple unrelated changes into a single pull request.

View File

@ -4960,7 +4960,7 @@ espinotee.menu.baud.3000000.upload.speed=3000000
wifinfo.name=WifInfo
wifinfo.build.board=WIFINFO
wifinfo.build.variant=wifinfo
wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K SPIFFS)
wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K FS)
wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07
wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld
wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M
@ -4968,7 +4968,7 @@ wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096
wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000
wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000
wifinfo.menu.ESPModule.ESP07192.upload.maximum_size=827376
wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS)
wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M FS)
wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12
wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld
wifinfo.menu.ESPModule.ESP12.build.flash_size=4M

View File

@ -12,6 +12,7 @@
#include <string.h>
#include "flash.h"
#include "eboot_command.h"
#include "spi_vendors.h"
#include <uzlib.h>
extern unsigned char _gzip_dict;
@ -189,6 +190,28 @@ int copy_raw(const uint32_t src_addr,
return 0;
}
//#define XMC_SUPPORT
#ifdef XMC_SUPPORT
// Define a few SPI0 registers we need access to
#define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr)))
#define SPI0CMD ESP8266_REG(0x200)
#define SPI0CLK ESP8266_REG(0x218)
#define SPI0C ESP8266_REG(0x208)
#define SPI0W0 ESP8266_REG(0x240)
#define SPICMDRDID (1 << 28)
/* spi_flash_get_id()
Returns the flash chip ID - same as the SDK function.
We need our own version as the SDK isn't available here.
*/
uint32_t __attribute__((noinline)) spi_flash_get_id() {
SPI0W0=0;
SPI0CMD=SPICMDRDID;
while (SPI0CMD) {}
return SPI0W0;
}
#endif // XMC_SUPPORT
int main()
{
@ -211,9 +234,48 @@ int main()
if (cmd.action == ACTION_COPY_RAW) {
ets_putc('c'); ets_putc('p'); ets_putc(':');
#ifdef XMC_SUPPORT
// save the flash access speed registers
uint32_t spi0clk = SPI0CLK;
uint32_t spi0c = SPI0C;
uint32_t vendor = spi_flash_get_id() & 0x000000ff;
if (vendor == SPI_FLASH_VENDOR_XMC) {
uint32_t flashinfo=0;
if (SPIRead(0, &flashinfo, 4)) {
// failed to read the configured flash speed.
// Do not change anything,
} else {
// select an appropriate flash speed
// Register values are those used by ROM
switch ((flashinfo >> 24) & 0x0f) {
case 0x0: // 40MHz, slow to 20
case 0x1: // 26MHz, slow to 20
SPI0CLK = 0x00003043;
SPI0C = 0x00EAA313;
break;
case 0x2: // 20MHz, no change
break;
case 0xf: // 80MHz, slow to 26
SPI0CLK = 0x00002002;
SPI0C = 0x00EAA202;
break;
default:
break;
}
}
}
#endif // XMC_SUPPORT
ets_wdt_disable();
res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
ets_wdt_enable();
#ifdef XMC_SUPPORT
// restore the saved flash access speed registers
SPI0CLK = spi0clk;
SPI0C = spi0c;
#endif
ets_putc('0'+res); ets_putc('\n');
if (res == 0) {
cmd.action = ACTION_LOAD_APP;

Binary file not shown.

View File

@ -0,0 +1,63 @@
/*
spi_vendors.h - Vendor IDs for SPI chips
Copyright (c) 2019 Mike Nix. 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 __SPI_VENDORS_H__
#define __SPI_VENDORS_H__
// Vendor IDs taken from Flashrom project
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
// Moved here from ../../cores/esp8266/Esp.h
typedef enum {
SPI_FLASH_VENDOR_ALLIANCE = 0x52, /* Alliance Semiconductor */
SPI_FLASH_VENDOR_AMD = 0x01, /* AMD */
SPI_FLASH_VENDOR_AMIC = 0x37, /* AMIC */
SPI_FLASH_VENDOR_ATMEL = 0x1F, /* Atmel (now used by Adesto) */
SPI_FLASH_VENDOR_BRIGHT = 0xAD, /* Bright Microelectronics */
SPI_FLASH_VENDOR_CATALYST = 0x31, /* Catalyst */
SPI_FLASH_VENDOR_EON = 0x1C, /* EON Silicon Devices, missing 0x7F prefix */
SPI_FLASH_VENDOR_ESMT = 0x8C, /* Elite Semiconductor Memory Technology (ESMT) / EFST Elite Flash Storage */
SPI_FLASH_VENDOR_EXCEL = 0x4A, /* ESI, missing 0x7F prefix */
SPI_FLASH_VENDOR_FIDELIX = 0xF8, /* Fidelix */
SPI_FLASH_VENDOR_FUJITSU = 0x04, /* Fujitsu */
SPI_FLASH_VENDOR_GIGADEVICE = 0xC8, /* GigaDevice */
SPI_FLASH_VENDOR_HYUNDAI = 0xAD, /* Hyundai */
SPI_FLASH_VENDOR_INTEL = 0x89, /* Intel */
SPI_FLASH_VENDOR_ISSI = 0xD5, /* ISSI Integrated Silicon Solutions, see also PMC. */
SPI_FLASH_VENDOR_MACRONIX = 0xC2, /* Macronix (MX) */
SPI_FLASH_VENDOR_NANTRONICS = 0xD5, /* Nantronics, missing prefix */
SPI_FLASH_VENDOR_PMC = 0x9D, /* PMC, missing 0x7F prefix */
SPI_FLASH_VENDOR_PUYA = 0x85, /* Puya semiconductor (shanghai) co. ltd */
SPI_FLASH_VENDOR_SANYO = 0x62, /* Sanyo */
SPI_FLASH_VENDOR_SHARP = 0xB0, /* Sharp */
SPI_FLASH_VENDOR_SPANSION = 0x01, /* Spansion, same ID as AMD */
SPI_FLASH_VENDOR_SST = 0xBF, /* SST */
SPI_FLASH_VENDOR_ST = 0x20, /* ST / SGS/Thomson / Numonyx (later acquired by Micron) */
SPI_FLASH_VENDOR_SYNCMOS_MVC = 0x40, /* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */
SPI_FLASH_VENDOR_TENX = 0x5E, /* Tenx Technologies */
SPI_FLASH_VENDOR_TI = 0x97, /* Texas Instruments */
SPI_FLASH_VENDOR_TI_OLD = 0x01, /* TI chips from last century */
SPI_FLASH_VENDOR_WINBOND = 0xDA, /* Winbond */
SPI_FLASH_VENDOR_WINBOND_NEX = 0xEF, /* Winbond (ex Nexcom) serial flashes */
SPI_FLASH_VENDOR_XMC = 0x20, /* Wuhan Xinxin Semiconductor Manufacturing Corp */
SPI_FLASH_VENDOR_UNKNOWN = 0xFF
} SPI_FLASH_VENDOR_t;
#endif // __SPI_VENDORS_H__

View File

@ -276,6 +276,8 @@ long secureRandom(long);
long secureRandom(long, long);
long map(long, long, long, long, long);
void setTZ(const char* tz);
void configTime(int timezone, int daylightOffset_sec, const char* server1,
const char* server2 = nullptr, const char* server3 = nullptr);

552
cores/esp8266/Crypto.cpp Normal file
View File

@ -0,0 +1,552 @@
/*
BearSSL Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
Rest of this file Copyright (C) 2019 Anders Löfgren
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <bearssl/bearssl.h>
#include "Crypto.h"
#include <TypeConversion.h>
#include <assert.h>
namespace TypeCast = experimental::TypeConversion;
namespace
{
size_t _ctMinDataLength = 0;
size_t _ctMaxDataLength = 1024;
uint8_t *defaultNonceGenerator(uint8_t *nonceArray, const size_t nonceLength)
{
/**
The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266):
"When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number.
These true random numbers are generated based on the noise in the Wi-Fi/BT RF system.
When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers.
When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz).
Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz.
A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz,
has been tested using the Dieharder Random Number Testsuite (version 3.31.1).
The sample passed all tests."
Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency.
A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266.
It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers.
It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available.
However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during nonce generation.
Thus only delayMicroseconds() is used below.
*/
return ESP.random(nonceArray, nonceLength);
}
experimental::crypto::nonceGeneratorType _nonceGenerator = defaultNonceGenerator;
void *createBearsslHmac(const br_hash_class *hashType, const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__hmac_8h.html
// 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.
br_hmac_key_context keyContext; // Holds general HMAC info
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current operation
// HMAC key context initialisation.
// Initialise the key context with the provided hash key, using the hash function identified by hashType. This supports arbitrary key lengths.
br_hmac_key_init(&keyContext, hashType, hashKey, hashKeyLength);
// 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 outputLength is 0, then the natural HMAC output length is selected. The "natural output length" is the output length of the underlying hash function.
br_hmac_init(&hmacContext, &keyContext, outputLength);
// Provide the HMAC context with the data to create a HMAC from.
// The provided dataLength bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
// It is acceptable that dataLength is zero, in which case data is ignored (and may be NULL) and this function does nothing.
br_hmac_update(&hmacContext, data, dataLength);
// 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.
// Optionally the constant-time version br_hmac_outCT() can be used. More info here: https://www.bearssl.org/constanttime.html .
br_hmac_out(&hmacContext, resultArray); // returns size_t outputLength
return resultArray;
}
String createBearsslHmac(const br_hash_class *hashType, const uint8_t hashTypeNaturalLength, const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
uint8_t hmac[hmacLength];
createBearsslHmac(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
return TypeCast::uint8ArrayToHexString(hmac, hmacLength);
}
void *createBearsslHmacCT(const br_hash_class *hashType, const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
assert(_ctMinDataLength <= dataLength && dataLength <= _ctMaxDataLength);
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__hmac_8h.html
// 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.
br_hmac_key_context keyContext; // Holds general HMAC info
br_hmac_context hmacContext; // Holds general HMAC info + specific info for the current operation
// HMAC key context initialisation.
// Initialise the key context with the provided hash key, using the hash function identified by hashType. This supports arbitrary key lengths.
br_hmac_key_init(&keyContext, hashType, hashKey, hashKeyLength);
// 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 outputLength is 0, then the natural HMAC output length is selected. The "natural output length" is the output length of the underlying hash function.
br_hmac_init(&hmacContext, &keyContext, outputLength);
// Provide the HMAC context with the data to create a HMAC from.
// The provided dataLength bytes are injected as extra input in the HMAC computation incarnated by the hmacContext.
// It is acceptable that dataLength is zero, in which case data is ignored (and may be NULL) and this function does nothing.
// No need for br_hmac_update when using constant-time version it seems. If it is used, the data provided to br_hmac_outCT will just be appended.
// br_hmac_update(&hmacContext, data, dataLength);
// Compute the HMAC output. Assumes message is minimum _ctMinDataLength bytes and maximum _ctMaxDataLength bytes.
// As long as this is true, the correct HMAC output is calculated in constant-time. More constant-time info here: https://www.bearssl.org/constanttime.html
// Some extra input bytes are processed, then the output is computed.
// The extra input consists in the dataLength bytes pointed to by data. The dataLength parameter must lie between _ctMinDataLength and _ctMaxDataLength (inclusive);
// _ctMaxDataLength bytes are actually read from data (indicating each data byte can be read multiple times, if dataLength < _ctMaxDataLength).
// Computing time (and memory access pattern) will not depend upon the data byte contents or the value of dataLength.
// The output is written in the resultArray buffer, that MUST be large enough to receive it.
// The difference _ctMaxDataLength - _ctMinDataLength MUST be less than 2^30 (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.
br_hmac_outCT(&hmacContext, data, dataLength, _ctMinDataLength, _ctMaxDataLength, resultArray); // returns size_t outputLength
return resultArray;
}
String createBearsslHmacCT(const br_hash_class *hashType, const uint8_t hashTypeNaturalLength, const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
assert(1 <= hmacLength && hmacLength <= hashTypeNaturalLength);
uint8_t hmac[hmacLength];
createBearsslHmacCT(hashType, message.c_str(), message.length(), hashKey, hashKeyLength, hmac, hmacLength);
return TypeCast::uint8ArrayToHexString(hmac, hmacLength);
}
// Helper function to avoid deprecated warnings.
void *md5HashHelper(const void *data, const size_t dataLength, void *resultArray)
{
br_md5_context context;
br_md5_init(&context);
br_md5_update(&context, data, dataLength);
br_md5_out(&context, resultArray);
return resultArray;
}
// Helper function to avoid deprecated warnings.
void *sha1HashHelper(const void *data, const size_t dataLength, void *resultArray)
{
br_sha1_context context;
br_sha1_init(&context);
br_sha1_update(&context, data, dataLength);
br_sha1_out(&context, resultArray);
return resultArray;
}
}
namespace experimental
{
namespace crypto
{
void setCtMinDataLength(const size_t ctMinDataLength)
{
assert(getCtMaxDataLength() - ctMinDataLength <= CT_MAX_DIFF);
_ctMinDataLength = ctMinDataLength;
}
size_t getCtMinDataLength()
{
return _ctMinDataLength;
}
void setCtMaxDataLength(const size_t ctMaxDataLength)
{
assert(ctMaxDataLength - getCtMinDataLength() <= CT_MAX_DIFF);
_ctMaxDataLength = ctMaxDataLength;
}
size_t getCtMaxDataLength()
{
return _ctMaxDataLength;
}
void setNonceGenerator(nonceGeneratorType nonceGenerator)
{
_nonceGenerator = nonceGenerator;
}
nonceGeneratorType getNonceGenerator()
{
return _nonceGenerator;
}
// #################### MD5 ####################
// resultArray must have size MD5::NATURAL_LENGTH or greater
void *MD5::hash(const void *data, const size_t dataLength, void *resultArray)
{
return md5HashHelper(data, dataLength, resultArray);
}
String MD5::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
md5HashHelper(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *MD5::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String MD5::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_md5_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *MD5::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_md5_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String MD5::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_md5_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-1 ####################
// resultArray must have size SHA1::NATURAL_LENGTH or greater
void *SHA1::hash(const void *data, const size_t dataLength, void *resultArray)
{
return sha1HashHelper(data, dataLength, resultArray);
}
String SHA1::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
sha1HashHelper(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA1::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA1::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha1_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA1::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_sha1_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA1::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha1_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-224 ####################
// resultArray must have size SHA224::NATURAL_LENGTH or greater
void *SHA224::hash(const void *data, const size_t dataLength, void *resultArray)
{
br_sha224_context context;
br_sha224_init(&context);
br_sha224_update(&context, data, dataLength);
br_sha224_out(&context, resultArray);
return resultArray;
}
String SHA224::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA224::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA224::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha224_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA224::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_sha224_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA224::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha224_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-256 ####################
// resultArray must have size SHA256::NATURAL_LENGTH or greater
void *SHA256::hash(const void *data, const size_t dataLength, void *resultArray)
{
br_sha256_context context;
br_sha256_init(&context);
br_sha256_update(&context, data, dataLength);
br_sha256_out(&context, resultArray);
return resultArray;
}
String SHA256::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA256::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA256::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha256_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA256::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_sha256_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA256::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha256_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-384 ####################
// resultArray must have size SHA384::NATURAL_LENGTH or greater
void *SHA384::hash(const void *data, const size_t dataLength, void *resultArray)
{
br_sha384_context context;
br_sha384_init(&context);
br_sha384_update(&context, data, dataLength);
br_sha384_out(&context, resultArray);
return resultArray;
}
String SHA384::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA384::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA384::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha384_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA384::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_sha384_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA384::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha384_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### SHA-512 ####################
// resultArray must have size SHA512::NATURAL_LENGTH or greater
void *SHA512::hash(const void *data, const size_t dataLength, void *resultArray)
{
br_sha512_context context;
br_sha512_init(&context);
br_sha512_update(&context, data, dataLength);
br_sha512_out(&context, resultArray);
return resultArray;
}
String SHA512::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
void *SHA512::hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmac(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA512::hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmac(&br_sha512_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
void *SHA512::hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength)
{
return createBearsslHmacCT(&br_sha512_vtable, data, dataLength, hashKey, hashKeyLength, resultArray, outputLength);
}
String SHA512::hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength)
{
return createBearsslHmacCT(&br_sha512_vtable, NATURAL_LENGTH, message, hashKey, hashKeyLength, hmacLength);
}
// #################### MD5+SHA-1 ####################
// resultArray must have size MD5SHA1::NATURAL_LENGTH or greater
void *MD5SHA1::hash(const void *data, const size_t dataLength, void *resultArray)
{
br_md5sha1_context context;
br_md5sha1_init(&context);
br_md5sha1_update(&context, data, dataLength);
br_md5sha1_out(&context, resultArray);
return resultArray;
}
String MD5SHA1::hash(const String &message)
{
uint8_t hashArray[NATURAL_LENGTH];
hash(message.c_str(), message.length(), hashArray);
return TypeCast::uint8ArrayToHexString(hashArray, NATURAL_LENGTH);
}
// #################### HKDF ####################
HKDF::HKDF(const void *keyMaterial, const size_t keyMaterialLength, const void *salt, const size_t saltLength)
{
init(keyMaterial, keyMaterialLength, salt, saltLength);
}
void HKDF::init(const void *keyMaterial, const size_t keyMaterialLength, const void *salt, const size_t saltLength)
{
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__kdf_8h.html
// Initialize an HKDF context, with a hash function, and the salt. This starts the HKDF-Extract process.
br_hkdf_init(&hkdfContext, &br_sha256_vtable, salt, saltLength);
// Inject more input bytes. This function may be called repeatedly if the input data is provided by chunks, after br_hkdf_init() but before br_hkdf_flip().
br_hkdf_inject(&hkdfContext, keyMaterial, keyMaterialLength);
// End the HKDF-Extract process, and start the HKDF-Expand process.
br_hkdf_flip(&hkdfContext);
}
size_t HKDF::produce(void *resultArray, const size_t outputLength, const void *info, const size_t infoLength)
{
// Comments mainly from https://www.bearssl.org/apidoc/bearssl__kdf_8h.html
// HKDF output production (HKDF-Expand).
// Produces 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.
return br_hkdf_produce(&hkdfContext, info, infoLength, resultArray, outputLength);
}
// #################### Authenticated Encryption with Associated Data (AEAD) ####################
// #################### ChaCha20+Poly1305 AEAD ####################
void chacha20Poly1305Kernel(const int encrypt, void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
const void *nonce, void *tag, const void *aad, const size_t aadLength)
{
if (keySalt == nullptr)
{
br_poly1305_ctmul32_run(key, nonce, data, dataLength, aad, aadLength, tag, br_chacha20_ct_run, encrypt);
}
else
{
HKDF hkdfInstance(key, ENCRYPTION_KEY_LENGTH, keySalt, keySaltLength);
uint8_t derivedEncryptionKey[ENCRYPTION_KEY_LENGTH] {0};
hkdfInstance.produce(derivedEncryptionKey, ENCRYPTION_KEY_LENGTH);
br_poly1305_ctmul32_run(derivedEncryptionKey, nonce, data, dataLength, aad, aadLength, tag, br_chacha20_ct_run, encrypt);
}
}
void ChaCha20Poly1305::encrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
void *resultingNonce, void *resultingTag, const void *aad, const size_t aadLength)
{
uint8_t *nonce = (uint8_t *)resultingNonce;
getNonceGenerator()(nonce, 12);
chacha20Poly1305Kernel(1, data, dataLength, key, keySalt, keySaltLength, nonce, resultingTag, aad, aadLength);
}
bool ChaCha20Poly1305::decrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength,
const void *encryptionNonce, const void *encryptionTag, const void *aad, const size_t aadLength)
{
const uint8_t *oldTag = (const uint8_t *)encryptionTag;
uint8_t newTag[16] {0};
chacha20Poly1305Kernel(0, data, dataLength, key, keySalt, keySaltLength, encryptionNonce, newTag, aad, aadLength);
for (uint32_t i = 0; i < sizeof newTag; ++i)
{
if (newTag[i] != oldTag[i])
{
return false;
}
}
return true;
}
}
}

845
cores/esp8266/Crypto.h Normal file
View File

@ -0,0 +1,845 @@
/*
BearSSL Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
Rest of this file Copyright (C) 2019 Anders Löfgren
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __ESP8266_ARDUINO_CRYPTO_H__
#define __ESP8266_ARDUINO_CRYPTO_H__
#include <Arduino.h>
namespace experimental
{
namespace crypto
{
/**
Regarding constant-time (CT) HMAC:
Basically, constant-time algorithms makes it harder for attackers to learn things about your system based on the execution time of code.
Good intro here: https://www.bearssl.org/constanttime.html
It should be noted that every HMAC is already partially constant-time. Quoting the link above:
"Hash functions implemented by BearSSL (MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512) consist in bitwise logical operations and additions on 32-bit or 64-bit words,
naturally yielding constant-time operations. HMAC is naturally as constant-time as the underlying hash function. The size of the MACed data, and the size of the key,
may leak, though; only the contents are protected."
For messages much smaller than getCtMaxDataLength(), constant-time processing takes substantially longer time to complete than a normal HMAC,
determined by the size of (getCtMaxDataLength() - getCtMinDataLength()).
Constant-time processing also sets limits on the data length.
Making the fixed data length limits variable will generally defeat the purpose of using constant-time.
Using data that exceeds the fixed data length limits will create the wrong HMAC.
*/
/**
The nonce generator should take an uint8_t array with a given size in bytes and fill it with the nonce.
The uint8_t array should then be returned by the nonce generator.
*/
using nonceGeneratorType = std::function<uint8_t *(uint8_t *, const size_t)>;
constexpr uint8_t ENCRYPTION_KEY_LENGTH = 32;
constexpr uint32_t CT_MAX_DIFF = 1073741823; // 2^30 - 1
/**
This function allows for fine-tuning of the specifications for the constant time calculations.
It should not be changed once a constant time function has been used at least once.
Otherwise the constant time will not be constant for the used functions.
The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMinDataLength(const size_t ctMinDataLength);
/**
0 by default.
*/
size_t getCtMinDataLength();
/**
This function allows for fine-tuning of the specifications for the constant time calculations.
It should not be changed once a constant time function has been used at least once.
Otherwise the constant time will not be constant for the used functions.
The difference getCtMaxDataLength() - getCtMinDataLength() MUST be less than 2^30 (i.e. about one gigabyte).
*/
void setCtMaxDataLength(const size_t ctMaxDataLength);
/**
1024 by default.
*/
size_t getCtMaxDataLength();
/**
Set the nonce generator used by the Crypto functions.
@param nonceGenerator The nonce generator to use.
*/
void setNonceGenerator(nonceGeneratorType nonceGenerator);
nonceGeneratorType getNonceGenerator();
// #################### MD5 ####################
struct MD5
{
static constexpr uint8_t NATURAL_LENGTH = 16;
/**
WARNING! The MD5 hash is broken in terms of attacker resistance.
Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
Create a MD5 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
/**
WARNING! The MD5 hash is broken in terms of attacker resistance.
Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
Create a MD5 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message) __attribute__((deprecated));
/**
Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a MD5 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a MD5 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a MD5 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-1 ####################
struct SHA1
{
static constexpr uint8_t NATURAL_LENGTH = 20;
/**
WARNING! The SHA-1 hash is broken in terms of attacker resistance.
Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
Create a SHA1 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray) __attribute__((deprecated));
/**
WARNING! The SHA-1 hash is broken in terms of attacker resistance.
Only use it in those cases where attacker resistance is not important. Prefer SHA-256 or higher otherwise.
Create a SHA1 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message) __attribute__((deprecated));
/**
Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA1 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a SHA1 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA1 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-224 ####################
struct SHA224
{
static constexpr uint8_t NATURAL_LENGTH = 28;
/**
Create a SHA224 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA224 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message);
/**
Create a SHA224 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA224 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a SHA224 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA224 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-256 ####################
struct SHA256
{
static constexpr uint8_t NATURAL_LENGTH = 32;
/**
Create a SHA256 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA256 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message);
/**
Create a SHA256 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA256 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a SHA256 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA256 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-384 ####################
struct SHA384
{
static constexpr uint8_t NATURAL_LENGTH = 48;
/**
Create a SHA384 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA384 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message);
/**
Create a SHA384 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA384 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a SHA384 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA384 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### SHA-512 ####################
struct SHA512
{
static constexpr uint8_t NATURAL_LENGTH = 64;
/**
Create a SHA512 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a SHA512 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message);
/**
Create a SHA512 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmac(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA512 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC.
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
/**
Create a SHA512 HMAC from the data, using the provided hashKey. The result will be up to outputLength bytes long and stored in resultArray.
Constant-time version.
Uses the BearSSL cryptographic library.
@param data The data array from which to create the HMAC.
@param dataLength The length of the data array in bytes. Valid values are in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param resultArray The array wherein to store the resulting HMAC.
@param outputLength The desired length of the generated HMAC, in bytes. Must fit within resultArray. If outputLength is greater than NATURAL_LENGTH,
the first (lowest index) NATURAL_LENGTH bytes of resultArray will be used for the HMAC.
If outputLength is 0, then the natural HMAC output length is selected.
@return A pointer to resultArray.
*/
static void *hmacCT(const void *data, const size_t dataLength, const void *hashKey, const size_t hashKeyLength, void *resultArray, const size_t outputLength);
/**
Create a SHA512 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format.
Constant-time version.
Uses the BearSSL cryptographic library.
@param message The string from which to create the HMAC. Must have a length in the range [getCtMinDataLength(), getCtMaxDataLength()].
@param hashKey The hash key to use when creating the HMAC.
@param hashKeyLength The length of the hash key in bytes.
@param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to NATURAL_LENGTH.
@return A String with the generated HMAC in HEX format.
*/
static String hmacCT(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength);
};
// #################### MD5+SHA-1 ####################
struct MD5SHA1
{
static constexpr uint8_t NATURAL_LENGTH = 36;
/**
Create a MD5+SHA-1 hash of the data. The result will be NATURAL_LENGTH bytes long and stored in resultArray.
Uses the BearSSL cryptographic library.
MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the same input; in the implementation, the internal data buffer is shared,
thus making it more memory-efficient than separate MD5 and SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS 1.1.
@param data The data array from which to create the hash.
@param dataLength The length of the data array in bytes.
@param resultArray The array wherein to store the resulting hash. MUST be be able to contain NATURAL_LENGTH bytes or more.
@return A pointer to resultArray.
*/
static void *hash(const void *data, const size_t dataLength, void *resultArray);
/**
Create a MD5+SHA-1 hash of the data. The result will be NATURAL_LENGTH bytes long and returned as a String in HEX format.
Uses the BearSSL cryptographic library.
MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the same input; in the implementation, the internal data buffer is shared,
thus making it more memory-efficient than separate MD5 and SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS 1.1.
@param message The string from which to create the hash.
@return A String with the generated hash in HEX format.
*/
static String hash(const String &message);
};
// #################### HKDF ####################
struct HKDF
{
/**
KDFs (key derivation functions) 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 is a KDF defined by RFC 5869. It is based on HMAC. The provided implementation uses SHA-256 as the underlying hash function.
The constructor initializes the HKDF implementation with the input data to use for HKDF processing. (calls HKDF::init())
@param keyMaterial An array containing the key material to use when deriving subkeys. Typically this would be the master key.
@param keyMaterialLength The length of keyMaterial in bytes.
@param salt An array containing the salt to use when ingesting key material. Salt is non-secret and can be empty.
Its role is normally to bind the input to a conventional identifier that qualify it within the used protocol or application.
@param saltLength The length of the salt array, in bytes.
*/
HKDF(const void *keyMaterial, const size_t keyMaterialLength, const void *salt = nullptr, const size_t saltLength = 0);
/**
This method initializes the HKDF implementation with the input data to use for HKDF processing.
Uses the BearSSL cryptographic library.
@param keyMaterial An array containing the key material to use when deriving subkeys. Typically this would be the master key.
@param keyMaterialLength The length of keyMaterial in bytes.
@param salt An array containing the salt to use when ingesting key material. Salt is non-secret and can be empty.
Its role is normally to bind the input to a conventional identifier that qualify it within the used protocol or application.
@param saltLength The length of the salt array, in bytes.
*/
void init(const void *keyMaterial, const size_t keyMaterialLength, const void *salt = nullptr, const size_t saltLength = 0);
/**
Produce more output bytes from the current HKDF state. This method may be called several times to obtain the full output by chunks.
The total output size is limited to 255 * SHA256::NATURAL_LENGTH bytes per unique HKDF::init()/constructor call.
Uses the BearSSL cryptographic library.
@param resultArray The array wherein to store the resulting HKDF.
@param outputLength The requested number of bytes to fill with HKDF output in resultArray.
@param info NOTE: For correct HKDF processing, the same "info" string must be provided for every call until there's a new unique HKDF::init().
An array containing the information string to use when producing output. Info is non-secret and can be empty.
Its role is normally to bind the output to a conventional identifier that qualify it within the used protocol or application.
@param infoLength The length of the info array, in bytes.
@return The number of HKDF bytes actually produced.
*/
size_t produce(void *resultArray, const size_t outputLength, const void *info = nullptr, size_t infoLength = 0);
private:
// Use an opaque type to avoid #include <bearssl/bearssl.h> which drags the lib declarations into userland. The global scope prefix is required for compilation to succeed, it seems.
::br_hkdf_context hkdfContext;
};
// #################### Authenticated Encryption with Associated Data (AEAD) ####################
/**
From https://www.bearssl.org/apidoc/bearssl__aead_8h.html
An AEAD algorithm processes messages and provides confidentiality (encryption) and checked integrity (MAC). It uses the following parameters:
- A symmetric key. Exact size depends on the AEAD algorithm.
- A nonce (IV). Size depends on the AEAD algorithm; for most algorithms, it is crucial for security that any given nonce value is never used twice for the same key and distinct messages.
- Data to encrypt and protect.
- Additional authenticated data, which is covered by the MAC but otherwise left untouched (i.e. not encrypted).
The AEAD algorithm encrypts the data, and produces an authentication tag.
It is assumed that the encrypted data, the tag, the additional authenticated data and the nonce are sent to the receiver;
the additional data and the nonce may be implicit (e.g. using elements of the underlying transport protocol, such as record sequence numbers).
The receiver will recompute the tag value and compare it with the one received;
if they match, then the data is correct, and can be decrypted and used;
otherwise, at least one of the elements was altered in transit, normally leading to wholesale rejection of the complete message.
*/
// #################### ChaCha20+Poly1305 AEAD ####################
struct ChaCha20Poly1305
{
/**
Encrypt the data array using the ChaCha20 stream cipher and use Poly1305 for message authentication.
The function generates in place an equal-length ChaCha20 encrypted version of the data array.
More information about this encryption standard can be found here: https://tools.ietf.org/html/rfc7539 , https://tools.ietf.org/html/rfc8439
Uses the BearSSL cryptographic library.
Encryption of small messages (up to a few hundred data bytes) takes around 0.5-1 ms with the default nonceGenerator, half of this without keySalt.
The output values of ChaCha20Poly1305::encrypt should be passed as input values to ChaCha20Poly1305::decrypt.
Note that a 12 byte nonce is generated via getNonceGenerator() every time ChaCha20Poly1305::encrypt is called.
If the same key and nonce combination is used more than once for distinct messages, the encryption will be broken, so keep the following in mind:
By default the nonce is generated via the hardware random number generator of the ESP8266.
The entropy of this source may not be sufficient to avoid nonce collisions, so to further reduce the risk of encryption failure
it is recommended that a keySalt is always provided when using the default nonceGenerator. Using a keySalt will create a
pseudorandom subkey from the original key via HKDF, and use that for the encryption/decryption.
The same key + keySalt will always generate the same subkey.
An alternative to using a keySalt is to change the nonceGenerator so that it does not rely on random numbers.
One way to do this would be to use a counter that guarantees the same key + nonce combination is never used.
This may not be easily achievable in all scenarios, however.
@param data An array containing the data to encrypt. The encrypted data is generated in place, so when the function returns the data array will contain the encrypted data.
@param dataLength The length of the data array in bytes.
@param key The secret encryption key to use. Must be 32 bytes (ENCRYPTION_KEY_LENGTH) long.
@param keySalt The salt to use when generating a subkey from key. Note that the same salt must be used during decryption as during encryption. Set to nullptr to prevent subkey generation.
@param keySaltLength The length of keySalt in bytes.
@param resultingNonce The array that will store the nonce generated during encryption. Must be able to contain at least 12 bytes. The nonce is not secret and must be passed to the decryption function.
@param resultingTag The array that will store the message authentication tag generated during encryption. Must be able to contain at least 16 bytes. The tag is not secret and must be passed to the decryption function.
@param aad Additional authenticated data. This data will be covered by the Poly1305 MAC, but not encrypted.
You can include the unencrypted parts of your message as AAD to ensure that the encrypted content cannot
be re-sent with replaced unencrypted data by an attacker.
Defaults to nullptr.
@param aadLength The length of the aad array in bytes. Defaults to 0.
*/
static void encrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength, void *resultingNonce, void *resultingTag, const void *aad = nullptr, const size_t aadLength = 0);
/**
Decrypt the data array using the ChaCha20 stream cipher and use Poly1305 for message authentication.
The function generates in place an equal-length ChaCha20 decrypted version of the data array.
More information about this encryption standard can be found here: https://tools.ietf.org/html/rfc7539 , https://tools.ietf.org/html/rfc8439
Uses the BearSSL cryptographic library.
Decryption of small messages (up to a few hundred data bytes) takes around 0.5-1 ms, half of this without keySalt.
The output values of ChaCha20Poly1305::encrypt should be passed as input values to ChaCha20Poly1305::decrypt.
@param data An array containing the data to decrypt. The decrypted data is generated in place, so when the function returns the data array will contain the decrypted data.
@param dataLength The length of the data array in bytes.
@param key The secret encryption key to use. Must be 32 bytes (ENCRYPTION_KEY_LENGTH) long.
@param keySalt The salt to use when generating a subkey from key. Note that the same salt must be used during decryption as during encryption. Set to nullptr to prevent subkey generation.
@param keySaltLength The length of keySalt in bytes.
@param encryptionNonce An array containing the nonce that was generated during encryption. The nonce should be 12 bytes.
@param encryptionTag An array containing the message authentication tag that was generated during encryption. The tag should be 16 bytes.
@param aad Additional authenticated data. This data will be covered by the Poly1305 MAC, but not decrypted.
You can include the unencrypted parts of your message as AAD to ensure that the encrypted content cannot
be re-sent with replaced unencrypted data by an attacker.
Defaults to nullptr.
@param aadLength The length of the aad array in bytes. Defaults to 0.
@return True if the decryption was successful (the generated tag matches encryptionTag). False otherwise. Note that the data array is modified regardless of this outcome.
*/
static bool decrypt(void *data, const size_t dataLength, const void *key, const void *keySalt, const size_t keySaltLength, const void *encryptionNonce, const void *encryptionTag, const void *aad = nullptr, const size_t aadLength = 0);
};
}
}
#endif

View File

@ -404,6 +404,8 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
return (64_kB);
// Winbond
case 0x1840EF: // W25Q128
return (16_MB);
case 0x1640EF: // W25Q32
return (4_MB);
case 0x1540EF: // W25Q16
@ -423,6 +425,10 @@ uint32_t EspClass::getFlashChipSizeByChipId(void) {
case 0x1340E0: // BG25Q40
return (512_kB);
// XMC - Wuhan Xinxin Semiconductor Manufacturing Corp
case 0x164020: // XM25QH32B
return (4_MB);
default:
return 0;
}
@ -516,6 +522,63 @@ bool EspClass::eraseConfig(void) {
return true;
}
uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) const
{
/**
* The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266):
*
* "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number.
* These true random numbers are generated based on the noise in the Wi-Fi/BT RF system.
* When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers.
*
* When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz).
* Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz.
* A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz,
* has been tested using the Dieharder Random Number Testsuite (version 3.31.1).
* The sample passed all tests."
*
* Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency.
* A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266.
* It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers.
*
* It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available.
* However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation.
* Thus only delayMicroseconds() is used below.
*/
constexpr uint8_t cooldownMicros = 2;
static uint32_t lastCalledMicros = micros() - cooldownMicros;
uint32_t randomNumber = 0;
for(size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex)
{
if(byteIndex % 4 == 0)
{
// Old random number has been used up (random number could be exactly 0, so we can't check for that)
uint32_t timeSinceLastCall = micros() - lastCalledMicros;
if(timeSinceLastCall < cooldownMicros)
delayMicroseconds(cooldownMicros - timeSinceLastCall);
randomNumber = RANDOM_REG32;
lastCalledMicros = micros();
}
resultArray[byteIndex] = randomNumber;
randomNumber >>= 8;
}
return resultArray;
}
uint32_t EspClass::random() const
{
union { uint32_t b32; uint8_t b8[4]; } result;
random(result.b8, 4);
return result.b32;
}
uint32_t EspClass::getSketchSize() {
static uint32_t result = 0;
if (result)

View File

@ -22,43 +22,7 @@
#define ESP_H
#include <Arduino.h>
// Vendor IDs taken from Flashrom project
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
typedef enum {
SPI_FLASH_VENDOR_ALLIANCE = 0x52, /* Alliance Semiconductor */
SPI_FLASH_VENDOR_AMD = 0x01, /* AMD */
SPI_FLASH_VENDOR_AMIC = 0x37, /* AMIC */
SPI_FLASH_VENDOR_ATMEL = 0x1F, /* Atmel (now used by Adesto) */
SPI_FLASH_VENDOR_BRIGHT = 0xAD, /* Bright Microelectronics */
SPI_FLASH_VENDOR_CATALYST = 0x31, /* Catalyst */
SPI_FLASH_VENDOR_EON = 0x1C, /* EON Silicon Devices, missing 0x7F prefix */
SPI_FLASH_VENDOR_ESMT = 0x8C, /* Elite Semiconductor Memory Technology (ESMT) / EFST Elite Flash Storage */
SPI_FLASH_VENDOR_EXCEL = 0x4A, /* ESI, missing 0x7F prefix */
SPI_FLASH_VENDOR_FIDELIX = 0xF8, /* Fidelix */
SPI_FLASH_VENDOR_FUJITSU = 0x04, /* Fujitsu */
SPI_FLASH_VENDOR_GIGADEVICE = 0xC8, /* GigaDevice */
SPI_FLASH_VENDOR_HYUNDAI = 0xAD, /* Hyundai */
SPI_FLASH_VENDOR_INTEL = 0x89, /* Intel */
SPI_FLASH_VENDOR_ISSI = 0xD5, /* ISSI Integrated Silicon Solutions, see also PMC. */
SPI_FLASH_VENDOR_MACRONIX = 0xC2, /* Macronix (MX) */
SPI_FLASH_VENDOR_NANTRONICS = 0xD5, /* Nantronics, missing prefix */
SPI_FLASH_VENDOR_PMC = 0x9D, /* PMC, missing 0x7F prefix */
SPI_FLASH_VENDOR_PUYA = 0x85, /* Puya semiconductor (shanghai) co. ltd */
SPI_FLASH_VENDOR_SANYO = 0x62, /* Sanyo */
SPI_FLASH_VENDOR_SHARP = 0xB0, /* Sharp */
SPI_FLASH_VENDOR_SPANSION = 0x01, /* Spansion, same ID as AMD */
SPI_FLASH_VENDOR_SST = 0xBF, /* SST */
SPI_FLASH_VENDOR_ST = 0x20, /* ST / SGS/Thomson / Numonyx (later acquired by Micron) */
SPI_FLASH_VENDOR_SYNCMOS_MVC = 0x40, /* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */
SPI_FLASH_VENDOR_TENX = 0x5E, /* Tenx Technologies */
SPI_FLASH_VENDOR_TI = 0x97, /* Texas Instruments */
SPI_FLASH_VENDOR_TI_OLD = 0x01, /* TI chips from last century */
SPI_FLASH_VENDOR_WINBOND = 0xDA, /* Winbond */
SPI_FLASH_VENDOR_WINBOND_NEX = 0xEF, /* Winbond (ex Nexcom) serial flashes */
SPI_FLASH_VENDOR_UNKNOWN = 0xFF
} SPI_FLASH_VENDOR_t;
#include "spi_vendors.h"
/**
* AVR macros for WDT managment
@ -200,6 +164,9 @@ class EspClass {
bool eraseConfig();
uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes) const;
uint32_t random() const;
#ifndef CORE_MOCK
inline uint32_t getCycleCount() __attribute__((always_inline));
#else

View File

@ -266,7 +266,7 @@ using fs::SPIFFSConfig;
#endif //FS_NO_GLOBALS
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS)
extern fs::FS SPIFFS;
extern fs::FS SPIFFS __attribute__((deprecated("SPIFFS has been deprecated. Please consider moving to LittleFS or other filesystems.")));
#endif
#endif //FS_H

View File

@ -2,6 +2,7 @@
#define ESP_SCHEDULE_H
#include <functional>
#include <stdint.h>
#define SCHEDULED_FN_MAX_COUNT 32

View File

@ -1,7 +1,7 @@
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
// by script <esp8266 arduino core>/tools/TZupdate.sh
// Mon Dec 16 13:08:18 UTC 2019
// Thu May 7 19:02:21 UTC 2020
//
// This database is autogenerated from IANA timezone database
// https://www.iana.org/time-zones

View File

@ -22,14 +22,14 @@
*/
#include "Arduino.h"
#include "user_interface.h"
#include "core_esp8266_waveform.h"
#include "user_interface.h"
// Which pins have a tone running on them?
static uint32_t _toneMap = 0;
static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration) {
static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, uint32_t duration) {
if (_pin > 16) {
return;
}
@ -39,7 +39,10 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long
high = std::max(high, (uint32_t)microsecondsToClockCycles(25)); // new 20KHz maximum tone frequency,
low = std::max(low, (uint32_t)microsecondsToClockCycles(25)); // (25us high + 25us low period = 20KHz)
if (startWaveformCycles(_pin, high, low, microsecondsToClockCycles(duration * 1000))) {
duration = microsecondsToClockCycles(duration * 1000UL);
duration += high + low - 1;
duration -= duration % (high + low);
if (startWaveformClockCycles(_pin, high, low, duration)) {
_toneMap |= 1 << _pin;
}
}
@ -49,7 +52,7 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
if (frequency == 0) {
noTone(_pin);
} else {
uint32_t period = (1000000L * system_get_cpu_freq()) / frequency;
uint32_t period = microsecondsToClockCycles(1000000UL) / frequency;
uint32_t high = period / 2;
uint32_t low = period - high;
_startTone(_pin, high, low, duration);
@ -63,7 +66,7 @@ void tone(uint8_t _pin, double frequency, unsigned long duration) {
if (frequency < 1.0) { // FP means no exact comparisons
noTone(_pin);
} else {
double period = (1000000.0L * system_get_cpu_freq()) / frequency;
double period = (double)microsecondsToClockCycles(1000000UL) / frequency;
uint32_t high = (uint32_t)((period / 2.0) + 0.5);
uint32_t low = (uint32_t)(period + 0.5) - high;
_startTone(_pin, high, low, duration);

View File

@ -0,0 +1,91 @@
/*
TypeConversion functionality
Copyright (C) 2019 Anders Löfgren
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <assert.h>
#include "TypeConversion.h"
namespace experimental
{
namespace TypeConversion
{
const char base36Chars[36] PROGMEM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
const uint8_t base36CharValues[75] PROGMEM {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 // Lower case letters
};
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength)
{
String hexString;
if (!hexString.reserve(2 * arrayLength)) // Each uint8_t will become two characters (00 to FF)
{
return emptyString;
}
for (uint32_t i = 0; i < arrayLength; ++i)
{
hexString += (char)pgm_read_byte(base36Chars + (uint8Array[i] >> 4));
hexString += (char)pgm_read_byte(base36Chars + uint8Array[i] % 16);
}
return hexString;
}
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength)
{
assert(hexString.length() >= arrayLength * 2); // Each array element can hold two hexString characters
for (uint32_t i = 0; i < arrayLength; ++i)
{
uint8Array[i] = (pgm_read_byte(base36CharValues + hexString.charAt(i * 2) - '0') << 4) + pgm_read_byte(base36CharValues + hexString.charAt(i * 2 + 1) - '0');
}
return uint8Array;
}
uint8_t *uint64ToUint8ArrayBE(const uint64_t value, uint8_t *resultArray)
{
resultArray[7] = value;
resultArray[6] = value >> 8;
resultArray[5] = value >> 16;
resultArray[4] = value >> 24;
resultArray[3] = value >> 32;
resultArray[2] = value >> 40;
resultArray[1] = value >> 48;
resultArray[0] = value >> 56;
return resultArray;
}
uint64_t uint8ArrayToUint64BE(const uint8_t *inputArray)
{
uint64_t result = (uint64_t)inputArray[0] << 56 | (uint64_t)inputArray[1] << 48 | (uint64_t)inputArray[2] << 40 | (uint64_t)inputArray[3] << 32
| (uint64_t)inputArray[4] << 24 | (uint64_t)inputArray[5] << 16 | (uint64_t)inputArray[6] << 8 | (uint64_t)inputArray[7];
return result;
}
}
}

View File

@ -0,0 +1,80 @@
/*
TypeConversion functionality
Copyright (C) 2019 Anders Löfgren
License (MIT license):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __ESP8266_TYPECONVERSION_H__
#define __ESP8266_TYPECONVERSION_H__
#include <Arduino.h>
namespace experimental
{
namespace TypeConversion
{
extern const char base36Chars[36];
// Subtract '0' to normalize the char before lookup.
extern const uint8_t base36CharValues[75];
/**
Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array.
All array elements will be padded with zeroes to ensure they are converted to 2 String characters each.
@param uint8Array The array to make into a HEX String.
@param arrayLength The size of uint8Array, in bytes.
@return Normally a String containing the HEX representation of the uint8Array. An empty String if the memory allocation for the String failed.
*/
String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength);
/**
Convert the contents of a String in HEX format to a uint8_t array. Index 0 of the array will represent the start of the String.
There must be 2 String characters for each array element. Use padding with zeroes where required.
@param hexString The HEX String to convert to a uint8_t array. Must contain at least 2*arrayLength characters.
@param uint8Array The array to fill with the contents of the hexString.
@param arrayLength The number of bytes to fill in uint8Array.
@return A pointer to the uint8Array.
*/
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength);
/**
Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB (big endian).
@param value The uint64_t value to convert to a uint8_t array.
@param resultArray A uint8_t array that will hold the result once the function returns. Should have a size of at least 8 bytes.
@return The resultArray.
*/
uint8_t *uint64ToUint8ArrayBE(const uint64_t value, uint8_t *resultArray);
/**
Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB (big endian).
@param inputArray A uint8_t array containing the data to convert to a uint64_t. Should have a size of at least 8 bytes.
@return A uint64_t representation of the first 8 bytes of the array.
*/
uint64_t uint8ArrayToUint64BE(const uint8_t *inputArray);
}
}
#endif

View File

@ -0,0 +1,85 @@
/*
flash_quirks.cpp - Chip specific flash init
Copyright (c) 2019 Mike Nix. 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
*/
#include <c_types.h>
#include "spi_flash.h"
#include "spi_utils.h"
#include "flash_quirks.h"
#ifdef __cplusplus
extern "C" {
#endif
namespace experimental {
static int get_flash_mhz() {
// FIXME: copied from Esp.cpp - we really should define the magic values
uint32_t data;
uint8_t * bytes = (uint8_t *) &data;
// read first 4 byte (magic byte + flash config)
if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) {
switch (bytes[3] & 0x0F) {
case 0x0: // 40 MHz
return 40;
case 0x1: // 26 MHz
return 26;
case 0x2: // 20 MHz
return 20;
case 0xf: // 80 MHz
return 80;
default: // fail?
return 0;
}
}
return 0;
}
/* initFlashQuirks()
* Do any chip-specific initialization to improve performance and reliability.
*/
void initFlashQuirks() {
using namespace experimental;
uint32_t vendor = spi_flash_get_id() & 0x000000ff;
switch (vendor) {
case SPI_FLASH_VENDOR_XMC:
uint32_t SR3, newSR3;
if (SPI0Command(SPI_FLASH_CMD_RSR3, &SR3, 0, 8)==SPI_RESULT_OK) { // read SR3
newSR3=SR3;
if (get_flash_mhz()>26) { // >26Mhz?
// Set the output drive to 100%
newSR3 &= ~(SPI_FLASH_SR3_XMC_DRV_MASK << SPI_FLASH_SR3_XMC_DRV_S);
newSR3 |= (SPI_FLASH_SR3_XMC_DRV_100 << SPI_FLASH_SR3_XMC_DRV_S);
}
if (newSR3 != SR3) { // only write if changed
if (SPI0Command(SPI_FLASH_CMD_WEVSR,NULL,0,0)==SPI_RESULT_OK) // write enable volatile SR
SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0); // write to SR3
SPI0Command(SPI_FLASH_CMD_WRDI,NULL,0,0); // write disable - probably not needed
}
}
}
}
} // namespace experimental
#ifdef __cplusplus
}
#endif

View File

@ -464,14 +464,28 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) {
div1 &= I2SBDM;
div2 &= I2SCDM;
/*
Following this post: https://github.com/esp8266/Arduino/issues/2590
We should reset the transmitter while changing the configuration bits to avoid random distortion.
*/
uint32_t i2sc_temp = I2SC;
i2sc_temp |= (I2STXR); // Hold transmitter in reset
I2SC = i2sc_temp;
// trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
i2sc_temp &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
// I2SMR = MSB recv/xmit first
// I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format)
// div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this
I2SC |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD);
i2sc_temp |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD);
I2SC = i2sc_temp;
i2sc_temp &= ~(I2STXR); // Release reset
I2SC = i2sc_temp;
}
float i2s_get_real_rate(){

View File

@ -34,6 +34,7 @@ extern "C" {
}
#include <core_version.h>
#include "gdb_hooks.h"
#include "flash_quirks.h"
#define LOOP_TASK_PRIORITY 1
#define LOOP_QUEUE_SIZE 1
@ -334,6 +335,8 @@ extern "C" void user_init(void) {
initVariant();
experimental::initFlashQuirks(); // Chip specific flash init.
cont_init(g_pcont);
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.

View File

@ -31,31 +31,31 @@ extern "C" {
#include "ets_sys.h"
};
// Inline helpers
static inline __attribute__((always_inline)) void SDA_LOW(const int twi_sda)
{
GPES = (1 << twi_sda);
}
static inline __attribute__((always_inline)) void SDA_HIGH(const int twi_sda)
{
GPEC = (1 << twi_sda);
}
static inline __attribute__((always_inline)) bool SDA_READ(const int twi_sda)
{
return (GPI & (1 << twi_sda)) != 0;
}
static inline __attribute__((always_inline)) void SCL_LOW(const int twi_scl)
{
GPES = (1 << twi_scl);
}
static inline __attribute__((always_inline)) void SCL_HIGH(const int twi_scl)
{
GPEC = (1 << twi_scl);
}
static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
{
return (GPI & (1 << twi_scl)) != 0;
}
// Inline helpers
static inline __attribute__((always_inline)) void SDA_LOW(const int twi_sda)
{
GPES = (1 << twi_sda);
}
static inline __attribute__((always_inline)) void SDA_HIGH(const int twi_sda)
{
GPEC = (1 << twi_sda);
}
static inline __attribute__((always_inline)) bool SDA_READ(const int twi_sda)
{
return (GPI & (1 << twi_sda)) != 0;
}
static inline __attribute__((always_inline)) void SCL_LOW(const int twi_scl)
{
GPES = (1 << twi_scl);
}
static inline __attribute__((always_inline)) void SCL_HIGH(const int twi_scl)
{
GPEC = (1 << twi_scl);
}
static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
{
return (GPI & (1 << twi_scl)) != 0;
}
// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer
@ -126,10 +126,12 @@ private:
{
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
while(!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
while (!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
{
if (yieldTimeout) // inner loop yields every 5ms
{
yield();
}
}
}
@ -161,23 +163,29 @@ static Twi twi;
void Twi::setClock(unsigned int freq)
{
if (freq < 1000) // minimum freq 1000Hz to minimize slave timeouts and WDT resets
{
freq = 1000;
}
preferred_si2c_clock = freq;
#if F_CPU == FCPU80
if (freq > 400000)
{
freq = 400000;
}
twi_dcount = (500000000 / freq); // half-cycle period in ns
twi_dcount = (1000*(twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time
twi_dcount = (1000 * (twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time
#else
if (freq > 800000)
{
freq = 800000;
}
twi_dcount = (500000000 / freq); // half-cycle period in ns
twi_dcount = (1000*(twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time
twi_dcount = (1000 * (twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time
#endif
}
@ -221,7 +229,7 @@ void Twi::enableSlave()
}
}
void ICACHE_RAM_ATTR Twi::busywait(unsigned int v)
void ICACHE_RAM_ATTR Twi::busywait(unsigned int v)
{
unsigned int i;
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz

View File

@ -5,13 +5,13 @@
Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
The core idea is to have a programmable waveform generator with a unique
high and low period (defined in microseconds). TIMER1 is set to 1-shot
mode and is always loaded with the time until the next edge of any live
waveforms.
high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
set to 1-shot mode and is always loaded with the time until the next edge
of any live waveforms.
Up to one waveform generator per pin supported.
Each waveform generator is synchronized to the ESP cycle counter, not the
Each waveform generator is synchronized to the ESP clock cycle counter, not the
timer. This allows for removing interrupt jitter and delay as the counter
always increments once per 80MHz clock. Changes to a waveform are
contiguous and only take effect on the next waveform transition,
@ -19,8 +19,9 @@
This replaces older tone(), analogWrite(), and the Servo classes.
Everywhere in the code where "cycles" is used, it means ESP.getCycleTime()
cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz).
Everywhere in the code where "cycles" is used, it means ESP.getCycleCount()
clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1
cycles (which may be 2 CPU clock cycles @ 160MHz).
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -112,10 +113,10 @@ void setTimer1Callback(uint32_t (*fn)()) {
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
// first, then it will immediately begin.
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) {
return startWaveformCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS));
return startWaveformClockCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS));
}
int startWaveformCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) {
int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) {
if ((pin > 16) || isFlashInterfacePin(pin)) {
return false;
}
@ -177,17 +178,15 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
}
// If user sends in a pin >16 but <32, this will always point to a 0 bit
// If they send >=32, then the shift will result in 0 and it will also return false
uint32_t mask = 1<<pin;
if (!(waveformEnabled & mask)) {
return false; // It's not running, nothing to do here
}
waveformToDisable |= mask;
// Ensure timely service....
if (T1L > microsecondsToClockCycles(10)) {
timer1_write(microsecondsToClockCycles(10));
}
while (waveformToDisable) {
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
if (waveformEnabled & (1UL << pin)) {
waveformToDisable = 1UL << pin;
// Must not interfere if Timer is due shortly
if (T1L > microsecondsToClockCycles(10)) {
timer1_write(microsecondsToClockCycles(10));
}
while (waveformToDisable) {
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
}
}
if (!waveformEnabled && !timer1CB) {
deinitTimer();

View File

@ -5,13 +5,13 @@
Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
The core idea is to have a programmable waveform generator with a unique
high and low period (defined in microseconds). TIMER1 is set to 1-shot
mode and is always loaded with the time until the next edge of any live
waveforms.
high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
set to 1-shot mode and is always loaded with the time until the next edge
of any live waveforms.
Up to one waveform generator per pin supported.
Each waveform generator is synchronized to the ESP cycle counter, not the
Each waveform generator is synchronized to the ESP clock cycle counter, not the
timer. This allows for removing interrupt jitter and delay as the counter
always increments once per 80MHz clock. Changes to a waveform are
contiguous and only take effect on the next waveform transition,
@ -19,8 +19,9 @@
This replaces older tone(), analogWrite(), and the Servo classes.
Everywhere in the code where "cycles" is used, it means ESP.getCycleTime()
cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz).
Everywhere in the code where "cycles" is used, it means ESP.getCycleCount()
clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1
cycles (which may be 2 CPU clock cycles @ 160MHz).
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -50,8 +51,10 @@ extern "C" {
// If runtimeUS > 0 then automatically stop it after that many usecs.
// Returns true or false on success or failure.
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS);
// Same as above, but pass in CPU clock cycles instead of microseconds
int startWaveformCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles);
// Start or change a waveform of the specified high and low CPU clock cycles on specific pin.
// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles.
// Returns true or false on success or failure.
int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles);
// Stop a waveform, if any, on the specified pin.
// Returns true or false on success or failure.
int stopWaveform(uint8_t pin);

View File

@ -25,7 +25,6 @@
#include "core_esp8266_waveform.h"
extern "C" {
#include "user_interface.h"
static uint32_t analogMap = 0;
static int32_t analogScale = PWMRANGE;
@ -51,7 +50,7 @@ extern void __analogWrite(uint8_t pin, int val) {
if (pin > 16) {
return;
}
uint32_t analogPeriod = (1000000L * system_get_cpu_freq()) / analogFreq;
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
if (val < 0) {
val = 0;
} else if (val > analogScale) {
@ -63,13 +62,11 @@ extern void __analogWrite(uint8_t pin, int val) {
uint32_t low = analogPeriod - high;
pinMode(pin, OUTPUT);
if (low == 0) {
stopWaveform(pin);
digitalWrite(pin, HIGH);
} else if (high == 0) {
stopWaveform(pin);
digitalWrite(pin, LOW);
} else {
if (startWaveformCycles(pin, high, low, 0)) {
if (startWaveformClockCycles(pin, high, low, 0)) {
analogMap |= (1 << pin);
}
}

View File

@ -250,7 +250,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
#define UIFF 0 //RX FIFO Full
//UART STATUS Registers Bits
#define USTX 31 //TX PIN Level
#define USTX 31 //TX PIN Level (Doesn't seem to work, always reads as 0 for both uarts. HW bug? Possible workaround: Enable loopback UxC0 |= 1<<UCLBE and read USRXD, see https://github.com/esp8266/Arduino/issues/7256 for discussion.)
#define USRTS 30 //RTS PIN Level
#define USDTR 39 //DTR PIN Level
#define USTXC 16 //TX FIFO COUNT (8bit)

View File

@ -0,0 +1,42 @@
/*
flash_quirks.h
Copyright (c) 2019 Mike Nix. 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 FLASH_QUIRKS_H
#define FLASH_QUIRKS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "spi_vendors.h"
#include "spi_flash_defs.h"
namespace experimental {
void initFlashQuirks();
} // namespace experimental
#ifdef __cplusplus
}
#endif
#endif // FLASH_QUIRKS_H

View File

@ -0,0 +1,44 @@
/*
spi_flash_defs.h - SPI Flash chip commands and status registers
Copyright (c) 2019 Mike Nix. 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 SPI_FLASH_DEFS_H
#define SPI_FLASH_DEFS_H
// Flash chip Status Register 3: Vendor XMC Output Drive levels
#define SPI_FLASH_SR3_XMC_DRV_25 1
#define SPI_FLASH_SR3_XMC_DRV_50 0
#define SPI_FLASH_SR3_XMC_DRV_75 2
#define SPI_FLASH_SR3_XMC_DRV_100 3
#define SPI_FLASH_SR3_XMC_DRV_S 5
#define SPI_FLASH_SR3_XMC_DRV_MASK 0x03
// Flash Chip commands
#define SPI_FLASH_CMD_RSR1 0x05 //Read Flash Status Register...
#define SPI_FLASH_CMD_RSR2 0x35
#define SPI_FLASH_CMD_RSR3 0x15
#define SPI_FLASH_CMD_WSR1 0x01 //Write Flash Status Register...
#define SPI_FLASH_CMD_WSR2 0x31
#define SPI_FLASH_CMD_WSR3 0x11
#define SPI_FLASH_CMD_WEVSR 0x50 //Write Enable Volatile Status Registers
#define SPI_FLASH_CMD_WREN 0x06 //Write Enable
#define SPI_FLASH_CMD_WRDI 0x04 //Write Disable
#endif // SPI_FLASH_DEFS_H

View File

@ -0,0 +1,40 @@
/*
spi_vendors.h - Vendor IDs for SPI chips
Copyright (c) 2019 Mike Nix. 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 SPI_VENDORS_H
#define SPI_VENDORS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Definitions are placed in eboot. Include them here rather than duplicate them.
* Also, prefer to have eboot standalone as much as possible and have the core depend on it
* rather than have eboot depend on the core.
*/
#include <../../bootloaders/eboot/spi_vendors.h>
#ifdef __cplusplus
}
#endif
#endif // SPI_VENDORS_H

View File

@ -37,8 +37,8 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
return flash_hal_read(addr, size, dst);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
namespace spiffs_impl {
@ -149,6 +149,8 @@ extern "C" void spiffs_request_end(void)
SPIFFS.end();
}
#pragma GCC diagnostic pop
#endif // ARDUINO
#endif // !CORE_MOCK

View File

@ -171,7 +171,7 @@ public:
return false;
}
_cfg = *static_cast<const SPIFFSConfig *>(&cfg);
return true;
return true;
}
bool begin() override

View File

@ -186,6 +186,14 @@ void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, c
/*** end of posix replacement ***/
}
void setTZ(const char* tz){
char tzram[strlen_P(tz) + 1];
memcpy_P(tzram, tz, sizeof(tzram));
setenv("TZ", tzram, 1/*overwrite*/);
tzset();
}
void configTime(const char* tz, const char* server1, const char* server2, const char* server3)
{
sntp_stop();
@ -193,10 +201,8 @@ void configTime(const char* tz, const char* server1, const char* server2, const
setServer(0, server1);
setServer(1, server2);
setServer(2, server3);
char tzram[strlen_P(tz) + 1];
memcpy_P(tzram, tz, sizeof(tzram));
setenv("TZ", tzram, 1/*overwrite*/);
tzset();
setTZ(tz);
sntp_init();
}

View File

@ -344,7 +344,7 @@ Parameters in Arduino IDE:
~~~~~~~~~~~~~~~~~~~~~~~~~~
- Card: "WEMOS D1 Mini Lite"
- Flash Size: "1M (512K SPIFFS)"
- Flash Size: "1M (512K FS)"
- CPU Frequency: "80 Mhz"
Power:

View File

@ -102,9 +102,9 @@ The web browser you're using to read this document keeps a list of 100s of certi
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 SPIFFS or SD when uploading your application binary, and pass it to a `BearSSL::CertStore()` in order to validate TLS peers.
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 as the `BearSSL::CertStore` requires the creation of a cookie-cutter object for filesystem access (because the SD and SPIFFS filesystems are presently incompatible with each other). At a high level in your `setup()` you will call `BearSSL::initCertStore()` on a global object, and then pass this global certificate store to `client.setCertStore(&gCA)` before every connection attempt to enable it as a validation option.
See the `BearSSL_CertStore` example for full details.
Supported Crypto
~~~~~~~~~~~~~~~~

View File

@ -42,23 +42,24 @@ Load client certificate from file system.
.. code:: cpp
#include <FS.h>
#include <LittleFS.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
const char* certyficateFile = "/client.cer";
const char* certificateFile = "/client.cer";
*setup() or loop()*
.. code:: cpp
if (!SPIFFS.begin())
if (!LittleFS.begin())
{
Serial.println("Failed to mount the file system");
return;
}
Serial.printf("Opening %s", certyficateFile);
File crtFile = SPIFFS.open(certyficateFile, "r");
Serial.printf("Opening %s", certificateFile);
File crtFile = LittleFS.open(certificateFile, "r");
if (!crtFile)
{
Serial.println(" Failed!");
@ -66,7 +67,7 @@ Load client certificate from file system.
WiFiClientSecure client;
Serial.print("Loading %s", certyficateFile);
Serial.print("Loading %s", certificateFile);
if (!client.loadCertificate(crtFile))
{
Serial.println(" Failed!");

View File

@ -410,7 +410,7 @@ Get the DHCP hostname assigned to ESP station.
WiFi.hostname()
Function returns ``String`` type. Default hostname is in format ``ESP_24xMAC``\ where 24xMAC are the last 24 bits of module's MAC address.
Function returns ``String`` type. Default hostname is in format ``ESP_24xMAC`` where 24xMAC are the last 24 bits of module's MAC address.
The hostname may be changed using the following function:
@ -644,13 +644,13 @@ The Smart Config connection of an ESP module an access point is done by sniffing
The following three functions are provided to implement Smart Config.
Start smart configuration mode by sniffing for special packets that contain SSID and password of desired Access Point. Depending on result either ``true`` or \`false is returned.
Start smart configuration mode by sniffing for special packets that contain SSID and password of desired Access Point. Depending on result either ``true`` or ``false`` is returned.
.. code:: cpp
beginSmartConfig()
Query Smart Config status, to decide when stop configuration. Function returns either ``true`` or ``false of``\ boolean\` type.
Query Smart Config status, to decide when stop configuration. Function returns either ``true`` or ``false`` of ``boolean`` type.
.. code:: cpp

View File

@ -79,7 +79,7 @@ perform. It is not listed among libraries verified to work with ESP8266.
`Read more <a03-library-does-not-work.rst>`__.
In the IDE, for ESP-12E that has 4M flash, I can choose 4M (1M SPIFFS) or 4M (3M SPIFFS). No matter what I select, the IDE tells me the maximum code space is about 1M. Where does my flash go?
In the IDE, for ESP-12E that has 4M flash, I can choose 4M (1M FS) or 4M (3M FS). No matter what I select, the IDE tells me the maximum code space is about 1M. Where does my flash go?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The reason we cannot have more than 1MB of code in flash has to do with
@ -90,7 +90,7 @@ total, but switching such "banks" on the fly is not easy and efficient,
so we don't bother doing that. Besides, no one has so far complained
about 1MB of code space being insufficient for practical purposes.
The option to choose 3M or 1M SPIFFS is to optimize the upload time.
The option to choose 3M or 1M filesystem is to optimize the upload time.
Uploading 3MB takes a long time so sometimes you can just use 1MB. Other
2MB of flash can still be used with ``ESP.flashRead`` and
``ESP.flashWrite`` APIs if necessary.

View File

@ -63,6 +63,16 @@ following include to the sketch:
#include "FS.h"
SPIFFS Deprecation Warning
--------------------------
SPIFFS is currently deprecated and may be removed in future releases of
the core. Please consider moving your code to LittleFS. SPIFFS is not
actively supported anymore by the upstream developer, while LittleFS is
under active development, supports real directories, and is many times
faster for most operations.
SPIFFS and LittleFS
-------------------

View File

@ -244,6 +244,6 @@ BeagleBone, CubieBoard).
- `What is PlatformIO? <https://docs.platformio.org/en/latest/what-is-platformio.html?utm_source=arduino-esp8266>`__
- `PlatformIO IDE <https://platformio.org/platformio-ide?utm_source=arduino-esp8266>`__
- `PlatformIO Core <https://docs.platformio.org/en/latest/core.html?utm_source=arduino-esp8266>`__ (command line tool)
- `Advanced usage <https://docs.platformio.org/en/latest/platforms/espressif8266.html?utm_source=arduino-esp8266>`__ - custom settings, uploading to SPIFFS, Over-the-Air (OTA), staging version
- `Advanced usage <https://docs.platformio.org/en/latest/platforms/espressif8266.html?utm_source=arduino-esp8266>`__ - custom settings, uploading to LittleFS, Over-the-Air (OTA), staging version
- `Integration with Cloud and Standalone IDEs <https://docs.platformio.org/en/latest/ide.html?utm_source=arduino-esp8266>`__ - Cloud9, Codeanywhere, Eclipse Che (Codenvy), Atom, CLion, Eclipse, Emacs, NetBeans, Qt Creator, Sublime Text, VIM, Visual Studio, and VSCode
- `Project Examples <https://docs.platformio.org/en/latest/platforms/espressif8266.html?utm_source=arduino-esp8266#examples>`__

View File

@ -25,7 +25,7 @@ This is a bit different from standard EEPROM class. You need to call ``EEPROM.be
``EEPROM.write`` does not write to flash immediately, instead you must call ``EEPROM.commit()`` whenever you wish to save changes to flash. ``EEPROM.end()`` will also commit, and will release the RAM copy of EEPROM contents.
EEPROM library uses one sector of flash located just after the SPIFFS.
EEPROM library uses one sector of flash located just after the embedded filesystem.
`Three examples <https://github.com/esp8266/Arduino/tree/master/libraries/EEPROM>`__ included.
@ -113,6 +113,8 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab
``ESP.getCycleCount()`` returns the cpu instruction cycle count since start as an unsigned 32-bit. This is useful for accurate timing of very short actions like bit banging.
``ESP.random()`` should be used to generate true random numbers on the ESP. Returns an unsigned 32-bit integer with the random number. An alternate version is also available that fills an array of arbitrary length. Note that it seems as though the WiFi needs to be enabled to generate entropy for the random numbers, otherwise pseudo-random numbers are used.
``ESP.checkFlashCRC()`` calculates the CRC of the program memory (not including any filesystems) and compares it to the one embedded in the image. If this call returns ``false`` then the flash has been corrupted. At that point, you may want to consider trying to send a MQTT message, to start a re-download of the application, blink a LED in an `SOS` pattern, etc. However, since the flash is known corrupted at this point there is no guarantee the app will be able to perform any of these operations, so in safety critical deployments an immediate shutdown to a fail-safe mode may be indicated.
``ESP.getVcc()`` may be used to measure supply voltage. ESP needs to reconfigure the ADC at startup in order for this feature to be available. Add the following line to the top of your sketch to use ``getVcc``:

View File

@ -360,7 +360,7 @@ If this is the case, then most likely ESP module has not been reset after initia
The most common causes of OTA failure are as follows:
- not enough physical memory on the chip (e.g. ESP01 with 512K flash memory is not enough for OTA).
- too much memory declared for SPIFFS so new sketch will not fit between existing sketch and SPIFFS see `Update process - memory view <#update-process-memory-view>`__.
- too much memory declared for the filesystem so new sketch will not fit between existing sketch and the filesystem see `Update process - memory view <#update-process-memory-view>`__.
- too little memory declared in Arduino IDE for your selected board (i.e. less than physical size).
- not resetting the ESP module after initial upload using serial port.

View File

@ -8,9 +8,11 @@ DNSServer dnsServer;
ESP8266WebServer webServer(80);
String responseHTML = ""
"<!DOCTYPE html><html><head><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>";
"<!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);

View File

@ -30,8 +30,8 @@ const char *softAP_password = APPSK;
const char *myHostname = "esp8266";
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
char ssid[32] = "";
char password[32] = "";
char ssid[33] = "";
char password[65] = "";
// DNS server
const byte DNS_PORT = 53;
@ -56,7 +56,7 @@ unsigned int status = WL_IDLE_STATUS;
void setup() {
delay(1000);
Serial.begin(9600);
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
@ -140,4 +140,3 @@ void loop() {
//HTTP
server.handleClient();
}

View File

@ -9,7 +9,9 @@ void handleRoot() {
String Page;
Page += F(
"<html><head></head><body>"
"<!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>");
@ -43,7 +45,9 @@ void handleWifi() {
String Page;
Page += F(
"<html><head></head><body>"
"<!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>");
@ -130,4 +134,3 @@ void handleNotFound() {
server.sendHeader("Expires", "-1");
server.send(404, "text/plain", message);
}

View File

@ -15,9 +15,6 @@ byte value;
void setup() {
// initialize serial and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
EEPROM.begin(512);
}

View File

@ -8,8 +8,6 @@
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define USE_SERIAL Serial
/* this can be run with an emulated server on host:
cd esp8266-core-root-dir
cd tests/host
@ -27,21 +25,21 @@
void setup() {
USE_SERIAL.begin(115200);
Serial.begin(115200);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
Serial.println();
Serial.println();
Serial.println();
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
USE_SERIAL.print(".");
Serial.print(".");
}
USE_SERIAL.println("");
USE_SERIAL.print("Connected! IP address: ");
USE_SERIAL.println(WiFi.localIP());
Serial.println("");
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());
}
@ -52,29 +50,29 @@ void loop() {
WiFiClient client;
HTTPClient http;
USE_SERIAL.print("[HTTP] begin...\n");
Serial.print("[HTTP] begin...\n");
// configure traged server and url
http.begin(client, "http://" SERVER_IP "/postplain/"); //HTTP
http.addHeader("Content-Type", "application/json");
USE_SERIAL.print("[HTTP] POST...\n");
Serial.print("[HTTP] POST...\n");
// start connection and send HTTP header and body
int httpCode = http.POST("{\"hello\":\"world\"}");
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
USE_SERIAL.printf("[HTTP] POST... code: %d\n", httpCode);
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
const String& payload = http.getString();
USE_SERIAL.println("received payload:\n<<");
USE_SERIAL.println(payload);
USE_SERIAL.println(">>");
Serial.println("received payload:\n<<");
Serial.println(payload);
Serial.println(">>");
}
} else {
USE_SERIAL.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();

View File

@ -1,5 +1,6 @@
/*
FSWebServer - Example WebServer with SPIFFS backend for esp8266
FSBrowser - A web-based FileSystem Browser for ESP8266 filesystems
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266WebServer library for Arduino environment.
@ -7,30 +8,65 @@
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
upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE)
or you can upload the contents of a folder if you CD in that folder and run the following command:
for file in `\ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done
access the sample web page at http://esp8266fs.local
edit the page by going to http://esp8266fs.local/edit
See readme.md for more information.
*/
////////////////////////////////
// Select the FileSystem by uncommenting one of the lines below
//#define USE_SPIFFS
#define USE_LITTLEFS
//#define USE_SDFS
// Uncomment the following line to embed a version of the web page in the code
// (program code will be larger, but no file will have to be written to the filesystem).
// Note: the source file "extras/index_htm.h" must have been generated by "extras/reduce_index.sh"
//#define INCLUDE_FALLBACK_INDEX_HTM
////////////////////////////////
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h>
#include <LittleFS.h>
#include <SPI.h>
#ifdef INCLUDE_FALLBACK_INDEX_HTM
#include "extras/index_htm.h"
#endif
#if defined USE_SPIFFS
#include <FS.h>
const char* fsName = "SPIFFS";
FS* fileSystem = &SPIFFS;
SPIFFSConfig fileSystemConfig = SPIFFSConfig();
#elif defined USE_LITTLEFS
#include <LittleFS.h>
const char* fsName = "LittleFS";
FS* fileSystem = &LittleFS;
LittleFSConfig fileSystemConfig = LittleFSConfig();
#elif defined USE_SDFS
#include <SDFS.h>
const char* fsName = "SDFS";
FS* fileSystem = &SDFS;
SDFSConfig fileSystemConfig = SDFSConfig();
// fileSystemConfig.setCSPin(chipSelectPin);
#else
#error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch.
#endif
//FS* filesystem = &SPIFFS;
FS* filesystem = &LittleFS;
#define DBG_OUTPUT_PORT Serial
@ -41,154 +77,123 @@ FS* filesystem = &LittleFS;
const char* ssid = STASSID;
const char* password = STAPSK;
const char* host = "esp8266fs";
const char* host = "fsbrowser";
ESP8266WebServer server(80);
//holds the current upload
File fsUploadFile;
//format bytes
String formatBytes(size_t bytes) {
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
static bool fsOK;
String unsupportedFiles = String();
File uploadFile;
static const char TEXT_PLAIN[] PROGMEM = "text/plain";
static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR";
static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound";
////////////////////////////////
// Utils to return HTTP codes, and determine content-type
void replyOK() {
server.send(200, FPSTR(TEXT_PLAIN), "");
}
void replyOKWithMsg(String msg) {
server.send(200, FPSTR(TEXT_PLAIN), msg);
}
void replyNotFound(String msg) {
server.send(404, FPSTR(TEXT_PLAIN), msg);
}
void replyBadRequest(String msg) {
DBG_OUTPUT_PORT.println(msg);
server.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n");
}
void replyServerError(String msg) {
DBG_OUTPUT_PORT.println(msg);
server.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n");
}
#ifdef USE_SPIFFS
/*
Checks filename for character combinations that are not supported by FSBrowser (alhtough valid on SPIFFS).
Returns an empty String if supported, or detail of error(s) if unsupported
*/
String checkForUnsupportedPath(String filename) {
String error = String();
if (!filename.startsWith("/")) {
error += F("!NO_LEADING_SLASH! ");
}
if (filename.indexOf("//") != -1) {
error += F("!DOUBLE_SLASH! ");
}
if (filename.endsWith("/")) {
error += F("!TRAILING_SLASH! ");
}
return error;
}
#endif
////////////////////////////////
// Request handlers
/*
Return the FS type, status and size info
*/
void handleStatus() {
DBG_OUTPUT_PORT.println("handleStatus");
FSInfo fs_info;
String json;
json.reserve(128);
json = "{\"type\":\"";
json += fsName;
json += "\", \"isOk\":";
if (fsOK) {
fileSystem->info(fs_info);
json += F("\"true\", \"totalBytes\":\"");
json += fs_info.totalBytes;
json += F("\", \"usedBytes\":\"");
json += fs_info.usedBytes;
json += "\"";
} else {
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
json += "\"false\"";
}
json += F(",\"unsupportedFiles\":\"");
json += unsupportedFiles;
json += "\"}";
server.send(200, "application/json", json);
}
String getContentType(String filename) {
if (server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
}
return "text/plain";
}
bool handleFileRead(String path) {
DBG_OUTPUT_PORT.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (filesystem->exists(pathWithGz) || filesystem->exists(path)) {
if (filesystem->exists(pathWithGz)) {
path += ".gz";
}
File file = filesystem->open(path, "r");
server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if (!filename.startsWith("/")) {
filename = "/" + filename;
}
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
fsUploadFile = filesystem->open(filename, "w");
filename.clear();
} else if (upload.status == UPLOAD_FILE_WRITE) {
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
if (fsUploadFile) {
fsUploadFile.write(upload.buf, upload.currentSize);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) {
fsUploadFile.close();
}
DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void handleFileDelete() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileDelete: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (!filesystem->exists(path)) {
return server.send(404, "text/plain", "FileNotFound");
}
filesystem->remove(path);
server.send(200, "text/plain", "");
path.clear();
}
void handleFileCreate() {
if (server.args() == 0) {
return server.send(500, "text/plain", "BAD ARGS");
}
String path = server.arg(0);
DBG_OUTPUT_PORT.println("handleFileCreate: " + path);
if (path == "/") {
return server.send(500, "text/plain", "BAD PATH");
}
if (filesystem->exists(path)) {
return server.send(500, "text/plain", "FILE EXISTS");
}
File file = filesystem->open(path, "w");
if (file) {
file.close();
} else {
return server.send(500, "text/plain", "CREATE FAILED");
}
server.send(200, "text/plain", "");
path.clear();
}
/*
Return the list of files in the directory specified by the "dir" query string parameter.
Also demonstrates the use of chuncked responses.
*/
void handleFileList() {
if (!fsOK) {
return replyServerError(FPSTR(FS_INIT_ERROR));
}
if (!server.hasArg("dir")) {
server.send(500, "text/plain", "BAD ARGS");
return;
return replyBadRequest(F("DIR ARG MISSING"));
}
String path = server.arg("dir");
DBG_OUTPUT_PORT.println("handleFileList: " + path);
Dir dir = filesystem->openDir(path);
if (path != "/" && !fileSystem->exists(path)) {
return replyBadRequest("BAD PATH");
}
DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
Dir dir = fileSystem->openDir(path);
path.clear();
// use HTTP/1.1 Chunked response to avoid building a huge temporary string
if (!server.chunkedResponseModeStart(200, "text/json")) {
server.send(505, FPSTR("text/html"), FPSTR("HTTP1.1 required"));
server.send(505, F("text/html"), F("HTTP1.1 required"));
return;
}
@ -196,7 +201,13 @@ void handleFileList() {
String output;
output.reserve(64);
while (dir.next()) {
#ifdef USE_SPIFFS
String error = checkForUnsupportedPath(dir.fileName());
if (error.length() > 0) {
DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName());
continue;
}
#endif
if (output.length()) {
// send string from previous iteration
// as an HTTP chunk
@ -206,18 +217,23 @@ void handleFileList() {
output = '[';
}
File entry = dir.openFile("r");
bool isDir = false;
output += "{\"type\":\"";
output += (isDir) ? "dir" : "file";
output += "\",\"name\":\"";
if (entry.name()[0] == '/') {
output += &(entry.name()[1]);
if (dir.isDirectory()) {
output += "dir";
} else {
output += entry.name();
output += F("file\",\"size\":\"");
output += dir.fileSize();
}
output += F("\",\"name\":\"");
// Always return names without leading "/"
if (dir.fileName()[0] == '/') {
output += &(dir.fileName()[1]);
} else {
output += dir.fileName();
}
output += "\"}";
entry.close();
}
// send last string
@ -226,85 +242,393 @@ void handleFileList() {
server.chunkedResponseFinalize();
}
void setup(void) {
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.print("\n");
DBG_OUTPUT_PORT.setDebugOutput(true);
filesystem->begin();
{
Dir dir = filesystem->openDir("/");
while (dir.next()) {
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
/*
Read the given file from the filesystem and stream it back to the client
*/
bool handleFileRead(String path) {
DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
if (!fsOK) {
replyServerError(FPSTR(FS_INIT_ERROR));
return true;
}
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType;
if (server.hasArg("download")) {
contentType = F("application/octet-stream");
} else {
contentType = mime::getContentType(path);
}
if (!fileSystem->exists(path)) {
// File not found, try gzip version
path = path + ".gz";
}
if (fileSystem->exists(path)) {
File file = fileSystem->open(path, "r");
if (server.streamFile(file, contentType) != file.size()) {
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
DBG_OUTPUT_PORT.printf("\n");
file.close();
return true;
}
return false;
}
//WIFI INIT
/*
As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed,
return the path of the closest parent still existing
*/
String lastExistingParent(String path) {
while (!path.isEmpty() && !fileSystem->exists(path)) {
if (path.lastIndexOf('/') > 0) {
path = path.substring(0, path.lastIndexOf('/'));
} else {
path = String(); // No slash => the top folder does not exist
}
}
DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path);
return path;
}
/*
Handle the creation/rename of a new file
Operation | req.responseText
---------------+--------------------------------------------------------------
Create file | parent of created file
Create folder | parent of created folder
Rename file | parent of source file
Move file | parent of source file, or remaining ancestor
Rename folder | parent of source folder
Move folder | parent of source folder, or remaining ancestor
*/
void handleFileCreate() {
if (!fsOK) {
return replyServerError(FPSTR(FS_INIT_ERROR));
}
String path = server.arg("path");
if (path.isEmpty()) {
return replyBadRequest(F("PATH ARG MISSING"));
}
#ifdef USE_SPIFFS
if (checkForUnsupportedPath(path).length() > 0) {
return replyServerError(F("INVALID FILENAME"));
}
#endif
if (path == "/") {
return replyBadRequest("BAD PATH");
}
if (fileSystem->exists(path)) {
return replyBadRequest(F("PATH FILE EXISTS"));
}
String src = server.arg("src");
if (src.isEmpty()) {
// No source specified: creation
DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
if (path.endsWith("/")) {
// Create a folder
path.remove(path.length() - 1);
if (!fileSystem->mkdir(path)) {
return replyServerError(F("MKDIR FAILED"));
}
} else {
// Create a file
File file = fileSystem->open(path, "w");
if (file) {
file.write((const char *)0);
file.close();
} else {
return replyServerError(F("CREATE FAILED"));
}
}
if (path.lastIndexOf('/') > -1) {
path = path.substring(0, path.lastIndexOf('/'));
}
replyOKWithMsg(path);
} else {
// Source specified: rename
if (src == "/") {
return replyBadRequest("BAD SRC");
}
if (!fileSystem->exists(src)) {
return replyBadRequest(F("SRC FILE NOT FOUND"));
}
DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
if (path.endsWith("/")) {
path.remove(path.length() - 1);
}
if (src.endsWith("/")) {
src.remove(src.length() - 1);
}
if (!fileSystem->rename(src, path)) {
return replyServerError(F("RENAME FAILED"));
}
replyOKWithMsg(lastExistingParent(src));
}
}
/*
Delete the file or folder designed by the given path.
If it's a file, delete it.
If it's a folder, delete all nested contents first then the folder itself
IMPORTANT NOTE: using recursion is generally not recommended on embedded devices and can lead to crashes (stack overflow errors).
This use is just for demonstration purpose, and FSBrowser might crash in case of deeply nested filesystems.
Please don't do this on a production system.
*/
void deleteRecursive(String path) {
File file = fileSystem->open(path, "r");
bool isDir = file.isDirectory();
file.close();
// If it's a plain file, delete it
if (!isDir) {
fileSystem->remove(path);
return;
}
// Otherwise delete its contents first
Dir dir = fileSystem->openDir(path);
while (dir.next()) {
deleteRecursive(path + '/' + dir.fileName());
}
// Then delete the folder itself
fileSystem->rmdir(path);
}
/*
Handle a file deletion request
Operation | req.responseText
---------------+--------------------------------------------------------------
Delete file | parent of deleted file, or remaining ancestor
Delete folder | parent of deleted folder, or remaining ancestor
*/
void handleFileDelete() {
if (!fsOK) {
return replyServerError(FPSTR(FS_INIT_ERROR));
}
String path = server.arg(0);
if (path.isEmpty() || path == "/") {
return replyBadRequest("BAD PATH");
}
DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
if (!fileSystem->exists(path)) {
return replyNotFound(FPSTR(FILE_NOT_FOUND));
}
deleteRecursive(path);
replyOKWithMsg(lastExistingParent(path));
}
/*
Handle a file upload request
*/
void handleFileUpload() {
if (!fsOK) {
return replyServerError(FPSTR(FS_INIT_ERROR));
}
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
// Make sure paths always start with "/"
if (!filename.startsWith("/")) {
filename = "/" + filename;
}
DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
uploadFile = fileSystem->open(filename, "w");
if (!uploadFile) {
return replyServerError(F("CREATE FAILED"));
}
DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (uploadFile) {
size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize);
if (bytesWritten != upload.currentSize) {
return replyServerError(F("WRITE FAILED"));
}
}
DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
if (uploadFile) {
uploadFile.close();
}
DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
}
}
/*
The "Not Found" handler catches all URI not explicitely declared in code
First try to find and return the requested file from the filesystem,
and if it fails, return a 404 page with debug information
*/
void handleNotFound() {
if (!fsOK) {
return replyServerError(FPSTR(FS_INIT_ERROR));
}
String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
if (handleFileRead(uri)) {
return;
}
// Dump debug data
String message;
message.reserve(100);
message = F("Error: File not found\n\nURI: ");
message += uri;
message += F("\nMethod: ");
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += F("\nArguments: ");
message += server.args();
message += '\n';
for (uint8_t i = 0; i < server.args(); i++) {
message += F(" NAME:");
message += server.argName(i);
message += F("\n VALUE:");
message += server.arg(i);
message += '\n';
}
message += "path=";
message += server.arg("path");
message += '\n';
DBG_OUTPUT_PORT.print(message);
return replyNotFound(message);
}
/*
This specific handler returns the index.htm (or a gzipped version) from the /edit folder.
If the file is not present but the flag INCLUDE_FALLBACK_INDEX_HTM has been set, falls back to the version
embedded in the program code.
Otherwise, fails with a 404 page with debug information
*/
void handleGetEdit() {
if (handleFileRead(F("/edit/index.htm"))) {
return;
}
#ifdef INCLUDE_FALLBACK_INDEX_HTM
server.sendHeader(F("Content-Encoding"), "gzip");
server.send(200, "text/html", index_htm_gz, index_htm_gz_len);
#else
replyNotFound(FPSTR(FILE_NOT_FOUND));
#endif
}
void setup(void) {
////////////////////////////////
// SERIAL INIT
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.setDebugOutput(true);
DBG_OUTPUT_PORT.print('\n');
////////////////////////////////
// FILESYSTEM INIT
fileSystemConfig.setAutoFormat(false);
fileSystem->setConfig(fileSystemConfig);
fsOK = fileSystem->begin();
DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!"));
#ifdef USE_SPIFFS
// Debug: dump on console contents of filessytem with no filter and check filenames validity
Dir dir = fileSystem->openDir("");
DBG_OUTPUT_PORT.println(F("List of files at root of filesystem:"));
while (dir.next()) {
String error = checkForUnsupportedPath(dir.fileName());
String fileInfo = dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + "b)");
DBG_OUTPUT_PORT.println(error + fileInfo);
if (error.length() > 0) {
unsupportedFiles += error + fileInfo + '\n';
}
}
DBG_OUTPUT_PORT.println();
// Keep the "unsupportedFiles" variable to show it, but clean it up
unsupportedFiles.replace("\n", "<br/>");
unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5);
#endif
////////////////////////////////
// WI-FI INIT
DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DBG_OUTPUT_PORT.print(".");
}
DBG_OUTPUT_PORT.println("");
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.print(F("Connected! IP address: "));
DBG_OUTPUT_PORT.println(WiFi.localIP());
MDNS.begin(host);
DBG_OUTPUT_PORT.print("Open http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local/edit to see the file browser");
////////////////////////////////
// MDNS INIT
if (MDNS.begin(host)) {
MDNS.addService("http", "tcp", 80);
DBG_OUTPUT_PORT.print(F("Open http://"));
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(F(".local/edit to open the FileSystem Browser"));
}
////////////////////////////////
// WEB SERVER INIT
//SERVER INIT
//list directory
// Filesystem status
server.on("/status", HTTP_GET, handleStatus);
// List directory
server.on("/list", HTTP_GET, handleFileList);
//load editor
server.on("/edit", HTTP_GET, []() {
if (!handleFileRead("/edit.htm")) {
server.send(404, "text/plain", "FileNotFound");
}
});
//create file
server.on("/edit", HTTP_PUT, handleFileCreate);
//delete file
server.on("/edit", HTTP_DELETE, handleFileDelete);
//first callback is called after the request has ended with all parsed arguments
//second callback handles file uploads at that location
server.on("/edit", HTTP_POST, []() {
server.send(200, "text/plain", "");
}, handleFileUpload);
//called when the url is not defined here
//use it to load content from SPIFFS
server.onNotFound([]() {
if (!handleFileRead(server.uri())) {
server.send(404, "text/plain", "FileNotFound");
}
});
// Load editor
server.on("/edit", HTTP_GET, handleGetEdit);
//get heap status, analog input value and all GPIO statuses in one json call
server.on("/all", HTTP_GET, []() {
String json('{');
json += "\"heap\":" + String(ESP.getFreeHeap());
json += ", \"analog\":" + String(analogRead(A0));
json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16)));
json += "}";
server.send(200, "text/json", json);
json.clear();
});
// Create file
server.on("/edit", HTTP_PUT, handleFileCreate);
// Delete file
server.on("/edit", HTTP_DELETE, handleFileDelete);
// Upload file
// - first callback is called after the request has ended with all parsed arguments
// - second callback handles file upload at that location
server.on("/edit", HTTP_POST, replyOK, handleFileUpload);
// Default handler for all URIs not defined above
// Use it to read files from filesystem
server.onNotFound(handleNotFound);
// Start server
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
}
void loop(void) {
server.handleClient();
MDNS.update();

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +1,22 @@
<!--
FSWebServer - Example Index Page
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266WebServer library 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
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Monitor</title>
<script type="text/javascript" src="graphs.js"></script>
<script type="text/javascript">
var heap,temp,digi;
var reloadPeriod = 1000;
var running = false;
function loadValues(){
if(!running) return;
var xh = new XMLHttpRequest();
xh.onreadystatechange = function(){
if (xh.readyState == 4){
if(xh.status == 200) {
var res = JSON.parse(xh.responseText);
heap.add(res.heap);
temp.add(res.analog);
digi.add(res.gpio);
if(running) setTimeout(loadValues, reloadPeriod);
} else running = false;
}
};
xh.open("GET", "/all", true);
xh.send(null);
};
function run(){
if(!running){
running = true;
loadValues();
}
<title>ESP Index</title>
<style>
body {
background-color:black;
color:white;
}
</style>
<script type="text/javascript">
function onBodyLoad(){
var refreshInput = document.getElementById("refresh-rate");
refreshInput.value = reloadPeriod;
refreshInput.onchange = function(e){
var value = parseInt(e.target.value);
reloadPeriod = (value > 0)?value:0;
e.target.value = reloadPeriod;
}
var stopButton = document.getElementById("stop-button");
stopButton.onclick = function(e){
running = false;
}
var startButton = document.getElementById("start-button");
startButton.onclick = function(e){
run();
}
// Example with 10K thermistor
//function calcThermistor(v) {
// var t = Math.log(((10230000 / v) - 10000));
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15;
// return (t>120)?0:Math.round(t*10)/10;
//}
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor);
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan");
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange");
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold");
run();
console.log("we are loaded!!");
}
</script>
</head>
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()">
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);">
<label>Period (ms):</label>
<input type="number" id="refresh-rate"/>
<input type="button" id="start-button" value="Start"/>
<input type="button" id="stop-button" value="Stop"/>
</div>
<div id="heap"></div>
<div id="analog"></div>
<div id="digital"></div>
<body id="index" onload="onBodyLoad()">
<h1>ESP8266 Pin Functions</h1>
<img src="pins.png" />
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,529 @@
// WARNING: Auto-generated file. Please do not modify by hand.
// This file is an embeddable version of the gzipped index.htm file.
// To update it, please rerun the `reduce_index.sh` script located in the `extras` subfolder
// then recompile the sketch after each change to the `index.html` file.
unsigned char index_htm_gz[] = {
0x1f, 0x8b, 0x08, 0x08, 0x96, 0xc9, 0xa8, 0x5e, 0x00, 0x03, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xdc, 0x3b, 0x89, 0x7b,
0xda, 0xb8, 0x97, 0xff, 0x8a, 0xe3, 0xee, 0x24, 0xf6, 0x02, 0x06, 0x92,
0xe6, 0x28, 0xc4, 0xc9, 0xe4, 0x4e, 0x9a, 0xb3, 0xb9, 0xd3, 0x6e, 0xf7,
0xfb, 0x04, 0x16, 0xa0, 0xc4, 0xd8, 0xae, 0x2d, 0x07, 0x48, 0xca, 0xfe,
0xed, 0xfb, 0x9e, 0xe4, 0x13, 0x4c, 0x32, 0xdd, 0x9d, 0xf9, 0xcd, 0xec,
0xb6, 0xf3, 0x15, 0x5b, 0x96, 0x9e, 0xde, 0x7d, 0x49, 0xb3, 0x3e, 0xb7,
0x7b, 0xbe, 0x73, 0xfd, 0x70, 0xb1, 0xa7, 0xf4, 0x78, 0xdf, 0x56, 0x2e,
0x6e, 0xb6, 0x4f, 0x8e, 0x76, 0x14, 0xb5, 0x52, 0xad, 0xde, 0x2d, 0xed,
0x54, 0xab, 0xbb, 0xd7, 0xbb, 0xca, 0xfd, 0xe1, 0xf5, 0xe9, 0x89, 0x52,
0x37, 0x6a, 0xca, 0xb5, 0x4f, 0x9c, 0x80, 0x71, 0xe6, 0x3a, 0xc4, 0xae,
0x56, 0xf7, 0xce, 0x54, 0x45, 0xed, 0x71, 0xee, 0x35, 0xaa, 0xd5, 0xc1,
0x60, 0x60, 0x0c, 0x96, 0x0c, 0xd7, 0xef, 0x56, 0xaf, 0x2f, 0xab, 0x43,
0x84, 0x55, 0xc7, 0xc5, 0xd1, 0x63, 0x85, 0x67, 0x56, 0x1a, 0x16, 0xb7,
0xd4, 0x8d, 0x75, 0xb1, 0x9f, 0x4d, 0x9c, 0xae, 0x49, 0x9d, 0x8d, 0x75,
0xce, 0xb8, 0x4d, 0x37, 0xf6, 0x99, 0x4d, 0x95, 0x3e, 0x71, 0x48, 0x97,
0xfa, 0xeb, 0x55, 0x39, 0xb6, 0x1e, 0xf0, 0x11, 0x8e, 0x52, 0x8b, 0x11,
0x33, 0x68, 0xfb, 0x14, 0xa6, 0xb7, 0x5c, 0x6b, 0xf4, 0xda, 0x71, 0x1d,
0x5e, 0x09, 0xd8, 0x0b, 0x6d, 0xd4, 0x17, 0xbd, 0x61, 0x53, 0xbc, 0x76,
0x48, 0x9f, 0xd9, 0xa3, 0xc6, 0x2d, 0xf5, 0x2d, 0x80, 0x52, 0xde, 0xf2,
0x19, 0xb1, 0xcb, 0x57, 0xb0, 0x77, 0x25, 0xa0, 0x3e, 0xeb, 0x8c, 0x8d,
0x36, 0xcc, 0xa2, 0x43, 0x7e, 0x4a, 0x9d, 0xf0, 0xf5, 0xa5, 0xc2, 0x1c,
0x8b, 0x0e, 0x1b, 0x4b, 0xb5, 0x5a, 0xd3, 0x73, 0x25, 0x7a, 0x0d, 0xd2,
0x0a, 0x5c, 0x3b, 0xe4, 0xb4, 0x69, 0xd3, 0x0e, 0x6f, 0x2c, 0x03, 0xe0,
0x96, 0xeb, 0x5b, 0xd4, 0x6f, 0xd4, 0xbd, 0xa1, 0x02, 0x9f, 0x98, 0xa5,
0x7c, 0xf8, 0xf8, 0xf1, 0x63, 0xb3, 0x45, 0xda, 0x4f, 0x5d, 0xdf, 0x0d,
0x1d, 0xab, 0xd2, 0x76, 0x6d, 0xd7, 0x6f, 0x7c, 0xe8, 0x2c, 0xe3, 0xdf,
0xa6, 0xc5, 0x02, 0xcf, 0x26, 0xa3, 0x86, 0xe3, 0x3a, 0x14, 0xd6, 0x0e,
0x2b, 0x41, 0x8f, 0x58, 0xee, 0xa0, 0x51, 0x53, 0x6a, 0x4a, 0xbd, 0x06,
0x40, 0xfc, 0x6e, 0x8b, 0x68, 0xb5, 0x32, 0xfe, 0x35, 0x3e, 0xea, 0xcd,
0x0c, 0x19, 0xf5, 0x98, 0x8c, 0x01, 0x65, 0xdd, 0x1e, 0x6f, 0xac, 0xd6,
0x6a, 0x39, 0x8c, 0x95, 0xd0, 0x7e, 0xb5, 0x59, 0x00, 0xd3, 0x91, 0x25,
0x72, 0x07, 0xee, 0x7a, 0x8d, 0x9a, 0x44, 0xb6, 0xd6, 0xec, 0x13, 0xbf,
0xcb, 0x1c, 0x78, 0xf0, 0x88, 0x65, 0x31, 0xa7, 0xdb, 0x98, 0x58, 0x6f,
0xb3, 0xd7, 0x84, 0x50, 0x9f, 0xda, 0x84, 0xb3, 0x67, 0xda, 0xec, 0x33,
0xa7, 0x32, 0x60, 0x16, 0xef, 0x35, 0x56, 0x00, 0xbd, 0x66, 0x3b, 0xf4,
0x03, 0x20, 0xc7, 0x73, 0x19, 0xac, 0xf3, 0xf3, 0xeb, 0x03, 0x8f, 0x38,
0xaf, 0x11, 0xb9, 0xc8, 0x84, 0x98, 0x56, 0xe6, 0xd8, 0xcc, 0xa1, 0x95,
0x96, 0xed, 0xb6, 0x9f, 0x92, 0xbd, 0x57, 0xbc, 0xe1, 0xe4, 0xee, 0x8d,
0x9e, 0xfb, 0x4c, 0xfd, 0xd7, 0x94, 0x77, 0x02, 0x4c, 0xf1, 0xac, 0xdc,
0x66, 0x94, 0x52, 0x98, 0x15, 0x04, 0xa0, 0x48, 0x94, 0x3e, 0x33, 0x3a,
0x80, 0x69, 0xe5, 0xfc, 0x00, 0xf0, 0x26, 0xa1, 0x3a, 0x65, 0xc4, 0x04,
0xbb, 0x26, 0x80, 0x30, 0xc7, 0x0b, 0xf9, 0xeb, 0xb4, 0xec, 0x5d, 0x8f,
0xb4, 0x19, 0x1f, 0x09, 0xf6, 0x65, 0xe6, 0xbf, 0x4e, 0xc8, 0xaa, 0xd2,
0x77, 0x5f, 0x2a, 0x21, 0x68, 0x16, 0x68, 0x97, 0x4d, 0xdb, 0x5c, 0x4a,
0x04, 0xa4, 0xd7, 0x7a, 0x62, 0x7c, 0xfa, 0xc3, 0xe4, 0xc0, 0x04, 0x32,
0x59, 0x7a, 0x6b, 0xb5, 0xce, 0xb4, 0x20, 0x26, 0x27, 0x47, 0xdc, 0x44,
0xce, 0x55, 0x2c, 0xda, 0x76, 0x7d, 0x22, 0xa8, 0x00, 0xb6, 0x52, 0x1f,
0xe5, 0x51, 0x44, 0x6c, 0xc9, 0x26, 0x2d, 0x6a, 0x97, 0x80, 0x59, 0x31,
0x8b, 0x14, 0xfc, 0xbb, 0xb8, 0x28, 0x84, 0x35, 0x35, 0xfd, 0xbf, 0x60,
0x62, 0x56, 0xa1, 0x27, 0x85, 0x80, 0xc0, 0xca, 0x05, 0x63, 0x8d, 0x46,
0x8b, 0x76, 0x5c, 0x9f, 0xbe, 0xbe, 0x49, 0x84, 0xd8, 0xa2, 0x01, 0xf0,
0x49, 0xcb, 0xa6, 0x96, 0x44, 0x2d, 0x5e, 0x61, 0xd1, 0x0e, 0x09, 0x6d,
0x9e, 0x88, 0xc2, 0x58, 0x29, 0x5c, 0xdc, 0xee, 0xd1, 0xf6, 0x13, 0xb5,
0x00, 0x39, 0xae, 0x25, 0x90, 0xf4, 0x2c, 0xda, 0x42, 0x29, 0x7f, 0x15,
0xef, 0x72, 0x81, 0x68, 0x32, 0x5a, 0x1b, 0xfa, 0xb6, 0x66, 0x11, 0x4e,
0x1a, 0xac, 0x0f, 0xae, 0xaa, 0xea, 0x39, 0x5d, 0xf0, 0x07, 0x01, 0x5d,
0xf9, 0x58, 0x66, 0xb7, 0xdb, 0xe7, 0x97, 0x83, 0xda, 0xf1, 0x41, 0xd7,
0xdd, 0x82, 0x3f, 0x67, 0x57, 0x37, 0xbd, 0xbd, 0x9b, 0x2e, 0x3c, 0x6d,
0xe3, 0xeb, 0x4e, 0x77, 0x67, 0xeb, 0x14, 0x1f, 0x46, 0xcb, 0xc3, 0x41,
0x1f, 0x1f, 0x5a, 0xf5, 0xed, 0xd3, 0xdb, 0xbd, 0xdb, 0xc3, 0xf6, 0xde,
0xe8, 0xae, 0xbf, 0xbc, 0x7c, 0x77, 0xb7, 0xb8, 0xb7, 0xf2, 0xe5, 0xc6,
0xda, 0xfa, 0xb2, 0xb7, 0xcd, 0xc8, 0x41, 0xfd, 0x91, 0x1c, 0xac, 0x56,
0xab, 0xd5, 0xb5, 0xe7, 0xb3, 0xc7, 0xa5, 0xe3, 0x97, 0xd3, 0xd5, 0x9d,
0xe1, 0x69, 0xab, 0xbf, 0x1c, 0x76, 0x4e, 0x5f, 0xda, 0xd5, 0x87, 0x45,
0xeb, 0xc7, 0x90, 0x9f, 0x90, 0x03, 0xe6, 0x2e, 0xaf, 0x75, 0x1f, 0xee,
0x3e, 0x3f, 0x7e, 0xdd, 0xbf, 0xbc, 0xdd, 0xff, 0xfa, 0xf9, 0x7a, 0xaf,
0x7a, 0xf2, 0xd2, 0x2e, 0x3d, 0x07, 0xad, 0x33, 0xeb, 0xfe, 0x76, 0xf5,
0x63, 0xe9, 0xa2, 0xf7, 0x6c, 0x1d, 0xda, 0x41, 0xeb, 0x6e, 0xf1, 0xc9,
0x5b, 0xf9, 0xb1, 0xfa, 0x7c, 0xf2, 0x32, 0x5a, 0x7b, 0x3e, 0x0d, 0xcf,
0xae, 0x5f, 0x3a, 0x4b, 0x9f, 0x4a, 0x3d, 0x77, 0xe5, 0x66, 0x74, 0x7e,
0xb3, 0xb3, 0xdf, 0x7b, 0xb8, 0xbb, 0xb1, 0x97, 0x9d, 0xe7, 0xd5, 0x52,
0xd5, 0x5b, 0xa1, 0x4f, 0x5f, 0x58, 0xf5, 0xe0, 0x12, 0x71, 0xdc, 0xba,
0xbf, 0xbc, 0xba, 0xb6, 0x4f, 0xb7, 0xbe, 0x9c, 0xb7, 0x1e, 0xbe, 0x22,
0x2d, 0x57, 0x97, 0x9f, 0x2f, 0xf7, 0xf6, 0x6f, 0xae, 0x4e, 0x3b, 0xfc,
0xe9, 0x13, 0x1f, 0x0d, 0xd8, 0xd6, 0x97, 0x9e, 0x7b, 0xb3, 0xd5, 0xbb,
0xdd, 0x1a, 0x7c, 0xf6, 0x7e, 0xec, 0x5e, 0xfe, 0xe8, 0x90, 0xe7, 0xe7,
0xb5, 0x17, 0x3b, 0x3c, 0x3b, 0x7e, 0x0a, 0xfd, 0xbd, 0x47, 0xff, 0x61,
0xa9, 0x44, 0x57, 0x3f, 0x7e, 0x66, 0x2f, 0x27, 0xce, 0xe2, 0x5d, 0xbd,
0xcf, 0xb6, 0x8e, 0x87, 0x5e, 0xef, 0x7c, 0xfb, 0x94, 0xde, 0x3c, 0xfc,
0x58, 0x09, 0x0f, 0xab, 0x5b, 0x4b, 0x5b, 0x2b, 0x2b, 0x0f, 0xde, 0xe5,
0xf6, 0xe5, 0x8f, 0xcf, 0x5f, 0xc9, 0xe9, 0xd1, 0x1a, 0x1b, 0x04, 0xb7,
0xd5, 0x1d, 0xeb, 0x74, 0x65, 0x6b, 0x71, 0xf8, 0xb8, 0xec, 0x1c, 0xdd,
0x04, 0xc7, 0xb5, 0x2a, 0xbb, 0xbe, 0xb9, 0xf0, 0x0f, 0xce, 0xfa, 0xb5,
0xd3, 0x9b, 0xdd, 0xa3, 0x27, 0x7a, 0x50, 0xfd, 0xbc, 0xfc, 0x31, 0x3c,
0x67, 0x4f, 0x41, 0xeb, 0x53, 0xef, 0xbe, 0xb7, 0xbc, 0x7c, 0xd1, 0x3b,
0x3a, 0x7a, 0xec, 0x1c, 0x77, 0xad, 0xcf, 0xd7, 0x87, 0x57, 0x7b, 0xa3,
0xc5, 0xea, 0xfe, 0x6e, 0x6d, 0xe5, 0xbe, 0xef, 0x5a, 0x6b, 0x67, 0xe7,
0x03, 0xdf, 0x1f, 0xec, 0xdf, 0x04, 0x5f, 0xfa, 0xf7, 0x5f, 0x0f, 0xbf,
0xf6, 0x7a, 0xf4, 0xe9, 0x70, 0x9b, 0x6d, 0x8f, 0x1e, 0x8e, 0x5c, 0x72,
0xf4, 0x79, 0xeb, 0xf1, 0x62, 0xed, 0xe6, 0xea, 0x8e, 0xed, 0x6c, 0xad,
0x1d, 0xf7, 0xf6, 0xee, 0xd6, 0x6e, 0x0e, 0xae, 0x57, 0x8f, 0x2f, 0xc8,
0xd7, 0xbd, 0x61, 0x70, 0xde, 0x3a, 0x1c, 0xf9, 0x37, 0xdd, 0xeb, 0xa7,
0xc7, 0xeb, 0x97, 0x35, 0x9b, 0x5d, 0xdc, 0x0f, 0x5e, 0x06, 0x7b, 0xdb,
0xa5, 0xf3, 0x8b, 0xfd, 0xdb, 0xe1, 0xe1, 0xde, 0xda, 0xfd, 0x62, 0xfb,
0xe9, 0x72, 0x7b, 0x74, 0x42, 0x6e, 0x47, 0xbd, 0xdb, 0xe3, 0xe1, 0xc5,
0xe2, 0xea, 0xf1, 0x59, 0xc9, 0xd9, 0xe2, 0x87, 0xab, 0x97, 0xcf, 0xa1,
0xbf, 0xb8, 0xeb, 0xaf, 0x2c, 0xd6, 0x39, 0x7d, 0x3a, 0xa5, 0x41, 0xe9,
0x8e, 0x1d, 0xac, 0xad, 0x1c, 0xfa, 0x2b, 0x8f, 0xc7, 0x0f, 0x8f, 0xa5,
0xd5, 0x2f, 0xf5, 0x63, 0xab, 0x76, 0xe1, 0x0d, 0x8f, 0x96, 0xd7, 0xce,
0x82, 0x2f, 0xd6, 0x59, 0x75, 0x71, 0xf9, 0xc5, 0xfe, 0xb2, 0xfb, 0xc5,
0x3a, 0x6e, 0x7d, 0xda, 0x72, 0x4e, 0x57, 0x3a, 0x87, 0x57, 0x07, 0x4f,
0x17, 0xc1, 0x17, 0xf2, 0x99, 0xf4, 0x8f, 0xbc, 0x2f, 0x2f, 0x3b, 0xfe,
0x68, 0xd0, 0xdb, 0xad, 0xb3, 0xeb, 0xc5, 0xfb, 0xa7, 0xe0, 0x64, 0x67,
0x10, 0x54, 0x8f, 0xbe, 0x3e, 0xaf, 0x7d, 0x75, 0x3b, 0xab, 0x7c, 0x71,
0xf9, 0xc1, 0x7e, 0x12, 0x62, 0xba, 0xba, 0xb9, 0x3d, 0xbf, 0x3c, 0x5e,
0xde, 0x79, 0x38, 0x3a, 0x32, 0x75, 0xc5, 0x71, 0x2b, 0x3e, 0xf5, 0x28,
0xe1, 0x7f, 0x82, 0xee, 0x17, 0x0c, 0x25, 0x16, 0x5e, 0x18, 0x03, 0x7a,
0x32, 0x80, 0xd5, 0x21, 0x06, 0x34, 0xc5, 0x70, 0x76, 0x00, 0x9c, 0x16,
0x67, 0x6d, 0x62, 0x57, 0x88, 0xcd, 0xba, 0x4e, 0xa3, 0xcf, 0x2c, 0xcb,
0x2e, 0xf4, 0x2c, 0x19, 0x93, 0xab, 0x24, 0x4e, 0xba, 0xbe, 0x06, 0x31,
0xb4, 0x56, 0xe4, 0x3b, 0x67, 0xce, 0xae, 0x7c, 0x5c, 0x9b, 0x72, 0x6f,
0xb8, 0xc2, 0xe0, 0x43, 0xfe, 0xc6, 0xaa, 0x95, 0x8f, 0xc5, 0xab, 0x58,
0xbf, 0xfb, 0xc6, 0xaa, 0xb5, 0xda, 0xd4, 0xaa, 0x09, 0x97, 0x88, 0xc1,
0xce, 0xe1, 0x0d, 0x55, 0x6d, 0xca, 0xb0, 0x2b, 0x98, 0x92, 0xb8, 0x63,
0x74, 0xc5, 0xe8, 0x93, 0x8b, 0xd9, 0xd4, 0x2c, 0xda, 0xb8, 0xa6, 0x54,
0x96, 0xa6, 0x1d, 0x78, 0x4e, 0x4a, 0x7f, 0x70, 0xd3, 0xb7, 0x7c, 0x6c,
0x29, 0x4f, 0xc6, 0x0c, 0x44, 0x10, 0xee, 0xf8, 0x77, 0x91, 0xad, 0x29,
0x32, 0x5b, 0x53, 0x88, 0x63, 0x29, 0x5a, 0x1c, 0x15, 0x31, 0xdb, 0xb0,
0x00, 0x7a, 0x9b, 0x56, 0x3c, 0x36, 0xa4, 0x76, 0x45, 0x44, 0xad, 0x46,
0x4d, 0x7f, 0xcd, 0x87, 0xda, 0x78, 0x3e, 0x71, 0xc0, 0xc7, 0x0a, 0xe0,
0xf1, 0x80, 0xf5, 0x48, 0xda, 0x40, 0x4a, 0x05, 0x02, 0x69, 0x1f, 0x7f,
0x65, 0x44, 0x75, 0xfd, 0x4a, 0x2b, 0xec, 0x76, 0xd8, 0x10, 0x90, 0xee,
0x30, 0x87, 0x71, 0xaa, 0xd4, 0x83, 0xf1, 0xef, 0x31, 0x98, 0x27, 0x3a,
0xea, 0xf8, 0xa4, 0x4f, 0x03, 0xe5, 0x0f, 0x82, 0x79, 0xed, 0xf8, 0x6e,
0x3f, 0xcd, 0x28, 0xc6, 0xdc, 0xcd, 0xbc, 0x8c, 0xc7, 0x1f, 0x7a, 0x94,
0x40, 0x98, 0x2d, 0xc8, 0x1d, 0x64, 0x42, 0xe6, 0x0b, 0x75, 0x4f, 0x12,
0xb3, 0x48, 0xfd, 0x17, 0x41, 0xa3, 0x92, 0xfc, 0x08, 0x13, 0xca, 0xba,
0x60, 0x3b, 0xa6, 0x85, 0x05, 0x09, 0x25, 0x66, 0x58, 0x99, 0xfc, 0xe7,
0x03, 0xf2, 0x66, 0xc6, 0x86, 0x8b, 0x32, 0x51, 0xe5, 0xdc, 0xed, 0xa7,
0x9b, 0x4a, 0x41, 0x2f, 0xd6, 0x7e, 0x4b, 0xb6, 0x44, 0x33, 0xf8, 0x00,
0xa2, 0x01, 0x32, 0xcb, 0x1f, 0x3c, 0x5f, 0x72, 0xfa, 0x0d, 0x88, 0x31,
0x15, 0x79, 0xc8, 0x00, 0x71, 0x9c, 0xac, 0x9e, 0x46, 0x1b, 0x70, 0x4d,
0x36, 0x5c, 0xc6, 0x0d, 0x03, 0x4e, 0x78, 0x18, 0xcc, 0xd8, 0x67, 0x29,
0xd9, 0x46, 0x70, 0x21, 0x93, 0x61, 0x89, 0xb5, 0x9d, 0xe0, 0x94, 0x42,
0xe2, 0xf0, 0x1a, 0x29, 0x6d, 0xad, 0x96, 0x32, 0xb0, 0x12, 0x61, 0x85,
0x9a, 0xff, 0x61, 0x40, 0x7c, 0x07, 0xc6, 0x5e, 0x63, 0x3f, 0x53, 0x03,
0xaa, 0xa7, 0x50, 0x83, 0x0c, 0xc9, 0xe9, 0xd2, 0x66, 0x92, 0x63, 0x81,
0xba, 0x73, 0xd7, 0xb5, 0x39, 0xf3, 0x0a, 0x90, 0x8b, 0xab, 0x83, 0xc5,
0x84, 0x0b, 0x82, 0x2d, 0x88, 0xc0, 0x33, 0x0b, 0x58, 0x8b, 0xd9, 0x98,
0x96, 0xf4, 0xc0, 0x2a, 0xa9, 0x53, 0x54, 0x0d, 0x74, 0x3a, 0x99, 0x8d,
0x9a, 0x22, 0x49, 0x93, 0x86, 0x8c, 0x5a, 0x47, 0xfd, 0x82, 0xb2, 0x02,
0xe7, 0xc5, 0x8c, 0x5b, 0xca, 0xf3, 0xa2, 0x96, 0xa1, 0x31, 0x4a, 0x8f,
0x13, 0xd4, 0x33, 0xd8, 0x88, 0x47, 0xf0, 0xa5, 0x1f, 0x6c, 0x97, 0x20,
0x98, 0x19, 0x3c, 0xaf, 0x00, 0x7b, 0x9e, 0x7b, 0x79, 0x35, 0xc1, 0xa1,
0x41, 0x33, 0x65, 0x1f, 0x7c, 0x8f, 0x39, 0x00, 0x6f, 0xd3, 0x04, 0x66,
0xab, 0x98, 0x65, 0x3d, 0xcd, 0x98, 0x9b, 0x69, 0xa9, 0xd7, 0x88, 0x06,
0x15, 0x63, 0x39, 0x50, 0x28, 0xe4, 0x47, 0x00, 0xaf, 0xe2, 0x86, 0x3c,
0x41, 0xcf, 0x08, 0x7a, 0xee, 0xc0, 0x79, 0x95, 0x16, 0x13, 0x43, 0xa8,
0x27, 0x9f, 0x2b, 0xfd, 0xa0, 0x5b, 0x1c, 0x5e, 0x66, 0x59, 0x9d, 0xac,
0xd9, 0x40, 0xf4, 0x02, 0x09, 0x70, 0x53, 0xfd, 0x86, 0x78, 0x82, 0x4a,
0x87, 0xde, 0x6b, 0x15, 0xf8, 0xa2, 0x37, 0x33, 0xf2, 0x49, 0x19, 0x2c,
0xdc, 0xe7, 0xef, 0xa9, 0x93, 0x08, 0x3c, 0xe6, 0x38, 0x90, 0xad, 0xa3,
0xf7, 0x79, 0xad, 0xfd, 0xf6, 0x9a, 0xc2, 0xf3, 0x5d, 0xd0, 0x66, 0xaa,
0xd5, 0xf4, 0x31, 0xea, 0xd8, 0xf4, 0x87, 0xa5, 0x95, 0x9a, 0x45, 0xbb,
0xfa, 0x78, 0x6c, 0x64, 0x61, 0xc8, 0xfc, 0xd4, 0xa7, 0x3f, 0x42, 0xe6,
0x43, 0x7e, 0xfa, 0x0e, 0x55, 0x49, 0x69, 0x86, 0x54, 0x21, 0x39, 0x6f,
0xd1, 0x25, 0xc8, 0x2a, 0x4b, 0xda, 0x52, 0x67, 0x99, 0xdd, 0x1c, 0x1c,
0x61, 0xea, 0x14, 0x71, 0x3f, 0x92, 0x6a, 0xdf, 0x4a, 0xaa, 0x7e, 0x68,
0xb7, 0x72, 0xb8, 0x22, 0x34, 0x3e, 0x12, 0xb4, 0xd8, 0xc8, 0x23, 0x3e,
0x68, 0x6d, 0xf2, 0x19, 0xc4, 0x13, 0x06, 0x82, 0x69, 0xb2, 0xc2, 0x65,
0x2f, 0xa8, 0x97, 0xd1, 0x57, 0x18, 0x69, 0xa2, 0x86, 0x76, 0x6c, 0xa8,
0x7a, 0x23, 0xfb, 0x10, 0xea, 0x8f, 0xda, 0x04, 0x21, 0xa8, 0xf2, 0x09,
0xfe, 0xc0, 0xca, 0xa8, 0xe6, 0x44, 0x97, 0x18, 0xa9, 0x9d, 0x08, 0xb8,
0xeb, 0x55, 0x51, 0xae, 0x41, 0xdd, 0xdf, 0xf6, 0x99, 0xc7, 0x37, 0x9e,
0x89, 0xaf, 0xa0, 0xe7, 0x2b, 0x77, 0x82, 0x23, 0xa7, 0xe3, 0x36, 0x3b,
0xa1, 0xd3, 0x46, 0x12, 0x95, 0x80, 0xf2, 0x13, 0xa9, 0x28, 0x1a, 0x2d,
0x73, 0xfd, 0x95, 0xcf, 0xcf, 0x43, 0x94, 0x03, 0x62, 0xa8, 0x61, 0xbb,
0x5d, 0x8d, 0xeb, 0x65, 0xcb, 0x6d, 0x87, 0xe8, 0xdb, 0x8d, 0x2e, 0xe5,
0x7b, 0xd2, 0xcd, 0x6f, 0x8f, 0x8e, 0x2c, 0x4d, 0xcd, 0x28, 0x98, 0xaa,
0x1b, 0x82, 0x53, 0xd8, 0xe4, 0x30, 0xf9, 0xcf, 0x9f, 0xaa, 0x5a, 0xa6,
0x9b, 0xef, 0x2d, 0x84, 0x45, 0x6d, 0x9b, 0x04, 0xc1, 0x09, 0x54, 0x97,
0x06, 0x58, 0xac, 0xa6, 0x0a, 0x4d, 0x56, 0xf5, 0xc6, 0x2f, 0xad, 0xf4,
0x69, 0x1f, 0x18, 0x95, 0x2c, 0x4e, 0xf1, 0xc5, 0xfe, 0x86, 0x21, 0xd8,
0x60, 0xc8, 0x72, 0xc8, 0xa4, 0x9b, 0xea, 0x80, 0x30, 0xae, 0x36, 0xd4,
0xa8, 0x30, 0x52, 0xc7, 0x09, 0x23, 0x7c, 0x08, 0x45, 0x58, 0xf7, 0x5c,
0x81, 0x26, 0x6b, 0x54, 0x7f, 0x65, 0x1d, 0x8d, 0xae, 0xd7, 0x6b, 0x8b,
0x1f, 0x75, 0x9f, 0xf2, 0xd0, 0x77, 0x14, 0x5a, 0x52, 0x95, 0x6d, 0x15,
0xd4, 0xdd, 0xd7, 0x04, 0x37, 0xcd, 0x4a, 0xbd, 0xc9, 0x4b, 0xa5, 0x32,
0xce, 0x59, 0xd7, 0x68, 0xd5, 0x14, 0x93, 0x9b, 0x7a, 0x33, 0x9e, 0x0f,
0xbe, 0x65, 0x1f, 0x82, 0xb3, 0xa5, 0x2d, 0xea, 0xa5, 0x6f, 0xaa, 0x72,
0xcc, 0xb6, 0xd5, 0xb2, 0xaa, 0x9c, 0xca, 0x9f, 0x03, 0xf9, 0x73, 0x2d,
0x7e, 0x2e, 0xe0, 0xdf, 0xef, 0xdf, 0xf8, 0xf7, 0x2c, 0x36, 0x1d, 0x9f,
0x06, 0xbd, 0x2b, 0xe1, 0xf4, 0x35, 0x50, 0xf5, 0x59, 0x1c, 0x91, 0x61,
0x21, 0xc7, 0x7f, 0x55, 0x8b, 0x56, 0xa3, 0x73, 0x30, 0x0c, 0x5d, 0x6d,
0x22, 0xbe, 0xae, 0xe9, 0x40, 0x2e, 0x72, 0x7f, 0x7a, 0x72, 0xc8, 0xb9,
0x77, 0x09, 0x36, 0x44, 0x03, 0xa8, 0x0b, 0x0d, 0xd7, 0x41, 0x9e, 0x9a,
0xf1, 0xc6, 0x9a, 0x20, 0x7c, 0xb1, 0x56, 0x9b, 0x33, 0x5d, 0x43, 0xc2,
0xd6, 0x91, 0xb1, 0xb8, 0x68, 0xcf, 0xf7, 0x81, 0x76, 0x57, 0x6f, 0x52,
0x3b, 0xa0, 0xaf, 0x08, 0x93, 0x9a, 0x9a, 0xd4, 0x27, 0xf3, 0xf3, 0xd5,
0xf9, 0x99, 0x01, 0xfa, 0x1d, 0x50, 0xcd, 0x05, 0x89, 0x04, 0x1e, 0xe8,
0x10, 0xbd, 0x06, 0x7d, 0xd5, 0x75, 0x83, 0x8f, 0x3c, 0xe4, 0x5d, 0x45,
0x51, 0x9b, 0x00, 0x5b, 0x2e, 0x30, 0x58, 0x70, 0xfe, 0xa4, 0xbf, 0x4a,
0x46, 0xd6, 0x4b, 0xa7, 0x84, 0xf7, 0x0c, 0xe1, 0x18, 0xb5, 0x4f, 0x9f,
0xfe, 0x3d, 0x9a, 0x02, 0x95, 0xbe, 0xb5, 0x3d, 0xe2, 0x34, 0xa8, 0x46,
0x03, 0x1c, 0x9c, 0x83, 0x2d, 0x46, 0xf4, 0xb2, 0x63, 0xe6, 0x04, 0x36,
0x35, 0xa3, 0x32, 0x09, 0x44, 0x07, 0x14, 0x80, 0x2b, 0x54, 0x71, 0x3b,
0x8a, 0x5a, 0x7a, 0x7b, 0x31, 0x90, 0x58, 0x32, 0xd5, 0xf5, 0x3e, 0x06,
0x4d, 0x85, 0x59, 0xe6, 0x42, 0x14, 0x40, 0x17, 0x14, 0x48, 0xbc, 0xcc,
0x85, 0xda, 0x82, 0xe2, 0x7a, 0x9c, 0xf5, 0xc3, 0x3e, 0x3e, 0x83, 0x81,
0x9a, 0x0b, 0x9f, 0x60, 0xac, 0x07, 0xe6, 0x07, 0x4f, 0xcb, 0x30, 0x8b,
0x0c, 0xcd, 0x05, 0xf0, 0x6c, 0x0b, 0xca, 0x33, 0xb1, 0x43, 0x6a, 0x2e,
0xa8, 0x25, 0x5e, 0x52, 0x17, 0x14, 0xd1, 0x8b, 0xc3, 0x37, 0x07, 0xde,
0x36, 0xc4, 0xcf, 0x7a, 0x55, 0xec, 0xb2, 0xa1, 0x96, 0x63, 0x84, 0x9d,
0x20, 0xf4, 0x3c, 0xd7, 0xe7, 0xd4, 0xc2, 0x46, 0x5e, 0x30, 0x3f, 0xaf,
0x21, 0x32, 0xca, 0x3a, 0x66, 0xa2, 0x02, 0x99, 0x28, 0x82, 0x2d, 0x6c,
0xdc, 0x6d, 0x5d, 0x9e, 0x1d, 0x9d, 0x1d, 0xc8, 0x2f, 0xc2, 0x20, 0xcc,
0x85, 0x28, 0x9e, 0x2d, 0x88, 0x2e, 0x60, 0x30, 0x0a, 0x38, 0xed, 0xcf,
0x3b, 0xad, 0xc0, 0x6b, 0x62, 0xee, 0x4a, 0x98, 0x13, 0xc8, 0xb7, 0xcc,
0x2e, 0x72, 0xa0, 0x03, 0xd3, 0x1d, 0xf4, 0xda, 0x8d, 0xf5, 0x96, 0x5f,
0x05, 0xdc, 0x66, 0xa0, 0x83, 0x18, 0xe3, 0x7e, 0x1b, 0xd1, 0x8f, 0xaa,
0x8f, 0x51, 0x1d, 0x14, 0xc1, 0x30, 0x81, 0x88, 0xb0, 0x38, 0x73, 0x61,
0x3a, 0xd6, 0x51, 0x2b, 0x0a, 0x1b, 0x83, 0x1e, 0xf8, 0xd0, 0x5c, 0x8f,
0xae, 0xe5, 0xda, 0xd6, 0xc2, 0xc6, 0xd1, 0xd9, 0xd1, 0xb5, 0xb2, 0x77,
0x79, 0x79, 0x7e, 0xa9, 0xcc, 0xc5, 0xe0, 0x9b, 0xbf, 0xa2, 0xf9, 0xb4,
0xac, 0x5e, 0x5d, 0x1c, 0xed, 0xef, 0x5f, 0xa9, 0x73, 0x66, 0x2c, 0x54,
0x50, 0x3e, 0x60, 0xe1, 0x4c, 0x28, 0xfd, 0x27, 0x8b, 0xf9, 0x00, 0x44,
0xfa, 0x89, 0x28, 0xa2, 0x98, 0xaa, 0x0c, 0x29, 0x40, 0xdc, 0xb8, 0x0c,
0x36, 0xe2, 0x51, 0x47, 0x53, 0x0f, 0xf6, 0xae, 0xc1, 0x54, 0xab, 0xd1,
0xb6, 0xe5, 0xb9, 0x9a, 0x0e, 0x9f, 0x02, 0x0a, 0x2a, 0xeb, 0x84, 0xb6,
0xad, 0xa7, 0xb6, 0x9b, 0xb7, 0x17, 0x70, 0x25, 0xc4, 0x86, 0x22, 0x44,
0x53, 0x05, 0x65, 0x0d, 0xe5, 0x9b, 0x5a, 0xa2, 0x91, 0x69, 0x95, 0xd4,
0xef, 0x0a, 0xbe, 0xe5, 0x0c, 0x26, 0x05, 0xd4, 0x26, 0x0e, 0xfa, 0xe6,
0x33, 0x3a, 0xd8, 0x91, 0x85, 0x07, 0x7a, 0x02, 0xe9, 0x5d, 0xe6, 0xe6,
0x66, 0xf3, 0x85, 0x3c, 0xd3, 0x6d, 0x0e, 0x9e, 0xd0, 0x88, 0x3b, 0x39,
0x3f, 0x7f, 0xce, 0xcd, 0x81, 0xf8, 0x3b, 0xcc, 0xef, 0x6b, 0xea, 0x4e,
0x0f, 0xd3, 0xb7, 0x40, 0xe1, 0xae, 0x32, 0x72, 0x43, 0x5f, 0x89, 0xe1,
0x28, 0x03, 0x66, 0xdb, 0x4a, 0x0b, 0x62, 0x9b, 0x1b, 0x70, 0x85, 0x75,
0xf0, 0xab, 0x82, 0x4a, 0xc3, 0x9c, 0x10, 0x18, 0x81, 0x6a, 0xe8, 0x08,
0x8b, 0x01, 0xf0, 0xbb, 0x2c, 0x68, 0x13, 0xdf, 0x82, 0x5d, 0x02, 0x6d,
0xae, 0xae, 0x23, 0x2f, 0x52, 0xac, 0x8b, 0xa7, 0xd1, 0xb7, 0x7c, 0xd8,
0x14, 0xc6, 0xe6, 0x1c, 0x9d, 0x1d, 0x77, 0xac, 0x04, 0x6c, 0x7e, 0x45,
0x8a, 0x02, 0xac, 0xb8, 0x10, 0xb1, 0x76, 0x1f, 0xf4, 0x8a, 0x0a, 0x19,
0x24, 0x4e, 0x19, 0x38, 0xef, 0xf3, 0xe0, 0x8e, 0xf1, 0x9e, 0xa6, 0x56,
0x55, 0xfd, 0xe7, 0x4f, 0x8d, 0x9a, 0xf0, 0x50, 0xa2, 0x7a, 0x99, 0x1a,
0x20, 0xcd, 0xf4, 0x13, 0x92, 0x6c, 0xc2, 0x02, 0x1b, 0xca, 0x2c, 0x48,
0xd2, 0x2a, 0x75, 0x1d, 0xa7, 0x04, 0x61, 0x2b, 0xe0, 0x3e, 0xc6, 0xcb,
0x1a, 0xbc, 0x81, 0xd1, 0xf1, 0x23, 0xcc, 0xef, 0xce, 0x3b, 0x62, 0x4d,
0x56, 0x7a, 0xe0, 0x63, 0x38, 0x3d, 0x14, 0x15, 0x0e, 0xc4, 0x56, 0x07,
0xa3, 0xab, 0x74, 0xc4, 0xb3, 0x28, 0x03, 0x14, 0x48, 0xfa, 0x51, 0xae,
0x8f, 0xbe, 0x6b, 0xaa, 0xa8, 0x22, 0x55, 0xc8, 0x4e, 0x84, 0x4a, 0x9b,
0x2a, 0x1a, 0xac, 0x5a, 0x26, 0x46, 0x3f, 0x44, 0x7b, 0x07, 0x8b, 0x9b,
0xab, 0xc3, 0x1b, 0x9a, 0xb0, 0xa9, 0x62, 0x57, 0x4d, 0x05, 0xed, 0x24,
0x1e, 0x68, 0xae, 0xb5, 0xd3, 0x63, 0xb6, 0xa5, 0x11, 0x5d, 0x84, 0x01,
0xeb, 0xdd, 0x0d, 0x2c, 0x03, 0x9c, 0x8c, 0xea, 0x81, 0x47, 0x3e, 0x12,
0x23, 0x65, 0x2b, 0xda, 0x11, 0xd3, 0x0f, 0x7c, 0x93, 0x7b, 0xe0, 0x04,
0x7c, 0x8b, 0x22, 0xe9, 0xad, 0x70, 0x74, 0xc0, 0x82, 0x89, 0x6d, 0x2d,
0xb9, 0xad, 0x3d, 0x73, 0xdb, 0x56, 0x08, 0xf5, 0x07, 0x48, 0xb2, 0x69,
0x67, 0x43, 0xd8, 0x8d, 0x87, 0x61, 0x69, 0x12, 0x98, 0x2d, 0x81, 0xb1,
0xf7, 0x81, 0x31, 0x41, 0x84, 0x34, 0xee, 0x32, 0x9b, 0xb4, 0x6e, 0x6c,
0xc2, 0xe2, 0x70, 0x66, 0xc3, 0xd3, 0xa7, 0x5d, 0x9c, 0x9a, 0xdf, 0x8f,
0xc9, 0xfd, 0x82, 0xf7, 0xf7, 0x0b, 0xf2, 0xb0, 0xf6, 0x85, 0x70, 0xf2,
0xc0, 0x02, 0x09, 0xac, 0x3d, 0x13, 0x18, 0x3a, 0x3c, 0x00, 0xd5, 0x16,
0xa8, 0xcb, 0xfa, 0x72, 0x5b, 0xc0, 0x07, 0x6f, 0xd3, 0x2e, 0x26, 0x21,
0xbf, 0x43, 0x5b, 0xee, 0xe0, 0xbf, 0x8f, 0xae, 0x2f, 0xf6, 0x88, 0xed,
0xae, 0xec, 0x67, 0xb1, 0x47, 0xcb, 0xc5, 0x21, 0xb9, 0xa1, 0x6c, 0x6e,
0x9c, 0x40, 0xea, 0x6c, 0xaa, 0x4b, 0x50, 0x40, 0xe1, 0x97, 0xd4, 0xe6,
0x6a, 0x80, 0x58, 0x16, 0x03, 0x5f, 0x62, 0xd0, 0x7f, 0x1f, 0x83, 0xbe,
0xc0, 0x20, 0x63, 0xca, 0xe5, 0x7e, 0x16, 0x89, 0xc8, 0x75, 0xe0, 0xe8,
0xec, 0xdd, 0xfa, 0x72, 0xb7, 0xf0, 0xfd, 0xdd, 0x42, 0xb1, 0x5b, 0x8f,
0xda, 0x9e, 0xd8, 0x2a, 0xcc, 0x6e, 0xb5, 0xa9, 0x4e, 0x80, 0x0d, 0x25,
0x58, 0xef, 0x3d, 0x41, 0x79, 0x92, 0x89, 0x51, 0x3c, 0xf0, 0x72, 0x19,
0x58, 0x94, 0xa8, 0xea, 0x93, 0x22, 0xf2, 0xc0, 0xc4, 0x21, 0xe7, 0x6a,
0x0b, 0x27, 0x9c, 0x66, 0x5d, 0x32, 0xdf, 0x84, 0xa4, 0xcb, 0x24, 0x06,
0x1a, 0x76, 0x60, 0x40, 0x34, 0xee, 0xf2, 0x5e, 0x9c, 0x21, 0x45, 0xa3,
0xdf, 0x6a, 0xdf, 0x85, 0xf5, 0x35, 0x9f, 0x5d, 0xa8, 0x35, 0x6a, 0x60,
0x7d, 0x22, 0xbf, 0x28, 0xf0, 0x69, 0xd1, 0x17, 0xe1, 0xd9, 0xa2, 0x67,
0x3d, 0x9e, 0x6e, 0x4e, 0xba, 0xc7, 0x78, 0x42, 0x09, 0x67, 0x73, 0x08,
0x79, 0x36, 0xa2, 0x08, 0x6e, 0xef, 0x29, 0x87, 0xe1, 0x34, 0x7a, 0xf3,
0xf3, 0x8e, 0x81, 0x67, 0x97, 0xd2, 0x5a, 0xb5, 0x14, 0xcb, 0x78, 0x2b,
0x7d, 0x5c, 0x0e, 0x0a, 0x61, 0x49, 0xb2, 0x62, 0xfc, 0xc1, 0x9f, 0xf6,
0x35, 0xbd, 0xa9, 0xaa, 0xa6, 0x69, 0xf2, 0xcd, 0x28, 0x5c, 0x6e, 0x29,
0x71, 0x4e, 0xa2, 0xf4, 0x43, 0x08, 0x49, 0x10, 0x9a, 0xba, 0x50, 0xce,
0x61, 0x7d, 0xc0, 0xf3, 0x8e, 0x3a, 0x5e, 0xb1, 0x1f, 0xe7, 0x30, 0x72,
0x01, 0x54, 0x8b, 0x10, 0x8c, 0x2c, 0x08, 0x6c, 0xbc, 0xa7, 0x10, 0x65,
0xa1, 0xba, 0xa0, 0x00, 0xdf, 0x7d, 0xd2, 0x86, 0x7c, 0x0b, 0x80, 0x48,
0xd4, 0x77, 0x84, 0x68, 0xa1, 0xce, 0x19, 0x83, 0x3f, 0xf8, 0x9f, 0x63,
0x2a, 0x18, 0xa9, 0x14, 0x22, 0xab, 0x4d, 0x60, 0x0b, 0xd2, 0xe1, 0x25,
0x14, 0x0c, 0x24, 0xb1, 0x79, 0x14, 0x00, 0x07, 0xbf, 0x10, 0x07, 0x6e,
0xa0, 0xa9, 0x6a, 0xf0, 0xbd, 0x3f, 0xe3, 0x7b, 0x64, 0x48, 0x38, 0x25,
0x9c, 0x05, 0x02, 0xd2, 0x92, 0xab, 0x1e, 0x64, 0x72, 0xed, 0x10, 0x13,
0x89, 0xf1, 0x64, 0xa4, 0xba, 0x86, 0xe4, 0x18, 0xe2, 0x14, 0x93, 0x24,
0x3b, 0x33, 0xa3, 0x94, 0x1a, 0x35, 0xad, 0x00, 0x7f, 0x3e, 0xd3, 0x44,
0x2c, 0xf6, 0x0c, 0x16, 0x92, 0xa6, 0x44, 0x6f, 0xc6, 0xff, 0x34, 0xd6,
0xe8, 0x91, 0x86, 0xbe, 0x11, 0xfd, 0xa5, 0x63, 0x9c, 0xce, 0xd8, 0xa4,
0x43, 0x7c, 0x67, 0x59, 0xec, 0x4f, 0x67, 0xac, 0x76, 0x26, 0x87, 0x45,
0x4b, 0x01, 0xc7, 0x33, 0xb6, 0xbd, 0xce, 0xfa, 0x5d, 0x25, 0xf0, 0xdb,
0x98, 0xc7, 0x53, 0xcc, 0xea, 0xa3, 0x84, 0x17, 0x92, 0xfe, 0x4a, 0xd2,
0x05, 0xfa, 0xad, 0x89, 0x45, 0x40, 0x25, 0xdb, 0x48, 0x53, 0xa2, 0x3e,
0x31, 0x09, 0xb9, 0xdb, 0x54, 0x72, 0x07, 0x6b, 0xcd, 0x05, 0x05, 0x52,
0xee, 0x8c, 0x40, 0xfe, 0x3f, 0xb1, 0x2b, 0x5b, 0x14, 0xa4, 0x95, 0xc0,
0xc2, 0xc6, 0x56, 0x1b, 0xaa, 0x06, 0xb1, 0x0d, 0xa4, 0x99, 0xa1, 0x6d,
0x09, 0x7b, 0x15, 0xf9, 0x27, 0x64, 0x4c, 0x96, 0x82, 0x9d, 0x63, 0x85,
0xf7, 0xa8, 0x22, 0xce, 0x3c, 0x1d, 0x8a, 0xf6, 0xec, 0xcb, 0xd1, 0x2a,
0x2e, 0xab, 0x92, 0x36, 0x35, 0x1e, 0x03, 0xc5, 0x50, 0x76, 0x65, 0x06,
0x02, 0xee, 0x16, 0x93, 0x5a, 0xcc, 0x52, 0x14, 0xd4, 0x4f, 0xea, 0x43,
0xf1, 0x1b, 0x17, 0x2b, 0xa0, 0xb3, 0xa2, 0x76, 0xe2, 0x43, 0x1e, 0xe7,
0xd2, 0x89, 0xe0, 0x92, 0x36, 0x8b, 0x22, 0x44, 0xb3, 0x00, 0xa5, 0x0d,
0x4c, 0xdf, 0x90, 0x65, 0x33, 0x2f, 0x2a, 0x9b, 0x79, 0x41, 0xd9, 0x3c,
0x93, 0x95, 0x99, 0x2d, 0x81, 0x91, 0x88, 0x5e, 0xf4, 0x6a, 0xf2, 0x1e,
0x0b, 0x72, 0xb9, 0xff, 0xb8, 0xcc, 0xb3, 0xc5, 0x06, 0xb8, 0x6c, 0x2e,
0x2b, 0x8c, 0x4c, 0x5a, 0xe9, 0xa7, 0x4e, 0xa9, 0xaa, 0x6d, 0x36, 0xfe,
0xc3, 0xd0, 0xbe, 0xfd, 0xa7, 0xf1, 0xbd, 0xa4, 0xeb, 0x9b, 0xff, 0x56,
0x35, 0xe8, 0x90, 0xa2, 0xee, 0x7c, 0xab, 0x7f, 0xc7, 0x52, 0x5b, 0x46,
0x09, 0xf0, 0xda, 0x5c, 0x0f, 0xc0, 0x09, 0xb6, 0x7b, 0xe0, 0x8a, 0xb8,
0x7b, 0xe2, 0x02, 0x6b, 0x76, 0x08, 0x54, 0xea, 0xba, 0xfe, 0xda, 0x86,
0x5f, 0xc4, 0x50, 0x6d, 0x88, 0xa7, 0x1e, 0xef, 0xa7, 0x4f, 0x76, 0xf4,
0xf8, 0x18, 0x24, 0x0f, 0x10, 0x4c, 0xe5, 0x63, 0x3b, 0x9e, 0x16, 0xbf,
0x7b, 0x5e, 0xfc, 0x14, 0xc4, 0xd3, 0x87, 0x08, 0x21, 0xca, 0xbb, 0xeb,
0x19, 0x77, 0xd3, 0xff, 0xf3, 0x29, 0xf0, 0x9c, 0x6e, 0x8c, 0xa3, 0x97,
0x3e, 0xd1, 0xf8, 0xb1, 0xcb, 0x3a, 0xd1, 0x13, 0x6b, 0xbb, 0x85, 0x38,
0x85, 0x9a, 0x53, 0x76, 0xf5, 0xa8, 0xa9, 0x31, 0xcb, 0xa9, 0x85, 0x36,
0xf8, 0x34, 0x27, 0x17, 0xcf, 0xa9, 0x1e, 0x69, 0xc9, 0xac, 0x35, 0x36,
0x83, 0x35, 0x34, 0xb7, 0x86, 0xeb, 0x65, 0x6c, 0xa3, 0x6c, 0xaa, 0x78,
0xc7, 0xa0, 0x03, 0x65, 0xa6, 0x05, 0xd1, 0x04, 0x53, 0x6c, 0xb7, 0xa3,
0x80, 0x52, 0x6f, 0x02, 0x8d, 0x93, 0xf6, 0xb3, 0x71, 0x0b, 0x0a, 0x1d,
0x97, 0xc4, 0xa8, 0x24, 0x45, 0xe1, 0x79, 0x3d, 0xdf, 0xf9, 0x4a, 0xf5,
0x30, 0xd8, 0x1e, 0xed, 0x60, 0x77, 0xe0, 0x0c, 0xc2, 0x93, 0xa6, 0x66,
0xae, 0x89, 0x80, 0x3e, 0xc6, 0x81, 0x3c, 0xbf, 0x56, 0xf6, 0xd4, 0x24,
0xba, 0x8e, 0x5e, 0x2e, 0x2a, 0x44, 0xe7, 0xe7, 0xdb, 0x40, 0xc4, 0x58,
0xc4, 0xb7, 0x29, 0x74, 0xf7, 0xc0, 0x40, 0xff, 0x69, 0xe8, 0x32, 0x03,
0x4d, 0xf6, 0xc6, 0xb7, 0x25, 0xda, 0x7d, 0xf8, 0x81, 0x0a, 0xaf, 0x00,
0xf9, 0x0b, 0x19, 0xdf, 0xfe, 0x69, 0xf8, 0x07, 0x02, 0x6f, 0xa1, 0x6e,
0xb3, 0xab, 0xc4, 0x02, 0x75, 0x23, 0x98, 0x72, 0x4e, 0x11, 0xb9, 0xeb,
0x0e, 0x84, 0x0b, 0x4b, 0xa8, 0x24, 0xb3, 0xb3, 0x9f, 0xe6, 0x5f, 0x48,
0x2b, 0x37, 0xdd, 0x37, 0x2a, 0xfd, 0x08, 0xc9, 0x8a, 0x38, 0x50, 0xc0,
0x30, 0x04, 0x61, 0x97, 0x97, 0xd4, 0xcd, 0xf8, 0x83, 0xc9, 0xfd, 0x90,
0xaa, 0xe3, 0x77, 0x2a, 0xdb, 0x02, 0xa6, 0x58, 0x98, 0x0d, 0x4f, 0x31,
0xe5, 0x52, 0xa4, 0x8f, 0xd5, 0x53, 0x40, 0x30, 0xe1, 0x8b, 0xf5, 0x2f,
0x96, 0x7e, 0xe4, 0x4f, 0x3c, 0x08, 0x74, 0x1e, 0xe0, 0x2e, 0x51, 0x52,
0xd4, 0x92, 0x5b, 0x52, 0x21, 0xbc, 0x41, 0x3d, 0x01, 0x2e, 0x28, 0xb4,
0xed, 0x39, 0x93, 0xcf, 0xcf, 0xf3, 0x39, 0xd3, 0x9d, 0x9f, 0x1f, 0x69,
0x6e, 0x19, 0xf2, 0xd7, 0x77, 0xea, 0xec, 0x02, 0x26, 0xd8, 0x7a, 0xd9,
0x2e, 0xd0, 0x0c, 0x6a, 0x53, 0x9e, 0xd2, 0x3f, 0xa3, 0x16, 0xf8, 0xeb,
0x34, 0xa2, 0x8b, 0x7a, 0x9e, 0x7a, 0x66, 0x1b, 0xcf, 0x25, 0xca, 0xce,
0x54, 0xef, 0xa4, 0x28, 0xe1, 0xcc, 0xb6, 0x4f, 0x64, 0xf7, 0xbf, 0xed,
0xbb, 0xb6, 0x7d, 0xed, 0x7a, 0x9b, 0x33, 0xc6, 0xd3, 0xa3, 0x86, 0xf8,
0x21, 0x82, 0x98, 0x4e, 0x29, 0x5b, 0x85, 0x40, 0xb1, 0x24, 0x2e, 0x84,
0x8a, 0x1f, 0xde, 0x03, 0x8b, 0x73, 0xca, 0xb6, 0x49, 0x0d, 0xe0, 0x2c,
0x8c, 0xde, 0x97, 0xac, 0x32, 0x4b, 0xde, 0x1e, 0x4a, 0xa4, 0xe9, 0xca,
0x03, 0x8e, 0x33, 0xd1, 0x6a, 0xc9, 0x32, 0x11, 0x7b, 0x8e, 0x85, 0x49,
0x57, 0x3c, 0x8e, 0xa7, 0x5c, 0xa6, 0x5d, 0x52, 0xb1, 0x54, 0x8f, 0xc7,
0xb8, 0xeb, 0x99, 0x4c, 0x0e, 0x69, 0xce, 0x66, 0xd8, 0x48, 0x24, 0xf9,
0xaf, 0x0c, 0x7a, 0x93, 0x9e, 0x6b, 0xc2, 0xd8, 0x5d, 0xdd, 0x68, 0xe3,
0xcc, 0x33, 0xd7, 0x92, 0x85, 0x6e, 0x74, 0x61, 0xa2, 0xfc, 0x8e, 0x5d,
0xe3, 0x39, 0xcd, 0xa4, 0x69, 0x13, 0x48, 0xc7, 0xa6, 0x15, 0x7b, 0x07,
0x18, 0x4f, 0xbc, 0xe0, 0xef, 0x33, 0xed, 0x99, 0x8e, 0x6e, 0x16, 0xed,
0xe6, 0x5c, 0xfd, 0x4f, 0x33, 0xeb, 0x4b, 0x79, 0x26, 0xf4, 0xf7, 0xd9,
0x75, 0x4f, 0xd8, 0xb5, 0x38, 0x2c, 0x28, 0x10, 0xce, 0xde, 0x10, 0xfe,
0xb5, 0xfe, 0x2f, 0x89, 0xa6, 0x26, 0x29, 0x7a, 0xa7, 0x23, 0x59, 0x20,
0x1f, 0xa6, 0xe7, 0x5b, 0x8f, 0xb3, 0x63, 0x4f, 0x71, 0x47, 0xe2, 0x9f,
0x1a, 0x7b, 0x66, 0xb7, 0x49, 0x0b, 0x98, 0x10, 0xe8, 0xe5, 0xe0, 0xdd,
0xd8, 0x53, 0xdc, 0x3b, 0xfa, 0xcb, 0x63, 0x8f, 0x2e, 0x88, 0x9a, 0x38,
0x48, 0xce, 0x22, 0xef, 0xc6, 0x7d, 0x61, 0xd7, 0x70, 0x3b, 0x9d, 0x80,
0xf2, 0x3b, 0xac, 0xfb, 0xcb, 0xed, 0xe4, 0xfd, 0x50, 0xd4, 0xfd, 0xe2,
0x78, 0xb5, 0xef, 0x86, 0x01, 0x75, 0x43, 0x9e, 0x23, 0x41, 0x4b, 0x3c,
0xff, 0xba, 0xfd, 0xf3, 0x67, 0xf2, 0xb2, 0x61, 0x97, 0x82, 0xf4, 0xf5,
0x61, 0x9d, 0x65, 0x5e, 0x36, 0x58, 0xa9, 0x0d, 0x59, 0xe0, 0x5f, 0x45,
0x7c, 0x2e, 0xe6, 0x7a, 0x7f, 0x2c, 0xe6, 0x0a, 0xb1, 0xba, 0xd8, 0x05,
0xc5, 0xc6, 0x96, 0x89, 0xc7, 0xeb, 0x6a, 0x23, 0x6e, 0x23, 0xbe, 0x93,
0xa8, 0x46, 0x3d, 0xd4, 0xa8, 0x14, 0xf3, 0x21, 0x3e, 0x6c, 0x92, 0xc9,
0xeb, 0x00, 0x58, 0x97, 0x62, 0xa2, 0xce, 0x81, 0xee, 0xa9, 0x8f, 0xac,
0xdf, 0x55, 0xf3, 0x99, 0x2d, 0x24, 0x86, 0xca, 0x3a, 0xdb, 0xd0, 0x26,
0x4e, 0x77, 0x1d, 0x40, 0x48, 0x5f, 0xaf, 0xb2, 0x8d, 0xe9, 0x13, 0x11,
0x3c, 0xdb, 0x2b, 0x50, 0x30, 0xc2, 0x39, 0x05, 0x03, 0xc0, 0x1c, 0x5c,
0x43, 0xf2, 0xf4, 0xb1, 0x9c, 0x28, 0x59, 0x0a, 0xf8, 0x87, 0xb9, 0xe9,
0xd4, 0xc0, 0x8e, 0x18, 0x50, 0x15, 0x35, 0x22, 0x34, 0x71, 0x4c, 0x04,
0x31, 0xf7, 0xc2, 0x77, 0x3d, 0xd2, 0x25, 0xb2, 0x41, 0x50, 0xc6, 0x44,
0x06, 0xa1, 0x89, 0xd3, 0xb2, 0xb2, 0x9b, 0x72, 0xbb, 0xf3, 0xd7, 0x73,
0x7b, 0xf2, 0xf0, 0x48, 0x38, 0xb1, 0x96, 0x0b, 0xf9, 0x40, 0x52, 0x61,
0x3b, 0xf3, 0xf3, 0xf0, 0x9f, 0x46, 0xd2, 0x7e, 0xbb, 0x1a, 0x3f, 0xa9,
0xfa, 0xaf, 0x9e, 0x25, 0x89, 0x4b, 0x8e, 0xa9, 0x78, 0xad, 0x7c, 0xdb,
0x63, 0xea, 0x88, 0x68, 0x56, 0x4f, 0x9c, 0xc4, 0xde, 0x76, 0x7e, 0xbe,
0x17, 0x4b, 0xa2, 0x38, 0x32, 0x24, 0x33, 0x37, 0x49, 0x26, 0x78, 0x36,
0x34, 0x32, 0xe1, 0xaf, 0x11, 0xc6, 0x9f, 0x2d, 0xce, 0x7a, 0x5e, 0x9c,
0x2e, 0x66, 0x55, 0x98, 0x82, 0x44, 0xc4, 0xe7, 0xaf, 0x56, 0x64, 0xee,
0xdb, 0xe0, 0xc9, 0xa9, 0xb8, 0x69, 0xe1, 0x14, 0xdf, 0xb4, 0x70, 0x72,
0x37, 0x2d, 0x66, 0x07, 0x28, 0x91, 0x02, 0xc5, 0x1d, 0x15, 0x79, 0x24,
0x19, 0x4d, 0x10, 0xfc, 0x6d, 0x82, 0xb3, 0x56, 0x6f, 0x4e, 0xb0, 0xc7,
0x60, 0x70, 0xd2, 0x45, 0xf7, 0x30, 0x3f, 0x4f, 0x73, 0xc6, 0x0f, 0x1e,
0x7c, 0x2e, 0x65, 0x81, 0xb8, 0x06, 0x64, 0x04, 0xae, 0xcf, 0xb5, 0xfc,
0x60, 0x7a, 0xb1, 0x05, 0x95, 0x08, 0xc1, 0xc1, 0xef, 0x26, 0x15, 0x47,
0x12, 0x50, 0x5c, 0xb7, 0x89, 0x4d, 0x77, 0x20, 0x72, 0x10, 0x9f, 0x42,
0x59, 0x8d, 0x83, 0x7a, 0x43, 0xce, 0x9d, 0xfa, 0x88, 0x83, 0x71, 0x31,
0xeb, 0xbc, 0x9d, 0x7a, 0xce, 0x3e, 0x2c, 0xcd, 0x29, 0x91, 0xa3, 0x27,
0x37, 0x73, 0x5c, 0xc0, 0x4c, 0x3a, 0x3c, 0xa8, 0x07, 0x6a, 0x4d, 0xb2,
0xee, 0x36, 0x49, 0xa9, 0x24, 0x19, 0x64, 0x41, 0xd6, 0xcd, 0xbf, 0x91,
0xef, 0x4d, 0x2b, 0x3a, 0x41, 0x35, 0x4d, 0xd3, 0x96, 0x74, 0xa0, 0xd3,
0xb3, 0x05, 0xda, 0xf0, 0x83, 0xf7, 0xd9, 0xf4, 0x46, 0x27, 0x19, 0xc2,
0x5e, 0x7d, 0x5e, 0x67, 0xc7, 0x63, 0x88, 0x12, 0x99, 0x6b, 0x2f, 0xce,
0xc4, 0xb5, 0x97, 0xd4, 0xb2, 0x7e, 0x35, 0xb5, 0x78, 0xe3, 0xba, 0xc2,
0x1b, 0xd9, 0x08, 0x18, 0x11, 0x7a, 0xc9, 0xc4, 0xd3, 0x83, 0x91, 0x8e,
0x33, 0x2e, 0x9d, 0x68, 0xc2, 0xc5, 0xfc, 0x61, 0x9d, 0xe4, 0xc5, 0x3a,
0xc9, 0xa5, 0x4e, 0x2a, 0xb0, 0xca, 0x89, 0x2b, 0x87, 0xc9, 0xe3, 0x24,
0x10, 0x05, 0xcf, 0x31, 0x03, 0x92, 0x85, 0xdc, 0xfb, 0x9c, 0x09, 0x1a,
0x18, 0xdd, 0x51, 0xba, 0x20, 0x1c, 0x9b, 0x7a, 0x39, 0xde, 0x95, 0xb3,
0xdf, 0xf0, 0x50, 0x3c, 0xe3, 0x8f, 0x9d, 0xe8, 0xbe, 0xc9, 0x1b, 0xcb,
0x9b, 0x13, 0x97, 0xa7, 0x32, 0x5c, 0xe8, 0x49, 0x45, 0xce, 0x12, 0x5c,
0x2b, 0xab, 0x18, 0x54, 0xb0, 0x7b, 0x1c, 0x35, 0xf4, 0x0d, 0xc3, 0x00,
0x8f, 0x37, 0xec, 0xdb, 0x48, 0x76, 0x41, 0xfb, 0x37, 0xfe, 0x14, 0x37,
0x81, 0x5d, 0x2d, 0x1a, 0x28, 0x23, 0xf0, 0xf4, 0x6b, 0xf6, 0xd6, 0x08,
0xfe, 0x1f, 0x54, 0x9b, 0x16, 0xf3, 0x4d, 0xd8, 0x42, 0x5c, 0x1d, 0x89,
0x67, 0x15, 0x5d, 0x20, 0xb1, 0xb2, 0xe6, 0xec, 0xb9, 0x9e, 0xa6, 0xa3,
0x0d, 0x03, 0x9d, 0x65, 0x9a, 0x99, 0x35, 0x2a, 0xa4, 0x45, 0xe4, 0x6c,
0x19, 0x62, 0xb0, 0x25, 0x1e, 0x5d, 0x3f, 0x92, 0x74, 0xf1, 0x82, 0xd3,
0x42, 0x2e, 0xce, 0x09, 0xf9, 0xaf, 0x10, 0x4d, 0x12, 0xa2, 0x79, 0x6c,
0xc6, 0xb8, 0x68, 0xdf, 0xf5, 0xfb, 0xbb, 0x84, 0x93, 0xa4, 0x62, 0xd4,
0xa2, 0x8b, 0x02, 0x3c, 0xb5, 0x20, 0x08, 0xfe, 0x7e, 0x5b, 0x74, 0xb7,
0xf3, 0xac, 0xba, 0xb8, 0x11, 0xac, 0xc2, 0xe6, 0xbe, 0x3a, 0xc9, 0xa0,
0x0c, 0xdd, 0x5d, 0xe4, 0xce, 0x04, 0xd5, 0x22, 0x6f, 0xfc, 0x5f, 0x88,
0x30, 0xa1, 0x46, 0xcf, 0x34, 0xfd, 0x13, 0x5a, 0xf8, 0x04, 0x2d, 0x53,
0x98, 0xef, 0xee, 0x9d, 0xec, 0x5d, 0xef, 0xcd, 0x42, 0x1e, 0xbc, 0x6b,
0x64, 0x76, 0x3c, 0x57, 0xd5, 0x67, 0x2e, 0xea, 0x63, 0x93, 0x11, 0x0f,
0x93, 0xab, 0xb3, 0x8f, 0x64, 0x26, 0xfc, 0x1d, 0xf0, 0x53, 0x1c, 0x1f,
0xb4, 0x6d, 0x4a, 0xfc, 0x53, 0xc2, 0x9c, 0x0b, 0xe2, 0x50, 0xfb, 0x0f,
0x1d, 0x48, 0xfc, 0xfd, 0x47, 0x42, 0x16, 0x7b, 0x8e, 0x0f, 0x5f, 0xa6,
0xae, 0x74, 0x2f, 0x6c, 0x68, 0xe8, 0x98, 0xc5, 0x69, 0x50, 0x07, 0xaf,
0x8f, 0x29, 0x78, 0xec, 0x03, 0xa2, 0x20, 0xf2, 0x44, 0x37, 0xb9, 0x8e,
0x06, 0x39, 0x1d, 0xc0, 0xd9, 0x50, 0xc7, 0x92, 0x11, 0x19, 0x27, 0x91,
0x0b, 0xe7, 0xd9, 0xdb, 0xac, 0xea, 0x96, 0x9c, 0x84, 0x9a, 0x82, 0x72,
0xc7, 0xbb, 0x80, 0x39, 0x8d, 0xf9, 0xa5, 0x33, 0xb7, 0xe2, 0x36, 0xad,
0x86, 0x07, 0x34, 0x33, 0x7b, 0xfb, 0x78, 0xb6, 0xd1, 0x48, 0x3b, 0xd1,
0x14, 0xd3, 0x5b, 0x98, 0x8e, 0xe7, 0xa3, 0x8d, 0xbc, 0x28, 0x35, 0xcc,
0x53, 0xa2, 0x13, 0xa2, 0xc4, 0xd5, 0x4d, 0xde, 0x18, 0x88, 0xaf, 0xbb,
0x99, 0xd9, 0xeb, 0x6e, 0x7a, 0x8e, 0xe4, 0xcb, 0xe4, 0x2e, 0x28, 0x9e,
0x80, 0x4b, 0x32, 0xa5, 0xe1, 0x27, 0x9e, 0x7c, 0xe6, 0xfc, 0x2c, 0x6b,
0x2a, 0x75, 0xd3, 0x2c, 0xb8, 0xef, 0x94, 0x01, 0xf5, 0x9a, 0x5e, 0x8d,
0xfd, 0xf6, 0xbd, 0xec, 0x98, 0xb4, 0x59, 0xa9, 0x63, 0x72, 0x33, 0xb9,
0x66, 0x7e, 0x7e, 0xe6, 0xe5, 0x35, 0x08, 0x1d, 0x3a, 0x37, 0xbc, 0x30,
0xe8, 0x61, 0x35, 0xe6, 0x14, 0x85, 0x16, 0x3c, 0x7a, 0x77, 0x36, 0xa3,
0x49, 0x08, 0xae, 0x91, 0x2e, 0x10, 0xb6, 0x16, 0x71, 0x2d, 0xbd, 0x97,
0x60, 0xe6, 0xb3, 0x98, 0x09, 0xd7, 0x21, 0xe7, 0x44, 0xe4, 0xfe, 0x8d,
0x6e, 0x52, 0xde, 0xd9, 0x2a, 0x88, 0x20, 0x17, 0xff, 0xdd, 0xc9, 0xb5,
0xf5, 0xb4, 0x0d, 0x43, 0xe1, 0xbf, 0x02, 0xd6, 0x54, 0x25, 0x23, 0x34,
0x30, 0xed, 0x61, 0x4b, 0x49, 0x10, 0xa0, 0x49, 0x93, 0x36, 0xc4, 0xa4,
0xb0, 0x27, 0x84, 0x50, 0x92, 0x06, 0x1a, 0x48, 0x63, 0xd4, 0x84, 0xb1,
0xaa, 0xea, 0x7f, 0xdf, 0xb9, 0x38, 0xa9, 0xed, 0xa6, 0x65, 0xf4, 0xa5,
0x4d, 0x63, 0xc7, 0xb1, 0x8f, 0x8f, 0xcf, 0xf5, 0x3b, 0xbd, 0x8a, 0xb7,
0xc9, 0xc5, 0xd5, 0x6a, 0x19, 0x47, 0x60, 0xb0, 0x88, 0xb5, 0x56, 0xea,
0xb1, 0xb6, 0xb3, 0x5b, 0x31, 0x71, 0xbb, 0x2c, 0x35, 0xdf, 0x4d, 0x8a,
0x6e, 0x91, 0xff, 0x08, 0xcf, 0xe0, 0xb9, 0xd1, 0x72, 0x6d, 0xe8, 0xc2,
0x37, 0x12, 0x4d, 0xec, 0x46, 0xb1, 0xed, 0xdd, 0xa7, 0x4b, 0x05, 0x48,
0xa4, 0xa2, 0x02, 0x59, 0xf4, 0xff, 0x69, 0xc7, 0xaa, 0x4d, 0x3b, 0x56,
0x7a, 0xaa, 0xb4, 0x1b, 0x6a, 0x94, 0xc2, 0xeb, 0x9f, 0x46, 0xab, 0xd4,
0x29, 0xb4, 0x50, 0xe2, 0x54, 0x6f, 0xc0, 0xf4, 0x29, 0xdc, 0x7f, 0x4c,
0xfe, 0x24, 0x0c, 0xc6, 0x37, 0x5a, 0x33, 0x3d, 0x81, 0x0a, 0xdd, 0xb2,
0x3b, 0xbc, 0x32, 0x7a, 0x74, 0x09, 0xd5, 0x7a, 0x75, 0xf9, 0x3c, 0x79,
0xee, 0x4b, 0xd4, 0x76, 0xf9, 0x59, 0xca, 0xbd, 0xc2, 0x06, 0x74, 0x9a,
0x67, 0xc9, 0x6b, 0x42, 0xd8, 0x0a, 0x66, 0xbd, 0x60, 0x7b, 0xa9, 0x2c,
0x65, 0x88, 0xd3, 0x6e, 0x6d, 0xd5, 0x90, 0xbc, 0x40, 0x07, 0x0c, 0x72,
0x67, 0x65, 0xc0, 0xc2, 0x4d, 0x09, 0x37, 0x25, 0xe3, 0xff, 0x40, 0x0a,
0xe7, 0x7a, 0xff, 0x04, 0x5d, 0x46, 0x6e, 0x02, 0x7e, 0xa9, 0x3c, 0x35,
0x7f, 0x35, 0x52, 0xdb, 0xc2, 0xd4, 0x72, 0x5b, 0x28, 0x20, 0x9a, 0x3b,
0x5e, 0x11, 0x62, 0x02, 0x1f, 0x37, 0x1b, 0xe3, 0xc9, 0x1a, 0x52, 0xc4,
0xe4, 0xda, 0xd6, 0x1e, 0x2d, 0x95, 0x3d, 0x3a, 0x18, 0x98, 0x06, 0x69,
0x09, 0x1c, 0x01, 0x4a, 0x54, 0x17, 0x92, 0x8e, 0x2d, 0x35, 0x70, 0x31,
0xb6, 0x4d, 0xa8, 0x61, 0x2d, 0xd6, 0x5e, 0xf8, 0x36, 0xf2, 0x65, 0x27,
0xd5, 0xb9, 0xfe, 0x98, 0x52, 0x8e, 0xbb, 0xaa, 0x5c, 0x05, 0x52, 0x46,
0x02, 0x85, 0x1d, 0x81, 0x4e, 0x9d, 0x02, 0xce, 0x0c, 0xe3, 0x31, 0x9d,
0xd2, 0x32, 0xac, 0x0b, 0xb6, 0x17, 0x62, 0xaa, 0xd2, 0x63, 0x3f, 0x76,
0x23, 0xaa, 0x17, 0x04, 0x2b, 0x12, 0xd6, 0xd6, 0x4a, 0x16, 0x12, 0xc1,
0x36, 0xa5, 0x57, 0xb2, 0x74, 0x25, 0x60, 0x9c, 0xb2, 0x47, 0x7c, 0xb8,
0xad, 0xdc, 0xc8, 0x30, 0x14, 0xad, 0x81, 0x1e, 0xc8, 0x3c, 0x2e, 0x75,
0xc3, 0x98, 0x79, 0x58, 0x9d, 0x39, 0x0e, 0x55, 0x14, 0x48, 0xab, 0x38,
0xaf, 0x6b, 0x5a, 0x04, 0x2e, 0xf8, 0x12, 0x5c, 0x22, 0x47, 0x00, 0x4f,
0xf9, 0x53, 0xb8, 0x42, 0x66, 0xc4, 0xe5, 0x42, 0xc3, 0xf5, 0x24, 0x9f,
0xaa, 0x96, 0x06, 0x2f, 0xa1, 0x49, 0x62, 0xd3, 0x07, 0xa2, 0x7e, 0x4c,
0xf9, 0x18, 0x98, 0x72, 0x78, 0xec, 0x1f, 0x79, 0xeb, 0xc3, 0xfe, 0xae,
0xf3, 0x58, 0xde, 0x37, 0xd7, 0x49, 0x0a, 0x44, 0x39, 0x72, 0x7b, 0x7a,
0x40, 0x13, 0xc5, 0x99, 0x3e, 0xa9, 0xf7, 0x7d, 0x2f, 0x1e, 0x26, 0x25,
0x86, 0xff, 0xce, 0x32, 0x2c, 0x3f, 0xfa, 0x09, 0x5b, 0xa4, 0x9e, 0x84,
0xc6, 0x18, 0x98, 0xf7, 0xd7, 0xac, 0xa8, 0x9a, 0x4b, 0x02, 0x03, 0x11,
0xbb, 0xc1, 0xa6, 0xc8, 0xe9, 0x34, 0xa9, 0xc6, 0x35, 0x46, 0xb6, 0x2e,
0xf8, 0xda, 0x59, 0xa0, 0xdb, 0x19, 0x10, 0x46, 0x53, 0x78, 0x29, 0x1c,
0xd6, 0x1f, 0xf9, 0x3c, 0x58, 0xbc, 0x16, 0x55, 0x20, 0x2e, 0x9a, 0x59,
0x79, 0x18, 0x0b, 0x6f, 0x9a, 0x64, 0xf0, 0x83, 0xfb, 0xc3, 0xef, 0xa5,
0x87, 0x52, 0x2c, 0x30, 0x63, 0x19, 0x2d, 0x70, 0x0c, 0x43, 0x62, 0x57,
0x55, 0x39, 0x0f, 0xf6, 0x8f, 0x97, 0x6f, 0xbd, 0x12, 0xe6, 0x08, 0x2f,
0x4b, 0x25, 0x30, 0x43, 0x07, 0x18, 0xeb, 0x9d, 0xc3, 0x59, 0xd9, 0x1c,
0x4e, 0xac, 0x79, 0xf0, 0xbd, 0xfe, 0xb9, 0xd8, 0x08, 0x34, 0x26, 0x0a,
0xd1, 0x12, 0xb8, 0xc1, 0x11, 0x1c, 0x02, 0x12, 0x9e, 0xf1, 0x58, 0x3f,
0x7f, 0x1e, 0xb9, 0xf4, 0xb4, 0x32, 0xa4, 0x0c, 0xa5, 0xf7, 0x2e, 0x23,
0x6e, 0x33, 0xa8, 0x9d, 0xc4, 0x60, 0x48, 0x98, 0x6c, 0x4d, 0xbe, 0xbd,
0x9f, 0x09, 0x67, 0x0a, 0x5c, 0x88, 0x3b, 0xa1, 0xdb, 0xe7, 0x9b, 0xdf,
0xac, 0xdb, 0x2b, 0xe8, 0xb4, 0x5b, 0x67, 0x0c, 0x1e, 0x79, 0xe7, 0x11,
0xab, 0xb5, 0x0a, 0xa0, 0x4e, 0x1f, 0x4b, 0xcb, 0xf4, 0xc0, 0xc6, 0xf3,
0x52, 0xa6, 0xce, 0x4d, 0x73, 0xeb, 0x2d, 0xd0, 0x90, 0x0c, 0x2a, 0xa0,
0x71, 0xee, 0x76, 0xe7, 0xd3, 0xb2, 0x44, 0xd4, 0x19, 0x95, 0xee, 0x12,
0x3c, 0x53, 0xa2, 0x05, 0x0b, 0x1d, 0xf7, 0x40, 0x08, 0xd0, 0xc1, 0xb8,
0x66, 0x05, 0x4b, 0x34, 0xca, 0x8b, 0x3a, 0xe3, 0xb7, 0xd9, 0x22, 0x7c,
0x88, 0x60, 0x3a, 0xbb, 0xe8, 0x43, 0xa0, 0xe2, 0xa0, 0x7a, 0x86, 0x07,
0x1a, 0x0b, 0x88, 0xfe, 0x52, 0x2a, 0xb2, 0xe3, 0x3e, 0x3d, 0x01, 0xef,
0x02, 0xaf, 0x02, 0x8d, 0xee, 0xa6, 0x94, 0x32, 0x35, 0xb9, 0x10, 0x2b,
0xf8, 0x38, 0x07, 0xd3, 0xcb, 0xe8, 0xc0, 0x97, 0x78, 0x5c, 0x70, 0x76,
0x85, 0x16, 0xd4, 0xab, 0xce, 0xe5, 0x78, 0x4e, 0x11, 0x88, 0x36, 0x4e,
0xbb, 0x58, 0x8e, 0xe0, 0x24, 0x8c, 0xe5, 0x2b, 0x45, 0xb7, 0xb0, 0xd7,
0x70, 0x02, 0x5a, 0x06, 0x24, 0x2e, 0xf0, 0x48, 0x96, 0x3b, 0xfe, 0xcd,
0xe9, 0xe0, 0xf6, 0x00, 0x8c, 0x8c, 0x10, 0xbe, 0xdc, 0x10, 0x2e, 0x06,
0xb7, 0x1f, 0x5d, 0xff, 0xa1, 0x58, 0xdb, 0x5f, 0x09, 0x14, 0x07, 0x55,
0x4d, 0x81, 0x3c, 0xcd, 0x71, 0xd8, 0xd7, 0x1c, 0x07, 0x97, 0x43, 0x2d,
0x86, 0xb9, 0xd3, 0xaa, 0x15, 0x4f, 0x12, 0xee, 0x16, 0xbe, 0xf0, 0x8f,
0x80, 0xe0, 0x8b, 0xc4, 0x9c, 0x3b, 0x32, 0x0a, 0x10, 0x04, 0x97, 0x5a,
0x0b, 0xd2, 0x96, 0xa1, 0x86, 0xf8, 0x14, 0x78, 0x83, 0xac, 0xb1, 0x7c,
0x5d, 0x4b, 0x9e, 0xf8, 0xaa, 0x7e, 0x50, 0xd5, 0x11, 0x12, 0xee, 0x11,
0x6d, 0xcd, 0x3a, 0xf0, 0xfd, 0x6c, 0x5c, 0x3d, 0xa2, 0x03, 0x2a, 0x5f,
0xc6, 0xf7, 0x25, 0xe8, 0x5b, 0x94, 0x29, 0x7e, 0xf2, 0x98, 0xfc, 0xf5,
0xcb, 0x22, 0xad, 0x11, 0x9f, 0xe7, 0x1f, 0x0f, 0x3f, 0x0f, 0xbf, 0xb6,
0x48, 0x3d, 0x04, 0xdf, 0x02, 0x2b, 0x87, 0x2f, 0xcd, 0xfd, 0xe1, 0x97,
0xc8, 0x1e, 0x3a, 0x32, 0x17, 0xaf, 0x7b, 0x4d, 0x96, 0x13, 0x77, 0x91,
0x54, 0xe8, 0x08, 0x92, 0x03, 0xa7, 0x86, 0xee, 0xc0, 0x82, 0xaf, 0x79,
0xea, 0xed, 0x35, 0xb3, 0x39, 0x7b, 0x78, 0x59, 0x52, 0xee, 0x65, 0xf2,
0x79, 0x2e, 0x54, 0xbe, 0x86, 0x5e, 0xb4, 0x39, 0x25, 0xc1, 0xd6, 0x99,
0x3b, 0xe2, 0x0b, 0x02, 0x9b, 0x08, 0x1d, 0x6b, 0x28, 0x3c, 0xd5, 0x92,
0xd4, 0xf3, 0x2a, 0xc3, 0x82, 0x8b, 0x6e, 0x28, 0xa4, 0xae, 0x99, 0xdf,
0xa2, 0x9e, 0x3a, 0x05, 0x31, 0xe5, 0xb2, 0xd7, 0x86, 0x8c, 0x34, 0x76,
0x8a, 0xc8, 0x1d, 0x06, 0xef, 0x9f, 0x77, 0x28, 0x62, 0xb7, 0xb6, 0xbd,
0x89, 0xbb, 0x63, 0xdd, 0xe2, 0x6d, 0xb7, 0x6e, 0x2a, 0xd3, 0x44, 0xb9,
0xd5, 0xfa, 0xbf, 0xcd, 0x58, 0x1d, 0x95, 0xb7, 0x13, 0x75, 0xf5, 0x64,
0x5a, 0x19, 0x67, 0x5b, 0xd4, 0x75, 0x92, 0xce, 0xb8, 0x3f, 0x17, 0x95,
0xe9, 0x65, 0xb0, 0x91, 0x92, 0x44, 0x84, 0xad, 0xa4, 0x71, 0xf9, 0xb3,
0x20, 0x88, 0x0e, 0x8e, 0x67, 0x82, 0x76, 0xfa, 0x27, 0xc4, 0xbd, 0xa3,
0x7f, 0xb9, 0xa1, 0x68, 0x0e, 0x38, 0x4b, 0x00, 0x00
};
unsigned int index_htm_gz_len = 6261;

View File

@ -0,0 +1,60 @@
#/bin/sh
# Processing script to optionally reduce filesystem use by miniying, gzipping and preparing index.htm for embedding in code.
# Please see readme.md for more information.
# Requires xdd which is part of the VIM package
# Requires npm
# sudo apt install npm
# ln -s /usr/bin/nodejs /usr/bin/node
# Requires html-minifier
# sudo npm install html-minifier -g
html-minifier \
--case-sensitive \
--collapse-boolean-attributes \
--collapse-whitespace \
--minify-css true \
--minify-js true \
--process-conditional-comments \
--remove-attribute-quotes \
--remove-comments \
--remove-empty-attributes \
--remove-optional-tags \
--remove-redundant-attributes \
--remove-script-type-attributes \
--remove-style-link-type-attributes \
-o index.htm \
../data/edit/index.htm
if [ $? -ne 0 ]
then
echo "Error minifying index.htm"
exit -1
fi
if [ -e index.htm.gz ]
then
rm index.htm.gz
fi
gzip index.htm
if [ $? -ne 0 ]
then
echo "Error gzipping minified index.htm"
exit -1
fi
echo '// WARNING: Auto-generated file. Please do not modify by hand.' > index_htm.h
echo '// This file is an embeddable version of the gzipped index.htm file.' >> index_htm.h
echo '// To update it, please rerun the `reduce_index.sh` script located in the `extras` subfolder' >> index_htm.h
echo '// then recompile the sketch after each change to the `index.html` file.' >> index_htm.h
xxd -i index.htm.gz >> index_htm.h
if [ $? -ne 0 ]
then
echo "Error creating include file from index.htm.gz"
exit -1
fi
echo Reduce complete.

View File

@ -0,0 +1,138 @@
# FSBrowser readme
## What is this sketch about ?
This example is a FileSystem Browser for the ESP8266 using http requests and a html/javascript frontend,
working for all of SPIFFS, LittleFS and SDFS.
This unified version is based on the previous examples named FSWebServer, FSBrowser and SDWebServer, Copyright (c) 2015 Hristo Gochkov. All rights reserved.
## How to use it ?
1. Uncomment one of the `#define USE_xxx` directives in the sketch
2. Add the credentials of your WiFi network (search for `STASSID`)
3. Compile and upload the sketch to your ESP8266 device
4. For normal use, copy the contents of the `data` folder to the filesystem. To do so:
- for SDFS, copy that contents (not the data folder itself, just its contents) to the root of a FAT/FAT32-formated SD card connected to the SPI port of the ESP8266
- for SPIFFS or LittleFS, please follow the instructions at https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#uploading-files-to-file-system
5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit
## Options
If you need to free some space on the ESP filesystem, you can delete all the sample files at the root but also replace the `index.htm` file in the `data/edit` subfolder by the `index.htm.gz` file from the `extras` folder. That compressed version is not suited for learning or debugging, but will bring the total FS usage under 7KB.
If you want to use the browser on a an existing filesystem or don't want to perform step 4 above, you have two possibilities :
- either upload the `index.htm` file to the filesystem by opening a command shell in the `data` folder and running the following cURL command:
`curl -F file=@edit/index.htm;filename=/edit/index.htm fsbrowser.local/edit`
- or embed a version of the html page in the source code itself by uncommenting the following line in the sketch and rebuilding:
`#define INCLUDE_FALLBACK_INDEX_HTM`
That embedded version is functionally equivalent and will be returned if no `/edit/index.htm` or `/edit/index.htm.gz` file can be found on the filesystem, at the expense of a higher binary size.
If you use the gzipped or `INCLUDE_FALLBACK_INDEX_HTM` options, please remember to rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile the sketch after each change to the `index.html` file.
## Dependency
The html page uses the [Ace.js](https://ace.c9.io/) (v1.4.9 at the time of writing) text editor which is loaded from a CDN. Consequently, internet access from your web browser is required for the FSBrowser editing feature to work as-is.
If your browser has no web access (e.g. if you are connected to the ESP8266 as an access-point), you can copy the `ace.js` file to the `edit` subfolder of the ESP filesystem, along with optional plugins etc. according to your needs. A typical set might be:
```
ace.js
ext-keybinding_menu.js
ext-searchbox.js
mode-html.js
worker-html.js
worker-css.js
worker-javascript.js
mode-xml.js
worker-xml.js
mode-json.js
worker-json.js
```
(see https://github.com/ajaxorg/ace-builds for a full list).
If `ace.js` cannot be found on the ESP filesystem either, the page will default to a plain text viewer, with a warning message.
## Notes
- See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems supported by the ESP8266.
- For SDFS, if your card's CS pin is not connected to the default pin (4), uncomment the `fileSystemConfig.setCSPin(chipSelectPin);` line, specifying the GPIO the CS pin is connected to
- `index.htm` is the default index returned if your URL does not end with a filename (works on subfolders as well)
- Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - see https://en.wikipedia.org/wiki/8.3_filename - SPIFFS and LittleFS also have limitations, please see https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#spiffs-file-system-limitations
- Directories are supported on SDFS and LittleFS. On SPIFFS, all files are at the root, although their names may contain the "/" character.
- The convention here is that the root of the filesystem is "/". On SPIFFS, paths not started with a slash are not supported
- For creation, the convention is that a path ending with a "/" means create a folder, while without a "/" we create a file. Having an extension or not does not matter.
## Changelog since original FSBrowser
### Fixes to work on LittleFS based on SDFS
- #define logic to select FS
- switched from SD to SDFS
- begin() does not support parameters > removed SS and added optional config
- LittleFS.open() second parametsr is mandatory > specified "r" where needed
- 'FILE_WRITE' was not declared in this scope > replaced by "w"
### UI/usability improvements
- Never format filesystem, just return "FS INIT ERROR" when FS cannot be mounted
- Tree panel width is now proportional (20%) to see long names on big screens
- Added icons for files, and indented them to the same level as folders
- Changed file/folder icon set to use a lighter and more neutral one, and added specific "text" and "image" icons for formats recognized as such
- Items are now sorted (folders first, then plain files, each in alphabetic order)
- Added file size after each file name
- Added FS status information at the top right
- Made clear that an async operation is in progress by dimming screen and showing operation status
- Filled filename box in header with the name of the last clicked file
- Selecting a file for upload defaults to putting it in the same folder as the last clicked file
- Removed limitation to 8.3 lowercase filenames
- Support Filenames without extension, Dirnames with extension
- Improved recursive refresh of parts of the tree (e.g. refresh folder upon file delete, show last folder upon creating nested file)
- Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree and status upon save
- Removed "Upload" from context menu (which didn't work anyway)
- Added "Rename/Move" feature to context menu
- Sketch can be used on a preexisting filesystem by embedding the index.htm file in the program
## TODO (maybe)
- ? How can we query the fatType of the SDFS (FAT16 or FAT32) to limit filenames to 8.3 on FAT16 ?
- ? Add a visible root node "/" (with no delete option) + add the FS type next to it, like <i>LittleFS</i>
- ? move "Mkdir" and "MkFile" to context menu, with prompt like for Rename/Move
- ? implement drag/drop for move + make "rename" only a local rename operation (no move)
- ? Optionally present SPIFFS as a hierarchical FS too
- ? Optionally mount several filesystems at the same time (SPIFFS + SDFS or LittleFS + SDFS)
## Test suite
These tests are a checklist of operations to verify the FSBrowser behaviour.
### On SPIFFS
#### 8.3 filenames
- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image
- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image
- Create nested file '/a/b.txt' and delete it
- Attempt creation of unsupported filenames
#### Long filenames
- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image
- In subdir : MkFile '/My Directory/My text 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image 2.png' / View image
- Create nested file '/My folder/My test file.txt' and delete it
### On LittleFS
#### 8.3 filenames
- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image / Mkdir '/dir'
- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image / Mkdir '/dir/sub'
- Delete root folder '/dir'
- Create nested file '/a/b.txt' and delete file 'b.txt'
#### Long filenames
- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image / Mkdir '/My Directory'
- In subdir : MkFile '/My Directory/My text file 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image file 2.png' / View image / Mkdir '/My Directory/My Subdirectory'
- Delete root folder '/My Directory'
- Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt'
### On SDFS
#### 8.3 filenames
- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image / Mkdir '/dir'
- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image / Mkdir '/dir/sub'
- Delete root folder '/dir'
- Create nested file '/a/b.txt' and delete file 'b.txt', then delete '/a'
#### Long filenames
- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image / Mkdir '/My Directory'
- In subdir : MkFile '/My Directory/My text file 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image file 2.png' / View image / Mkdir '/My Directory/My Subdirectory'
- Delete root folder '/My Directory'
- Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt'
## Credits
- Original version of FSBrowser written by me-no-dev, contributions over time by various contributors
- Icons are from https://feathericons.com/ . The resulting PNG is passed first through https://compresspng.com/ before being converted to base64 using https://www.base64-image.de/
- The spinner is based on https://github.com/jlong/css-spinners
- Minifiying of index.htm is done using the command line version of https://kangax.github.io/html-minifier/
- Idea of embedding webpage in code borrowed from https://github.com/me-no-dev/ESPAsyncWebServer

View File

@ -7,10 +7,11 @@
1. Creating a secure web server using ESP8266ESP8266WebServerSecure
2. Use of HTTP authentication on this secure server
3. A simple web interface to allow an authenticated user to change Credentials
4. Persisting those credentials through a reboot of the ESP by saving them to SPIFFS without storing them as plain text
4. Persisting those credentials through a reboot of the ESP by saving them to LittleFS without storing them as plain text
*/
#include <FS.h>
#include <LittleFS.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServerSecure.h>
@ -23,8 +24,8 @@
const char* ssid = STASSID;
const char* wifi_pw = STAPSK;
const String file_credentials = R"(/credentials.txt)"; //SPIFFS file name for the saved credentials
const String change_creds = "changecreds"; //address for a credential change
const String file_credentials = R"(/credentials.txt)"; // LittleFS file name for the saved credentials
const String change_creds = "changecreds"; // Address for a credential change
//The ESP8266WebServerSecure requires an encryption certificate and matching key.
//These can generated with the bash script available in the ESP8266 Arduino repository.
@ -83,7 +84,7 @@ gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
ESP8266WebServerSecure server(443);
//These are temporary credentials that will only be used if none are found saved in SPIFFS.
//These are temporary credentials that will only be used if none are found saved in LittleFS.
String login = "admin";
const String realm = "global";
String H1 = "";
@ -92,9 +93,9 @@ String authentication_failed = "User authentication has failed.";
void setup() {
Serial.begin(115200);
//Initialize SPIFFS to save credentials
if(!SPIFFS.begin()){
Serial.println("SPIFFS initialization error, programmer flash configured?");
//Initialize LittleFS to save credentials
if(!LittleFS.begin()){
Serial.println("LittleFS initialization error, programmer flash configured?");
ESP.restart();
}
@ -187,16 +188,16 @@ void showcredentialpage(){
server.send(200, "text/html", page);
}
//Saves credentials to SPIFFS
//Saves credentials to LittleFS
void savecredentials(String new_login, String new_password)
{
//Set global variables to new values
login=new_login;
H1=ESP8266WebServer::credentialHash(new_login,realm,new_password);
//Save new values to SPIFFS for loading on next reboot
//Save new values to LittleFS for loading on next reboot
Serial.println("Saving credentials.");
File f=SPIFFS.open(file_credentials,"w"); //open as a brand new file, discard old contents
File f=LittleFS.open(file_credentials,"w"); //open as a brand new file, discard old contents
if(f){
Serial.println("Modifying credentials in file system.");
f.println(login);
@ -208,12 +209,12 @@ void savecredentials(String new_login, String new_password)
Serial.println("Credentials saved.");
}
//loads credentials from SPIFFS
//loads credentials from LittleFS
void loadcredentials()
{
Serial.println("Searching for credentials.");
File f;
f=SPIFFS.open(file_credentials,"r");
f=LittleFS.open(file_credentials,"r");
if(f){
Serial.println("Loading credentials from file system.");
String mod=f.readString(); //read the file to a String

View File

@ -1,320 +0,0 @@
/*
SDWebServer - Example WebServer with SD Card backend for esp8266
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the ESP8266WebServer library 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
Have a FAT Formatted SD Card connected to the SPI port of the ESP8266
The web root is the SD Card root folder
File extensions with more than 3 charecters are not supported by the SD Library
File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter
index.htm is the default index (works on subfolders as well)
upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <SPI.h>
#include <SD.h>
#define DBG_OUTPUT_PORT Serial
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
const char* host = "esp8266sd";
ESP8266WebServer server(80);
static bool hasSD = false;
File uploadFile;
void returnOK() {
server.send(200, "text/plain", "");
}
void returnFail(String msg) {
server.send(500, "text/plain", msg + "\r\n");
}
bool loadFromSdCard(String path) {
String dataType = "text/plain";
if (path.endsWith("/")) {
path += "index.htm";
}
if (path.endsWith(".src")) {
path = path.substring(0, path.lastIndexOf("."));
} else if (path.endsWith(".htm")) {
dataType = "text/html";
} else if (path.endsWith(".css")) {
dataType = "text/css";
} else if (path.endsWith(".js")) {
dataType = "application/javascript";
} else if (path.endsWith(".png")) {
dataType = "image/png";
} else if (path.endsWith(".gif")) {
dataType = "image/gif";
} else if (path.endsWith(".jpg")) {
dataType = "image/jpeg";
} else if (path.endsWith(".ico")) {
dataType = "image/x-icon";
} else if (path.endsWith(".xml")) {
dataType = "text/xml";
} else if (path.endsWith(".pdf")) {
dataType = "application/pdf";
} else if (path.endsWith(".zip")) {
dataType = "application/zip";
}
File dataFile = SD.open(path.c_str());
if (dataFile.isDirectory()) {
path += "/index.htm";
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
if (!dataFile) {
return false;
}
if (server.hasArg("download")) {
dataType = "application/octet-stream";
}
if (server.streamFile(dataFile, dataType) != dataFile.size()) {
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
dataFile.close();
return true;
}
void handleFileUpload() {
if (server.uri() != "/edit") {
return;
}
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
if (SD.exists((char *)upload.filename.c_str())) {
SD.remove((char *)upload.filename.c_str());
}
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (uploadFile) {
uploadFile.write(upload.buf, upload.currentSize);
}
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
if (uploadFile) {
uploadFile.close();
}
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
void deleteRecursive(String path) {
File file = SD.open((char *)path.c_str());
if (!file.isDirectory()) {
file.close();
SD.remove((char *)path.c_str());
return;
}
file.rewindDirectory();
while (true) {
File entry = file.openNextFile();
if (!entry) {
break;
}
String entryPath = path + "/" + entry.name();
if (entry.isDirectory()) {
entry.close();
deleteRecursive(entryPath);
} else {
entry.close();
SD.remove((char *)entryPath.c_str());
}
yield();
}
SD.rmdir((char *)path.c_str());
file.close();
}
void handleDelete() {
if (server.args() == 0) {
return returnFail("BAD ARGS");
}
String path = server.arg(0);
if (path == "/" || !SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
deleteRecursive(path);
returnOK();
}
void handleCreate() {
if (server.args() == 0) {
return returnFail("BAD ARGS");
}
String path = server.arg(0);
if (path == "/" || SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
if (path.indexOf('.') > 0) {
File file = SD.open((char *)path.c_str(), FILE_WRITE);
if (file) {
file.write((const char *)0);
file.close();
}
} else {
SD.mkdir((char *)path.c_str());
}
returnOK();
}
void printDirectory() {
if (!server.hasArg("dir")) {
return returnFail("BAD ARGS");
}
String path = server.arg("dir");
if (path != "/" && !SD.exists((char *)path.c_str())) {
return returnFail("BAD PATH");
}
File dir = SD.open((char *)path.c_str());
path.clear();
if (!dir.isDirectory()) {
dir.close();
return returnFail("NOT DIR");
}
dir.rewindDirectory();
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/json", "");
WiFiClient client = server.client();
server.sendContent("[");
for (int cnt = 0; true; ++cnt) {
File entry = dir.openNextFile();
if (!entry) {
break;
}
String output;
if (cnt > 0) {
output = ',';
}
output += "{\"type\":\"";
output += (entry.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
output += entry.name();
output += "\"";
output += "}";
server.sendContent(output);
entry.close();
}
server.sendContent("]");
server.sendContent(""); // Terminate the HTTP chunked transmission with a 0-length chunk
dir.close();
}
void handleNotFound() {
if (hasSD && loadFromSdCard(server.uri())) {
return;
}
String message = "SDCARD Not Detected\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
DBG_OUTPUT_PORT.print(message);
}
void setup(void) {
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.setDebugOutput(true);
DBG_OUTPUT_PORT.print("\n");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
DBG_OUTPUT_PORT.print("Connecting to ");
DBG_OUTPUT_PORT.println(ssid);
// Wait for connection
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
delay(500);
}
if (i == 21) {
DBG_OUTPUT_PORT.print("Could not connect to");
DBG_OUTPUT_PORT.println(ssid);
while (1) {
delay(500);
}
}
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
if (MDNS.begin(host)) {
MDNS.addService("http", "tcp", 80);
DBG_OUTPUT_PORT.println("MDNS responder started");
DBG_OUTPUT_PORT.print("You can now connect to http://");
DBG_OUTPUT_PORT.print(host);
DBG_OUTPUT_PORT.println(".local");
}
server.on("/list", HTTP_GET, printDirectory);
server.on("/edit", HTTP_DELETE, handleDelete);
server.on("/edit", HTTP_PUT, handleCreate);
server.on("/edit", HTTP_POST, []() {
returnOK();
}, handleFileUpload);
server.onNotFound(handleNotFound);
server.begin();
DBG_OUTPUT_PORT.println("HTTP server started");
if (SD.begin(SS)) {
DBG_OUTPUT_PORT.println("SD Card initialized.");
hasSD = true;
}
}
void loop(void) {
server.handleClient();
MDNS.update();
}

View File

@ -1,674 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>SD Editor</title>
<style type="text/css" media="screen">
.contextMenu {
z-index: 300;
position: absolute;
left: 5px;
border: 1px solid #444;
background-color: #F5F5F5;
display: none;
box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
font-size: 12px;
font-family: sans-serif;
font-weight:bold;
}
.contextMenu ul {
list-style: none;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.contextMenu li {
position: relative;
min-width: 60px;
cursor: pointer;
}
.contextMenu span {
color: #444;
display: inline-block;
padding: 6px;
}
.contextMenu li:hover { background: #444; }
.contextMenu li:hover span { color: #EEE; }
.css-treeview ul, .css-treeview li {
padding: 0;
margin: 0;
list-style: none;
}
.css-treeview input {
position: absolute;
opacity: 0;
}
.css-treeview {
font: normal 11px Verdana, Arial, Sans-serif;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.css-treeview span {
color: #00f;
cursor: pointer;
}
.css-treeview span:hover {
text-decoration: underline;
}
.css-treeview input + label + ul {
margin: 0 0 0 22px;
}
.css-treeview input ~ ul {
display: none;
}
.css-treeview label, .css-treeview label::before {
cursor: pointer;
}
.css-treeview input:disabled + label {
cursor: default;
opacity: .6;
}
.css-treeview input:checked:not(:disabled) ~ ul {
display: block;
}
.css-treeview label, .css-treeview label::before {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACgCAYAAAAFOewUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrslM1u00AQgGdthyalFFOK+ClIIKQKyqUVQvTEE3DmAhLwAhU8QZoH4A2Q2gMSFace4MCtJ8SPBFwAkRuiHKpA6sRN/Lu7zG5i14kctaUqRGhGXnu9O/Pt7MzsMiklvF+9t2kWTDvyIrAsA0aKRRi1T0C/hJ4LUbt5/8rNpWVlp8RSr9J40b48fxFaTQ9+ft8EZ6MJYb0Ok+dnYGpmPgXwKIAvLx8vYXc5GdMAQJgQEkpjRTh36TS2U+DWW/D17WuYgm8pwJyY1npZsZKOxImOV1I/h4+O6vEg5GCZBpgmA6hX8wHKUHDRBXQYicQ4rlc3Tf0VMs8DHBS864F2YFspjgUYjKX/Az3gsdQd2eeBHwmdGWXHcgBGSkZXOXohcEXebRoQcAgjqediNY+AVyu3Z3sAKqfKoGMsewBeEIOPgQxxPJIjcGH6qtL/0AdADzKGnuuD+2tLK7Q8DhHHbOBW+KEzcHLuYc82MkEUekLiwuvVH+guQBQzOG4XdAb8EOcRcqQvDkY2iCLuxECJ43JobMXoutqGgDa2T7UqLKwt9KRyuxKVByqVXXqIoCCUCAqhUOioTWC7G4TQEOD0APy2/7G2Xpu1J4+lxeQ4TXBbITDpoVelRN/BVFbwu5oMMJUBhoXy5tmdRcMwymP2OLQaLjx9/vnBo6V3K6izATmSnMa0Dq7ferIohJhr1p01zrlz49rZF4OMs8JkX23vVQzYp+wbYGV/KpXKjvspl8tsIKCrMNAYFxj2GKS5ZWxg4ewKsJfaGMIY5KXqPz8LBBj6+yDvVP79+yDp/9F9oIx3OisHWwe7Oal0HxCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgwD8E/BZgAP0qhKj3rXO7AAAAAElFTkSuQmCC") no-repeat;
}
.css-treeview label, .css-treeview span, .css-treeview label::before {
display: inline-block;
height: 16px;
line-height: 16px;
vertical-align: middle;
}
.css-treeview label {
background-position: 18px 0;
}
.css-treeview label::before {
content: "";
width: 16px;
margin: 0 22px 0 0;
vertical-align: middle;
background-position: 0 -32px;
}
.css-treeview input:checked + label::before {
background-position: 0 -16px;
}
/* webkit adjacent element selector bugfix */
@media screen and (-webkit-min-device-pixel-ratio:0)
{
.css-treeview{
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
}
@-webkit-keyframes webkit-adjacent-element-selector-bugfix
{
from {
padding: 0;
}
to {
padding: 0;
}
}
}
#uploader {
position: absolute;
top: 0;
right: 0;
left: 0;
height:28px;
line-height: 24px;
padding-left: 10px;
background-color: #444;
color:#EEE;
}
#tree {
position: absolute;
top: 28px;
bottom: 0;
left: 0;
width:200px;
padding: 8px;
}
#editor, #preview {
position: absolute;
top: 28px;
right: 0;
bottom: 0;
left: 200px;
}
#preview {
background-color: #EEE;
padding:5px;
}
</style>
<script>
function createFileUploader(element, tree, editor){
var xmlHttp;
var input = document.createElement("input");
input.type = "file";
input.multiple = false;
input.name = "data";
document.getElementById(element).appendChild(input);
var path = document.createElement("input");
path.id = "upload-path";
path.type = "text";
path.name = "path";
path.defaultValue = "/";
document.getElementById(element).appendChild(path);
var button = document.createElement("button");
button.innerHTML = 'Upload';
document.getElementById(element).appendChild(button);
var mkdir = document.createElement("button");
mkdir.innerHTML = 'MkDir';
document.getElementById(element).appendChild(mkdir);
var mkfile = document.createElement("button");
mkfile.innerHTML = 'MkFile';
document.getElementById(element).appendChild(mkfile);
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
else {
tree.refreshPath(path.value);
}
}
}
function createPath(p){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("path", p);
xmlHttp.open("PUT", "/edit");
xmlHttp.send(formData);
}
mkfile.onclick = function(e){
if(path.value.indexOf(".") === -1) return;
createPath(path.value);
editor.loadUrl(path.value);
};
mkdir.onclick = function(e){
if(path.value.length < 2) return;
var dir = path.value
if(dir.indexOf(".") !== -1){
if(dir.lastIndexOf("/") === 0) return;
dir = dir.substring(0, dir.lastIndexOf("/"));
}
createPath(dir);
};
button.onclick = function(e){
if(input.files.length === 0){
return;
}
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", input.files[0], path.value);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
input.onchange = function(e){
if(input.files.length === 0) return;
var filename = input.files[0].name;
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
var name = /(.*)\.[^.]+$/.exec(filename)[1];
if(typeof name !== undefined){
if(name.length > 8) name = name.substring(0, 8);
filename = name;
}
if(typeof ext !== undefined){
if(ext === "html") ext = "htm";
else if(ext === "jpeg") ext = "jpg";
filename = filename + "." + ext;
}
if(path.value === "/" || path.value.lastIndexOf("/") === 0){
path.value = "/"+filename;
} else {
path.value = path.value.substring(0, path.value.lastIndexOf("/")+1)+filename;
}
}
}
function createTree(element, editor){
var preview = document.getElementById("preview");
var treeRoot = document.createElement("div");
treeRoot.className = "css-treeview";
document.getElementById(element).appendChild(treeRoot);
function loadDownload(path){
document.getElementById('download-frame').src = path+"?download=true";
}
function loadPreview(path){
document.getElementById("editor").style.display = "none";
preview.style.display = "block";
preview.innerHTML = '<img src="'+path+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
}
function fillFolderMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
var isChecked = document.getElementById(path).checked;
var expnd = document.createElement("li");
list.appendChild(expnd);
if(isChecked){
expnd.innerHTML = "<span>Collapse</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = false;
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var refrsh = document.createElement("li");
list.appendChild(refrsh);
refrsh.innerHTML = "<span>Refresh</span>";
refrsh.onclick = function(e){
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else {
expnd.innerHTML = "<span>Expand</span>";
expnd.onclick = function(e){
document.getElementById(path).checked = true;
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var upload = document.createElement("li");
list.appendChild(upload);
upload.innerHTML = "<span>Upload</span>";
upload.onclick = function(e){
var pathEl = document.getElementById("upload-path");
if(pathEl){
var subPath = pathEl.value;
if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath;
else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath;
}
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function fillFileMenu(el, path){
var list = document.createElement("ul");
el.appendChild(list);
var action = document.createElement("li");
list.appendChild(action);
if(isTextFile(path)){
action.innerHTML = "<span>Edit</span>";
action.onclick = function(e){
editor.loadUrl(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
} else if(isImageFile(path)){
action.innerHTML = "<span>Preview</span>";
action.onclick = function(e){
loadPreview(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
var download = document.createElement("li");
list.appendChild(download);
download.innerHTML = "<span>Download</span>";
download.onclick = function(e){
loadDownload(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
var delFile = document.createElement("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
};
}
function showContextMenu(e, path, isfile){
var divContext = document.createElement("div");
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
var left = e.clientX + scrollLeft;
var top = e.clientY + scrollTop;
divContext.className = 'contextMenu';
divContext.style.display = 'block';
divContext.style.left = left + 'px';
divContext.style.top = top + 'px';
if(isfile) fillFileMenu(divContext, path);
else fillFolderMenu(divContext, path);
document.body.appendChild(divContext);
var width = divContext.offsetWidth;
var height = divContext.offsetHeight;
divContext.onmouseout = function(e){
if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(divContext);
}
};
}
function createTreeLeaf(path, name, size){
var leaf = document.createElement("li");
leaf.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
var label = document.createElement("span");
label.textContent = name.toLowerCase();
leaf.appendChild(label);
leaf.onclick = function(e){
if(isTextFile(leaf.id)){
editor.loadUrl(leaf.id);
} else if(isImageFile(leaf.id)){
loadPreview(leaf.id);
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, leaf.id, true);
};
return leaf;
}
function createTreeBranch(path, name, disabled){
var leaf = document.createElement("li");
var check = document.createElement("input");
check.type = "checkbox";
check.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
leaf.appendChild(check);
var label = document.createElement("label");
label.for = check.id;
label.textContent = name.toLowerCase();
leaf.appendChild(label);
check.onchange = function(e){
if(check.checked){
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
}
};
label.onclick = function(e){
if(!check.checked){
check.checked = true;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, check.id);
} else {
check.checked = false;
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, check.id, false);
}
return leaf;
}
function addList(parent, path, items){
var list = document.createElement("ul");
parent.appendChild(list);
var ll = items.length;
for(var i = 0; i < ll; i++){
var item = items[i];
var itemEl;
if(item.type === "file"){
itemEl = createTreeLeaf(path, item.name, item.size);
} else {
itemEl = createTreeBranch(path, item.name);
}
list.appendChild(itemEl);
}
}
function isTextFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt":
case "htm":
case "html":
case "js":
case "json":
case "c":
case "h":
case "cpp":
case "css":
case "xml":
return true;
}
}
return false;
}
function isImageFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "png":
case "jpg":
case "gif":
case "ico":
return true;
}
}
return false;
}
this.refreshPath = function(path){
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
};
function delCb(path){
return function(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200){
alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
} else {
if(path.lastIndexOf('/') < 1){
path = '/';
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
} else {
path = path.substring(0, path.lastIndexOf('/'));
var leaf = document.getElementById(path).parentNode;
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
httpGet(leaf, path);
}
}
}
}
}
function httpDelete(filename){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = delCb(filename);
var formData = new FormData();
formData.append("path", filename);
xmlHttp.open("DELETE", "/edit");
xmlHttp.send(formData);
}
function getCb(parent, path){
return function(){
if (xmlHttp.readyState == 4){
//clear loading
if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText));
}
}
}
function httpGet(parent, path){
xmlHttp = new XMLHttpRequest(parent, path);
xmlHttp.onreadystatechange = getCb(parent, path);
xmlHttp.open("GET", "/list?dir="+path, true);
xmlHttp.send(null);
//start loading
}
httpGet(treeRoot, "/");
return this;
}
function createEditor(element, file, lang, theme, type){
function getLangFromFilename(filename){
var lang = "plain";
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt": lang = "plain"; break;
case "htm": lang = "html"; break;
case "js": lang = "javascript"; break;
case "c": lang = "c_cpp"; break;
case "cpp": lang = "c_cpp"; break;
case "css":
case "scss":
case "php":
case "html":
case "json":
case "xml":
lang = ext;
}
}
return lang;
}
if(typeof file === "undefined") file = "/index.htm";
if(typeof lang === "undefined"){
lang = getLangFromFilename(file);
}
if(typeof theme === "undefined") theme = "textmate";
if(typeof type === "undefined"){
type = "text/"+lang;
if(lang === "c_cpp") type = "text/plain";
}
var xmlHttp = null;
var editor = ace.edit(element);
//post
function httpPostProcessRequest(){
if (xmlHttp.readyState == 4){
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
}
}
function httpPost(filename, data, type){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpPostProcessRequest;
var formData = new FormData();
formData.append("data", new Blob([data], { type: type }), filename);
xmlHttp.open("POST", "/edit");
xmlHttp.send(formData);
}
//get
function httpGetProcessRequest(){
if (xmlHttp.readyState == 4){
document.getElementById("preview").style.display = "none";
document.getElementById("editor").style.display = "block";
if(xmlHttp.status == 200) editor.setValue(xmlHttp.responseText);
else editor.setValue("");
editor.clearSelection();
}
}
function httpGet(theUrl){
xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = httpGetProcessRequest;
xmlHttp.open("GET", theUrl, true);
xmlHttp.send(null);
}
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
editor.setTheme("ace/theme/"+theme);
editor.$blockScrolling = Infinity;
editor.getSession().setUseSoftTabs(true);
editor.getSession().setTabSize(2);
editor.setHighlightActiveLine(true);
editor.setShowPrintMargin(false);
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
httpPost(file, editor.getValue()+"", type);
},
readOnly: false
});
editor.commands.addCommand({
name: 'undoCommand',
bindKey: {win: 'Ctrl-Z', mac: 'Command-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().undo(false);
},
readOnly: false
});
editor.commands.addCommand({
name: 'redoCommand',
bindKey: {win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().redo(false);
},
readOnly: false
});
httpGet(file);
editor.loadUrl = function(filename){
file = filename;
lang = getLangFromFilename(file);
type = "text/"+lang;
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
httpGet(file);
}
return editor;
}
function onBodyLoad(){
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
var tree = createTree("tree", editor);
createFileUploader("uploader", tree, editor);
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript" charset="utf-8"></script>
</head>
<body onload="onBodyLoad();">
<div id="uploader"></div>
<div id="tree"></div>
<div id="editor"></div>
<div id="preview" style="display:none;"></div>
<iframe id=download-frame style='display:none;'></iframe>
</body>
</html>

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>ESP Index</title>
<style>
body {
background-color:black;
color:white;
}
</style>
<script type="text/javascript">
function onBodyLoad(){
console.log("we are loaded!!");
}
</script>
</head>
<body id="index" onload="onBodyLoad()">
<h1>ESP8266 Pin Functions</h1>
<img src="pins.png" />
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

View File

@ -120,7 +120,7 @@ public:
}
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = getContentType(path);
String contentType = mime::getContentType(path);
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
@ -146,19 +146,9 @@ public:
return true;
}
static String getContentType(const String& path) {
char buff[sizeof(mimeTable[0].mimeType)];
// Check all entries but last one for match, return if found
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
strcpy_P(buff, mimeTable[i].endsWith);
if (path.endsWith(buff)) {
strcpy_P(buff, mimeTable[i].mimeType);
return String(buff);
}
}
// Fall-through and just return default type
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
return String(buff);
/* Deprecated version. Please use mime::getContentType instead */
static String getContentType(const String& path) __attribute__((deprecated)) {
return mime::getContentType(path);
}
protected:

View File

@ -1,5 +1,6 @@
#include "mimetable.h"
#include "pgmspace.h"
#include "WString.h"
namespace mime
{
@ -16,6 +17,7 @@ const Entry mimeTable[maxType] PROGMEM =
{ ".png", "image/png" },
{ ".gif", "image/gif" },
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".ico", "image/x-icon" },
{ ".svg", "image/svg+xml" },
{ ".ttf", "application/x-font-ttf" },
@ -32,4 +34,19 @@ const Entry mimeTable[maxType] PROGMEM =
{ "", "application/octet-stream" }
};
String getContentType(const String& path) {
char buff[sizeof(mimeTable[0].mimeType)];
// Check all entries but last one for match, return if found
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
strcpy_P(buff, mimeTable[i].endsWith);
if (path.endsWith(buff)) {
strcpy_P(buff, mimeTable[i].mimeType);
return String(buff);
}
}
// Fall-through and just return default type
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
return String(buff);
}
}

View File

@ -1,6 +1,7 @@
#ifndef __MIMETABLE_H__
#define __MIMETABLE_H__
#include "WString.h"
namespace mime
{
@ -16,6 +17,7 @@ enum type
png,
gif,
jpg,
jpeg,
ico,
svg,
ttf,
@ -41,7 +43,8 @@ struct Entry
extern const Entry mimeTable[maxType];
String getContentType(const String& path);
}
#endif

View File

@ -2,11 +2,11 @@
//
// Before running, you must download the set of certs using
// the script "certs-from-mozilla.py" (no parameters)
// and then uploading the generated .AR file to SPIFFS or SD.
// and then uploading the generated .AR file to LittleFS or SD.
//
// You do not need to generate the ".IDX" file listed below,
// it is generated automatically when the CertStore object
// is created and written to SD or SPIFFS by the ESP8266.
// is created and written to SD or LittleFS by the ESP8266.
//
// Why would you need a CertStore?
//
@ -37,6 +37,7 @@
#include <CertStoreBearSSL.h>
#include <time.h>
#include <FS.h>
#include <LittleFS.h>
#ifndef STASSID
#define STASSID "your-ssid"
@ -117,8 +118,7 @@ void setup() {
Serial.println();
Serial.println();
SPIFFS.begin();
// If using a SD card or LittleFS, call the appropriate ::begin instead
LittleFS.begin();
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
@ -138,10 +138,10 @@ void setup() {
setClock(); // Required for X.509 validation
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
Serial.printf("Number of CA certs read: %d\n", numCerts);
if (numCerts == 0) {
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the SPIFFS directory before running?\n");
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the LittleFS directory before running?\n");
return; // Can't connect to anything w/o certs!
}

View File

@ -3,7 +3,7 @@
# This script pulls the list of Mozilla trusted certificate authorities
# from the web at the "mozurl" below, parses the file to grab the PEM
# for each cert, and then generates DER files in a new ./data directory
# Upload these to a SPIFFS filesystem and use the CertManager to parse
# Upload these to an on-chip filesystem and use the CertManager to parse
# and use them for your outgoing SSL connections.
#
# Script by Earle F. Philhower, III. Released to the public domain.

View File

@ -23,8 +23,6 @@ BearSSL KEYWORD1
X509List KEYWORD1
PrivateKey KEYWORD1
PublicKey KEYWORD1
CertStoreSPIFFSBearSSL KEYWORD1
CertStoreSDBearSSL KEYWORD1
Session KEYWORD1
ESP8266WiFiGratuitous KEYWORD1

View File

@ -234,6 +234,8 @@ namespace brssl {
if (po) {
free(po->name);
free(po->data);
po->name = nullptr;
po->data = nullptr;
}
}

View File

@ -26,7 +26,7 @@
#include <FS.h>
// Base class for the certificate stores, which allow use
// of a large set of certificates stored on SPIFFS of SD card to
// of a large set of certificates stored on FS or SD card to
// be dynamically used when validating a X509 certificate
namespace BearSSL {

View File

@ -367,7 +367,7 @@ uint16_t WiFiClient::remotePort()
IPAddress WiFiClient::localIP()
{
if (!_client)
if (!_client || !_client->getLocalAddress())
return IPAddress(0U);
return IPAddress(_client->getLocalAddress());

View File

@ -125,6 +125,9 @@ int WiFiClientSecure::_connectSSL(const char* hostName)
{
if (!_ssl) {
_ssl = std::make_shared<SSLContext>();
if (!_ssl) {
return 0;
}
}
_ssl->connect(_client, hostName, _timeout);
@ -170,8 +173,7 @@ size_t WiFiClientSecure::write(Stream& stream)
size_t totalSent = 0;
size_t countRead;
size_t countSent;
if (!_ssl)
{
if (!_ssl) {
return 0;
}
do {
@ -399,61 +401,63 @@ void WiFiClientSecure::_initSSLContext()
bool WiFiClientSecure::setCACert(const uint8_t* pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_X509_CACERT, pk, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_X509_CACERT, pk, size) : false;
}
bool WiFiClientSecure::setCertificate(const uint8_t* pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_X509_CERT, pk, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_X509_CERT, pk, size) : false;
}
bool WiFiClientSecure::setPrivateKey(const uint8_t* pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_RSA_KEY, pk, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_RSA_KEY, pk, size) : false;
}
bool WiFiClientSecure::setCACert_P(PGM_VOID_P pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject_P(SSL_OBJ_X509_CACERT, pk, size);
return _ssl ? _ssl->loadObject_P(SSL_OBJ_X509_CACERT, pk, size) : false;
}
bool WiFiClientSecure::setCertificate_P(PGM_VOID_P pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject_P(SSL_OBJ_X509_CERT, pk, size);
return _ssl ? _ssl->loadObject_P(SSL_OBJ_X509_CERT, pk, size) : false;
}
bool WiFiClientSecure::setPrivateKey_P(PGM_VOID_P pk, size_t size)
{
_initSSLContext();
return _ssl->loadObject_P(SSL_OBJ_RSA_KEY, pk, size);
return _ssl ? _ssl->loadObject_P(SSL_OBJ_RSA_KEY, pk, size) : false;
}
bool WiFiClientSecure::loadCACert(Stream& stream, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_X509_CACERT, stream, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_X509_CACERT, stream, size) : false;
}
bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_X509_CERT, stream, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_X509_CERT, stream, size) : false;
}
bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size)
{
_initSSLContext();
return _ssl->loadObject(SSL_OBJ_RSA_KEY, stream, size);
return _ssl ? _ssl->loadObject(SSL_OBJ_RSA_KEY, stream, size) : false;
}
void WiFiClientSecure::allowSelfSignedCerts()
{
_initSSLContext();
_ssl->allowSelfSignedCerts();
if (_ssl) {
_ssl->allowSelfSignedCerts();
}
}
extern "C" int __ax_port_read(int fd, uint8_t* buffer, size_t count)

View File

@ -13,8 +13,6 @@
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#define USE_SERIAL Serial
#ifndef APSSID
#define APSSID "APSSID"
#define APPSK "APPSK"
@ -24,16 +22,16 @@ ESP8266WiFiMulti WiFiMulti;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
Serial.begin(115200);
// Serial.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
@ -44,19 +42,19 @@ void setup() {
}
void update_started() {
USE_SERIAL.println("CALLBACK: HTTP update process started");
Serial.println("CALLBACK: HTTP update process started");
}
void update_finished() {
USE_SERIAL.println("CALLBACK: HTTP update process finished");
Serial.println("CALLBACK: HTTP update process finished");
}
void update_progress(int cur, int total) {
USE_SERIAL.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
}
void update_error(int err) {
USE_SERIAL.printf("CALLBACK: HTTP update fatal error code %d\n", err);
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
}
@ -86,15 +84,15 @@ void loop() {
switch (ret) {
case HTTP_UPDATE_FAILED:
USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
USE_SERIAL.println("HTTP_UPDATE_OK");
Serial.println("HTTP_UPDATE_OK");
break;
}
}

View File

@ -1,5 +1,5 @@
/**
httpUpdateSPIFFS.ino
httpUpdateLittleFS.ino
Created on: 05.12.2015
@ -13,8 +13,6 @@
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
#ifndef APSSID
@ -24,16 +22,16 @@ ESP8266WiFiMulti WiFiMulti;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
Serial.begin(115200);
// Serial.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
@ -46,7 +44,7 @@ void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
USE_SERIAL.println("Update SPIFFS...");
Serial.println("Update LittleFS...");
WiFiClient client;
@ -58,22 +56,22 @@ void loop() {
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs(client, "http://server/spiffs.bin");
t_httpUpdate_return ret = ESPhttpUpdate.updateFS(client, "http://server/spiffs.bin");
if (ret == HTTP_UPDATE_OK) {
USE_SERIAL.println("Update sketch...");
Serial.println("Update sketch...");
ret = ESPhttpUpdate.update(client, "http://server/file.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
USE_SERIAL.printf("HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
USE_SERIAL.println("HTTP_UPDATE_OK");
Serial.println("HTTP_UPDATE_OK");
break;
}
}

View File

@ -14,8 +14,7 @@
#include <time.h>
#include <FS.h>
#define USE_SERIAL Serial
#include <LittleFS.h>
#ifndef APSSID
#define APSSID "APSSID"
@ -34,46 +33,47 @@ BearSSL::CertStore certStore;
void setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
USE_SERIAL.print(F("Waiting for NTP time sync: "));
Serial.print(F("Waiting for NTP time sync: "));
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
yield();
delay(500);
USE_SERIAL.print(F("."));
Serial.print(F("."));
now = time(nullptr);
}
USE_SERIAL.println(F(""));
Serial.println(F(""));
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
USE_SERIAL.print(F("Current time: "));
USE_SERIAL.print(asctime(&timeinfo));
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
}
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
Serial.begin(115200);
// Serial.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(APSSID, APPSK);
SPIFFS.begin();
LittleFS.begin();
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
USE_SERIAL.print(F("Number of CA certs read: ")); USE_SERIAL.println(numCerts);
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
Serial.print(F("Number of CA certs read: "));
Serial.println(numCerts);
if (numCerts == 0) {
USE_SERIAL.println(F("No certs found. Did you run certs-from-mozill.py and upload the SPIFFS directory before running?"));
Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
return; // Can't connect to anything w/o certs!
}
}
@ -86,7 +86,7 @@ void loop() {
BearSSL::WiFiClientSecure client;
bool mfln = client.probeMaxFragmentLength("server", 443, 1024); // server must be the same as in ESPhttpUpdate.update()
USE_SERIAL.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
if (mfln) {
client.setBufferSizes(1024, 1024);
}
@ -107,15 +107,15 @@ void loop() {
switch (ret) {
case HTTP_UPDATE_FAILED:
USE_SERIAL.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
USE_SERIAL.println("HTTP_UPDATE_OK");
Serial.println("HTTP_UPDATE_OK");
break;
}
}

View File

@ -126,7 +126,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String
}
#endif
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion)
HTTPUpdateResult ESP8266HTTPUpdate::updateFS(WiFiClient& client, const String& url, const String& currentVersion)
{
HTTPClient http;
http.begin(client, url);

View File

@ -146,7 +146,10 @@ public:
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) __attribute__((deprecated));
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); // BearSSL
#endif
t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "");
t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = "");
t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "") __attribute__((deprecated)) {
return updateFS(client, url, currentVersion);
};
// Notification callbacks
void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; }

View File

@ -1,5 +1,5 @@
/**
@file OTA-mDNS-SPIFFS.ino
@file OTA-mDNS-LittleFS.ino
@author Pascal Gollor (http://www.pgollor.de/cms/)
@date 2015-09-18
@ -22,6 +22,7 @@
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <FS.h>
#include <LittleFS.h>
#include <ArduinoOTA.h>
@ -55,7 +56,7 @@ const char* ap_default_psk = STAPSK; ///< Default PSK.
*/
bool loadConfig(String *ssid, String *pass) {
// open file for reading.
File configFile = SPIFFS.open("/cl_conf.txt", "r");
File configFile = LittleFS.open("/cl_conf.txt", "r");
if (!configFile) {
Serial.println("Failed to open cl_conf.txt.");
@ -115,7 +116,7 @@ bool loadConfig(String *ssid, String *pass) {
*/
bool saveConfig(String *ssid, String *pass) {
// Open config file for writing.
File configFile = SPIFFS.open("/cl_conf.txt", "w");
File configFile = LittleFS.open("/cl_conf.txt", "w");
if (!configFile) {
Serial.println("Failed to open cl_conf.txt for writing");
@ -158,7 +159,7 @@ void setup() {
// Initialize file system.
if (!SPIFFS.begin()) {
if (!LittleFS.begin()) {
Serial.println("Failed to mount file system");
return;
}

View File

@ -194,6 +194,11 @@ public:
// for compatibility...
bool setHostname(const String& p_strHostname);
bool isRunning(void)
{
return (m_pUDPContext != 0);
}
/**
hMDNSService (opaque handle to access the service)
*/

View File

@ -1,8 +1,17 @@
// Simple speed test for filesystem objects
// Released to the public domain by Earle F. Philhower, III
#include <FS.h>
#include <LittleFS.h>
// Choose the filesystem to test
// WARNING: The filesystem will be formatted at the start of the test!
#define TESTFS LittleFS
//#define TESTFS SPIFFS
//#define TESTFS SDFS
// How large of a file to test
#define TESTSIZEKB 512
void DoTest(FS *fs) {
@ -112,12 +121,9 @@ void DoTest(FS *fs) {
void setup() {
Serial.begin(115200);
Serial.printf("Beginning LittleFS test\n");
Serial.printf("Beginning test\n");
Serial.flush();
DoTest(&LittleFS);
Serial.printf("Beginning SPIFFS test\n");
Serial.flush();
DoTest(&SPIFFS);
DoTest(&TESTFS);
}
void loop() {

@ -1 +1 @@
Subproject commit ce2c01f098f4d2b9479de5a796c3bb531f1fe14c
Subproject commit a049f1318eecbe502549f9d74a41951985fb956f

View File

@ -27,11 +27,7 @@ const int chipSelect = 4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.begin(115200);
Serial.print("Initializing SD card...");

View File

@ -27,11 +27,7 @@ const int chipSelect = 4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.begin(115200);
Serial.print("Initializing SD card...");

View File

@ -24,11 +24,7 @@ File myFile;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.begin(115200);
Serial.print("Initializing SD card...");

View File

@ -25,11 +25,7 @@ File myFile;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.begin(115200);
Serial.print("Initializing SD card...");

View File

@ -24,6 +24,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <Servo.h>
#include "core_esp8266_waveform.h"
uint32_t Servo::_servoMap = 0;
// similiar to map but will have increased accuracy that provides a more
// symetric api (call it and use result to reverse will provide the original value)
int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut)
@ -82,6 +84,7 @@ uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs)
void Servo::detach()
{
if (_attached) {
_servoMap &= ~(1 << _pin);
stopWaveform(_pin);
_attached = false;
digitalWrite(_pin, LOW);
@ -105,7 +108,10 @@ void Servo::writeMicroseconds(int value)
{
_valueUs = value;
if (_attached) {
startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0);
_servoMap &= ~(1 << _pin);
if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0)) {
_servoMap |= (1 << _pin);
}
}
}

View File

@ -72,6 +72,7 @@ public:
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
private:
static uint32_t _servoMap;
bool _attached;
uint8_t _pin;
uint16_t _minUs;

View File

@ -42,7 +42,7 @@ File bmpFile;
void setup() {
Serial.begin(9600);
Serial.begin(115200);
pinMode(PIN_SD_CS, OUTPUT);
digitalWrite(PIN_SD_CS, HIGH);

View File

@ -9,9 +9,10 @@
#include <ArduinoJson.h>
#include "FS.h"
#include <LittleFS.h>
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
File configFile = LittleFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
@ -56,7 +57,7 @@ bool saveConfig() {
doc["serverName"] = "api.example.com";
doc["accessToken"] = "128du9as8du12eoue8da98h123ueh9h98";
File configFile = SPIFFS.open("/config.json", "w");
File configFile = LittleFS.open("/config.json", "w");
if (!configFile) {
Serial.println("Failed to open config file for writing");
return false;
@ -72,7 +73,7 @@ void setup() {
delay(1000);
Serial.println("Mounting FS...");
if (!SPIFFS.begin()) {
if (!LittleFS.begin()) {
Serial.println("Failed to mount file system");
return;
}

View File

@ -0,0 +1,98 @@
/**
This example demonstrates the usage of the ESP8266 Crypto implementation, which aims to contain easy-to-use cryptographic functions.
Crypto is currently primarily a frontend for the cryptographic library BearSSL which is used by `BearSSL::WiFiClientSecure` and `BearSSL::WiFiServerSecure` in the ESP8266 Arduino Core.
Extensive documentation can be found in the Crypto source code files and on the [BearSSL homepage](https://www.bearssl.org).
*/
#include <ESP8266WiFi.h>
#include <TypeConversion.h>
#include <Crypto.h>
namespace TypeCast = experimental::TypeConversion;
/**
NOTE: Although we could define the strings below as normal String variables,
here we are using PROGMEM combined with the FPSTR() macro (and also just the F() macro further down in the file).
The reason is that this approach will place the strings in flash memory which will help save RAM during program execution.
Reading strings from flash will be slower than reading them from RAM,
but this will be a negligible difference when printing them to Serial.
More on F(), FPSTR() and PROGMEM:
https://github.com/esp8266/Arduino/issues/1143
https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html
*/
constexpr char masterKey[] PROGMEM = "w86vn@rpfA O+S"; // Use 8 random characters or more
void setup() {
// Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
// This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
WiFi.persistent(false);
Serial.begin(115200);
Serial.println();
Serial.println();
}
void loop() {
// This serves only to demonstrate the library use. See the header file for a full list of functions.
using namespace experimental::crypto;
String exampleData = F("Hello Crypto World!");
Serial.println(String(F("This is our example data: ")) + exampleData);
uint8_t resultArray[SHA256::NATURAL_LENGTH] { 0 };
uint8_t derivedKey[ENCRYPTION_KEY_LENGTH] { 0 };
static uint32_t encryptionCounter = 0;
// Generate the salt to use for HKDF
uint8_t hkdfSalt[16] { 0 };
getNonceGenerator()(hkdfSalt, sizeof hkdfSalt);
// Generate the key to use for HMAC and encryption
HKDF hkdfInstance(FPSTR(masterKey), (sizeof masterKey) - 1, hkdfSalt, sizeof hkdfSalt); // (sizeof masterKey) - 1 removes the terminating null value of the c-string
hkdfInstance.produce(derivedKey, sizeof derivedKey);
// Hash
SHA256::hash(exampleData.c_str(), exampleData.length(), resultArray);
Serial.println(String(F("\nThis is the SHA256 hash of our example data, in HEX format:\n")) + TypeCast::uint8ArrayToHexString(resultArray, sizeof resultArray));
Serial.println(String(F("This is the SHA256 hash of our example data, in HEX format, using String output:\n")) + SHA256::hash(exampleData));
// HMAC
// Note that HMAC output length is limited
SHA256::hmac(exampleData.c_str(), exampleData.length(), derivedKey, sizeof derivedKey, resultArray, sizeof resultArray);
Serial.println(String(F("\nThis is the SHA256 HMAC of our example data, in HEX format:\n")) + TypeCast::uint8ArrayToHexString(resultArray, sizeof resultArray));
Serial.println(String(F("This is the SHA256 HMAC of our example data, in HEX format, using String output:\n")) + SHA256::hmac(exampleData, derivedKey, sizeof derivedKey, SHA256::NATURAL_LENGTH));
// Authenticated Encryption with Associated Data (AEAD)
String dataToEncrypt = F("This data is not encrypted.");
uint8_t resultingNonce[12] { 0 }; // The nonce is always 12 bytes
uint8_t resultingTag[16] { 0 }; // The tag is always 16 bytes
Serial.println(String(F("\nThis is the data to encrypt: ")) + dataToEncrypt);
// Note that the key must be ENCRYPTION_KEY_LENGTH long.
ChaCha20Poly1305::encrypt(dataToEncrypt.begin(), dataToEncrypt.length(), derivedKey, &encryptionCounter, sizeof encryptionCounter, resultingNonce, resultingTag);
Serial.println(String(F("Encrypted data: ")) + dataToEncrypt);
bool decryptionSucceeded = ChaCha20Poly1305::decrypt(dataToEncrypt.begin(), dataToEncrypt.length(), derivedKey, &encryptionCounter, sizeof encryptionCounter, resultingNonce, resultingTag);
encryptionCounter++;
if (decryptionSucceeded) {
Serial.print(F("Decryption succeeded. Result: "));
} else {
Serial.print(F("Decryption failed. Result: "));
}
Serial.println(dataToEncrypt);
Serial.println(F("\n##########################################################################################################\n"));
delay(10000);
}

View File

@ -12,6 +12,17 @@
ESP KEYWORD1
nonceGeneratorType KEYWORD1
MD5 KEYWORD1
SHA1 KEYWORD1
SHA224 KEYWORD1
SHA256 KEYWORD1
SHA384 KEYWORD1
SHA512 KEYWORD1
MD5SHA1 KEYWORD1
HKDF KEYWORD1
ChaCha20Poly1305 KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
@ -60,6 +71,21 @@ getResetInfo KEYWORD2
getResetInfoPtr KEYWORD2
eraseConfig KEYWORD2
getCycleCount KEYWORD2
random->KEYWORD2
setCtMinDataLength KEYWORD2
getCtMinDataLength KEYWORD2
setCtMaxDataLength KEYWORD2
getCtMaxDataLength KEYWORD2
setNonceGenerator KEYWORD2
getNonceGenerator KEYWORD2
hash KEYWORD2
hmac KEYWORD2
hmacCT KEYWORD2
init KEYWORD2
produce KEYWORD2
encrypt KEYWORD2
decrypt KEYWORD2
#######################################
# Constants (LITERAL1)
@ -79,6 +105,10 @@ WAKE_RF_DISABLED LITERAL1
ADC_VCC LITERAL1
ADC_TOUT LITERAL1
NATURAL_LENGTH LITERAL1
ENCRYPTION_KEY_LENGTH LITERAL1
CT_MAX_DIFF LITERAL1
#######################################
# namespace esp8266
#######################################

View File

@ -1,6 +1,6 @@
name=ESP8266
version=1.0
author=Simon Peter,Markus Sattler,Ivan Grokhotkov
author=Anders Löfgren,Simon Peter,Markus Sattler,Ivan Grokhotkov
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
sentence=ESP8266 sketches examples
paragraph=

View File

@ -2,5 +2,5 @@
"name": "framework-arduinoespressif8266",
"description": "Arduino Wiring-based Framework (ESP8266 Core)",
"url": "https://github.com/esp8266/Arduino",
"version": "2.7.0-dev"
"version": "3.0.0-dev"
}

View File

@ -41,9 +41,9 @@ The purpose of the release process is to generate the following outputs from the
* Github Release for esp8266/Arduino project. This is used to host the boards manager package mentioned above, and also contains the release notes.
Here is an overview of the release process. See the section below for detailed instructions.
Here is a rough overview of the effective release process. See the section below for detailed instructions.
1. Release process starts when a maintainer pushes a tag into the repository.
1. Release process effectively starts when a maintainer pushes a tag into the repository.
2. Travis CI runs a build for this tag, and one of the jobs (with `BUILD_TYPE=package`) is used to prepare the boards manager package. This job runs `build_boards_manager_package.sh`.
@ -65,9 +65,11 @@ Here is an overview of the release process. See the section below for detailed i
## Creating a release (for maintainers)
0. Open a new issue to track activities, which will be closed after the release is done.
1. [Open a new issue](https://github.com/esp8266/Arduino/issues/new/choose) to track activities, which will be closed after the release is done. Copy the checklist below into it, and check the steps one by one as they get completed.
1. Assemble release notes.
2. Make sure that no issues or PRs are assigned to the milestone to be released. If there are any Issues/PRs assigned to the relevant milestone, they should either be addressed, pushed back to a future milestone, or closed.
3. Assemble release notes.
* Since most changes are integrated into master using squash-rebase policy (i.e. one commit per PR), `git log --oneline` gives a good overview of changes in the release.
@ -99,22 +101,24 @@ Here is an overview of the release process. See the section below for detailed i
* Not all commit descriptions which come from `git log` will explain changes well. Reword items as necessary, with the goal that a general user of this project should be able to understand what the change is related to. Preserve references to PRs or issues (`#XXXX`).
* Don't include fixes for regressions which have been introduced since last release.
* Aggregate minor fixes (e.g. typos, small documentation changes) in a few items. Focus on preparing a good overview of the release for the users, rather than mentioning every change.
* When done, put release notes into a private Gist and send the link to other maintainers for review.
* When done, put release notes into a private [Gist](https://gist.github.com) or [firepad](https://demo.firepad.io) and send the link to other maintainers for review.
The following points assume work in a direct clone of the repository, and not in a personal fork.
2. Make a PR with the following, wait for Travis CI, and merge.
4. Make a PR with the following, [wait for Travis CI](https://travis-ci.org/github/esp8266/Arduino/builds/), and merge.
* platform.txt and package.json: update `version` to the release E.g. `3.0.0`,
* [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) and [package.json](https://github.com/esp8266/Arduino/blob/master/package.json): update `version` to the release E.g. `3.0.0`,
* `cores/esp8266/TZ.h`: import the latest database with the following shell command:\
* [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h): import the latest database with the following shell command:\
`$ cd tools; sh TZupdate.sh`.
3. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out):
5. Wait until the release notes have been checked by other maintainers
6. Navigate to [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings), enable ´Build pushed branches´ (before tagging in next step)
7. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out):
```
git tag -a -m "Release 3.0.0" 3.0.0
@ -128,30 +132,78 @@ The following points assume work in a direct clone of the repository, and not in
git push origin 3.0.0
```
4. In case something goes wrong, release can be canceled at any time:
In case something goes wrong, release can be canceled at any time:
* Tag must be removed (`git tag -d X.Y.Z; git push --delete origin X.Y.Z`)
* Release must be deleted: github > releases > edit x.y.z > remove all files > delete button appears
5. Wait for Travis CI build for the tag to pass, see https://travis-ci.org/esp8266/Arduino/builds,
8. Wait for Travis CI build for the tag to pass, see https://travis-ci.org/esp8266/Arduino/builds,
return to the Travis CI options and disable `Build pushed branches`.
6. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. Check that the boards manager package .zip file has been successfully uploaded as a release artifact.
9. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases.
7. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one).
10. Check that the boards manager package .zip file has been successfully uploaded as a release artifact.
8. Navigate to release list in Github here https://github.com/esp8266/Arduino/releases, press "Edit" button to edit release description, paste release notes, and publish it.
11. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one).
9. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version.
12. Return to the [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings) and disable `Build pushed branches`.
10. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually.
13. Navigate to release list in Github here https://github.com/esp8266/Arduino/releases, press "Edit" button to edit release description, paste release notes, and publish it.
11. Create a commit to the master branch, updating:
14. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 2 above)
15. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually.
16. Create a commit to the master branch, updating:
* The version in platform.txt and package.json files. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`.
* In main README.md:
* In main README.md go to "Latest release" section, change version number in the readthedocs link to the version which was just released, and verify that all links work.
- in "Latest release" section, change version number in the readthedocs link to the version which was just released, and verify that all links work.
## Checklist helper, copy-paste into Release Process issue
```
--------------COPY BELOW THIS LINE--------------
[Reference](https://github.com/esp8266/Arduino/tree/master/package#creating-a-release-for-maintainers) for details.
- [ ] 1. Open a new issue to track activities.
- [ ] 2. Make sure that no issues or PRs are assigned to the milestone to be released.
- [ ] 3. Assemble release notes.
- [ ] 4. Make a PR with the following, [wait for Travis CI](https://travis-ci.org/github/esp8266/Arduino/builds/), and merge.
* [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt)
* [package.json](https://github.com/esp8266/Arduino/blob/master/package.json)
* [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h)
- [ ] 5. Wait until the release notes have been checked by other maintainers
- [ ] 6. Navigate to [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings), enable ´Build pushed branches´ (before tagging in next step)
- [ ] 7. Tag the latest commit on the master branch, then push it to esp8266/Arduino
- [ ] 8. Wait for Travis CI build for the tag to pass, see https://travis-ci.org/esp8266/Arduino/builds,
- [ ] 9. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases.
- [ ] 10. Check that the boards manager package .zip file has been successfully uploaded as a release artifact.
- [ ] 11. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one).
- [ ] 12. Return to the [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings) and disable `Build pushed branches`.
- [ ] 13. Navigate to [release list in Github](https://github.com/esp8266/Arduino/releases), press "Edit" button to edit release description, paste release notes, and publish it.
- [ ] 14. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 2 above)
- [ ] 15. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually.
- [ ] 16. Create a commit to the master branch, updating:
* The version in platform.txt and package.json files. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`.
* In main README.md go to "Latest release" section, change version number in the readthedocs link to the version which was just released, and verify that all links work.
--------------COPY ABOVE THIS LINE--------------
```

View File

@ -5,8 +5,8 @@
# For more info:
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification
name=ESP8266 Boards (2.7.0-dev)
version=2.7.0-dev
name=ESP8266 Boards (3.0.0-dev)
version=3.0.0-dev
# These will be removed by the packager script when doing a JSON release
runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf

View File

@ -12,4 +12,4 @@ ${org}/../restyle.sh
git --version || true
git submodule foreach --recursive 'git reset --hard'
git diff --exit-code -- $TRAVIS_BUILD_DIR/libraries
git diff --exit-code -- $TRAVIS_BUILD_DIR

View File

@ -88,7 +88,10 @@ uart_do_write_char(const int uart_nr, char c)
}
else
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
write(uart_nr + 1, &c, 1);
#pragma GCC diagnostic pop
w = true;
}
}

View File

@ -31,6 +31,9 @@
#define SPIFFS_FILE_NAME "spiffs.bin"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
FS SPIFFS(nullptr);
SpiffsMock::SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage)
@ -126,3 +129,6 @@ void SpiffsMock::save ()
if (::close(fs) == -1)
fprintf(stderr, "SPIFFS: closing %s: %s\n", m_storage.c_str(), strerror(errno));
}
#pragma GCC diagnostic pop

View File

@ -16,15 +16,16 @@
#include <catch.hpp>
#include <string.h>
#include <FS.h>
#include "../common/spiffs_mock.h"
#include <LittleFS.h>
#include "../common/littlefs_mock.h"
#include <spiffs/spiffs.h>
// Use a SPIFFS file because we can't instantiate a virtual class like Print
// Use a LittleFS file because we can't instantiate a virtual class like Print
TEST_CASE("Print::write overrides all compile properly", "[core][Print]")
{
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
REQUIRE(SPIFFS.begin());
auto p = SPIFFS.open("test.bin", "w");
LITTLEFS_MOCK_DECLARE(64, 8, 512, "");
REQUIRE(LittleFS.begin());
auto p = LittleFS.open("test.bin", "w");
REQUIRE(p);
uint8_t uint8 = 1;
uint16_t uint16 = 2;
@ -56,7 +57,7 @@ TEST_CASE("Print::write overrides all compile properly", "[core][Print]")
p.write(1);
p.close();
p = SPIFFS.open("test.bin", "r");
p = LittleFS.open("test.bin", "r");
REQUIRE(p);
uint8_t buff[16];
int len = p.read(buff, 16);

View File

@ -26,6 +26,9 @@
namespace spiffs_test {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#define FSTYPE SPIFFS
#define TESTPRE "SPIFFS - "
#define TESTPAT "[fs]"
@ -54,6 +57,7 @@ TEST_CASE("SPIFFS checks the config object passed in", "[fs]")
REQUIRE_FALSE(SPIFFS.setConfig(d));
REQUIRE_FALSE(LittleFS.setConfig(l));
}
#pragma GCC diagnostic pop
};

View File

@ -161,6 +161,19 @@ TEST_CASE(TESTPRE "truncate", TESTPAT)
REQUIRE( s == "some" );
}
TEST_CASE(TESTPRE "open(w+) truncates file", TESTPAT)
{
FS_MOCK_DECLARE(64, 8, 512, "");
REQUIRE(FSTYPE.begin());
createFile("/file1", "some text");
String s = readFile("/file1");
REQUIRE( s == "some text");
auto f = FSTYPE.open("/file1", "w+");
f.close();
String t = readFile("/file1");
REQUIRE( t == "");
}
#ifdef FS_HAS_DIRS
#if FSTYPE != SDFS

Some files were not shown because too many files have changed in this diff Show More