Compare commits
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib
|
||||
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino
|
||||
ignore_words_list: ser,dout
|
||||
|
||||
# Consistent style
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3]
|
||||
chunk: [0, 1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
BUILD_PARITY: custom
|
||||
mod: 4
|
||||
mod: 6
|
||||
rem: ${{ matrix.chunk }}
|
||||
run: |
|
||||
cd pico-sdk
|
||||
|
||||
+2
-1
@@ -3,4 +3,5 @@ system
|
||||
tools/dist
|
||||
docs/_build
|
||||
ota/build
|
||||
tools/libpico/build
|
||||
tools/libpico/build
|
||||
platform.local.txt
|
||||
|
||||
@@ -34,3 +34,6 @@
|
||||
[submodule "ota/uzlib"]
|
||||
path = ota/uzlib
|
||||
url = https://github.com/pfalcon/uzlib.git
|
||||
[submodule "libraries/http_parser/lib/http-parser"]
|
||||
path = libraries/http-parser/lib/http-parser
|
||||
url = https://github.com/nodejs/http-parser.git
|
||||
|
||||
@@ -25,6 +25,7 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
* ExtremeElectronics RC2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
@@ -147,6 +148,8 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
* Filesystems (LittleFS and SD/SDFS)
|
||||
* Multicore support (setup1() and loop1())
|
||||
@@ -187,7 +190,10 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [lwIP](https://savannah.nongnu.org/projects/lwip/) is (c) the Swedish Institute of Computer Science and licenced under the BSD license.
|
||||
* [BearSSL](https://bearssl.org) library written by Thomas Pornin, is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
* [UZLib](https://github.com/pfalcon/uzlib) is copyright (c) 2003 Joergen Ibsen and distributed under the zlib license.
|
||||
* [LEAmDMS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [LEAmDNS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [http-parser](https://github.com/nodejs/http-parser) is copyright Joyent, Inc. and other Node contributors.
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others
|
||||
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
+439
@@ -7644,6 +7644,445 @@ electroniccats_bombercatpicodebug.menu.ipstack.ipv4ipv6=IPv4 and IPv6
|
||||
electroniccats_bombercatpicodebug.menu.ipstack.ipv4ipv6.build.libpico=libpico-ipv6.a
|
||||
electroniccats_bombercatpicodebug.menu.ipstack.ipv4ipv6.build.lwipdefs=-DLWIP_IPV6=1 -DLWIP_IPV4=1
|
||||
|
||||
# -----------------------------------
|
||||
# ExtremeElectronics RC2040
|
||||
# -----------------------------------
|
||||
extelec_rc2040.name=ExtremeElectronics RC2040
|
||||
extelec_rc2040.vid.0=0x2e8a
|
||||
extelec_rc2040.pid.0=0xee20
|
||||
extelec_rc2040.build.usbpid=-DSERIALUSB_PID=0xee20
|
||||
extelec_rc2040.build.usbpwr=-DUSBD_MAX_POWER_MA=250
|
||||
extelec_rc2040.build.board=EXTREMEELEXTRONICS_RC2040
|
||||
extelec_rc2040.build.mcu=cortex-m0plus
|
||||
extelec_rc2040.build.variant=extelec_rc2040
|
||||
extelec_rc2040.upload.tool=uf2conv
|
||||
extelec_rc2040.upload.maximum_size=2097152
|
||||
extelec_rc2040.upload.maximum_data_size=262144
|
||||
extelec_rc2040.upload.wait_for_upload_port=true
|
||||
extelec_rc2040.upload.erase_cmd=
|
||||
extelec_rc2040.serial.disableDTR=false
|
||||
extelec_rc2040.serial.disableRTS=false
|
||||
extelec_rc2040.build.f_cpu=125000000
|
||||
extelec_rc2040.build.led=
|
||||
extelec_rc2040.build.core=rp2040
|
||||
extelec_rc2040.build.ldscript=memmap_default.ld
|
||||
extelec_rc2040.build.ram_length=256k
|
||||
extelec_rc2040.build.boot2=boot2_w25q080_2_padded_checksum
|
||||
extelec_rc2040.build.vid=0x2e8a
|
||||
extelec_rc2040.build.pid=0xee20
|
||||
extelec_rc2040.build.usb_manufacturer="ExtremeElectronics"
|
||||
extelec_rc2040.build.usb_product="RC2040"
|
||||
extelec_rc2040.menu.flash.2097152_0=2MB (no FS)
|
||||
extelec_rc2040.menu.flash.2097152_0.upload.maximum_size=2093056
|
||||
extelec_rc2040.menu.flash.2097152_0.build.flash_length=2093056
|
||||
extelec_rc2040.menu.flash.2097152_0.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_0.build.fs_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_0.build.fs_end=270528512
|
||||
extelec_rc2040.menu.flash.2097152_65536=2MB (Sketch: 1984KB, FS: 64KB)
|
||||
extelec_rc2040.menu.flash.2097152_65536.upload.maximum_size=2027520
|
||||
extelec_rc2040.menu.flash.2097152_65536.build.flash_length=2027520
|
||||
extelec_rc2040.menu.flash.2097152_65536.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_65536.build.fs_start=270462976
|
||||
extelec_rc2040.menu.flash.2097152_65536.build.fs_end=270528512
|
||||
extelec_rc2040.menu.flash.2097152_131072=2MB (Sketch: 1920KB, FS: 128KB)
|
||||
extelec_rc2040.menu.flash.2097152_131072.upload.maximum_size=1961984
|
||||
extelec_rc2040.menu.flash.2097152_131072.build.flash_length=1961984
|
||||
extelec_rc2040.menu.flash.2097152_131072.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_131072.build.fs_start=270397440
|
||||
extelec_rc2040.menu.flash.2097152_131072.build.fs_end=270528512
|
||||
extelec_rc2040.menu.flash.2097152_262144=2MB (Sketch: 1792KB, FS: 256KB)
|
||||
extelec_rc2040.menu.flash.2097152_262144.upload.maximum_size=1830912
|
||||
extelec_rc2040.menu.flash.2097152_262144.build.flash_length=1830912
|
||||
extelec_rc2040.menu.flash.2097152_262144.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_262144.build.fs_start=270266368
|
||||
extelec_rc2040.menu.flash.2097152_262144.build.fs_end=270528512
|
||||
extelec_rc2040.menu.flash.2097152_524288=2MB (Sketch: 1536KB, FS: 512KB)
|
||||
extelec_rc2040.menu.flash.2097152_524288.upload.maximum_size=1568768
|
||||
extelec_rc2040.menu.flash.2097152_524288.build.flash_length=1568768
|
||||
extelec_rc2040.menu.flash.2097152_524288.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_524288.build.fs_start=270004224
|
||||
extelec_rc2040.menu.flash.2097152_524288.build.fs_end=270528512
|
||||
extelec_rc2040.menu.flash.2097152_1048576=2MB (Sketch: 1MB, FS: 1MB)
|
||||
extelec_rc2040.menu.flash.2097152_1048576.upload.maximum_size=1044480
|
||||
extelec_rc2040.menu.flash.2097152_1048576.build.flash_length=1044480
|
||||
extelec_rc2040.menu.flash.2097152_1048576.build.eeprom_start=270528512
|
||||
extelec_rc2040.menu.flash.2097152_1048576.build.fs_start=269479936
|
||||
extelec_rc2040.menu.flash.2097152_1048576.build.fs_end=270528512
|
||||
extelec_rc2040.menu.freq.133=133 MHz
|
||||
extelec_rc2040.menu.freq.133.build.f_cpu=133000000L
|
||||
extelec_rc2040.menu.freq.50=50 MHz
|
||||
extelec_rc2040.menu.freq.50.build.f_cpu=50000000L
|
||||
extelec_rc2040.menu.freq.100=100 MHz
|
||||
extelec_rc2040.menu.freq.100.build.f_cpu=100000000L
|
||||
extelec_rc2040.menu.freq.120=120 MHz
|
||||
extelec_rc2040.menu.freq.120.build.f_cpu=120000000L
|
||||
extelec_rc2040.menu.freq.125=125 MHz
|
||||
extelec_rc2040.menu.freq.125.build.f_cpu=125000000L
|
||||
extelec_rc2040.menu.freq.150=150 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.150.build.f_cpu=150000000L
|
||||
extelec_rc2040.menu.freq.175=175 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.175.build.f_cpu=175000000L
|
||||
extelec_rc2040.menu.freq.200=200 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.200.build.f_cpu=200000000L
|
||||
extelec_rc2040.menu.freq.225=225 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.225.build.f_cpu=225000000L
|
||||
extelec_rc2040.menu.freq.240=240 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.240.build.f_cpu=240000000L
|
||||
extelec_rc2040.menu.freq.250=250 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.250.build.f_cpu=250000000L
|
||||
extelec_rc2040.menu.freq.275=275 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.275.build.f_cpu=275000000L
|
||||
extelec_rc2040.menu.freq.300=300 MHz (Overclock)
|
||||
extelec_rc2040.menu.freq.300.build.f_cpu=300000000L
|
||||
extelec_rc2040.menu.opt.Small=Small (-Os) (standard)
|
||||
extelec_rc2040.menu.opt.Small.build.flags.optimize=-Os
|
||||
extelec_rc2040.menu.opt.Optimize=Optimize (-O)
|
||||
extelec_rc2040.menu.opt.Optimize.build.flags.optimize=-O
|
||||
extelec_rc2040.menu.opt.Optimize2=Optimize More (-O2)
|
||||
extelec_rc2040.menu.opt.Optimize2.build.flags.optimize=-O2
|
||||
extelec_rc2040.menu.opt.Optimize3=Optimize Even More (-O3)
|
||||
extelec_rc2040.menu.opt.Optimize3.build.flags.optimize=-O3
|
||||
extelec_rc2040.menu.opt.Fast=Fast (-Ofast) (maybe slower)
|
||||
extelec_rc2040.menu.opt.Fast.build.flags.optimize=-Ofast
|
||||
extelec_rc2040.menu.opt.Debug=Debug (-Og)
|
||||
extelec_rc2040.menu.opt.Debug.build.flags.optimize=-Og
|
||||
extelec_rc2040.menu.rtti.Disabled=Disabled
|
||||
extelec_rc2040.menu.rtti.Disabled.build.flags.rtti=-fno-rtti
|
||||
extelec_rc2040.menu.rtti.Enabled=Enabled
|
||||
extelec_rc2040.menu.rtti.Enabled.build.flags.rtti=
|
||||
extelec_rc2040.menu.stackprotect.Disabled=Disabled
|
||||
extelec_rc2040.menu.stackprotect.Disabled.build.flags.stackprotect=
|
||||
extelec_rc2040.menu.stackprotect.Enabled=Enabled
|
||||
extelec_rc2040.menu.stackprotect.Enabled.build.flags.stackprotect=-fstack-protector
|
||||
extelec_rc2040.menu.exceptions.Disabled=Disabled
|
||||
extelec_rc2040.menu.exceptions.Disabled.build.flags.exceptions=-fno-exceptions
|
||||
extelec_rc2040.menu.exceptions.Disabled.build.flags.libstdcpp=-lstdc++
|
||||
extelec_rc2040.menu.exceptions.Enabled=Enabled
|
||||
extelec_rc2040.menu.exceptions.Enabled.build.flags.exceptions=-fexceptions
|
||||
extelec_rc2040.menu.exceptions.Enabled.build.flags.libstdcpp=-lstdc++-exc
|
||||
extelec_rc2040.menu.dbgport.Disabled=Disabled
|
||||
extelec_rc2040.menu.dbgport.Disabled.build.debug_port=
|
||||
extelec_rc2040.menu.dbgport.Serial=Serial
|
||||
extelec_rc2040.menu.dbgport.Serial.build.debug_port=-DDEBUG_RP2040_PORT=Serial
|
||||
extelec_rc2040.menu.dbgport.Serial1=Serial1
|
||||
extelec_rc2040.menu.dbgport.Serial1.build.debug_port=-DDEBUG_RP2040_PORT=Serial1
|
||||
extelec_rc2040.menu.dbgport.Serial2=Serial2
|
||||
extelec_rc2040.menu.dbgport.Serial2.build.debug_port=-DDEBUG_RP2040_PORT=Serial2
|
||||
extelec_rc2040.menu.dbglvl.None=None
|
||||
extelec_rc2040.menu.dbglvl.None.build.debug_level=
|
||||
extelec_rc2040.menu.dbglvl.Core=Core
|
||||
extelec_rc2040.menu.dbglvl.Core.build.debug_level=-DDEBUG_RP2040_CORE
|
||||
extelec_rc2040.menu.dbglvl.SPI=SPI
|
||||
extelec_rc2040.menu.dbglvl.SPI.build.debug_level=-DDEBUG_RP2040_SPI
|
||||
extelec_rc2040.menu.dbglvl.Wire=Wire
|
||||
extelec_rc2040.menu.dbglvl.Wire.build.debug_level=-DDEBUG_RP2040_WIRE
|
||||
extelec_rc2040.menu.dbglvl.All=All
|
||||
extelec_rc2040.menu.dbglvl.All.build.debug_level=-DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI -DDEBUG_RP2040_CORE
|
||||
extelec_rc2040.menu.dbglvl.NDEBUG=NDEBUG
|
||||
extelec_rc2040.menu.dbglvl.NDEBUG.build.debug_level=-DNDEBUG
|
||||
extelec_rc2040.menu.usbstack.picosdk=Pico SDK
|
||||
extelec_rc2040.menu.usbstack.picosdk.build.usbstack_flags=
|
||||
extelec_rc2040.menu.usbstack.tinyusb=Adafruit TinyUSB
|
||||
extelec_rc2040.menu.usbstack.tinyusb.build.usbstack_flags=-DUSE_TINYUSB "-I{runtime.platform.path}/libraries/Adafruit_TinyUSB_Arduino/src/arduino"
|
||||
extelec_rc2040.menu.ipstack.ipv4only=IPv4 Only
|
||||
extelec_rc2040.menu.ipstack.ipv4only.build.libpico=libpico.a
|
||||
extelec_rc2040.menu.ipstack.ipv4only.build.lwipdefs=-DLWIP_IPV6=0 -DLWIP_IPV4=1
|
||||
extelec_rc2040.menu.ipstack.ipv4ipv6=IPv4 and IPv6
|
||||
extelec_rc2040.menu.ipstack.ipv4ipv6.build.libpico=libpico-ipv6.a
|
||||
extelec_rc2040.menu.ipstack.ipv4ipv6.build.lwipdefs=-DLWIP_IPV6=1 -DLWIP_IPV4=1
|
||||
|
||||
# -----------------------------------
|
||||
# ExtremeElectronics RC2040 (Picoprobe)
|
||||
# -----------------------------------
|
||||
extelec_rc2040picoprobe.name=ExtremeElectronics RC2040 (Picoprobe)
|
||||
extelec_rc2040picoprobe.vid.0=0x2e8a
|
||||
extelec_rc2040picoprobe.pid.0=0x0004
|
||||
extelec_rc2040picoprobe.build.usbpid=-DSERIALUSB_PID=0xee20
|
||||
extelec_rc2040picoprobe.build.usbpwr=-DUSBD_MAX_POWER_MA=250
|
||||
extelec_rc2040picoprobe.build.board=EXTREMEELEXTRONICS_RC2040
|
||||
extelec_rc2040picoprobe.build.mcu=cortex-m0plus
|
||||
extelec_rc2040picoprobe.build.variant=extelec_rc2040
|
||||
extelec_rc2040picoprobe.upload.tool=picoprobe
|
||||
extelec_rc2040picoprobe.upload.maximum_size=2097152
|
||||
extelec_rc2040picoprobe.upload.maximum_data_size=262144
|
||||
extelec_rc2040picoprobe.upload.wait_for_upload_port=true
|
||||
extelec_rc2040picoprobe.upload.erase_cmd=
|
||||
extelec_rc2040picoprobe.serial.disableDTR=false
|
||||
extelec_rc2040picoprobe.serial.disableRTS=false
|
||||
extelec_rc2040picoprobe.build.f_cpu=125000000
|
||||
extelec_rc2040picoprobe.build.led=
|
||||
extelec_rc2040picoprobe.build.core=rp2040
|
||||
extelec_rc2040picoprobe.build.ldscript=memmap_default.ld
|
||||
extelec_rc2040picoprobe.build.ram_length=256k
|
||||
extelec_rc2040picoprobe.build.boot2=boot2_w25q080_2_padded_checksum
|
||||
extelec_rc2040picoprobe.build.vid=0x2e8a
|
||||
extelec_rc2040picoprobe.build.pid=0xee20
|
||||
extelec_rc2040picoprobe.build.usb_manufacturer="ExtremeElectronics"
|
||||
extelec_rc2040picoprobe.build.usb_product="RC2040 (Picoprobe)"
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0=2MB (no FS)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0.upload.maximum_size=2093056
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0.build.flash_length=2093056
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0.build.fs_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_0.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536=2MB (Sketch: 1984KB, FS: 64KB)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536.upload.maximum_size=2027520
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536.build.flash_length=2027520
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536.build.fs_start=270462976
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_65536.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072=2MB (Sketch: 1920KB, FS: 128KB)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072.upload.maximum_size=1961984
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072.build.flash_length=1961984
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072.build.fs_start=270397440
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_131072.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144=2MB (Sketch: 1792KB, FS: 256KB)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144.upload.maximum_size=1830912
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144.build.flash_length=1830912
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144.build.fs_start=270266368
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_262144.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288=2MB (Sketch: 1536KB, FS: 512KB)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288.upload.maximum_size=1568768
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288.build.flash_length=1568768
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288.build.fs_start=270004224
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_524288.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576=2MB (Sketch: 1MB, FS: 1MB)
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576.upload.maximum_size=1044480
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576.build.flash_length=1044480
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576.build.eeprom_start=270528512
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576.build.fs_start=269479936
|
||||
extelec_rc2040picoprobe.menu.flash.2097152_1048576.build.fs_end=270528512
|
||||
extelec_rc2040picoprobe.menu.freq.133=133 MHz
|
||||
extelec_rc2040picoprobe.menu.freq.133.build.f_cpu=133000000L
|
||||
extelec_rc2040picoprobe.menu.freq.50=50 MHz
|
||||
extelec_rc2040picoprobe.menu.freq.50.build.f_cpu=50000000L
|
||||
extelec_rc2040picoprobe.menu.freq.100=100 MHz
|
||||
extelec_rc2040picoprobe.menu.freq.100.build.f_cpu=100000000L
|
||||
extelec_rc2040picoprobe.menu.freq.120=120 MHz
|
||||
extelec_rc2040picoprobe.menu.freq.120.build.f_cpu=120000000L
|
||||
extelec_rc2040picoprobe.menu.freq.125=125 MHz
|
||||
extelec_rc2040picoprobe.menu.freq.125.build.f_cpu=125000000L
|
||||
extelec_rc2040picoprobe.menu.freq.150=150 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.150.build.f_cpu=150000000L
|
||||
extelec_rc2040picoprobe.menu.freq.175=175 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.175.build.f_cpu=175000000L
|
||||
extelec_rc2040picoprobe.menu.freq.200=200 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.200.build.f_cpu=200000000L
|
||||
extelec_rc2040picoprobe.menu.freq.225=225 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.225.build.f_cpu=225000000L
|
||||
extelec_rc2040picoprobe.menu.freq.240=240 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.240.build.f_cpu=240000000L
|
||||
extelec_rc2040picoprobe.menu.freq.250=250 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.250.build.f_cpu=250000000L
|
||||
extelec_rc2040picoprobe.menu.freq.275=275 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.275.build.f_cpu=275000000L
|
||||
extelec_rc2040picoprobe.menu.freq.300=300 MHz (Overclock)
|
||||
extelec_rc2040picoprobe.menu.freq.300.build.f_cpu=300000000L
|
||||
extelec_rc2040picoprobe.menu.opt.Small=Small (-Os) (standard)
|
||||
extelec_rc2040picoprobe.menu.opt.Small.build.flags.optimize=-Os
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize=Optimize (-O)
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize.build.flags.optimize=-O
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize2=Optimize More (-O2)
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize2.build.flags.optimize=-O2
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize3=Optimize Even More (-O3)
|
||||
extelec_rc2040picoprobe.menu.opt.Optimize3.build.flags.optimize=-O3
|
||||
extelec_rc2040picoprobe.menu.opt.Fast=Fast (-Ofast) (maybe slower)
|
||||
extelec_rc2040picoprobe.menu.opt.Fast.build.flags.optimize=-Ofast
|
||||
extelec_rc2040picoprobe.menu.opt.Debug=Debug (-Og)
|
||||
extelec_rc2040picoprobe.menu.opt.Debug.build.flags.optimize=-Og
|
||||
extelec_rc2040picoprobe.menu.rtti.Disabled=Disabled
|
||||
extelec_rc2040picoprobe.menu.rtti.Disabled.build.flags.rtti=-fno-rtti
|
||||
extelec_rc2040picoprobe.menu.rtti.Enabled=Enabled
|
||||
extelec_rc2040picoprobe.menu.rtti.Enabled.build.flags.rtti=
|
||||
extelec_rc2040picoprobe.menu.stackprotect.Disabled=Disabled
|
||||
extelec_rc2040picoprobe.menu.stackprotect.Disabled.build.flags.stackprotect=
|
||||
extelec_rc2040picoprobe.menu.stackprotect.Enabled=Enabled
|
||||
extelec_rc2040picoprobe.menu.stackprotect.Enabled.build.flags.stackprotect=-fstack-protector
|
||||
extelec_rc2040picoprobe.menu.exceptions.Disabled=Disabled
|
||||
extelec_rc2040picoprobe.menu.exceptions.Disabled.build.flags.exceptions=-fno-exceptions
|
||||
extelec_rc2040picoprobe.menu.exceptions.Disabled.build.flags.libstdcpp=-lstdc++
|
||||
extelec_rc2040picoprobe.menu.exceptions.Enabled=Enabled
|
||||
extelec_rc2040picoprobe.menu.exceptions.Enabled.build.flags.exceptions=-fexceptions
|
||||
extelec_rc2040picoprobe.menu.exceptions.Enabled.build.flags.libstdcpp=-lstdc++-exc
|
||||
extelec_rc2040picoprobe.menu.dbgport.Disabled=Disabled
|
||||
extelec_rc2040picoprobe.menu.dbgport.Disabled.build.debug_port=
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial=Serial
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial.build.debug_port=-DDEBUG_RP2040_PORT=Serial
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial1=Serial1
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial1.build.debug_port=-DDEBUG_RP2040_PORT=Serial1
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial2=Serial2
|
||||
extelec_rc2040picoprobe.menu.dbgport.Serial2.build.debug_port=-DDEBUG_RP2040_PORT=Serial2
|
||||
extelec_rc2040picoprobe.menu.dbglvl.None=None
|
||||
extelec_rc2040picoprobe.menu.dbglvl.None.build.debug_level=
|
||||
extelec_rc2040picoprobe.menu.dbglvl.Core=Core
|
||||
extelec_rc2040picoprobe.menu.dbglvl.Core.build.debug_level=-DDEBUG_RP2040_CORE
|
||||
extelec_rc2040picoprobe.menu.dbglvl.SPI=SPI
|
||||
extelec_rc2040picoprobe.menu.dbglvl.SPI.build.debug_level=-DDEBUG_RP2040_SPI
|
||||
extelec_rc2040picoprobe.menu.dbglvl.Wire=Wire
|
||||
extelec_rc2040picoprobe.menu.dbglvl.Wire.build.debug_level=-DDEBUG_RP2040_WIRE
|
||||
extelec_rc2040picoprobe.menu.dbglvl.All=All
|
||||
extelec_rc2040picoprobe.menu.dbglvl.All.build.debug_level=-DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI -DDEBUG_RP2040_CORE
|
||||
extelec_rc2040picoprobe.menu.dbglvl.NDEBUG=NDEBUG
|
||||
extelec_rc2040picoprobe.menu.dbglvl.NDEBUG.build.debug_level=-DNDEBUG
|
||||
extelec_rc2040picoprobe.menu.usbstack.picosdk=Pico SDK
|
||||
extelec_rc2040picoprobe.menu.usbstack.picosdk.build.usbstack_flags=
|
||||
extelec_rc2040picoprobe.menu.usbstack.tinyusb=Adafruit TinyUSB
|
||||
extelec_rc2040picoprobe.menu.usbstack.tinyusb.build.usbstack_flags=-DUSE_TINYUSB "-I{runtime.platform.path}/libraries/Adafruit_TinyUSB_Arduino/src/arduino"
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4only=IPv4 Only
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4only.build.libpico=libpico.a
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4only.build.lwipdefs=-DLWIP_IPV6=0 -DLWIP_IPV4=1
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4ipv6=IPv4 and IPv6
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4ipv6.build.libpico=libpico-ipv6.a
|
||||
extelec_rc2040picoprobe.menu.ipstack.ipv4ipv6.build.lwipdefs=-DLWIP_IPV6=1 -DLWIP_IPV4=1
|
||||
|
||||
# -----------------------------------
|
||||
# ExtremeElectronics RC2040 (pico-debug)
|
||||
# -----------------------------------
|
||||
extelec_rc2040picodebug.name=ExtremeElectronics RC2040 (pico-debug)
|
||||
extelec_rc2040picodebug.vid.0=0x1209
|
||||
extelec_rc2040picodebug.pid.0=0x2488
|
||||
extelec_rc2040picodebug.build.usbpid=-DSERIALUSB_PID=0xee20
|
||||
extelec_rc2040picodebug.build.usbpwr=-DUSBD_MAX_POWER_MA=250
|
||||
extelec_rc2040picodebug.build.board=EXTREMEELEXTRONICS_RC2040
|
||||
extelec_rc2040picodebug.build.mcu=cortex-m0plus
|
||||
extelec_rc2040picodebug.build.variant=extelec_rc2040
|
||||
extelec_rc2040picodebug.upload.tool=picodebug
|
||||
extelec_rc2040picodebug.upload.maximum_size=2097152
|
||||
extelec_rc2040picodebug.upload.maximum_data_size=245760
|
||||
extelec_rc2040picodebug.upload.wait_for_upload_port=true
|
||||
extelec_rc2040picodebug.upload.erase_cmd=
|
||||
extelec_rc2040picodebug.serial.disableDTR=false
|
||||
extelec_rc2040picodebug.serial.disableRTS=false
|
||||
extelec_rc2040picodebug.build.f_cpu=125000000
|
||||
extelec_rc2040picodebug.build.led=
|
||||
extelec_rc2040picodebug.build.core=rp2040
|
||||
extelec_rc2040picodebug.build.ldscript=memmap_default.ld
|
||||
extelec_rc2040picodebug.build.ram_length=240k
|
||||
extelec_rc2040picodebug.build.boot2=boot2_w25q080_2_padded_checksum
|
||||
extelec_rc2040picodebug.build.vid=0x2e8a
|
||||
extelec_rc2040picodebug.build.pid=0xee20
|
||||
extelec_rc2040picodebug.build.usb_manufacturer="ExtremeElectronics"
|
||||
extelec_rc2040picodebug.build.usb_product="RC2040 (pico-debug)"
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0=2MB (no FS)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0.upload.maximum_size=2093056
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0.build.flash_length=2093056
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0.build.fs_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_0.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536=2MB (Sketch: 1984KB, FS: 64KB)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536.upload.maximum_size=2027520
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536.build.flash_length=2027520
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536.build.fs_start=270462976
|
||||
extelec_rc2040picodebug.menu.flash.2097152_65536.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072=2MB (Sketch: 1920KB, FS: 128KB)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072.upload.maximum_size=1961984
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072.build.flash_length=1961984
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072.build.fs_start=270397440
|
||||
extelec_rc2040picodebug.menu.flash.2097152_131072.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144=2MB (Sketch: 1792KB, FS: 256KB)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144.upload.maximum_size=1830912
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144.build.flash_length=1830912
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144.build.fs_start=270266368
|
||||
extelec_rc2040picodebug.menu.flash.2097152_262144.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288=2MB (Sketch: 1536KB, FS: 512KB)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288.upload.maximum_size=1568768
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288.build.flash_length=1568768
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288.build.fs_start=270004224
|
||||
extelec_rc2040picodebug.menu.flash.2097152_524288.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576=2MB (Sketch: 1MB, FS: 1MB)
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576.upload.maximum_size=1044480
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576.build.flash_length=1044480
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576.build.eeprom_start=270528512
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576.build.fs_start=269479936
|
||||
extelec_rc2040picodebug.menu.flash.2097152_1048576.build.fs_end=270528512
|
||||
extelec_rc2040picodebug.menu.freq.133=133 MHz
|
||||
extelec_rc2040picodebug.menu.freq.133.build.f_cpu=133000000L
|
||||
extelec_rc2040picodebug.menu.freq.50=50 MHz
|
||||
extelec_rc2040picodebug.menu.freq.50.build.f_cpu=50000000L
|
||||
extelec_rc2040picodebug.menu.freq.100=100 MHz
|
||||
extelec_rc2040picodebug.menu.freq.100.build.f_cpu=100000000L
|
||||
extelec_rc2040picodebug.menu.freq.120=120 MHz
|
||||
extelec_rc2040picodebug.menu.freq.120.build.f_cpu=120000000L
|
||||
extelec_rc2040picodebug.menu.freq.125=125 MHz
|
||||
extelec_rc2040picodebug.menu.freq.125.build.f_cpu=125000000L
|
||||
extelec_rc2040picodebug.menu.freq.150=150 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.150.build.f_cpu=150000000L
|
||||
extelec_rc2040picodebug.menu.freq.175=175 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.175.build.f_cpu=175000000L
|
||||
extelec_rc2040picodebug.menu.freq.200=200 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.200.build.f_cpu=200000000L
|
||||
extelec_rc2040picodebug.menu.freq.225=225 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.225.build.f_cpu=225000000L
|
||||
extelec_rc2040picodebug.menu.freq.240=240 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.240.build.f_cpu=240000000L
|
||||
extelec_rc2040picodebug.menu.freq.250=250 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.250.build.f_cpu=250000000L
|
||||
extelec_rc2040picodebug.menu.freq.275=275 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.275.build.f_cpu=275000000L
|
||||
extelec_rc2040picodebug.menu.freq.300=300 MHz (Overclock)
|
||||
extelec_rc2040picodebug.menu.freq.300.build.f_cpu=300000000L
|
||||
extelec_rc2040picodebug.menu.opt.Small=Small (-Os) (standard)
|
||||
extelec_rc2040picodebug.menu.opt.Small.build.flags.optimize=-Os
|
||||
extelec_rc2040picodebug.menu.opt.Optimize=Optimize (-O)
|
||||
extelec_rc2040picodebug.menu.opt.Optimize.build.flags.optimize=-O
|
||||
extelec_rc2040picodebug.menu.opt.Optimize2=Optimize More (-O2)
|
||||
extelec_rc2040picodebug.menu.opt.Optimize2.build.flags.optimize=-O2
|
||||
extelec_rc2040picodebug.menu.opt.Optimize3=Optimize Even More (-O3)
|
||||
extelec_rc2040picodebug.menu.opt.Optimize3.build.flags.optimize=-O3
|
||||
extelec_rc2040picodebug.menu.opt.Fast=Fast (-Ofast) (maybe slower)
|
||||
extelec_rc2040picodebug.menu.opt.Fast.build.flags.optimize=-Ofast
|
||||
extelec_rc2040picodebug.menu.opt.Debug=Debug (-Og)
|
||||
extelec_rc2040picodebug.menu.opt.Debug.build.flags.optimize=-Og
|
||||
extelec_rc2040picodebug.menu.rtti.Disabled=Disabled
|
||||
extelec_rc2040picodebug.menu.rtti.Disabled.build.flags.rtti=-fno-rtti
|
||||
extelec_rc2040picodebug.menu.rtti.Enabled=Enabled
|
||||
extelec_rc2040picodebug.menu.rtti.Enabled.build.flags.rtti=
|
||||
extelec_rc2040picodebug.menu.stackprotect.Disabled=Disabled
|
||||
extelec_rc2040picodebug.menu.stackprotect.Disabled.build.flags.stackprotect=
|
||||
extelec_rc2040picodebug.menu.stackprotect.Enabled=Enabled
|
||||
extelec_rc2040picodebug.menu.stackprotect.Enabled.build.flags.stackprotect=-fstack-protector
|
||||
extelec_rc2040picodebug.menu.exceptions.Disabled=Disabled
|
||||
extelec_rc2040picodebug.menu.exceptions.Disabled.build.flags.exceptions=-fno-exceptions
|
||||
extelec_rc2040picodebug.menu.exceptions.Disabled.build.flags.libstdcpp=-lstdc++
|
||||
extelec_rc2040picodebug.menu.exceptions.Enabled=Enabled
|
||||
extelec_rc2040picodebug.menu.exceptions.Enabled.build.flags.exceptions=-fexceptions
|
||||
extelec_rc2040picodebug.menu.exceptions.Enabled.build.flags.libstdcpp=-lstdc++-exc
|
||||
extelec_rc2040picodebug.menu.dbgport.Disabled=Disabled
|
||||
extelec_rc2040picodebug.menu.dbgport.Disabled.build.debug_port=
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial=Serial
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial.build.debug_port=-DDEBUG_RP2040_PORT=Serial
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial1=Serial1
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial1.build.debug_port=-DDEBUG_RP2040_PORT=Serial1
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial2=Serial2
|
||||
extelec_rc2040picodebug.menu.dbgport.Serial2.build.debug_port=-DDEBUG_RP2040_PORT=Serial2
|
||||
extelec_rc2040picodebug.menu.dbglvl.None=None
|
||||
extelec_rc2040picodebug.menu.dbglvl.None.build.debug_level=
|
||||
extelec_rc2040picodebug.menu.dbglvl.Core=Core
|
||||
extelec_rc2040picodebug.menu.dbglvl.Core.build.debug_level=-DDEBUG_RP2040_CORE
|
||||
extelec_rc2040picodebug.menu.dbglvl.SPI=SPI
|
||||
extelec_rc2040picodebug.menu.dbglvl.SPI.build.debug_level=-DDEBUG_RP2040_SPI
|
||||
extelec_rc2040picodebug.menu.dbglvl.Wire=Wire
|
||||
extelec_rc2040picodebug.menu.dbglvl.Wire.build.debug_level=-DDEBUG_RP2040_WIRE
|
||||
extelec_rc2040picodebug.menu.dbglvl.All=All
|
||||
extelec_rc2040picodebug.menu.dbglvl.All.build.debug_level=-DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI -DDEBUG_RP2040_CORE
|
||||
extelec_rc2040picodebug.menu.dbglvl.NDEBUG=NDEBUG
|
||||
extelec_rc2040picodebug.menu.dbglvl.NDEBUG.build.debug_level=-DNDEBUG
|
||||
extelec_rc2040picodebug.menu.usbstack.nousb=No USB
|
||||
extelec_rc2040picodebug.menu.usbstack.nousb.build.usbstack_flags="-DNO_USB -DDISABLE_USB_SERIAL -I{runtime.platform.path}/tools/libpico"
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4only=IPv4 Only
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4only.build.libpico=libpico.a
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4only.build.lwipdefs=-DLWIP_IPV6=0 -DLWIP_IPV4=1
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4ipv6=IPv4 and IPv6
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4ipv6.build.libpico=libpico-ipv6.a
|
||||
extelec_rc2040picodebug.menu.ipstack.ipv4ipv6.build.lwipdefs=-DLWIP_IPV6=1 -DLWIP_IPV4=1
|
||||
|
||||
# -----------------------------------
|
||||
# iLabs Challenger 2040 LTE
|
||||
# -----------------------------------
|
||||
|
||||
@@ -90,6 +90,15 @@ extern bool __isFreeRTOS;
|
||||
#define HAVE_HWSERIAL1
|
||||
#define HAVE_HWSERIAL2
|
||||
|
||||
// PSTR/etc.
|
||||
#ifndef FPSTR
|
||||
#define FPSTR (const char *)
|
||||
#endif
|
||||
|
||||
#ifndef PGM_VOID_P
|
||||
#define PGM_VOID_P void *
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
|
||||
class CoreMutex {
|
||||
public:
|
||||
CoreMutex(mutex_t *mutex) {
|
||||
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
|
||||
uint32_t owner;
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
if (debugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
|
||||
+3
-1
@@ -50,7 +50,9 @@ enum SeekMode {
|
||||
|
||||
class File : public Stream {
|
||||
public:
|
||||
File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
|
||||
File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) {
|
||||
_startMillis = millis(); /* workaround -O3 spurious warning #768 */
|
||||
}
|
||||
|
||||
// Print methods:
|
||||
size_t write(uint8_t) override;
|
||||
|
||||
@@ -314,7 +314,10 @@ public:
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
watchdog_reboot(0, 0, 100);
|
||||
watchdog_reboot(0, 0, 10);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
inline void restart() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 4
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "2.4.0"
|
||||
#define ARDUINO_PICO_VERSION_STR "2.5.0"
|
||||
|
||||
@@ -64,7 +64,7 @@ void SerialUSB::end() {
|
||||
}
|
||||
|
||||
int SerialUSB::peek() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ int SerialUSB::peek() {
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ int SerialUSB::read() {
|
||||
}
|
||||
|
||||
int SerialUSB::available() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ int SerialUSB::available() {
|
||||
}
|
||||
|
||||
int SerialUSB::availableForWrite() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ int SerialUSB::availableForWrite() {
|
||||
}
|
||||
|
||||
void SerialUSB::flush() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ size_t SerialUSB::write(uint8_t c) {
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
}
|
||||
|
||||
SerialUSB::operator bool() {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
CoreMutex m(&__usb_mutex, false);
|
||||
if (!_running || !m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
libb64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Authors:
|
||||
-------
|
||||
|
||||
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include <stdint.h>
|
||||
#include "cdecode.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static int base64_decode_value_signed(int8_t value_in) {
|
||||
static const int8_t decoding[] PROGMEM = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
|
||||
static const int8_t decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) {
|
||||
return -1;
|
||||
}
|
||||
return pgm_read_byte(&decoding[(int)value_in]);
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in) {
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in) {
|
||||
const int8_t* codechar = code_in;
|
||||
int8_t* plainchar = plaintext_out;
|
||||
int8_t fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step) {
|
||||
while (1) {
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
// falls through
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
// falls through
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
// falls through
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in + length_in) {
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out) {
|
||||
base64_decodestate _state;
|
||||
base64_init_decodestate(&_state);
|
||||
int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state);
|
||||
if (len > 0) {
|
||||
plaintext_out[len] = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int base64_decode_value(char value_in) {
|
||||
return base64_decode_value_signed(*((int8_t *) &value_in));
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) {
|
||||
return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in);
|
||||
}
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) {
|
||||
return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out);
|
||||
}
|
||||
|
||||
};
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define base64_decode_expected_len(n) ((n * 3) / 4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in) {
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
state_in->stepsnewline = BASE64_CHARS_PER_LINE;
|
||||
}
|
||||
|
||||
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in) {
|
||||
base64_init_encodestate(state_in);
|
||||
state_in->stepsnewline = -1;
|
||||
}
|
||||
|
||||
char base64_encode_value(const char n) {
|
||||
char r;
|
||||
|
||||
if (n < 26) {
|
||||
r = n + 'A';
|
||||
} else if (n < 26 + 26) {
|
||||
r = n - 26 + 'a';
|
||||
} else if (n < 26 + 26 + 10) {
|
||||
r = n - 26 - 26 + '0';
|
||||
} else if (n == 62) {
|
||||
r = '+';
|
||||
} else {
|
||||
r = '/';
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) {
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step) {
|
||||
while (1) {
|
||||
case step_A:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
// falls through
|
||||
case step_B:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
// falls through
|
||||
case step_C:
|
||||
if (plainchar == plaintextend) {
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if ((state_in->stepcount == BASE64_CHARS_PER_LINE / 4) && (state_in->stepsnewline > 0)) {
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in) {
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step) {
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block(plaintext_in, length_in, code_out, &_state);
|
||||
return len + base64_encode_blockend((code_out + len), &_state);
|
||||
}
|
||||
|
||||
};
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BASE64_CHARS_PER_LINE 72
|
||||
|
||||
#define base64_encode_expected_len_nonewlines(n) ((((4 * (n)) / 3) + 3) & ~3)
|
||||
#define base64_encode_expected_len(n) \
|
||||
(base64_encode_expected_len_nonewlines(n) + ((n / ((BASE64_CHARS_PER_LINE * 3) / 4)) + 1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct {
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
int stepsnewline;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
void base64_init_encodestate_nonewlines(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -36,7 +36,6 @@
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
|
||||
volatile bool __inLWIP = false;
|
||||
|
||||
// note same code
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.4.0'
|
||||
version = u'2.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.4.0'
|
||||
release = u'2.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+11
-1
@@ -98,9 +98,19 @@ The included ``SD`` library is the Arduino standard one. Please refer to
|
||||
the [Arduino SD reference](https://www.arduino.cc/en/reference/SD) for
|
||||
more information.
|
||||
|
||||
Using Second SPI port for SD
|
||||
----------------------------
|
||||
The ``SD`` library ``begin()`` has been modified to allow you to use the
|
||||
second SPI port, ``SPI1``. Just use the following call in place of
|
||||
``SD.begin(cspin)``
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
SD.begin(cspin, SPI1);
|
||||
|
||||
|
||||
File system object (LittleFS/SD/SDFS)
|
||||
--------------------------------------------
|
||||
-------------------------------------
|
||||
|
||||
setConfig
|
||||
~~~~~~~~~
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
HTTPClient Library
|
||||
==================
|
||||
|
||||
A simple HTTP requestor that can handle both HTTP and HTTP requests is
|
||||
included as the ``HTTPClient`` library.
|
||||
|
||||
Check the examples for use under HTTP and HTTPS configurations. In general,
|
||||
for HTTP connections (unsecured and very uncommon on the internet today) simply
|
||||
passing in a URL and performiung a GET is sufficient to transfer data.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Error checking is left as an exercise for the reader...
|
||||
HTTPClient http;
|
||||
if (http.begin("http://my.server/url")) {
|
||||
if (http.GET() > 0) {
|
||||
String data = http.getString();
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
For HTTPS connections, simply add the appropriate WiFiClientSecure calls
|
||||
as needed (i.e. ``setInsecure()``, ``setTrustAnchor``, etc.). See the
|
||||
WiFiClientSecure documentation for more details.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Error checking is left as an exercise for the reader...
|
||||
HTTPClient https;
|
||||
https.setInsecure(); // Use certs, but do not check their authenticity
|
||||
if (https.begin("https://my.secure.server/url")) {
|
||||
if (http.GET() > 0) {
|
||||
String data = http.getString();
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
Unlike the ESP8266 and ESP32 ``HTTPClient`` implementations it is not necessary
|
||||
to create a ``WiFiClient`` or ``WiFiClientSecure`` to pass in to the ``HTTPClient``
|
||||
object.
|
||||
+1
-1
@@ -38,7 +38,7 @@ for normal operations.
|
||||
Generic RP2040 Support
|
||||
----------------------
|
||||
If your RP2040 board isn't in the menus you can still use it with the
|
||||
IDE bu using the `Board->Generic RP2040` menu option. You will need to
|
||||
IDE by using the `Board->Generic RP2040` menu option. You will need to
|
||||
then set the flash size (see above) and tell the IDE how to communicate
|
||||
with the flash chip using the `Tools->Boot Stage 2` menu.
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
WiFiClientSecure (TLS/SSL/HTTPS) <bearssl-client-secure-class>
|
||||
WiFiServerSecure (TLS/SSL/HTTPS) <bearssl-server-secure-class>
|
||||
|
||||
HTTP/HTTPS Client <httpclient>
|
||||
|
||||
Over-the-Air (OTA) Updates <ota>
|
||||
|
||||
Ported/Optimized Libraries <libraries>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ The Arduino-Pico core can be installed using the Arduino IDE Boards Manager
|
||||
or using `git`. If you want to simply write programs for your RP2040 board,
|
||||
the Boards Manager installation will suffice, but if you want to try the
|
||||
latest pre-release versions and submit improvements, you will need the `git`
|
||||
instllation.
|
||||
installation.
|
||||
|
||||
Installing via Arduino Boards Manager
|
||||
-------------------------------------
|
||||
|
||||
Executable → Regular
+130
-10
@@ -10,8 +10,9 @@ OTA (Over the Air) update is the process of uploading firmware to a Pico using a
|
||||
OTA may be done using:
|
||||
|
||||
- `Arduino IDE <#arduino-ide>`__
|
||||
- `Web Browser <#web-browser>`__ - Coming soon
|
||||
- `HTTP Server <#http-server>`__ - Coming soon
|
||||
- `Web Browser <#web-browser>`__
|
||||
- `HTTP Server <#http-server>`__
|
||||
- Any other method (ZModen receive over a UART port, etc.) by using the ``Updater`` object in your sketch
|
||||
|
||||
The Arduino IDE option is intended primarily for the software development phase. The other two options would be more useful after deployment, to provide the module with application updates either manually with a web browser, or automatically using an HTTP server.
|
||||
|
||||
@@ -48,16 +49,16 @@ Check functionality provided with the `ArduinoOTA <https://github.com/earlephilh
|
||||
void setHostname(const char* hostname);
|
||||
void setPassword(const char* password);
|
||||
|
||||
Certain basic protection is already built in and does not require any additional coding by the developer. `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ and espota.py use `Digest-MD5 <https://en.wikipedia.org/wiki/Digest_access_authentication>`__ to authenticate uploads. Integrity of transferred data is verified on the ESP side using `MD5 <https://en.wikipedia.org/wiki/MD5>`__ checksum.
|
||||
Certain basic protection is already built in and does not require any additional coding by the developer. `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ and espota.py use `Digest-MD5 <https://en.wikipedia.org/wiki/Digest_access_authentication>`__ to authenticate uploads. Integrity of transferred data is verified on the Pico side using `MD5 <https://en.wikipedia.org/wiki/MD5>`__ checksum.
|
||||
|
||||
Make your own risk analysis and, depending on the application, decide what library functions to implement. If required, consider implementation of other means of protection from being hacked, like exposing modules for uploads only according to a specific schedule, triggering OTA only when the user presses a dedicated “Update” button wired to the ESP, etc.
|
||||
Make your own risk analysis and, depending on the application, decide what library functions to implement. If required, consider implementation of other means of protection from being hacked, like exposing modules for uploads only according to a specific schedule, triggering OTA only when the user presses a dedicated “Update” button wired to the Pico, etc.
|
||||
|
||||
Advanced Security - Signed Updates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While the above password-based security will dissuade casual hacking attempts, it is not highly secure. For applications where a higher level of security is needed, cryptographically signed OTA updates can be required. This uses SHA256 hashing in place of MD5 (which is known to be cryptographically broken) and RSA-2048 bit level public-key encryption to guarantee that only the holder of a cryptographic private key can produce signed updates accepted by the OTA update mechanisms.
|
||||
|
||||
Signed updates are updates whose compiled binaries are signed with a private key (held by the developer) and verified with a public key (stored in the application and available for all to see). The signing process computes a hash of the binary code, encrypts the hash with the developer's private key, and appends this encrypted hash (also called a signature) to the binary that is uploaded (via OTA, web, or HTTP server). If the code is modified or replaced in any way by anyone except the holder of the developer's private key, the signature will not match and the ESP8266 will reject the upload.
|
||||
Signed updates are updates whose compiled binaries are signed with a private key (held by the developer) and verified with a public key (stored in the application and available for all to see). The signing process computes a hash of the binary code, encrypts the hash with the developer's private key, and appends this encrypted hash (also called a signature) to the binary that is uploaded (via OTA, web, or HTTP server). If the code is modified or replaced in any way by anyone except the holder of the developer's private key, the signature will not match and the Pico will reject the upload.
|
||||
|
||||
Cryptographic signing only protects against tampering with binaries delivered via OTA. If someone has physical access, they will always be able to flash the device over the serial port. Signing also does not encrypt anything but the hash (so that it can't be modified), so this does not protect code inside the device: if a user has physical access they can read out your program.
|
||||
|
||||
@@ -134,7 +135,7 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
|
||||
|
||||
.. code:: bash
|
||||
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
<PicoArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
|
||||
|
||||
Compression
|
||||
-----------
|
||||
@@ -155,14 +156,14 @@ If signing is desired, sign the gzip compressed file *after* compression.
|
||||
.. code:: bash
|
||||
|
||||
gzip -9 sketch.bin
|
||||
<ESP8266ArduinoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
<PicoPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin sketch.bin.gz --out sketch.bin.gz.signed
|
||||
|
||||
Safety
|
||||
~~~~~~
|
||||
|
||||
The OTA process consumes some of the ESP’s resources and bandwidth during upload. Then, the module is restarted and a new sketch executed. Analyse and test how this affects the functionality of the existing and new sketches.
|
||||
The OTA process consumes some of the Pico’s resources and bandwidth during upload. Then, the module is restarted and a new sketch executed. Analyse and test how this affects the functionality of the existing and new sketches.
|
||||
|
||||
If the ESP is in a remote location and controlling some equipment, you should devote additional attention to what happens if operation of this equipment is suddenly interrupted by the update process. Therefore, decide how to put this equipment into a safe state before starting the update. For instance, your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve is left open, the garden may be flooded.
|
||||
If the Pico is in a remote location and controlling some equipment, you should devote additional attention to what happens if operation of this equipment is suddenly interrupted by the update process. Therefore, decide how to put this equipment into a safe state before starting the update. For instance, your module may be controlling a garden watering system in a sequence. If this sequence is not properly shut down and a water valve is left open, the garden may be flooded.
|
||||
|
||||
The following functions are provided with the `ArduinoOTA <https://github.com/earlephilhower/arduino-pico/tree/master/libraries/ArduinoOTA>`__ library and intended to handle functionality of your application during specific stages of OTA, or on an OTA error:
|
||||
|
||||
@@ -215,6 +216,125 @@ You will not be prompted for a reentering the same password next time. Arduino I
|
||||
|
||||
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
|
||||
|
||||
|
||||
|
||||
|
||||
Web Browser
|
||||
-----------
|
||||
|
||||
Updates described in this chapter are done with a web browser that can be useful in the following typical scenarios:
|
||||
|
||||
- after application deployment if loading directly from Arduino IDE is inconvenient or not possible,
|
||||
- after deployment if user is unable to expose module for OTA from external update server,
|
||||
- to provide updates after deployment to small quantity of modules when setting an update server is not practicable.
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- The Pico and the computer must be connected to the same network, or the IP of the Pico should be known if on a different network.
|
||||
|
||||
Implementation Overview
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Updates with a web browser are implemented using ``HTTPUpdateServer`` class together with ``WebServer`` and ``LEAmDNS`` classes. The following code is required to get it work:
|
||||
|
||||
setup()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
loop()
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
httpServer.handleClient();
|
||||
|
||||
In case OTA update fails dead after entering modifications in your sketch, you can always recover module by loading it over a serial port. Then diagnose the issue with sketch using Serial Monitor. Once the issue is fixed try OTA again.
|
||||
|
||||
|
||||
HTTP Server
|
||||
-----------
|
||||
|
||||
``HTTPUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet.
|
||||
|
||||
Note that by default this class closes all other connections except the one used by the update, this is because the update method blocks. This means that if there's another application receiving data then TCP packets will build up in the buffer leading to out of memory errors causing the OTA update to fail. There's also a limited number of receive buffers available and all may be used up by other applications.
|
||||
|
||||
There are some cases where you know that you won't be receiving any data but would still like to send progress updates.
|
||||
It's possible to disable the default behaviour (and keep connections open) by calling closeConnectionsOnUpdate(false).
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- web server
|
||||
|
||||
Arduino code
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Simple updater downloads the file every time the function is called.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
HTTPUpdate.update(client, "192.168.0.2", 80, "/arduino.bin");
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
|
||||
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
WiFiClient client;
|
||||
t_httpUpdate_return ret = HTTPUpdate.update(client, "192.168.0.2", 80, "/pico/update/arduino.php", "optional current version string here");
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.println("[update] Update failed.");
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.println("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
|
||||
break;
|
||||
}
|
||||
|
||||
TLS updater
|
||||
^^^^^^^^^^^
|
||||
|
||||
Please read and try the examples provided with the library.
|
||||
|
||||
Server request handling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Simple updater
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
For the simple updater the server only needs to deliver the binary file for update.
|
||||
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
For advanced update management a script (such as a PHP script) can run on the server side. It will receive the following headers which it may use to choose a specific firmware file to serve:
|
||||
|
||||
::
|
||||
[User-Agent] => Pico-HTTP-Update
|
||||
[x-Pico-STA-MAC] => 18:FE:AA:AA:AA:AA
|
||||
[x-Pico-AP-MAC] => 1A:FE:AA:AA:AA:AA
|
||||
[x-Pico-Version] => DOOR-7-g14f53a19
|
||||
[x-Pico-Mode] => sketch
|
||||
|
||||
|
||||
Stream Interface
|
||||
----------------
|
||||
|
||||
@@ -233,7 +353,7 @@ A firmware file is uploaded via any method (Ethernet, WiFi, serial ZModem, etc.)
|
||||
|
||||
The ROM layout consists of:
|
||||
|
||||
... code:
|
||||
.. code:: cpp
|
||||
|
||||
[boot2.S] [OTA Bootloader] [0-pad] [OTA partition table] [Main sketch] [LittleFS filesystem] [EEPROM]
|
||||
|
||||
|
||||
+4
-4
@@ -33,8 +33,8 @@ values returned may not meet the most stringent random tests. **If your
|
||||
application needs absolute bulletproof random numbers, consider using
|
||||
dedicated external hardware.**
|
||||
|
||||
void reboot()
|
||||
~~~~~~~~~~~~~
|
||||
void rp2040.reboot()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Forces a hardware reboot of the Pico.
|
||||
|
||||
Memory Information
|
||||
@@ -47,8 +47,8 @@ that because there is some overhead, and there may be heap fragmentation,
|
||||
this number is an *upper bound* and you generally will only be able to allocate
|
||||
less than this returned number.
|
||||
|
||||
int rp2040.getUsedHeap
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
int rp2040.getUsedHeap()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of bytes allocated out of the heap.
|
||||
|
||||
int rp2040.getTotalHeap()
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
WiFi (Raspberry Pi Pico W) Support
|
||||
==================================
|
||||
|
||||
WiFi is supported on the Raspberry Pi Pico W by selecting the "Raspbery Pi Pico W" board in the Boards Manager. It is generally compatible with the `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ and the `ESP8266 Arduino WiFi library <https://github.com/esp8266/Arduino>`__.
|
||||
WiFi is supported on the Raspberry Pi Pico W by selecting the "Raspberry Pi Pico W" board in the Boards Manager. It is generally compatible with the `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ and the `ESP8266 Arduino WiFi library <https://github.com/esp8266/Arduino>`__.
|
||||
|
||||
Enable WiFi support by selecting the `Raspberry Pi Pico W` board in the IDE and adding ``#include <WiFi.h>`` in your sketch.
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
Arduino KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
@@ -30,9 +32,22 @@ usToPIOCycles KEYWORD2
|
||||
f_cpu KEYWORD2
|
||||
getCycleCount KEYWORD2
|
||||
getCycleCount64 KEYWORD2
|
||||
|
||||
getFreeHeap KEYWORD2
|
||||
getUsedHeap KEYWORD2
|
||||
getTotalHeap KEYWORD2
|
||||
|
||||
idleOtherCore KEYWORD2
|
||||
resumeOtherCore KEYWORD2
|
||||
|
||||
restartCore1 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
|
||||
PIOProgram KEYWORD2
|
||||
prepare KEYWORD2
|
||||
SerialPIO KEYWORD2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -243,6 +243,19 @@ void ArduinoOTAClass::_onRx() {
|
||||
void ArduinoOTAClass::_runUpdate() {
|
||||
IPAddress ota_ip = _ota_ip;
|
||||
|
||||
if (!LittleFS.begin()) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("LittleFS Begin Error");
|
||||
#endif
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append("No Filesystem", 13);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Update.begin(_size, _cmd)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("Update Begin Error");
|
||||
@@ -261,15 +274,6 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
if (!LittleFS.begin()) {
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
_udp_ota->append("nofilesystem", 6);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
delay(100);
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
_state = OTA_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
_udp_ota->append("OK", 2);
|
||||
_udp_ota->send(ota_ip, _ota_udp_port);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
String responseHTML = ""
|
||||
"<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Hello World!</h1><p>This is a captive portal example."
|
||||
" All requests will be redirected here.</p></body></html>";
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("DNSServer CaptivePortal example");
|
||||
|
||||
// if DNSServer is started with "*" for domain name, it will reply with
|
||||
// provided IP to all DNS request
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
// replay to all requests with same HTML
|
||||
webServer.onNotFound([]() {
|
||||
webServer.send(200, "text/html", responseHTML);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
/*
|
||||
This example serves a "hello world" on a WLAN and a SoftAP at the same time.
|
||||
The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.
|
||||
|
||||
Connect your computer or cell phone to wifi network pico_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
|
||||
Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from pico_ap and return to your regular WLAN.
|
||||
|
||||
Now the Pico is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://picow.local too.
|
||||
|
||||
This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
|
||||
*/
|
||||
|
||||
/* Set these to your desired softAP credentials. They are not configurable at runtime */
|
||||
#ifndef APSSID
|
||||
#define APSSID "pico_ap"
|
||||
#define APPSK "12345678"
|
||||
#endif
|
||||
|
||||
const char *softAP_ssid = APSSID;
|
||||
const char *softAP_password = APPSK;
|
||||
|
||||
/* hostname for mDNS. Should work at least on windows. Try http://picow.local */
|
||||
const char *myHostname = "picow";
|
||||
|
||||
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
|
||||
char ssid[33] = "";
|
||||
char password[65] = "";
|
||||
|
||||
// DNS server
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
|
||||
// Web server
|
||||
WebServer server(80);
|
||||
|
||||
/* Soft AP network parameters */
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
IPAddress netMsk(255, 255, 255, 0);
|
||||
|
||||
|
||||
/** Should I connect to WLAN asap? */
|
||||
boolean connect;
|
||||
|
||||
/** Last time I tried to connect to WLAN */
|
||||
unsigned long lastConnectTry = 0;
|
||||
|
||||
/** Current WLAN status */
|
||||
unsigned int status = WL_IDLE_STATUS;
|
||||
|
||||
void setup() {
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Configuring access point...");
|
||||
/* You can remove the password parameter if you want the AP to be open. */
|
||||
WiFi.softAPConfig(apIP, apIP, netMsk);
|
||||
WiFi.softAP(softAP_ssid, softAP_password);
|
||||
delay(500); // Without delay I've seen the IP address blank
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
/* Setup the DNS server redirecting all the domains to the apIP */
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer.start(DNS_PORT, "*", apIP);
|
||||
|
||||
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
|
||||
server.on("/", handleRoot);
|
||||
server.on("/wifi", handleWifi);
|
||||
server.on("/wifisave", handleWifiSave);
|
||||
server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin(); // Web server start
|
||||
Serial.println("HTTP server started");
|
||||
//loadCredentials(); // Load WLAN credentials from network
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
Serial.println("Connecting as wifi client...");
|
||||
WiFi.disconnect();
|
||||
WiFi.end();
|
||||
WiFi.begin(ssid, password);
|
||||
int connRes = WiFi.waitForConnectResult();
|
||||
Serial.print("connRes: ");
|
||||
Serial.println(connRes);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (connect) {
|
||||
Serial.println("Connect requested");
|
||||
connect = false;
|
||||
connectWifi();
|
||||
lastConnectTry = millis();
|
||||
}
|
||||
{
|
||||
unsigned int s = WiFi.status();
|
||||
if (s == 0 && millis() > (lastConnectTry + 60000)) {
|
||||
/* If WLAN disconnected and idle try to connect */
|
||||
/* Don't set retry time too low as retry interfere the softAP operation */
|
||||
connect = true;
|
||||
}
|
||||
if (status != s) { // WLAN status change
|
||||
Serial.print("Status: ");
|
||||
Serial.println(s);
|
||||
status = s;
|
||||
if (s == WL_CONNECTED) {
|
||||
/* Just connected to WLAN */
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Setup MDNS responder
|
||||
if (!MDNS.begin(myHostname)) {
|
||||
Serial.println("Error setting up MDNS responder!");
|
||||
} else {
|
||||
Serial.println("mDNS responder started");
|
||||
// Add service to MDNS-SD
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
} else if (s == WL_NO_SSID_AVAIL) {
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
||||
if (s == WL_CONNECTED) { MDNS.update(); }
|
||||
}
|
||||
// Do work:
|
||||
// DNS
|
||||
dnsServer.processNextRequest();
|
||||
// HTTP
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Load WLAN credentials from EEPROM */
|
||||
void loadCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.get(0, ssid);
|
||||
EEPROM.get(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1];
|
||||
EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.end();
|
||||
if (String(ok) != String("OK")) {
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
}
|
||||
Serial.println("Recovered credentials:");
|
||||
Serial.println(ssid);
|
||||
Serial.println(strlen(password) > 0 ? "********" : "<no password>");
|
||||
}
|
||||
|
||||
/** Store WLAN credentials to EEPROM */
|
||||
void saveCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.put(0, ssid);
|
||||
EEPROM.put(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1] = "OK";
|
||||
EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.commit();
|
||||
EEPROM.end();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/** Handle root or redirect to captive portal */
|
||||
void handleRoot() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||
return;
|
||||
}
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>HELLO WORLD!!</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += F("<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
"</body></html>");
|
||||
|
||||
server.send(200, "text/html", Page);
|
||||
}
|
||||
|
||||
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
|
||||
boolean captivePortal() {
|
||||
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
|
||||
Serial.println("Request redirected to captive portal");
|
||||
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Wifi config page handler */
|
||||
void handleWifi() {
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Wifi config</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += String(F("\r\n<br />"
|
||||
"<table><tr><th align='left'>SoftAP config</th></tr>"
|
||||
"<tr><td>SSID "))
|
||||
+ String(softAP_ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.softAPIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN config</th></tr>"
|
||||
"<tr><td>SSID ")
|
||||
+ String(ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.localIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
|
||||
Serial.println("scan start");
|
||||
int n = WiFi.scanNetworks();
|
||||
Serial.println("scan done");
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) { Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>"); }
|
||||
} else {
|
||||
Page += F("<tr><td>No WLAN found</td></tr>");
|
||||
}
|
||||
Page += F("</table>"
|
||||
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
|
||||
"<input type='text' placeholder='network' name='n'/>"
|
||||
"<br /><input type='password' placeholder='password' name='p'/>"
|
||||
"<br /><input type='submit' value='Connect/Disconnect'/></form>"
|
||||
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
|
||||
"</body></html>");
|
||||
server.send(200, "text/html", Page);
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
}
|
||||
|
||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
||||
void handleWifiSave() {
|
||||
Serial.println("wifi save");
|
||||
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
|
||||
server.arg("p").toCharArray(password, sizeof(password) - 1);
|
||||
server.sendHeader("Location", "wifi", true);
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
saveCredentials();
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
|
||||
return;
|
||||
}
|
||||
String message = F("File Not Found\n\n");
|
||||
message += F("URI: ");
|
||||
message += server.uri();
|
||||
message += F("\nMethod: ");
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += F("\nArguments: ");
|
||||
message += server.args();
|
||||
message += F("\n");
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); }
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/** Is this an IP? */
|
||||
boolean isIp(String str) {
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c != '.' && (c < '0' || c > '9')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** IP to String? */
|
||||
String toStringIp(IPAddress ip) {
|
||||
String res = "";
|
||||
for (int i = 0; i < 3; i++) { res += String((ip >> (8 * i)) & 0xFF) + "."; }
|
||||
res += String(((ip >> 8 * 3)) & 0xFF);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
DNSServer dnsServer;
|
||||
WebServer webServer(80);
|
||||
|
||||
void setup() {
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP("picow", "12345678");
|
||||
|
||||
// modify TTL associated with the domain name (in seconds)
|
||||
// default is 60 seconds
|
||||
dnsServer.setTTL(300);
|
||||
// set which return code will be used for all other domains (e.g. sending
|
||||
// ServerFailure instead of NonExistentDomain will reduce number of queries
|
||||
// sent by clients)
|
||||
// default is DNSReplyCode::NonExistentDomain
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
|
||||
|
||||
// start DNS server for a specific domain name
|
||||
dnsServer.start(DNS_PORT, "www.example.com", apIP);
|
||||
|
||||
// simple HTTP server to see that DNS server is working
|
||||
webServer.onNotFound([]() {
|
||||
String message = "Hello World!\n\n";
|
||||
message += "URI: ";
|
||||
message += webServer.uri();
|
||||
|
||||
webServer.send(200, "text/plain", message);
|
||||
});
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest();
|
||||
webServer.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For DNSServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
DNSServer KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
DNSReplyCode KEYWORD1 DATA_TYPE
|
||||
DNSHeader KEYWORD1 DATA_TYPE
|
||||
DNSServer KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
processNextRequest KEYWORD2
|
||||
setErrorReplyCode KEYWORD2
|
||||
setTTL KEYWORD2
|
||||
start KEYWORD2
|
||||
stop KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
DNS_QR_QUERY LITERAL1 RESERVED_WORD_2
|
||||
DNS_QR_RESPONSE LITERAL1 RESERVED_WORD_2
|
||||
DNS_OPCODE_QUERY LITERAL1 RESERVED_WORD_2
|
||||
MAX_DNSNAME_LENGTH LITERAL1 RESERVED_WORD_2
|
||||
NoError LITERAL1 RESERVED_WORD_2
|
||||
FormError LITERAL1 RESERVED_WORD_2
|
||||
ServerFailure LITERAL1 RESERVED_WORD_2
|
||||
NonExistentDomain LITERAL1 RESERVED_WORD_2
|
||||
NotImplemented LITERAL1 RESERVED_WORD_2
|
||||
Refused LITERAL1 RESERVED_WORD_2
|
||||
YXDomain LITERAL1 RESERVED_WORD_2
|
||||
YXRRSet LITERAL1 RESERVED_WORD_2
|
||||
NXRRSet LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=DNSServer
|
||||
version=1.1.1
|
||||
author=Kristijan Novoselić
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=A simple DNS server for ESP8266, ported to the Pico
|
||||
paragraph=This library implements a simple DNS server.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,447 @@
|
||||
#include "WiFi.h"
|
||||
#include "DNSServer.h"
|
||||
#include <lwip/def.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
extern struct rst_info resetInfo;
|
||||
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define CONSOLE DEBUG_ESP_PORT
|
||||
#else
|
||||
#define CONSOLE Serial
|
||||
#endif
|
||||
|
||||
#define _PRINTF(a, ...) printf(PSTR(a), ##__VA_ARGS__)
|
||||
#define _PRINT(a) print(String(F(a)))
|
||||
#define _PRINTLN(a) println(String(F(a)))
|
||||
#define _PRINTLN2(a, b) println(String(F(a)) + b )
|
||||
|
||||
#define CONSOLE_PRINTF CONSOLE._PRINTF
|
||||
#define CONSOLE_PRINT CONSOLE._PRINT
|
||||
#define CONSOLE_PRINTLN CONSOLE._PRINTLN
|
||||
#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2
|
||||
|
||||
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
#define DEBUG_PRINTF CONSOLE_PRINTF
|
||||
#define DEBUG_PRINT CONSOLE_PRINT
|
||||
#define DEBUG_PRINTLN CONSOLE_PRINTLN
|
||||
#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2
|
||||
#define DBGLOG_FAIL LOG_FAIL
|
||||
|
||||
#define DEBUG_(...) do { (__VA_ARGS__); } while(false)
|
||||
#define DEBUG__(...) __VA_ARGS__
|
||||
#define LOG_FAIL(a, fmt, ...) do { if (!(a)) { CONSOLE.printf( PSTR(fmt " line: %d, function: %s\r\n"), ##__VA_ARGS__, __LINE__, __FUNCTION__ ); } } while(false);
|
||||
|
||||
#else
|
||||
#define DEBUG_PRINTF(...) do { } while(false)
|
||||
#define DEBUG_PRINT(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN2(...) do { } while(false)
|
||||
#define DEBUG_(...) do { } while(false)
|
||||
#define DEBUG__(...) do { } while(false)
|
||||
#define LOG_FAIL(a, ...) do { a; } while(false)
|
||||
#define DBGLOG_FAIL(...) do { } while(false)
|
||||
#endif
|
||||
|
||||
#define DNS_HEADER_SIZE sizeof(DNSHeader)
|
||||
|
||||
// Want to keep IDs unique across restarts and continquious
|
||||
static uint32_t _ids __attribute__((section(".noinit")));
|
||||
|
||||
DNSServer::DNSServer()
|
||||
{
|
||||
// I have observed that using 0 for captive and non-zero (600) when
|
||||
// forwarding, will help Android devices recognize the change in connectivity.
|
||||
// They will then report connected.
|
||||
_ttl = lwip_htonl(60);
|
||||
|
||||
srand(rp2040.getCycleCount());
|
||||
_ids = random(0, (1UL << 16) - 1);
|
||||
|
||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
||||
}
|
||||
|
||||
void DNSServer::disableForwarder(const String &domainName, bool freeResources)
|
||||
{
|
||||
_forwarder = false;
|
||||
if (domainName != "") {
|
||||
_domainName = domainName;
|
||||
downcaseAndRemoveWwwPrefix(_domainName);
|
||||
}
|
||||
if (freeResources) {
|
||||
_dns = (uint32_t)0;
|
||||
if (_que) {
|
||||
_que = nullptr;
|
||||
DEBUG_PRINTF("from stop, deleted _que\r\n");
|
||||
DEBUG_(({
|
||||
if (_que_ov) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que overflow or no reply to request: ", (_que_ov));
|
||||
}
|
||||
if (_que_drop) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que wrapped, reply dropped: ", (_que_drop));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DNSServer::enableForwarder(const String &domainName, const IPAddress &dns)
|
||||
{
|
||||
disableForwarder(domainName, false); // Just happens to have the same logic needed here.
|
||||
|
||||
if (dns.isSet()) {
|
||||
_dns = dns;
|
||||
}
|
||||
|
||||
if (_dns.isSet()) {
|
||||
if (!_que) {
|
||||
_que = std::unique_ptr<DNSS_REQUESTER[]> (new (std::nothrow) DNSS_REQUESTER[kDNSSQueSize]);
|
||||
DEBUG_PRINTF("Created new _que\r\n");
|
||||
if (_que) {
|
||||
for (size_t i = 0; i < kDNSSQueSize; i++) {
|
||||
_que[i].ip = 0;
|
||||
}
|
||||
DEBUG_((_que_ov = 0));
|
||||
DEBUG_((_que_drop = 0));
|
||||
}
|
||||
}
|
||||
if (_que) {
|
||||
_forwarder = true;
|
||||
}
|
||||
}
|
||||
return _forwarder;
|
||||
}
|
||||
|
||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
||||
const IPAddress &resolvedIP, const IPAddress &dns)
|
||||
{
|
||||
_port = (port) ? port : IANA_DNS_PORT;
|
||||
|
||||
_resolvedIP[0] = resolvedIP[0];
|
||||
_resolvedIP[1] = resolvedIP[1];
|
||||
_resolvedIP[2] = resolvedIP[2];
|
||||
_resolvedIP[3] = resolvedIP[3];
|
||||
|
||||
if (!enableForwarder(domainName, dns) && (dns.isSet() || _dns.isSet())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _udp.begin(_port) == 1;
|
||||
}
|
||||
|
||||
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
|
||||
{
|
||||
_errorReplyCode = replyCode;
|
||||
}
|
||||
|
||||
void DNSServer::setTTL(const uint32_t &ttl)
|
||||
{
|
||||
_ttl = lwip_htonl(ttl);
|
||||
}
|
||||
|
||||
uint32_t DNSServer::getTTL()
|
||||
{
|
||||
return lwip_ntohl(_ttl);
|
||||
}
|
||||
|
||||
void DNSServer::stop()
|
||||
{
|
||||
_udp.stop();
|
||||
disableForwarder("", true);
|
||||
}
|
||||
|
||||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
|
||||
{
|
||||
domainName.toLowerCase();
|
||||
if (domainName.startsWith("www."))
|
||||
domainName.remove(0, 4);
|
||||
}
|
||||
|
||||
void DNSServer::forwardReply(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
uint16_t id = dnsHeader->ID;
|
||||
// if (kDNSSQueSize <= (uint16_t)((uint16_t)_ids - id)) {
|
||||
if ((uint16_t)kDNSSQueSize <= (uint16_t)_ids - id) {
|
||||
DEBUG_((++_que_drop));
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" dropped!")));
|
||||
return;
|
||||
}
|
||||
size_t i = id & (kDNSSQueSize - 1);
|
||||
|
||||
// Drop duplicate packets
|
||||
if (0 == _que[i].ip) {
|
||||
DEBUG_PRINTLN2("Duplicate reply dropped ID: 0x", String(id, HEX));
|
||||
return;
|
||||
}
|
||||
dnsHeader->ID = _que[i].id;
|
||||
_udp.beginPacket(_que[i].ip, _que[i].port);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" to ") + IPAddress(_que[i].ip).toString()));
|
||||
_que[i].ip = 0; // This gets used to detect duplicate packets and overflow
|
||||
}
|
||||
|
||||
void DNSServer::forwardRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_dns.isSet() || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
++_ids;
|
||||
size_t i = _ids & (kDNSSQueSize - 1);
|
||||
DEBUG_(({
|
||||
if (0 != _que[i].ip) {
|
||||
++_que_ov;
|
||||
}
|
||||
}));
|
||||
_que[i].ip = _udp.remoteIP();
|
||||
_que[i].port = _udp.remotePort();
|
||||
_que[i].id = dnsHeader->ID;
|
||||
dnsHeader->ID = (uint16_t)_ids;
|
||||
_udp.beginPacket(_dns, IANA_DNS_PORT);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward request ID: 0x", (String(dnsHeader->ID, HEX) + F(" to ") + _dns.toString()));
|
||||
}
|
||||
|
||||
bool DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
DNSHeader *dnsHeader;
|
||||
uint8_t *query, *start;
|
||||
const char *matchString;
|
||||
size_t remaining, labelLength, queryLength;
|
||||
uint16_t qtype, qclass;
|
||||
|
||||
dnsHeader = (DNSHeader *)buffer;
|
||||
|
||||
// Must be a query for us to do anything with it
|
||||
if (dnsHeader->QR != DNS_QR_QUERY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If operation is anything other than query, we don't do it
|
||||
if (dnsHeader->OPCode != DNS_OPCODE_QUERY) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NotImplemented);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only support requests containing single queries - everything else
|
||||
// is badly defined
|
||||
if (dnsHeader->QDCount != lwip_htons(1)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must return a FormError in the case of a non-zero ARCount to
|
||||
// be minimally compatible with EDNS resolvers
|
||||
if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0
|
||||
|| dnsHeader->ARCount != 0) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even if we're not going to use the query, we need to parse it
|
||||
// so we can check the address type that's being queried
|
||||
|
||||
query = start = buffer + DNS_HEADER_SIZE;
|
||||
remaining = length - DNS_HEADER_SIZE;
|
||||
while (remaining != 0 && *start != 0) {
|
||||
labelLength = *start;
|
||||
if (labelLength + 1 > remaining) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
remaining -= (labelLength + 1);
|
||||
start += (labelLength + 1);
|
||||
}
|
||||
|
||||
// 1 octet labelLength, 2 octet qtype, 2 octet qclass
|
||||
if (remaining < 5) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
start += 1; // Skip the 0 length label that we found above
|
||||
|
||||
memcpy(&qtype, start, sizeof(qtype));
|
||||
start += 2;
|
||||
memcpy(&qclass, start, sizeof(qclass));
|
||||
start += 2;
|
||||
|
||||
queryLength = start - query;
|
||||
|
||||
if (qclass != lwip_htons(DNS_QCLASS_ANY)
|
||||
&& qclass != lwip_htons(DNS_QCLASS_IN)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qtype != lwip_htons(DNS_QTYPE_A)
|
||||
&& qtype != lwip_htons(DNS_QTYPE_ANY)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have no domain name configured, just return an error
|
||||
if (_domainName == "") {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're running with a wildcard we can just return a result now
|
||||
if (_domainName == "*") {
|
||||
DEBUG_PRINTF("dnsServer - replyWithIP\r\n");
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
matchString = _domainName.c_str();
|
||||
|
||||
start = query;
|
||||
|
||||
// If there's a leading 'www', skip it
|
||||
if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0)
|
||||
start += 4;
|
||||
|
||||
while (*start != 0) {
|
||||
labelLength = *start;
|
||||
start += 1;
|
||||
while (labelLength > 0) {
|
||||
if (tolower(*start) != *matchString) {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++start;
|
||||
++matchString;
|
||||
--labelLength;
|
||||
}
|
||||
if (*start == 0 && *matchString == '\0') {
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*matchString != '.') {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
++matchString;
|
||||
}
|
||||
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DNSServer::processNextRequest()
|
||||
{
|
||||
size_t currentPacketSize;
|
||||
|
||||
currentPacketSize = _udp.parsePacket();
|
||||
if (currentPacketSize == 0)
|
||||
return;
|
||||
|
||||
// The DNS RFC requires that DNS packets be less than 512 bytes in size,
|
||||
// so just discard them if they are larger
|
||||
if (currentPacketSize > MAX_DNS_PACKETSIZE)
|
||||
return;
|
||||
|
||||
// If the packet size is smaller than the DNS header, then someone is
|
||||
// messing with us
|
||||
if (currentPacketSize < DNS_HEADER_SIZE)
|
||||
return;
|
||||
|
||||
std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[currentPacketSize]);
|
||||
if (buffer == nullptr)
|
||||
return;
|
||||
|
||||
_udp.read(buffer.get(), currentPacketSize);
|
||||
if (_dns.isSet() && _udp.remoteIP() == _dns) {
|
||||
// _forwarder may have been set to false; however, for now allow in-flight
|
||||
// replies to finish. //??
|
||||
forwardReply(buffer.get(), currentPacketSize);
|
||||
} else
|
||||
if (respondToRequest(buffer.get(), currentPacketSize)) {
|
||||
forwardRequest(buffer.get(), currentPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSServer::writeNBOShort(uint16_t value)
|
||||
{
|
||||
_udp.write((unsigned char *)&value, 2);
|
||||
}
|
||||
|
||||
void DNSServer::replyWithIP(DNSHeader *dnsHeader,
|
||||
unsigned char * query,
|
||||
size_t queryLength)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
dnsHeader->QDCount = lwip_htons(1);
|
||||
dnsHeader->ANCount = lwip_htons(1);
|
||||
dnsHeader->NSCount = 0;
|
||||
dnsHeader->ARCount = 0;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader));
|
||||
_udp.write(query, queryLength);
|
||||
|
||||
// Rather than restate the name here, we use a pointer to the name contained
|
||||
// in the query section. Pointers have the top two bits set.
|
||||
value = 0xC000 | DNS_HEADER_SIZE;
|
||||
writeNBOShort(lwip_htons(value));
|
||||
|
||||
// Answer is type A (an IPv4 address)
|
||||
writeNBOShort(lwip_htons(DNS_QTYPE_A));
|
||||
|
||||
// Answer is in the Internet Class
|
||||
writeNBOShort(lwip_htons(DNS_QCLASS_IN));
|
||||
|
||||
// Output TTL (already NBO)
|
||||
_udp.write((unsigned char*)&_ttl, 4);
|
||||
|
||||
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
|
||||
writeNBOShort(lwip_htons(sizeof(_resolvedIP)));
|
||||
_udp.write(_resolvedIP, sizeof(_resolvedIP));
|
||||
_udp.endPacket();
|
||||
}
|
||||
|
||||
void DNSServer::replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode,
|
||||
unsigned char *query,
|
||||
size_t queryLength)
|
||||
{
|
||||
dnsHeader->QR = DNS_QR_RESPONSE;
|
||||
dnsHeader->RCode = (unsigned char) rcode;
|
||||
if (query)
|
||||
dnsHeader->QDCount = lwip_htons(1);
|
||||
else
|
||||
dnsHeader->QDCount = 0;
|
||||
dnsHeader->ANCount = 0;
|
||||
dnsHeader->NSCount = 0;
|
||||
dnsHeader->ARCount = 0;
|
||||
|
||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||
_udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader));
|
||||
if (query != NULL)
|
||||
_udp.write(query, queryLength);
|
||||
_udp.endPacket();
|
||||
}
|
||||
|
||||
void DNSServer::replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode)
|
||||
{
|
||||
replyWithError(dnsHeader, rcode, NULL, 0);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#ifndef DNSServer_h
|
||||
#define DNSServer_h
|
||||
|
||||
#include <memory>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
// #define DEBUG_DNSSERVER
|
||||
|
||||
// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
|
||||
#ifndef IANA_DNS_PORT
|
||||
#define IANA_DNS_PORT 53 // AKA domain
|
||||
constexpr inline uint16_t kIanaDnsPort = IANA_DNS_PORT;
|
||||
#endif
|
||||
|
||||
#define DNS_QR_QUERY 0
|
||||
#define DNS_QR_RESPONSE 1
|
||||
#define DNS_OPCODE_QUERY 0
|
||||
|
||||
#define DNS_QCLASS_IN 1
|
||||
#define DNS_QCLASS_ANY 255
|
||||
|
||||
#define DNS_QTYPE_A 1
|
||||
#define DNS_QTYPE_ANY 255
|
||||
|
||||
#define MAX_DNSNAME_LENGTH 253
|
||||
#define MAX_DNS_PACKETSIZE 512
|
||||
|
||||
enum class DNSReplyCode
|
||||
{
|
||||
NoError = 0,
|
||||
FormError = 1,
|
||||
ServerFailure = 2,
|
||||
NonExistentDomain = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
YXDomain = 6,
|
||||
YXRRSet = 7,
|
||||
NXRRSet = 8
|
||||
};
|
||||
|
||||
struct DNSHeader
|
||||
{
|
||||
uint16_t ID; // identification number
|
||||
unsigned char RD : 1; // recursion desired
|
||||
unsigned char TC : 1; // truncated message
|
||||
unsigned char AA : 1; // authoritative answer
|
||||
unsigned char OPCode : 4; // message_type
|
||||
unsigned char QR : 1; // query/response flag
|
||||
unsigned char RCode : 4; // response code
|
||||
unsigned char Z : 3; // its z! reserved
|
||||
unsigned char RA : 1; // recursion available
|
||||
uint16_t QDCount; // number of question entries
|
||||
uint16_t ANCount; // number of answer entries
|
||||
uint16_t NSCount; // number of authority entries
|
||||
uint16_t ARCount; // number of resource entries
|
||||
};
|
||||
|
||||
constexpr inline size_t kDNSSQueSizeAddrBits = 3; // The number of bits used to address que entries
|
||||
constexpr inline size_t kDNSSQueSize = (1UL << (kDNSSQueSizeAddrBits));
|
||||
|
||||
struct DNSS_REQUESTER {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
uint16_t id;
|
||||
};
|
||||
|
||||
class DNSServer
|
||||
{
|
||||
public:
|
||||
DNSServer();
|
||||
~DNSServer() {
|
||||
stop();
|
||||
};
|
||||
/*
|
||||
If specified, `enableForwarder` will update the `domainName` that is used
|
||||
to match DNS request to this AP's IP Address. A non-matching request will
|
||||
be forwarded to the DNS server specified by `dns`.
|
||||
|
||||
Returns `true` on success.
|
||||
|
||||
Returns `false`,
|
||||
* when forwarding `dns` is not set, or
|
||||
* unable to allocate resources for managing the DNS forward function.
|
||||
*/
|
||||
bool enableForwarder(const String &domainName = String(""), const IPAddress &dns = (uint32_t)0);
|
||||
/*
|
||||
`disableForwarder` will stop forwarding DNS requests. If specified,
|
||||
updates the `domainName` that is matched for returning this AP's IP Address.
|
||||
Optionally, resources used for the DNS forward function can be freed.
|
||||
*/
|
||||
void disableForwarder(const String &domainName = String(""), bool freeResources = false);
|
||||
bool isForwarding() { return _forwarder && _dns.isSet(); }
|
||||
void setDNS(const IPAddress& dns) { _dns = dns; }
|
||||
IPAddress getDNS() { return _dns; }
|
||||
bool isDNSSet() { return _dns.isSet(); }
|
||||
|
||||
void processNextRequest();
|
||||
void setErrorReplyCode(const DNSReplyCode &replyCode);
|
||||
void setTTL(const uint32_t &ttl);
|
||||
uint32_t getTTL();
|
||||
String getDomainName() { return _domainName; }
|
||||
|
||||
// Returns true if successful, false if there are no sockets available
|
||||
bool start(const uint16_t &port,
|
||||
const String &domainName,
|
||||
const IPAddress &resolvedIP,
|
||||
const IPAddress &dns = (uint32_t)0);
|
||||
// stops the DNS server
|
||||
void stop();
|
||||
|
||||
private:
|
||||
WiFiUDP _udp;
|
||||
String _domainName;
|
||||
IPAddress _dns;
|
||||
std::unique_ptr<DNSS_REQUESTER[]> _que;
|
||||
uint32_t _ttl;
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
// There are 2 possibilities for overflow:
|
||||
// 1) we have more than kDNSSQueSize request already outstanding.
|
||||
// 2) we have request that never received a reply.
|
||||
uint32_t _que_ov;
|
||||
uint32_t _que_drop;
|
||||
#endif
|
||||
DNSReplyCode _errorReplyCode;
|
||||
bool _forwarder;
|
||||
unsigned char _resolvedIP[4];
|
||||
uint16_t _port;
|
||||
|
||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
||||
void replyWithIP(DNSHeader *dnsHeader,
|
||||
unsigned char * query,
|
||||
size_t queryLength);
|
||||
void replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode,
|
||||
unsigned char *query,
|
||||
size_t queryLength);
|
||||
void replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode);
|
||||
bool respondToRequest(uint8_t *buffer, size_t length);
|
||||
void forwardRequest(uint8_t *buffer, size_t length);
|
||||
void forwardReply(uint8_t *buffer, size_t length);
|
||||
void writeNBOShort(uint16_t value);
|
||||
};
|
||||
#endif
|
||||
@@ -62,6 +62,10 @@ public:
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T &update(int const address, const T &t) {
|
||||
return put(address, t);
|
||||
}
|
||||
size_t length() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
Submodule libraries/FreeRTOS/lib/FreeRTOS-Kernel updated: 0b55ee70ad...b5942ddd07
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
Authorization.ino
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
http.setInsecure();
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
// configure traged server and url
|
||||
|
||||
|
||||
http.begin("https://guest:guest@jigsaw.w3.org/HTTP/Basic/");
|
||||
|
||||
/*
|
||||
// or
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/");
|
||||
http.setAuthorization("guest", "guest");
|
||||
|
||||
// or
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/");
|
||||
http.setAuthorization("Z3Vlc3Q6Z3Vlc3Q=");
|
||||
*/
|
||||
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
BasicHTTPClient.ino
|
||||
|
||||
Created on: 24.05.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
if (http.begin("http://httpbin.org")) { // HTTP
|
||||
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
Serial.printf("[HTTP} Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
BasicHTTPSClient-Hard.ino
|
||||
|
||||
Demonstrates the manual way of making a WiFiClient and passing it in to the HTTPClient
|
||||
|
||||
Created on: 20.08.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure(); // Not safe against MITM attacks
|
||||
|
||||
HTTPClient https;
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
if (https.begin(client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = https.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("[HTTPS] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
BasicHTTPSClient.ino
|
||||
|
||||
Created on: 20.08.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
const char *jigsaw_cert = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFKTCCBM+gAwIBAgIQAbTKhAICxb7iDJbE6qU/NzAKBggqhkjOPQQDAjBKMQsw
|
||||
CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
|
||||
Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjIwMzE3MDAwMDAwWhcNMjMwMzE2
|
||||
MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
|
||||
A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
|
||||
MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
|
||||
zj0DAQcDQgAEYnkGDyrIltjRnxoVdy/xgndo+WGMOASzs2hHeCjbJ1KplKJc/ciK
|
||||
XCWq/4+pTzSiVgTFhRmCdLcU1Fa05YFNQaOCA2owggNmMB8GA1UdIwQYMBaAFKXO
|
||||
N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBRIzOWGCDBB/PMrMucSrjIKqlgE
|
||||
uDAvBgNVHREEKDAmghVzbmkuY2xvdWRmbGFyZXNzbC5jb22CDWppZ3Nhdy53My5v
|
||||
cmcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vQ2xv
|
||||
dWRmbGFyZUluY0VDQ0NBLTMuY3JsMDegNaAzhjFodHRwOi8vY3JsNC5kaWdpY2Vy
|
||||
dC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EM
|
||||
AQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB2
|
||||
BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
|
||||
LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Ns
|
||||
b3VkZmxhcmVJbmNFQ0NDQS0zLmNydDAMBgNVHRMBAf8EAjAAMIIBfwYKKwYBBAHW
|
||||
eQIEAgSCAW8EggFrAWkAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9
|
||||
bgAAAX+aFPh6AAAEAwBGMEQCICivjuh2ywUYvVpTKHo65JEheR8dFq8QvBgEiXfw
|
||||
m6q6AiAkxAgz77oboGQGetNmab45+peY+nAGOfyW9vi9S1gMaAB3ADXPGRu/sWxX
|
||||
vw+tTG1Cy7u2JyAmUeo/4SrvqAPDO9ZMAAABf5oU+GEAAAQDAEgwRgIhANKeTNMy
|
||||
GqUsCo7ph7YMWzrhMuDeyP8xPSiCtFzKcn/eAiEAyv5lgCUQ6K14V13zYfL99wZD
|
||||
LFcIP/KZ1y7nuPAksTAAdwCzc3cH4YRQ+GOG1gWp3BEJSnktsWcMC4fc8AMOeTal
|
||||
mgAAAX+aFPiWAAAEAwBIMEYCIQD6535jWw776D4vjyupP2fBw26CBMpVT5++k4rR
|
||||
xqeOXwIhAIbEaEKkEq6JtpWWfVpTyDkMpMfTuiqYVe6REy2XsmEhMAoGCCqGSM49
|
||||
BAMCA0gAMEUCIH3r/puXZcX1bfUoBq2njuHe0bxWtvzDaz5k6WLYrazTAiEA+ePL
|
||||
N6K5xrmaof185pVCxACPLc/BoKyUwMeC8iXCm00=
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
HTTPClient https;
|
||||
switch (cnt) {
|
||||
case 0:
|
||||
Serial.println("[HTTPS] using insecure SSL, not validating certificate");
|
||||
https.setInsecure(); // Note this is unsafe against MITM attacks
|
||||
cnt++;
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("[HTTPS] using secure SSL, validating certificate");
|
||||
https.setCACert(jigsaw_cert);
|
||||
cnt++;
|
||||
break;
|
||||
default:
|
||||
Serial.println("[HTTPS] not setting any SSL verification settings, will fail");
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
if (https.begin("https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = https.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("[HTTPS] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
ChunkedClient.ino
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
if (http.begin("http://anglesharp.azurewebsites.net/Chunked")) {
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
Serial.printf("[HTTP] Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait forever...");
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
This sketch shows how to handle HTTP Digest Authorization.
|
||||
|
||||
Written by Parham Alvani and Sajjad Rahnama, 2018-01-07.
|
||||
|
||||
This example is released into public domain,
|
||||
or, at your option, CC0 licensed.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "NOBABIES"
|
||||
#define STAPSK "ElephantsAreGreat"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssidPassword = STAPSK;
|
||||
|
||||
const char* username = "admin";
|
||||
const char* password = "admin";
|
||||
|
||||
const char* server = "http://httpbin.org";
|
||||
const char* uri = "/digest-auth/auth/admin/admin/MD5";
|
||||
|
||||
String exractParam(String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1) {
|
||||
return "";
|
||||
}
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
String getCNonce(const int len) {
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s = "";
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter) {
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String realm = exractParam(authReq, "realm=\"", '"');
|
||||
String nonce = exractParam(authReq, "nonce=\"", '"');
|
||||
String cNonce = getCNonce(8);
|
||||
|
||||
char nc[9];
|
||||
snprintf(nc, sizeof(nc), "%08x", counter);
|
||||
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.add(username + ":" + realm + ":" + password); // md5 of the user:realm:user
|
||||
md5.calculate();
|
||||
String h1 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(method + ":" + uri);
|
||||
md5.calculate();
|
||||
String h2 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(h1 + ":" + nonce + ":" + String(nc) + ":" + cNonce + ":" + "auth" + ":" + h2);
|
||||
md5.calculate();
|
||||
String response = md5.toString();
|
||||
|
||||
String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\"";
|
||||
Serial.println(authorization);
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, ssidPassword);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
srand(rp2040.getCycleCount());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
HTTPClient http;
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
|
||||
// configure target server and url
|
||||
http.begin(String(server) + String(uri));
|
||||
|
||||
|
||||
const char* keys[] = { "WWW-Authenticate" };
|
||||
http.collectHeaders(keys, 1);
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode > 0) {
|
||||
String authReq = http.header("WWW-Authenticate");
|
||||
Serial.println(authReq);
|
||||
|
||||
String authorization = getDigestAuth(authReq, String(username), String(password), "GET", String(uri), 1);
|
||||
|
||||
http.end();
|
||||
http.begin(String(server) + String(uri));
|
||||
|
||||
http.addHeader("Authorization", authorization);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
PostHTTPClient.ino
|
||||
|
||||
Created on: 21.11.2016
|
||||
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
WiFi.begin(STASSID, STAPSK);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected! IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
|
||||
HTTPClient http;
|
||||
http.setInsecure();
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
// configure target server and url
|
||||
http.begin("https://httpbin.org/post");
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
|
||||
Serial.print("[HTTP] POST...\n");
|
||||
// start connection and send HTTP header and body
|
||||
int httpCode = http.POST("{\"hello\":\"world\"}");
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
const String& payload = http.getString();
|
||||
Serial.println("received payload:\n<<");
|
||||
Serial.println(payload);
|
||||
Serial.println(">>");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
reuseConnectionV2.ino
|
||||
|
||||
Created on: 22.11.2015
|
||||
|
||||
This example reuses the http connection and also restores the connection if the connection is lost
|
||||
*/
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println("Connecting to WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(STASSID, STAPSK);
|
||||
|
||||
// wait for WiFi connection
|
||||
while ((WiFiMulti.run() != WL_CONNECTED)) {
|
||||
Serial.write('.');
|
||||
delay(500);
|
||||
}
|
||||
Serial.println(" connected to WiFi");
|
||||
|
||||
// allow reuse (if server supports it)
|
||||
http.setReuse(true);
|
||||
http.setInsecure();
|
||||
|
||||
http.begin("https://jigsaw.w3.org/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
int pass = 0;
|
||||
|
||||
void loop() {
|
||||
// First 10 loop()s, retrieve the URL
|
||||
if (pass < 10) {
|
||||
pass++;
|
||||
Serial.printf("Reuse connection example, GET url for the %d time\n", pass);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
http.writeToStream(&Serial);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
// Something went wrong with the connection, try to reconnect
|
||||
http.end();
|
||||
http.begin("https://jigsaw.w3.org/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
if (pass == 10) {
|
||||
http.end();
|
||||
Serial.println("Done testing");
|
||||
} else {
|
||||
Serial.println("\n\n\nWait 5 second...\n");
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
StreamHTTPClient.ino
|
||||
|
||||
Created on: 24.05.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
|
||||
// configure server and url
|
||||
const char *fp = "41:FA:FD:B6:96:5F:33:09:F4:ED:09:28:BF:66:4D:5B:A2:88:03:65";
|
||||
|
||||
HTTPClient https;
|
||||
https.setFingerprint(fp);
|
||||
|
||||
if (https.begin("https://www.trustedfirmware.org/projects/mbed-tls")) {
|
||||
|
||||
Serial.print("[HTTPS] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
int httpCode = https.GET();
|
||||
if (httpCode > 0) {
|
||||
// HTTP header has been send and Server response header has been handled
|
||||
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
|
||||
// get length of document (is -1 when Server sends no Content-Length header)
|
||||
int len = https.getSize();
|
||||
|
||||
// create buffer for read
|
||||
static uint8_t buff[128] = { 0 };
|
||||
|
||||
// read all data from server
|
||||
while (https.connected() && (len > 0 || len == -1)) {
|
||||
// get available data size
|
||||
size_t size = https.getStreamPtr()->available();
|
||||
|
||||
if (size) {
|
||||
// read up to 128 byte
|
||||
int c = https.getStreamPtr()->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
|
||||
|
||||
// write it to Serial
|
||||
Serial.write(buff, c);
|
||||
|
||||
if (len > 0) {
|
||||
len -= c;
|
||||
}
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("[HTTPS] connection closed or file end.\n");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
https.end();
|
||||
} else {
|
||||
Serial.printf("Unable to connect\n");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("Wait 10s before the next round...");
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For HTTPClient
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
HTTPClient KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
t_http_codes KEYWORD1 DATA_TYPE
|
||||
transferEncoding_t KEYWORD1 DATA_TYPE
|
||||
TransportTraits KEYWORD1 DATA_TYPE
|
||||
TransportTraitsPtr KEYWORD1 DATA_TYPE
|
||||
StreamString KEYWORD1 DATA_TYPE
|
||||
HTTPClient KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
connected KEYWORD2
|
||||
setReuse KEYWORD2
|
||||
setUserAgent KEYWORD2
|
||||
setAuthorization KEYWORD2
|
||||
setTimeout KEYWORD2
|
||||
useHTTP10 KEYWORD2
|
||||
GET KEYWORD2
|
||||
POST KEYWORD2
|
||||
PUT KEYWORD2
|
||||
PATCH KEYWORD2
|
||||
sendRequest KEYWORD2
|
||||
addHeader KEYWORD2
|
||||
collectHeaders KEYWORD2
|
||||
header KEYWORD2
|
||||
headerName KEYWORD2
|
||||
headers KEYWORD2
|
||||
hasHeader KEYWORD2
|
||||
getSize KEYWORD2
|
||||
getStream KEYWORD2
|
||||
getStreamPtr KEYWORD2
|
||||
writeToStream KEYWORD2
|
||||
getString KEYWORD2
|
||||
errorToString KEYWORD2
|
||||
|
||||
setSession KEYWORD2
|
||||
setInsecure KEYWORD2
|
||||
setKnownKey KEYWORD2
|
||||
setFingerprint KEYWORD2
|
||||
allowSelfSignedCerts KEYWORD2
|
||||
setTrustAnchors KEYWORD2
|
||||
setX509Time KEYWORD2
|
||||
setClientRSACert KEYWORD2
|
||||
setClientECCert KEYWORD2
|
||||
setBufferSizes KEYWORD2
|
||||
setCertStore KEYWORD2
|
||||
setCiphers KEYWORD2
|
||||
setCiphersLessSecure KEYWORD2
|
||||
setSSLVersion KEYWORD2
|
||||
setCACert KEYWORD2
|
||||
setCertificate KEYWORD2
|
||||
setPrivateKey KEYWORD2
|
||||
loadCACert KEYWORD2
|
||||
loadCertificate KEYWORD2
|
||||
loadPrivateKey KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTPCLIENT_DEFAULT_TCP_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_CONNECTION_REFUSED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_SEND_HEADER_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_SEND_PAYLOAD_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NOT_CONNECTED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_CONNECTION_LOST LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NO_STREAM LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_NO_HTTP_SERVER LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_TOO_LESS_RAM LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_ENCODING LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_STREAM_WRITE LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_ERROR_READ_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_TCP_BUFFER_SIZE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CONTINUE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PROCESSING LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_OK LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CREATED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_ACCEPTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NO_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_RESET_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PARTIAL_CONTENT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MULTI_STATUS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_ALREADY_REPORTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_IM_USED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MULTIPLE_CHOICES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MOVED_PERMANENTLY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SEE_OTHER LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_MODIFIED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_USE_PROXY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_TEMPORARY_REDIRECT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PERMANENT_REDIRECT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_BAD_REQUEST LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNAUTHORIZED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PAYMENT_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FORBIDDEN LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_ACCEPTABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_REQUEST_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_CONFLICT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_GONE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LENGTH_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PRECONDITION_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_URI_TOO_LONG LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_EXPECTATION_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_MISDIRECTED_REQUEST LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LOCKED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_FAILED_DEPENDENCY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_UPGRADE_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_PRECONDITION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_TOO_MANY_REQUESTS LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_IMPLEMENTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_BAD_GATEWAY LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_GATEWAY_TIMEOUT LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_LOOP_DETECTED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NOT_EXTENDED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_TE_IDENTITY LITERAL1 RESERVED_WORD_2
|
||||
HTTPC_TE_CHUNKED LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPClient
|
||||
version=1.2
|
||||
author=Markus Sattler
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=http Client for ESP8266, portes to the Pico
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico/blob/master/libraries/HTTPClient
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
HTTPClient.h
|
||||
|
||||
Modified 2022 by Earle F. Philhower, III
|
||||
|
||||
Created on: 02.11.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266HTTPClient for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified by Jeroen Döll, June 2018
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <StreamString.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_CLIENT
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#define DEBUG_HTTPCLIENT(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__ )
|
||||
#ifndef DEBUG_HTTPCLIENT
|
||||
#define DEBUG_HTTPCLIENT(...) do { (void)0; } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_FAILED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
constexpr int HTTPC_ERROR_CONNECTION_REFUSED __attribute__((deprecated)) = HTTPC_ERROR_CONNECTION_FAILED;
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
typedef enum {
|
||||
HTTP_CODE_CONTINUE = 100,
|
||||
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
HTTP_CODE_PROCESSING = 102,
|
||||
HTTP_CODE_OK = 200,
|
||||
HTTP_CODE_CREATED = 201,
|
||||
HTTP_CODE_ACCEPTED = 202,
|
||||
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
HTTP_CODE_NO_CONTENT = 204,
|
||||
HTTP_CODE_RESET_CONTENT = 205,
|
||||
HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
HTTP_CODE_MULTI_STATUS = 207,
|
||||
HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
HTTP_CODE_IM_USED = 226,
|
||||
HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
HTTP_CODE_FOUND = 302,
|
||||
HTTP_CODE_SEE_OTHER = 303,
|
||||
HTTP_CODE_NOT_MODIFIED = 304,
|
||||
HTTP_CODE_USE_PROXY = 305,
|
||||
HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
HTTP_CODE_BAD_REQUEST = 400,
|
||||
HTTP_CODE_UNAUTHORIZED = 401,
|
||||
HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
HTTP_CODE_FORBIDDEN = 403,
|
||||
HTTP_CODE_NOT_FOUND = 404,
|
||||
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
HTTP_CODE_CONFLICT = 409,
|
||||
HTTP_CODE_GONE = 410,
|
||||
HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
HTTP_CODE_URI_TOO_LONG = 414,
|
||||
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
HTTP_CODE_LOCKED = 423,
|
||||
HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
HTTP_CODE_BAD_GATEWAY = 502,
|
||||
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
HTTP_CODE_LOOP_DETECTED = 508,
|
||||
HTTP_CODE_NOT_EXTENDED = 510,
|
||||
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
} t_http_codes;
|
||||
|
||||
typedef enum {
|
||||
HTTPC_TE_IDENTITY,
|
||||
HTTPC_TE_CHUNKED
|
||||
} transferEncoding_t;
|
||||
|
||||
/**
|
||||
redirection follow mode.
|
||||
+ `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
+ `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
GET or HEAD methods will be redirected (using the same method),
|
||||
since the RFC requires end-user confirmation in other cases.
|
||||
+ `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
regardless of a used method. New request will use the same method,
|
||||
and they will include the same body data and the same headers.
|
||||
In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
typedef enum {
|
||||
HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
} followRedirects_t;
|
||||
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient() = default;
|
||||
~HTTPClient() = default;
|
||||
HTTPClient(HTTPClient&&) = default;
|
||||
HTTPClient& operator=(HTTPClient&&) = default;
|
||||
|
||||
// The easier way
|
||||
bool begin(String url);
|
||||
bool begin(String host, uint16_t port, String uri = "/", bool https = false);
|
||||
bool begin(String url, const uint8_t httpsFingerprint[20]) {
|
||||
setFingerprint(httpsFingerprint);
|
||||
return begin(url);
|
||||
}
|
||||
bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) {
|
||||
setFingerprint(httpsFingerprint);
|
||||
return begin(host, port, uri);
|
||||
}
|
||||
|
||||
// Let's do it the hard way, too
|
||||
bool begin(WiFiClient &client, const String& url);
|
||||
bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false);
|
||||
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setAuthorization(String auth);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String& url); // handy for handling redirects
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int DELETE();
|
||||
int POST(const uint8_t* payload, size_t size);
|
||||
int POST(const String& payload);
|
||||
int PUT(const uint8_t* payload, size_t size);
|
||||
int PUT(const String& payload);
|
||||
int PATCH(const uint8_t* payload, size_t size);
|
||||
int PATCH(const String& payload);
|
||||
int sendRequest(const char* type, const String& payload);
|
||||
int sendRequest(const char* type, const uint8_t* payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char* type, Stream * stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char* name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char* name); // check if header exists
|
||||
|
||||
|
||||
int getSize(void);
|
||||
const String& getLocation(void); // Location header from redirect if 3XX
|
||||
|
||||
WiFiClient& getStream(void);
|
||||
WiFiClient* getStreamPtr(void);
|
||||
int writeToPrint(Print* print);
|
||||
int writeToStream(Stream* stream);
|
||||
const String& getString(void);
|
||||
static String errorToString(int error);
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// HTTPS support, mirrors the WiFiClientSecure interface
|
||||
// Could possibly use a virtual interface class between the two, but for now it is more
|
||||
// straightforward to simply feed calls through manually here.
|
||||
void setSession(Session *session) {
|
||||
_tls()->setSession(session);
|
||||
}
|
||||
void setInsecure() {
|
||||
_tls()->setInsecure();
|
||||
}
|
||||
void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) {
|
||||
_tls()->setKnownKey(pk, usages);
|
||||
}
|
||||
bool setFingerprint(const uint8_t fingerprint[20]) {
|
||||
return _tls()->setFingerprint(fingerprint);
|
||||
}
|
||||
bool setFingerprint(const char *fpStr) {
|
||||
return _tls()->setFingerprint(fpStr);
|
||||
}
|
||||
void allowSelfSignedCerts() {
|
||||
_tls()->allowSelfSignedCerts();
|
||||
}
|
||||
void setTrustAnchors(const X509List *ta) {
|
||||
_tls()->setTrustAnchors(ta);
|
||||
}
|
||||
void setX509Time(time_t now) {
|
||||
_tls()->setX509Time(now);
|
||||
}
|
||||
void setClientRSACert(const X509List *cert, const PrivateKey *sk) {
|
||||
_tls()->setClientRSACert(cert, sk);
|
||||
}
|
||||
void setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
||||
_tls()->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
||||
}
|
||||
void setBufferSizes(int recv, int xmit) {
|
||||
_tls()->setBufferSizes(recv, xmit);
|
||||
}
|
||||
void setCertStore(CertStoreBase *certStore) {
|
||||
_tls()->setCertStore(certStore);
|
||||
}
|
||||
bool setCiphers(const uint16_t *cipherAry, int cipherCount) {
|
||||
return _tls()->setCiphers(cipherAry, cipherCount);
|
||||
}
|
||||
bool setCiphers(const std::vector<uint16_t>& list) {
|
||||
return _tls()->setCiphers(list);
|
||||
}
|
||||
bool setCiphersLessSecure() {
|
||||
return _tls()->setCiphersLessSecure();
|
||||
}
|
||||
bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12) {
|
||||
return _tls()->setSSLVersion(min, max);
|
||||
}
|
||||
void setCACert(const char *rootCA) {
|
||||
_tls()->setCACert(rootCA);
|
||||
}
|
||||
void setCertificate(const char *client_ca) {
|
||||
_tls()->setCertificate(client_ca);
|
||||
}
|
||||
void setPrivateKey(const char *private_key) {
|
||||
_tls()->setPrivateKey(private_key);
|
||||
}
|
||||
bool loadCACert(Stream& stream, size_t size) {
|
||||
return _tls()->loadCACert(stream, size);
|
||||
}
|
||||
bool loadCertificate(Stream& stream, size_t size) {
|
||||
return _tls()->loadCertificate(stream, size);
|
||||
}
|
||||
bool loadPrivateKey(Stream& stream, size_t size) {
|
||||
return _tls()->loadPrivateKey(stream, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
// HTTPS helpers
|
||||
WiFiClientSecure *_tls() {
|
||||
if (!_clientMade) {
|
||||
_clientMade = new WiFiClientSecure();
|
||||
_clientGiven = false;
|
||||
}
|
||||
_clientTLS = true;
|
||||
return (WiFiClientSecure*)_clientMade;
|
||||
}
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(const String& url, const char* expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char * type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
WiFiClient *_clientMade = nullptr;
|
||||
bool _clientTLS = false;
|
||||
|
||||
std::unique_ptr<WiFiClient> _clientIn;
|
||||
bool _clientGiven = false;
|
||||
|
||||
WiFiClient *_client() {
|
||||
if (_clientGiven) {
|
||||
return _clientIn.get();
|
||||
} else {
|
||||
return _clientMade;
|
||||
}
|
||||
}
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _base64Authorization;
|
||||
|
||||
static const String defaultUserAgent;
|
||||
String _userAgent = defaultUserAgent;
|
||||
|
||||
/// Response handling
|
||||
std::unique_ptr<RequestArgument[]> _currentHeaders;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
std::unique_ptr<StreamString> _payload;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
base64.cpp
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "libb64/cencode.h"
|
||||
}
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
convert input data to base64
|
||||
@param data const uint8_t
|
||||
@param length size_t
|
||||
@return String
|
||||
*/
|
||||
String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
|
||||
String base64;
|
||||
|
||||
// base64 needs more size then the source data, use cencode.h macros
|
||||
size_t size = ((doNewLines ? base64_encode_expected_len(length)
|
||||
: base64_encode_expected_len_nonewlines(length)) + 1);
|
||||
|
||||
if (base64.reserve(size)) {
|
||||
|
||||
base64_encodestate _state;
|
||||
if (doNewLines) {
|
||||
base64_init_encodestate(&_state);
|
||||
} else {
|
||||
base64_init_encodestate_nonewlines(&_state);
|
||||
}
|
||||
|
||||
constexpr size_t BUFSIZE = 48;
|
||||
char buf[BUFSIZE + 1 /* newline */ + 1 /* NUL */];
|
||||
for (size_t len = 0; len < length; len += BUFSIZE * 3 / 4) {
|
||||
size_t blocklen = base64_encode_block((const char*) data + len,
|
||||
std::min(BUFSIZE * 3 / 4, length - len), buf, &_state);
|
||||
buf[blocklen] = '\0';
|
||||
base64 += buf;
|
||||
}
|
||||
if (base64_encode_blockend(buf, &_state)) {
|
||||
base64 += buf;
|
||||
}
|
||||
} else {
|
||||
base64 = F("-FAIL-");
|
||||
}
|
||||
|
||||
return base64;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
base64.h
|
||||
|
||||
Created on: 09.12.2015
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 core for Arduino.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <api/String.h>
|
||||
|
||||
class base64 {
|
||||
public:
|
||||
// NOTE: The default behaviour of backend (lib64)
|
||||
// is to add a newline every 72 (encoded) characters output.
|
||||
// This may 'break' longer uris and json variables
|
||||
static String encode(const uint8_t * data, size_t length, bool doNewLines);
|
||||
static inline String encode(const String& text, bool doNewLines) {
|
||||
return encode((const uint8_t *) text.c_str(), text.length(), doNewLines);
|
||||
}
|
||||
|
||||
// esp32 compat:
|
||||
|
||||
static inline String encode(const uint8_t * data, size_t length) {
|
||||
return encode(data, length, false);
|
||||
}
|
||||
|
||||
static inline String encode(const String& text) {
|
||||
return encode(text, false);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
httpUpdate.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
#define UPDATE_URL "http://192.168.1.8/xfer/file.bin"
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void update_started() {
|
||||
Serial.println("CALLBACK: HTTP update process started");
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
Serial.println("CALLBACK: HTTP update process finished");
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
// Add optional callback notifiers
|
||||
httpUpdate.onStart(update_started);
|
||||
httpUpdate.onEnd(update_finished);
|
||||
httpUpdate.onProgress(update_progress);
|
||||
httpUpdate.onError(update_error);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(UPDATE_URL);
|
||||
// Or:
|
||||
// t_httpUpdate_return ret = httpUpdate.update("server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break;
|
||||
|
||||
case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
httpUpdateSecure.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
#define UPDATE_URL "https://www.ziplabel.com/file.bin"
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
}
|
||||
|
||||
void update_started() {
|
||||
Serial.println("CALLBACK: HTTP update process started");
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
Serial.println("CALLBACK: HTTP update process finished");
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
// Add optional callback notifiers
|
||||
httpUpdate.onStart(update_started);
|
||||
httpUpdate.onEnd(update_finished);
|
||||
httpUpdate.onProgress(update_progress);
|
||||
httpUpdate.onError(update_error);
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, UPDATE_URL);
|
||||
// Or:
|
||||
// t_httpUpdate_return ret = httpUpdate.update("server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break;
|
||||
|
||||
case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESP8266httpUpdate
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
HTTPUpdate KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HTTPUpdateResult KEYWORD1 DATA_TYPE
|
||||
ESPhttpUpdate KEYWORD1 DATA_TYPE
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
rebootOnUpdate KEYWORD2
|
||||
update KEYWORD2
|
||||
updateSpiffs KEYWORD2
|
||||
getLastError KEYWORD2
|
||||
getLastErrorString KEYWORD2
|
||||
setAuthorization KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTP_UE_TOO_LESS_SPACE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_NOT_REPORT_SIZE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FILE_NOT_FOUND LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FORBIDDEN LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_WRONG_HTTP_CODE LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_FAULTY_MD5 LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_VERIFY_HEADER_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_BIN_FOR_WRONG_FLASH LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UE_SERVER_UNAUTHORIZED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_NO_UPDATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_OK LITERAL1 RESERVED_WORD_2
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPUpdate
|
||||
version=1.3
|
||||
author=Markus Sattler
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Http Update for ESP8266, ported to Pico
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
Executable
+392
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
|
||||
@file HTTPUpdate.cpp
|
||||
@date 21.06.2015
|
||||
@author Markus Sattler
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the Http Updater.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "HTTPUpdate.h"
|
||||
#include <StreamString.h>
|
||||
|
||||
extern uint8_t _FS_start;
|
||||
extern uint8_t _FS_end;
|
||||
|
||||
HTTPUpdate::HTTPUpdate(void)
|
||||
: _httpClientTimeout(8000) {
|
||||
}
|
||||
|
||||
HTTPUpdate::HTTPUpdate(int httpClientTimeout)
|
||||
: _httpClientTimeout(httpClientTimeout) {
|
||||
}
|
||||
|
||||
HTTPUpdate::~HTTPUpdate(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
set the Authorization for the http request
|
||||
@param user const String&
|
||||
@param password const String&
|
||||
*/
|
||||
void HTTPUpdate::setAuthorization(const String &user, const String &password) {
|
||||
_user = user;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
set the Authorization for the http request
|
||||
@param auth const String& base64
|
||||
*/
|
||||
void HTTPUpdate::setAuthorization(const String &auth) {
|
||||
_auth = auth;
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, url);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateFS(WiFiClient& client, const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, url);
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
|
||||
const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(client, host, port, uri);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(host, port, uri);
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateFS(const String& url, const String& currentVersion) {
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
/**
|
||||
return error code as int
|
||||
@return int error code
|
||||
*/
|
||||
int HTTPUpdate::getLastError(void) {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
return error code as String
|
||||
@return String error
|
||||
*/
|
||||
String HTTPUpdate::getLastErrorString(void) {
|
||||
|
||||
if (_lastError == 0) {
|
||||
return String(); // no error
|
||||
}
|
||||
|
||||
// error from Update class
|
||||
if (_lastError > 0) {
|
||||
StreamString error;
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
return String(F("Update error: ")) + error;
|
||||
}
|
||||
|
||||
// error from http client
|
||||
if (_lastError > -100) {
|
||||
return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError);
|
||||
}
|
||||
|
||||
switch (_lastError) {
|
||||
case HTTP_UE_TOO_LESS_SPACE:
|
||||
return F("Not Enough space");
|
||||
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
|
||||
return F("Server Did Not Report Size");
|
||||
case HTTP_UE_SERVER_FILE_NOT_FOUND:
|
||||
return F("File Not Found (404)");
|
||||
case HTTP_UE_SERVER_FORBIDDEN:
|
||||
return F("Forbidden (403)");
|
||||
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
|
||||
return F("Wrong HTTP Code");
|
||||
case HTTP_UE_SERVER_FAULTY_MD5:
|
||||
return F("Wrong MD5");
|
||||
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
|
||||
return F("Verify Bin Header Failed");
|
||||
case HTTP_UE_BIN_FOR_WRONG_FLASH:
|
||||
return F("New Binary Does Not Fit Flash Size");
|
||||
case HTTP_UE_SERVER_UNAUTHORIZED:
|
||||
return F("Unauthorized (401)");
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@param http HTTPClient
|
||||
@param currentVersion const char
|
||||
@return HTTPUpdateResult
|
||||
*/
|
||||
HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) {
|
||||
|
||||
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
|
||||
|
||||
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
|
||||
http.useHTTP10(true);
|
||||
http.setTimeout(_httpClientTimeout);
|
||||
http.setFollowRedirects(_followRedirects);
|
||||
http.setUserAgent(F("Pico-HTTP-Update"));
|
||||
http.addHeader(F("x-Pico-Chip-ID"), String(rp2040.getChipID()));
|
||||
http.addHeader(F("x-Pico-STA-MAC"), WiFi.macAddress());
|
||||
http.addHeader(F("x-Pico-AP-MAC"), WiFi.softAPmacAddress());
|
||||
|
||||
if (spiffs) {
|
||||
http.addHeader(F("x-Pico-Mode"), F("spiffs"));
|
||||
} else {
|
||||
http.addHeader(F("x-Pico-Mode"), F("sketch"));
|
||||
}
|
||||
|
||||
if (currentVersion && currentVersion[0] != 0x00) {
|
||||
http.addHeader(F("x-Pico-Version"), currentVersion);
|
||||
}
|
||||
|
||||
if (_user != "" && _password != "") {
|
||||
http.setAuthorization(_user.c_str(), _password.c_str());
|
||||
}
|
||||
|
||||
if (_auth != "") {
|
||||
http.setAuthorization(_auth.c_str());
|
||||
}
|
||||
|
||||
const char * headerkeys[] = { "x-MD5" };
|
||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
|
||||
|
||||
// track these headers
|
||||
http.collectHeaders(headerkeys, headerkeyssize);
|
||||
|
||||
|
||||
int code = http.GET();
|
||||
int len = http.getSize();
|
||||
|
||||
if (code <= 0) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
|
||||
_setLastError(code);
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len);
|
||||
|
||||
String md5;
|
||||
if (_md5Sum.length()) {
|
||||
md5 = _md5Sum;
|
||||
} else if (http.hasHeader("x-MD5")) {
|
||||
md5 = http.header("x-MD5");
|
||||
}
|
||||
if (md5.length()) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", md5.c_str());
|
||||
}
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] info:\n");
|
||||
|
||||
if (currentVersion && currentVersion[0] != 0x00) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str());
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case HTTP_CODE_OK: ///< OK (Start Update)
|
||||
if (len > 0) {
|
||||
bool startUpdate = true;
|
||||
if (spiffs) {
|
||||
size_t spiffsSize = ((size_t)&_FS_end - (size_t)&_FS_start);
|
||||
if (len > (int) spiffsSize) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startUpdate) {
|
||||
_setLastError(HTTP_UE_TOO_LESS_SPACE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
} else {
|
||||
// Warn main app we're starting up...
|
||||
if (_cbStart) {
|
||||
_cbStart();
|
||||
}
|
||||
|
||||
WiFiClient * tcp = http.getStreamPtr();
|
||||
if (!tcp) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] WiFiClient connection unexpectedly absent\n");
|
||||
_setLastError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if (_closeConnectionsOnUpdate) {
|
||||
WiFiUDP::stopAll();
|
||||
WiFiClient::stopAllExcept(tcp);
|
||||
}
|
||||
|
||||
delay(100);
|
||||
|
||||
int command;
|
||||
|
||||
if (spiffs) {
|
||||
command = U_FS;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate filesystem...\n");
|
||||
} else {
|
||||
command = U_FLASH;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n");
|
||||
}
|
||||
|
||||
if (runUpdate(*tcp, len, md5, command)) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
|
||||
http.end();
|
||||
// Warn main app we're all done
|
||||
if (_cbEnd) {
|
||||
_cbEnd();
|
||||
}
|
||||
|
||||
#ifdef ATOMIC_FS_UPDATE
|
||||
if (_rebootOnUpdate) {
|
||||
#else
|
||||
if (_rebootOnUpdate && !spiffs) {
|
||||
#endif
|
||||
rp2040.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_setLastError(HTTP_UE_SERVER_NOT_REPORT_SIZE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length was 0 or wasn't set by Server?!\n");
|
||||
}
|
||||
break;
|
||||
case HTTP_CODE_NOT_MODIFIED:
|
||||
///< Not Modified (No updates)
|
||||
ret = HTTP_UPDATE_NO_UPDATES;
|
||||
break;
|
||||
case HTTP_CODE_NOT_FOUND:
|
||||
_setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_FORBIDDEN:
|
||||
_setLastError(HTTP_UE_SERVER_FORBIDDEN);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_UNAUTHORIZED:
|
||||
_setLastError(HTTP_UE_SERVER_UNAUTHORIZED);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
default:
|
||||
_setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE);
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
|
||||
//http.writeToStream(&Serial1);
|
||||
break;
|
||||
}
|
||||
|
||||
http.end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
write Update to flash
|
||||
@param in Stream&
|
||||
@param size uint32_t
|
||||
@param md5 String
|
||||
@return true if Update ok
|
||||
*/
|
||||
bool HTTPUpdate::runUpdate(Stream& in, uint32_t size, const String& md5, int command) {
|
||||
|
||||
StreamString error;
|
||||
|
||||
if (_cbProgress) {
|
||||
Update.onProgress(_cbProgress);
|
||||
}
|
||||
|
||||
if (!Update.begin(size, command)) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(0, size);
|
||||
}
|
||||
|
||||
if (md5.length()) {
|
||||
if (!Update.setMD5(md5.c_str())) {
|
||||
_setLastError(HTTP_UE_SERVER_FAULTY_MD5);
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Update.writeStream(in) != size) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cbProgress) {
|
||||
_cbProgress(size, size);
|
||||
}
|
||||
|
||||
if (!Update.end()) {
|
||||
_setLastError(Update.getError());
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
|
||||
@file ESP8266HTTPUpdate.h
|
||||
@date 21.06.2015
|
||||
@author Markus Sattler
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the ESP8266 Http Updater.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_UPDATE
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_HTTP_UPDATE
|
||||
#define DEBUG_HTTP_UPDATE(...) do { (void)0; } while(0)
|
||||
#endif
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
//TODO - in v3.0.0 make this an enum
|
||||
constexpr int HTTP_UE_TOO_LESS_SPACE = (-100);
|
||||
constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101);
|
||||
constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102);
|
||||
constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103);
|
||||
constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104);
|
||||
constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105);
|
||||
constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106);
|
||||
constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107);
|
||||
constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108);
|
||||
|
||||
enum HTTPUpdateResult {
|
||||
HTTP_UPDATE_FAILED,
|
||||
HTTP_UPDATE_NO_UPDATES,
|
||||
HTTP_UPDATE_OK
|
||||
};
|
||||
|
||||
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
|
||||
|
||||
using HTTPUpdateStartCB = std::function<void()>;
|
||||
using HTTPUpdateEndCB = std::function<void()>;
|
||||
using HTTPUpdateErrorCB = std::function<void(int)>;
|
||||
using HTTPUpdateProgressCB = std::function<void(int, int)>;
|
||||
|
||||
class HTTPUpdate {
|
||||
public:
|
||||
HTTPUpdate(void);
|
||||
HTTPUpdate(int httpClientTimeout);
|
||||
~HTTPUpdate(void);
|
||||
|
||||
void rebootOnUpdate(bool reboot) {
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
/**
|
||||
set true to follow redirects.
|
||||
@param follow
|
||||
@deprecated Please use `setFollowRedirects(followRedirects_t follow)`
|
||||
*/
|
||||
void followRedirects(bool follow) __attribute__((deprecated)) {
|
||||
_followRedirects = follow ? HTTPC_STRICT_FOLLOW_REDIRECTS : HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
}
|
||||
/**
|
||||
set redirect follow mode. See `followRedirects_t` enum for available modes.
|
||||
@param follow
|
||||
*/
|
||||
void setFollowRedirects(followRedirects_t follow) {
|
||||
_followRedirects = follow;
|
||||
}
|
||||
|
||||
void closeConnectionsOnUpdate(bool sever) {
|
||||
_closeConnectionsOnUpdate = sever;
|
||||
}
|
||||
|
||||
void setMD5sum(const String &md5Sum) {
|
||||
_md5Sum = md5Sum;
|
||||
}
|
||||
|
||||
void setAuthorization(const String& user, const String& password);
|
||||
void setAuthorization(const String& auth);
|
||||
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
||||
const String& currentVersion = "");
|
||||
t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
t_httpUpdate_return update(const String& url, const String& currentVersion = "");
|
||||
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", const String& currentVersion = "");
|
||||
t_httpUpdate_return updateFS(const String& url, const String& currentVersion = "");
|
||||
|
||||
// Notification callbacks
|
||||
void onStart(HTTPUpdateStartCB cbOnStart) {
|
||||
_cbStart = cbOnStart;
|
||||
}
|
||||
void onEnd(HTTPUpdateEndCB cbOnEnd) {
|
||||
_cbEnd = cbOnEnd;
|
||||
}
|
||||
void onError(HTTPUpdateErrorCB cbOnError) {
|
||||
_cbError = cbOnError;
|
||||
}
|
||||
void onProgress(HTTPUpdateProgressCB cbOnProgress) {
|
||||
_cbProgress = cbOnProgress;
|
||||
}
|
||||
|
||||
int getLastError(void);
|
||||
String getLastErrorString(void);
|
||||
|
||||
protected:
|
||||
t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
|
||||
bool runUpdate(Stream& in, uint32_t size, const String& md5, int command = U_FLASH);
|
||||
|
||||
// Set the error and potentially use a CB to notify the application
|
||||
void _setLastError(int err) {
|
||||
_lastError = err;
|
||||
if (_cbError) {
|
||||
_cbError(err);
|
||||
}
|
||||
}
|
||||
int _lastError;
|
||||
bool _rebootOnUpdate = true;
|
||||
bool _closeConnectionsOnUpdate = true;
|
||||
String _user;
|
||||
String _password;
|
||||
String _auth;
|
||||
String _md5Sum;
|
||||
private:
|
||||
int _httpClientTimeout;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
|
||||
// Callbacks
|
||||
HTTPUpdateStartCB _cbStart;
|
||||
HTTPUpdateEndCB _cbEnd;
|
||||
HTTPUpdateErrorCB _cbError;
|
||||
HTTPUpdateProgressCB _cbProgress;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
extern HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
SecureBearSSLUpdater - SSL encrypted, password-protected firmware update
|
||||
|
||||
This example starts a HTTPS server on the Pico to allow firmware updates
|
||||
to be performed. All communication, including the username and password,
|
||||
is encrypted via SSL. Be sure to update the SSID and PASSWORD before running
|
||||
to allow connection to your WiFi network.
|
||||
|
||||
To upload through terminal you can use:
|
||||
curl -u admin:admin -F "image=@firmware.bin" picow-webupdate.local/firmware
|
||||
|
||||
Adapted by Earle F. Philhower, III, from the SecureWebUpdater.ino example.
|
||||
This example is released into the public domain.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServerSecure.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* update_path = "/firmware";
|
||||
const char* update_username = "admin";
|
||||
const char* update_password = "admin";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServerSecure httpServer(443);
|
||||
HTTPUpdateServerSecure httpUpdater;
|
||||
|
||||
static const char serverCert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx
|
||||
EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w
|
||||
HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX
|
||||
DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh
|
||||
dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y
|
||||
X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj
|
||||
oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI
|
||||
t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO
|
||||
S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy
|
||||
+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM
|
||||
E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb
|
||||
fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC
|
||||
JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA
|
||||
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg==
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf
|
||||
zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+
|
||||
ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh
|
||||
BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8
|
||||
djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T
|
||||
yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M
|
||||
q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr
|
||||
eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN
|
||||
d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn
|
||||
geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y
|
||||
84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx
|
||||
/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim
|
||||
RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu
|
||||
DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg
|
||||
rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW
|
||||
YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK
|
||||
iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X
|
||||
jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ
|
||||
zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV
|
||||
kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt
|
||||
/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO
|
||||
j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg
|
||||
gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
)EOF";
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpServer.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
|
||||
httpUpdater.setup(&httpServer, update_path, update_username, update_password);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("https", "tcp", 443);
|
||||
Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in "
|
||||
"your browser and login with username '%s' and password "
|
||||
"'%s'\n",
|
||||
host, update_path, update_username, update_password);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -F "image=@firmware.bin" picow-webupdate.local/update
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer httpServer(80);
|
||||
HTTPUpdateServer httpUpdater;
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -u admin:admin -F "image=@firmware.bin" picow-webupdate.local/firmware
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* update_path = "/firmware";
|
||||
const char* update_username = "admin";
|
||||
const char* update_password = "admin";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer httpServer(80);
|
||||
HTTPUpdateServer httpUpdater;
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer, update_path, update_username, update_password);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
Serial.printf("HTTPUpdateServer ready! Open http://%s.local%s in your browser and login with username '%s' and password '%s'\n", host, update_path, update_username, update_password);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For HTTPUpdateServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HTTPUpdateServer KEYWORD1
|
||||
HTTPUpdateServerSecure KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setup KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=HTTPUpdateServer
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov, Miguel Angel Ajo
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Simple HTTP Update server based on the Pico WebServer
|
||||
paragraph=The library accepts HTTP post requests to the /update url, and updates the Pico firmware.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=false
|
||||
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
#include <WebServer.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <LittleFS.h>
|
||||
#include "StreamString.h"
|
||||
#include "HTTPUpdateServer.h"
|
||||
|
||||
extern uint8_t _FS_start;
|
||||
extern uint8_t _FS_end;
|
||||
|
||||
|
||||
static const char serverIndex[] PROGMEM =
|
||||
R"(<!DOCTYPE html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'/>
|
||||
</head>
|
||||
<body>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
Firmware:<br>
|
||||
<input type='file' accept='.bin,.bin.gz' name='firmware'>
|
||||
<input type='submit' value='Update Firmware'>
|
||||
</form>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
FileSystem:<br>
|
||||
<input type='file' accept='.bin,.bin.gz' name='filesystem'>
|
||||
<input type='submit' value='Update FileSystem'>
|
||||
</form>
|
||||
</body>
|
||||
</html>)";
|
||||
static const char successResponse[] PROGMEM =
|
||||
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
HTTPUpdateServerTemplate<ServerType, ServerPort>::HTTPUpdateServerTemplate(bool serial_debug) {
|
||||
_serial_output = serial_debug;
|
||||
_server = NULL;
|
||||
_username = "";
|
||||
_password = "";
|
||||
_authenticated = false;
|
||||
}
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
void HTTPUpdateServerTemplate<ServerType, ServerPort>::setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path, const String& username, const String& password) {
|
||||
_server = server;
|
||||
_username = username;
|
||||
_password = password;
|
||||
|
||||
// handler for the /update form page
|
||||
_server->on(path.c_str(), HTTP_GET, [&]() {
|
||||
if (_username != "" && _password != "" && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||
return _server->requestAuthentication();
|
||||
}
|
||||
_server->send_P(200, PSTR("text/html"), serverIndex);
|
||||
});
|
||||
|
||||
// handler for the /update form page - preflight options
|
||||
_server->on(path.c_str(), HTTP_OPTIONS, [&]() {
|
||||
_server->sendHeader("Access-Control-Allow-Headers", "*");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
_server->send(200, F("text/html"), String(F("y")));
|
||||
}, [&]() {
|
||||
_authenticated = (_username == "" || _password == "" || _server->authenticate(_username.c_str(), _password.c_str()));
|
||||
if (!_authenticated) {
|
||||
if (_serial_output) {
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// handler for the /update form POST (once file upload finishes)
|
||||
_server->on(path.c_str(), HTTP_POST, [&]() {
|
||||
_server->sendHeader("Access-Control-Allow-Headers", "*");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
if (!_authenticated) {
|
||||
return _server->requestAuthentication();
|
||||
}
|
||||
if (Update.hasError()) {
|
||||
_server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError);
|
||||
} else {
|
||||
_server->client().setNoDelay(true);
|
||||
_server->send_P(200, PSTR("text/html"), successResponse);
|
||||
delay(100);
|
||||
_server->client().stop();
|
||||
rp2040.restart();
|
||||
}
|
||||
}, [&]() {
|
||||
// handler for the file upload, gets the sketch bytes, and writes
|
||||
// them through the Update object
|
||||
HTTPUpload& upload = _server->upload();
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
_updaterError = "";
|
||||
|
||||
_authenticated = (_username == "" || _password == "" || _server->authenticate(_username.c_str(), _password.c_str()));
|
||||
if (!_authenticated) {
|
||||
if (_serial_output) {
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_serial_output) {
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
}
|
||||
if (upload.name == "filesystem") {
|
||||
size_t fsSize = ((size_t)&_FS_end - (size_t)&_FS_start);
|
||||
LittleFS.end();
|
||||
if (!Update.begin(fsSize, U_FS)) { //start with max available size
|
||||
if (_serial_output) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FSInfo64 i;
|
||||
LittleFS.begin();
|
||||
LittleFS.info64(i);
|
||||
uint32_t maxSketchSpace = i.totalBytes - i.usedBytes;
|
||||
if (!Update.begin(maxSketchSpace, U_FLASH)) { //start with max available size
|
||||
_setUpdaterError();
|
||||
}
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) {
|
||||
if (_serial_output) {
|
||||
Serial.printf(".");
|
||||
}
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
_setUpdaterError();
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) {
|
||||
if (Update.end(true)) { //true to set the size to the current progress
|
||||
if (_serial_output) {
|
||||
Serial.printf("Update Success: %zu\nRebooting...\n", upload.totalSize);
|
||||
}
|
||||
} else {
|
||||
_setUpdaterError();
|
||||
}
|
||||
} else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) {
|
||||
Update.end();
|
||||
if (_serial_output) {
|
||||
Serial.println("Update was aborted");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
void HTTPUpdateServerTemplate<ServerType, ServerPort>::_setUpdaterError() {
|
||||
if (_serial_output) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
StreamString str;
|
||||
Update.printError(str);
|
||||
_updaterError = str.c_str();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <WebServer.h>
|
||||
|
||||
template <typename ServerType, int ServerPort>
|
||||
class HTTPUpdateServerTemplate {
|
||||
public:
|
||||
HTTPUpdateServerTemplate(bool serial_debug = false);
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server) {
|
||||
setup(server, "", "");
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path) {
|
||||
setup(server, path, "", "");
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& username, const String& password) {
|
||||
setup(server, "/update", username, password);
|
||||
}
|
||||
|
||||
void setup(WebServerTemplate<ServerType, ServerPort> *server, const String& path, const String& username, const String& password);
|
||||
|
||||
void updateCredentials(const String& username, const String& password) {
|
||||
_username = username;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _setUpdaterError();
|
||||
|
||||
private:
|
||||
bool _serial_output;
|
||||
WebServerTemplate<ServerType, ServerPort> *_server;
|
||||
String _username;
|
||||
String _password;
|
||||
bool _authenticated;
|
||||
String _updaterError;
|
||||
};
|
||||
|
||||
#include "HTTPUpdateServer-impl.h"
|
||||
|
||||
using HTTPUpdateServer = HTTPUpdateServerTemplate<WiFiServer, 80>;
|
||||
using HTTPUpdateServerSecure = HTTPUpdateServerTemplate<WiFiServerSecure, 443>;
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
return write((uint32_t)s);
|
||||
}
|
||||
|
||||
// Write 32 bit value to port, user responsbile for packing/alignment, etc.
|
||||
// Write 32 bit value to port, user responsible for packing/alignment, etc.
|
||||
size_t write(int32_t val, bool sync);
|
||||
|
||||
// Write sample to I2S port, will block until completed
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
size_t write24(int32_t l, int32_t r); // Note that 24b must have values left-aligned (i.e. 0xABCDEF00)
|
||||
size_t write32(int32_t l, int32_t r);
|
||||
|
||||
// Read 32 bit value to port, user responsbile for packing/alignment, etc.
|
||||
// Read 32 bit value to port, user responsible for packing/alignment, etc.
|
||||
size_t read(int32_t *val, bool sync);
|
||||
|
||||
// Read samples from I2S port, will block until data available
|
||||
|
||||
@@ -1100,7 +1100,7 @@ protected:
|
||||
};
|
||||
|
||||
public:
|
||||
uint16_t m_u16ID; // Query ID (used only in lagacy queries)
|
||||
uint16_t m_u16ID; // Query ID (used only in legacy queries)
|
||||
stcMDNS_RRQuestion* m_pQuestions; // A list of queries
|
||||
uint8_t m_u8HostReplyMask; // Flags for reply components/answers
|
||||
bool m_bLegacyQuery; // Flag: Legacy query
|
||||
|
||||
@@ -1980,7 +1980,7 @@ uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader&
|
||||
#ifdef MDNS_IP6_SUPPORT
|
||||
// TODO
|
||||
#endif
|
||||
} // Address qeuest
|
||||
} // Address quest
|
||||
|
||||
stcMDNS_RRDomain hostDomain;
|
||||
if ((_buildDomainForHost(m_pcHostname, hostDomain))
|
||||
|
||||
@@ -32,8 +32,12 @@
|
||||
|
||||
class SDClass {
|
||||
public:
|
||||
boolean begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED) {
|
||||
SDFS.setConfig(SDFSConfig(csPin, cfg));
|
||||
boolean begin(uint8_t csPin, HardwareSPI &spi) {
|
||||
SDFS.setConfig(SDFSConfig(csPin, SPI_HALF_SPEED, spi));
|
||||
return (boolean)SDFS.begin();
|
||||
}
|
||||
boolean begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED, HardwareSPI &spi = SPI) {
|
||||
SDFS.setConfig(SDFSConfig(csPin, cfg, spi));
|
||||
return (boolean)SDFS.begin();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class SDFSConfig : public FSConfig {
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x53444653;
|
||||
|
||||
SDFSConfig(uint8_t csPin = 4, uint32_t spi = SD_SCK_MHZ(10)) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi) { }
|
||||
SDFSConfig(uint8_t csPin = 4, uint32_t spi = SD_SCK_MHZ(10), HardwareSPI &port = SPI) : FSConfig(FSId, false), _csPin(csPin), _part(0), _spiSettings(spi), _spi(&port) { }
|
||||
|
||||
SDFSConfig setAutoFormat(bool val = true) {
|
||||
_autoFormat = val;
|
||||
@@ -55,10 +55,14 @@ public:
|
||||
_csPin = pin;
|
||||
return *this;
|
||||
}
|
||||
SDFSConfig setSPI(uint32_t spi) {
|
||||
SDFSConfig setSPISpeed(uint32_t spi) {
|
||||
_spiSettings = spi;
|
||||
return *this;
|
||||
}
|
||||
SDFSConfig setSPI(HardwareSPI &spi) {
|
||||
_spi = &spi;
|
||||
return true;
|
||||
}
|
||||
SDFSConfig setPart(uint8_t part) {
|
||||
_part = part;
|
||||
return *this;
|
||||
@@ -68,6 +72,7 @@ public:
|
||||
uint8_t _csPin;
|
||||
uint8_t _part;
|
||||
uint32_t _spiSettings;
|
||||
HardwareSPI *_spi;
|
||||
};
|
||||
|
||||
class SDFSImpl : public FSImpl {
|
||||
@@ -146,10 +151,11 @@ public:
|
||||
if (_mounted) {
|
||||
return true;
|
||||
}
|
||||
_mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings);
|
||||
SdSpiConfig ssc(_cfg._csPin, SHARED_SPI, _cfg._spiSettings, _cfg._spi);
|
||||
_mounted = _fs.begin(ssc);
|
||||
if (!_mounted && _cfg._autoFormat) {
|
||||
format();
|
||||
_mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings);
|
||||
_mounted = _fs.begin(ssc);
|
||||
}
|
||||
FsDateTime::setCallback(dateTimeCB);
|
||||
return _mounted;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <hardware/flash.h>
|
||||
#include <PicoOTA.h>
|
||||
|
||||
#define DEBUG_UPDATER Serial
|
||||
|
||||
#include <Updater_Signing.h>
|
||||
#ifndef ARDUINO_SIGNING
|
||||
#define ARDUINO_SIGNING 0
|
||||
@@ -200,7 +198,7 @@ bool UpdaterClass::end(bool evenIfRemaining) {
|
||||
|
||||
int binSize = _size;
|
||||
if (expectedSigLen > 0) {
|
||||
_size -= (sigLen + sizeof(uint32_t) /* The siglen word */);
|
||||
binSize -= (sigLen + sizeof(uint32_t) /* The siglen word */);
|
||||
}
|
||||
_hash->begin();
|
||||
#ifdef DEBUG_UPDATER
|
||||
@@ -414,30 +412,37 @@ void UpdaterClass::_setError(int error) {
|
||||
}
|
||||
|
||||
void UpdaterClass::printError(Print &out) {
|
||||
out.printf_P(PSTR("ERROR[%u]: "), _error);
|
||||
String err;
|
||||
err = "ERROR[";
|
||||
err += _error;
|
||||
err += "]: ";
|
||||
if (_error == UPDATE_ERROR_OK) {
|
||||
out.println(F("No Error"));
|
||||
err += "No Error";
|
||||
} else if (_error == UPDATE_ERROR_WRITE) {
|
||||
out.println(F("Flash Write Failed"));
|
||||
err += "Flash Write Failed";
|
||||
} else if (_error == UPDATE_ERROR_ERASE) {
|
||||
out.println(F("Flash Erase Failed"));
|
||||
err += "Flash Erase Failed";
|
||||
} else if (_error == UPDATE_ERROR_READ) {
|
||||
out.println(F("Flash Read Failed"));
|
||||
err += "Flash Read Failed";
|
||||
} else if (_error == UPDATE_ERROR_SPACE) {
|
||||
out.println(F("Not Enough Space"));
|
||||
err += "Not Enough Space";
|
||||
} else if (_error == UPDATE_ERROR_SIZE) {
|
||||
out.println(F("Bad Size Given"));
|
||||
err += "Bad Size Given";
|
||||
} else if (_error == UPDATE_ERROR_STREAM) {
|
||||
out.println(F("Stream Read Timeout"));
|
||||
err += "Stream Read Timeout";
|
||||
} else if (_error == UPDATE_ERROR_NO_DATA) {
|
||||
out.println(F("No data supplied"));
|
||||
err += "No data supplied";
|
||||
} else if (_error == UPDATE_ERROR_MD5) {
|
||||
out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str());
|
||||
err += "MD5 Failed: expected:";
|
||||
err += _target_md5.c_str();
|
||||
err += " calculated:";
|
||||
err += _md5.toString();
|
||||
} else if (_error == UPDATE_ERROR_SIGN) {
|
||||
out.println(F("Signature verification failed"));
|
||||
err += "Signature verification failed";
|
||||
} else {
|
||||
out.println(F("UNKNOWN"));
|
||||
err += "UNKNOWN";
|
||||
}
|
||||
out.println(err.c_str());
|
||||
}
|
||||
|
||||
UpdaterClass Update;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
Copyright (c) 2015, Majenko Technologies
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
* * Neither the name of Majenko Technologies nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const int led = 13;
|
||||
|
||||
void handleRoot() {
|
||||
digitalWrite(led, 1);
|
||||
char temp[400];
|
||||
int sec = millis() / 1000;
|
||||
int min = sec / 60;
|
||||
int hr = min / 60;
|
||||
|
||||
snprintf(temp, 400,
|
||||
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv='refresh' content='5'/>\
|
||||
<title>Pico-W Demo</title>\
|
||||
<style>\
|
||||
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<h1>Hello from the Pico W!</h1>\
|
||||
<p>Uptime: %02d:%02d:%02d</p>\
|
||||
<img src=\"/test.svg\" />\
|
||||
</body>\
|
||||
</html>",
|
||||
|
||||
hr, min % 60, sec % 60);
|
||||
server.send(200, "text/html", temp);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\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 += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void drawGraph() {
|
||||
String out;
|
||||
out.reserve(2600);
|
||||
char temp[70];
|
||||
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
|
||||
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
|
||||
out += "<g stroke=\"black\">\n";
|
||||
int y = rand() % 130;
|
||||
for (int x = 10; x < 390; x += 10) {
|
||||
int y2 = rand() % 130;
|
||||
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
|
||||
out += temp;
|
||||
y = y2;
|
||||
}
|
||||
out += "</g>\n</svg>\n";
|
||||
|
||||
server.send(200, "image/svg+xml", out);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("picow")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", handleRoot);
|
||||
server.on("/test.svg", drawGraph);
|
||||
server.on("/inline", []() {
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
FSBrowser - A web-based FileSystem Browser for Pico filesystems
|
||||
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the Pico WebServer 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
|
||||
|
||||
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 <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.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
|
||||
|
||||
|
||||
#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 = "fsbrowser";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
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 {
|
||||
json += "\"false\"";
|
||||
}
|
||||
json += F(",\"unsupportedFiles\":\"");
|
||||
json += unsupportedFiles;
|
||||
json += "\"}";
|
||||
|
||||
server.send(200, "application/json", json);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Return the list of files in the directory specified by the "dir" query string parameter.
|
||||
Also demonstrates the use of chunked responses.
|
||||
*/
|
||||
void handleFileList() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
|
||||
if (!server.hasArg("dir")) {
|
||||
return replyBadRequest(F("DIR ARG MISSING"));
|
||||
}
|
||||
|
||||
String path = server.arg("dir");
|
||||
if (path != "/" && !fileSystem->exists(path)) {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
|
||||
DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
|
||||
Dir dir = fileSystem->openDir(path);
|
||||
path = "";
|
||||
|
||||
// use HTTP/1.1 Chunked response to avoid building a huge temporary string
|
||||
if (!server.chunkedResponseModeStart(200, "text/json")) {
|
||||
server.send(505, F("text/html"), F("HTTP1.1 required"));
|
||||
return;
|
||||
}
|
||||
|
||||
// use the same string for every line
|
||||
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
|
||||
Serial.println(output);
|
||||
server.sendContent(output);
|
||||
output = ',';
|
||||
} else {
|
||||
output = '[';
|
||||
}
|
||||
|
||||
output += "{\"type\":\"";
|
||||
if (dir.isDirectory()) {
|
||||
output += "dir";
|
||||
} else {
|
||||
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 += "\"}";
|
||||
}
|
||||
|
||||
// send last string
|
||||
output += "]";
|
||||
server.sendContent(output);
|
||||
server.chunkedResponseFinalize();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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!");
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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 != "" && !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 == "") {
|
||||
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 == "") {
|
||||
// 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 == "" || 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 explicitly 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 = WebServer::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.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 filesystem 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);
|
||||
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(F("Connected! IP address: "));
|
||||
DBG_OUTPUT_PORT.println(WiFi.localIP());
|
||||
|
||||
////////////////////////////////
|
||||
// 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
|
||||
|
||||
// Filesystem status
|
||||
server.on("/status", HTTP_GET, handleStatus);
|
||||
|
||||
// List directory
|
||||
server.on("/list", HTTP_GET, handleFileList);
|
||||
|
||||
// Load editor
|
||||
server.on("/edit", HTTP_GET, handleGetEdit);
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
I am in a subdir
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>Pico 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.
|
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
@@ -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;
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
# FSBrowser readme
|
||||
|
||||
## What is this sketch about ?
|
||||
|
||||
This example is a FileSystem Browser for the Pico 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 Pico
|
||||
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 Pico
|
||||
- for LittleFS, please follow the instructions at https://arduino-pico.readthedocs.io/en/latest/fs.html#uploading-files-to-the-littlefs-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 Pico 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 Pico as an access-point), you can copy the `ace.js` file to the `edit` subfolder of the Pico 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 Pico filesystem either, the page will default to a plain text viewer, with a warning message.
|
||||
|
||||
## Notes
|
||||
- See https://arduino-pico.readthedocs.io/en/latest/fs.html for more information on FileSystems supported by the Pico.
|
||||
- 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)
|
||||
- Directories are supported on SDFS and LittleFS.
|
||||
- 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
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
HelloServerBearSSL - Simple HTTPS server example
|
||||
|
||||
This example demonstrates a basic WebServerSecure HTTPS server
|
||||
that can serve "/" and "/inline" and generate detailed 404 (not found)
|
||||
HTTP responses. Be sure to update the SSID and PASSWORD before running
|
||||
to allow connection to your WiFi network.
|
||||
|
||||
Adapted by Earle F. Philhower, III, from the HelloServer.ino example.
|
||||
This example is released into the public domain.
|
||||
*/
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServerSecure.h>
|
||||
#include <LEAmDNS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServerSecure server(443);
|
||||
ServerSessions serverCache(5);
|
||||
|
||||
static const char serverCert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx
|
||||
EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w
|
||||
HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX
|
||||
DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh
|
||||
dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y
|
||||
X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj
|
||||
oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI
|
||||
t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO
|
||||
S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy
|
||||
+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM
|
||||
E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb
|
||||
fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC
|
||||
JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA
|
||||
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg==
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf
|
||||
zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+
|
||||
ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh
|
||||
BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8
|
||||
djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T
|
||||
yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M
|
||||
q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr
|
||||
eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN
|
||||
d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn
|
||||
geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y
|
||||
84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx
|
||||
/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim
|
||||
RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu
|
||||
DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg
|
||||
rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW
|
||||
YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK
|
||||
iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X
|
||||
jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ
|
||||
zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV
|
||||
kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt
|
||||
/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO
|
||||
j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg
|
||||
gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
)EOF";
|
||||
|
||||
|
||||
const int led = LED_BUILTIN;
|
||||
|
||||
void handleRoot() {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/plain", "Hello from the Pico W over HTTPS!");
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\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 += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("picow")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
|
||||
|
||||
// Cache SSL sessions to accelerate the TLS handshake.
|
||||
server.getServer().setCache(&serverCache);
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
server.on("/inline", []() {
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTPS server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
HTTP Advanced Authentication example
|
||||
Created Mar 16, 2017 by Ahmed El-Sharnoby.
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const char* www_username = "admin";
|
||||
const char* www_password = "picow";
|
||||
// allows you to set the realm of authentication Default:"Login Required"
|
||||
const char* www_realm = "Custom Auth Realm";
|
||||
// the Content of the HTML response in case of Unautherized Access Default:empty
|
||||
String authFailResponse = "Authentication Failed";
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
rp2040.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate(www_username, www_password))
|
||||
// Basic Auth Method with Custom realm and Failure Response
|
||||
// return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse);
|
||||
// Digest Auth Method with realm="Login Required" and empty Failure Response
|
||||
// return server.requestAuthentication(DIGEST_AUTH);
|
||||
// Digest Auth Method with Custom realm and empty Failure Response
|
||||
// return server.requestAuthentication(DIGEST_AUTH, www_realm);
|
||||
// Digest Auth Method with Custom realm and Failure Response
|
||||
{
|
||||
return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse);
|
||||
}
|
||||
server.send(200, "text/plain", "Login OK");
|
||||
});
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const char* www_username = "admin";
|
||||
const char* www_password = "picow";
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
rp2040.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate(www_username, www_password)) {
|
||||
return server.requestAuthentication();
|
||||
}
|
||||
server.send(200, "text/plain", "Login OK");
|
||||
});
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const int led = LED_BUILTIN;
|
||||
|
||||
const String postForms = "<html>\
|
||||
<head>\
|
||||
<title>Pico-W Web Server POST handling</title>\
|
||||
<style>\
|
||||
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<h1>POST plain text to /postplain/</h1><br>\
|
||||
<form method=\"post\" enctype=\"text/plain\" action=\"/postplain/\">\
|
||||
<input type=\"text\" name=\'{\"hello\": \"world\", \"trash\": \"\' value=\'\"}\'><br>\
|
||||
<input type=\"submit\" value=\"Submit\">\
|
||||
</form>\
|
||||
<h1>POST form data to /postform/</h1><br>\
|
||||
<form method=\"post\" enctype=\"application/x-www-form-urlencoded\" action=\"/postform/\">\
|
||||
<input type=\"text\" name=\"hello\" value=\"world\"><br>\
|
||||
<input type=\"submit\" value=\"Submit\">\
|
||||
</form>\
|
||||
</body>\
|
||||
</html>";
|
||||
|
||||
void handleRoot() {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/html", postForms);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handlePlain() {
|
||||
if (server.method() != HTTP_POST) {
|
||||
digitalWrite(led, 1);
|
||||
server.send(405, "text/plain", "Method Not Allowed");
|
||||
digitalWrite(led, 0);
|
||||
} else {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/plain", "POST body was:\n" + server.arg("plain"));
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void handleForm() {
|
||||
if (server.method() != HTTP_POST) {
|
||||
digitalWrite(led, 1);
|
||||
server.send(405, "text/plain", "Method Not Allowed");
|
||||
digitalWrite(led, 0);
|
||||
} else {
|
||||
digitalWrite(led, 1);
|
||||
String message = "POST form was:\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(200, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\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 += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("picow")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
server.on("/postplain/", handlePlain);
|
||||
|
||||
server.on("/postform/", handleForm);
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
// Check if header is present and correct
|
||||
bool is_authenticated() {
|
||||
Serial.println("Enter is_authenticated");
|
||||
if (server.hasHeader("Cookie")) {
|
||||
Serial.print("Found cookie: ");
|
||||
String cookie = server.header("Cookie");
|
||||
Serial.println(cookie);
|
||||
if (cookie.indexOf("PICOSESSIONID=1") != -1) {
|
||||
Serial.println("Authentication Successful");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Serial.println("Authentication Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// login page, also called for disconnect
|
||||
void handleLogin() {
|
||||
String msg;
|
||||
if (server.hasHeader("Cookie")) {
|
||||
Serial.print("Found cookie: ");
|
||||
String cookie = server.header("Cookie");
|
||||
Serial.println(cookie);
|
||||
}
|
||||
if (server.hasArg("DISCONNECT")) {
|
||||
Serial.println("Disconnection");
|
||||
server.sendHeader("Location", "/login");
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.sendHeader("Set-Cookie", "PICOSESSIONID=0");
|
||||
server.send(301);
|
||||
return;
|
||||
}
|
||||
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
|
||||
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
|
||||
server.sendHeader("Location", "/");
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.sendHeader("Set-Cookie", "PICOSESSIONID=1");
|
||||
server.send(301);
|
||||
Serial.println("Log in Successful");
|
||||
return;
|
||||
}
|
||||
msg = "Wrong username/password! try again.";
|
||||
Serial.println("Log in Failed");
|
||||
}
|
||||
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>";
|
||||
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>";
|
||||
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>";
|
||||
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>";
|
||||
content += "You also can go <a href='/inline'>here</a></body></html>";
|
||||
server.send(200, "text/html", content);
|
||||
}
|
||||
|
||||
// root page can be accessed only if authentication is ok
|
||||
void handleRoot() {
|
||||
Serial.println("Enter handleRoot");
|
||||
String header;
|
||||
if (!is_authenticated()) {
|
||||
server.sendHeader("Location", "/login");
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.send(301);
|
||||
return;
|
||||
}
|
||||
String content = "<html><body><H2>hello, you successfully connected to Pico W!</H2><br>";
|
||||
if (server.hasHeader("User-Agent")) {
|
||||
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
|
||||
}
|
||||
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
|
||||
server.send(200, "text/html", content);
|
||||
}
|
||||
|
||||
// no need authentication
|
||||
void handleNotFound() {
|
||||
String message = "File Not Found\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 += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
|
||||
server.on("/", handleRoot);
|
||||
server.on("/login", handleLogin);
|
||||
server.on("/inline", []() {
|
||||
server.send(200, "text/plain", "this works without need of authentication");
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
// ask server to track these headers
|
||||
server.collectHeaders("User-Agent", "Cookie");
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -F "image=@firmware.bin" picow-webupdate.local/update
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "picow-webupdate";
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
|
||||
MDNS.begin(host);
|
||||
server.on("/", HTTP_GET, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/html", serverIndex);
|
||||
});
|
||||
server.on(
|
||||
"/update", HTTP_POST, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
|
||||
rp2040.restart();
|
||||
},
|
||||
[]() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
WiFiUDP::stopAll();
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
FSInfo64 i;
|
||||
LittleFS.begin();
|
||||
LittleFS.info64(i);
|
||||
uint32_t maxSketchSpace = i.totalBytes - i.usedBytes;
|
||||
if (!Update.begin(maxSketchSpace)) { // start with max available size
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (Update.end(true)) { // true to set the size to the current progress
|
||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
});
|
||||
server.begin();
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
Serial.printf("Ready! Open http://%s.local in your browser\n", host);
|
||||
} else {
|
||||
Serial.println("WiFi Failed");
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
WebServer KEYWORD1
|
||||
WebServerSecure KEYWORD1
|
||||
HTTPServer KEYWORD1
|
||||
HTTPMethod KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
handleClient KEYWORD2
|
||||
on KEYWORD2
|
||||
addHandler KEYWORD2
|
||||
uri KEYWORD2
|
||||
method KEYWORD2
|
||||
client KEYWORD2
|
||||
send KEYWORD2
|
||||
arg KEYWORD2
|
||||
argName KEYWORD2
|
||||
args KEYWORD2
|
||||
hasArg KEYWORD2
|
||||
onNotFound KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
HTTP_GET LITERAL1
|
||||
HTTP_POST LITERAL1
|
||||
HTTP_ANY LITERAL1
|
||||
CONTENT_LENGTH_UNKNOWN LITERAL1
|
||||
@@ -0,0 +1,10 @@
|
||||
name=WebServer
|
||||
version=2.0.0
|
||||
author=Ivan Grokhotkov
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Simple web server library
|
||||
paragraph=The library supports HTTP(S) GET and POST requests, provides argument parsing, handles one client at a time.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
HTTPServer.cpp - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <libb64/cencode.h>
|
||||
#include "WiFiServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "HTTPServer.h"
|
||||
#include "FS.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
#include <MD5Builder.h>
|
||||
|
||||
#ifndef log_e
|
||||
#define log_e(...)
|
||||
#define log_w(...)
|
||||
#define log_v(...)
|
||||
#endif
|
||||
|
||||
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
||||
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
|
||||
static const char WWW_Authenticate[] = "WWW-Authenticate";
|
||||
static const char Content_Length[] = "Content-Length";
|
||||
|
||||
HTTPServer::HTTPServer()
|
||||
: _corsEnabled(false)
|
||||
, _currentMethod(HTTP_ANY)
|
||||
, _currentVersion(0)
|
||||
, _currentStatus(HC_NONE)
|
||||
, _statusChange(0)
|
||||
, _nullDelay(true)
|
||||
, _currentHandler(nullptr)
|
||||
, _firstHandler(nullptr)
|
||||
, _lastHandler(nullptr)
|
||||
, _currentArgCount(0)
|
||||
, _currentArgs(nullptr)
|
||||
, _postArgsLen(0)
|
||||
, _postArgs(nullptr)
|
||||
, _headerKeysCount(0)
|
||||
, _currentHeaders(nullptr)
|
||||
, _contentLength(0)
|
||||
, _clientContentLength(0)
|
||||
, _chunked(false) {
|
||||
log_v("HTTPServer::HTTPServer()");
|
||||
}
|
||||
|
||||
HTTPServer::~HTTPServer() {
|
||||
if (_currentHeaders) {
|
||||
delete[]_currentHeaders;
|
||||
}
|
||||
RequestHandler* handler = _firstHandler;
|
||||
while (handler) {
|
||||
RequestHandler* next = handler->next();
|
||||
delete handler;
|
||||
handler = next;
|
||||
}
|
||||
}
|
||||
|
||||
String HTTPServer::_extractParam(String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1) {
|
||||
return "";
|
||||
}
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
static String md5str(String &in) {
|
||||
char out[33] = {0};
|
||||
MD5Builder _ctx;
|
||||
uint8_t i;
|
||||
uint8_t * _buf = (uint8_t*)malloc(16);
|
||||
if (_buf == NULL) {
|
||||
return String(out);
|
||||
}
|
||||
memset(_buf, 0x00, 16);
|
||||
_ctx.begin();
|
||||
_ctx.add((const uint8_t *)in.c_str(), in.length());
|
||||
_ctx.calculate();
|
||||
_ctx.getBytes((uint8_t *)_buf);
|
||||
for (i = 0; i < 16; i++) {
|
||||
sprintf(out + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
out[32] = 0;
|
||||
free(_buf);
|
||||
return String(out);
|
||||
}
|
||||
|
||||
bool HTTPServer::authenticate(const char * username, const char * password) {
|
||||
if (hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||
if (authReq.startsWith(F("Basic"))) {
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username) + strlen(password) + 1;
|
||||
char *toencode = new char[toencodeLen + 1];
|
||||
if (toencode == NULL) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1];
|
||||
if (encoded == NULL) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq == encoded) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
} else if (authReq.startsWith(F("Digest"))) {
|
||||
authReq = authReq.substring(7);
|
||||
log_v("%s", authReq.c_str());
|
||||
String _username = _extractParam(authReq, F("username=\""), '\"');
|
||||
if (!_username.length() || _username != String(username)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""), '\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""), '\"');
|
||||
String _response = _extractParam(authReq, F("response=\""), '\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
||||
|
||||
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc, _cnonce;
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
||||
}
|
||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||
String _H2 = "";
|
||||
if (_currentMethod == HTTP_GET) {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_POST) {
|
||||
_H2 = md5str(String(F("POST:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_PUT) {
|
||||
_H2 = md5str(String(F("PUT:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_DELETE) {
|
||||
_H2 = md5str(String(F("DELETE:")) + _uri);
|
||||
} else {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}
|
||||
log_v("Hash of GET:uri=%s", _H2.c_str());
|
||||
String _responsecheck = "";
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
||||
if (_response == _responsecheck) {
|
||||
authReq = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
authReq = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String HTTPServer::_getRandomHexString() {
|
||||
char buffer[33]; // buffer to hold 32 Hex Digit + /0
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf(buffer + (i * 8), "%08lx", rp2040.hwrand32());
|
||||
}
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
void HTTPServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) {
|
||||
if (realm == NULL) {
|
||||
_srealm = String(F("Login Required"));
|
||||
} else {
|
||||
_srealm = String(realm);
|
||||
}
|
||||
if (mode == BASIC_AUTH) {
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
|
||||
} else {
|
||||
_snonce = _getRandomHexString();
|
||||
_sopaque = _getRandomHexString();
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) + _srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
|
||||
}
|
||||
using namespace mime;
|
||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||
}
|
||||
|
||||
void HTTPServer::on(const Uri &uri, HTTPServer::THandlerFunction handler) {
|
||||
on(uri, HTTP_ANY, handler);
|
||||
}
|
||||
|
||||
void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn) {
|
||||
on(uri, method, fn, _fileUploadHandler);
|
||||
}
|
||||
|
||||
void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn, HTTPServer::THandlerFunction ufn) {
|
||||
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
|
||||
}
|
||||
|
||||
void HTTPServer::addHandler(RequestHandler* handler) {
|
||||
_addRequestHandler(handler);
|
||||
}
|
||||
|
||||
void HTTPServer::_addRequestHandler(RequestHandler* handler) {
|
||||
if (!_lastHandler) {
|
||||
_firstHandler = handler;
|
||||
_lastHandler = handler;
|
||||
} else {
|
||||
_lastHandler->next(handler);
|
||||
_lastHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
|
||||
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
|
||||
}
|
||||
|
||||
void HTTPServer::httpHandleClient() {
|
||||
bool keepCurrentClient = false;
|
||||
bool callYield = false;
|
||||
|
||||
if (_currentClient->connected()) {
|
||||
switch (_currentStatus) {
|
||||
case HC_NONE:
|
||||
// No-op to avoid C++ compiler warning
|
||||
break;
|
||||
case HC_WAIT_READ:
|
||||
// Wait for data from client to become available
|
||||
if (_currentClient->available()) {
|
||||
if (_parseRequest(_currentClient)) {
|
||||
// because HTTP_MAX_SEND_WAIT is expressed in milliseconds,
|
||||
// it must be divided by 1000
|
||||
_currentClient->setTimeout(HTTP_MAX_SEND_WAIT / 1000);
|
||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||
_handleRequest();
|
||||
|
||||
// Fix for issue with Chrome based browsers: https://github.com/espressif/arduino-esp32/issues/3652
|
||||
// if (_currentClient->connected()) {
|
||||
// _currentStatus = HC_WAIT_CLOSE;
|
||||
// _statusChange = millis();
|
||||
// keepCurrentClient = true;
|
||||
// }
|
||||
}
|
||||
} else { // !_currentClient->available()
|
||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
}
|
||||
callYield = true;
|
||||
}
|
||||
break;
|
||||
case HC_WAIT_CLOSE:
|
||||
// Wait for client to close the connection
|
||||
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
callYield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepCurrentClient) {
|
||||
_currentClient = nullptr;
|
||||
_currentStatus = HC_NONE;
|
||||
_currentUpload.reset();
|
||||
}
|
||||
|
||||
if (callYield) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::httpClose() {
|
||||
_currentStatus = HC_NONE;
|
||||
if (!_headerKeysCount) {
|
||||
collectHeaders(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::sendHeader(const String& name, const String& value, bool first) {
|
||||
String headerLine = name;
|
||||
headerLine += F(": ");
|
||||
headerLine += value;
|
||||
headerLine += "\r\n";
|
||||
|
||||
if (first) {
|
||||
_responseHeaders = headerLine + _responseHeaders;
|
||||
} else {
|
||||
_responseHeaders += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::setContentLength(const size_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
|
||||
void HTTPServer::enableDelay(boolean value) {
|
||||
_nullDelay = value;
|
||||
}
|
||||
|
||||
void HTTPServer::enableCORS(boolean value) {
|
||||
_corsEnabled = value;
|
||||
}
|
||||
|
||||
void HTTPServer::enableCrossOrigin(boolean value) {
|
||||
enableCORS(value);
|
||||
}
|
||||
|
||||
void HTTPServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
|
||||
response = String(F("HTTP/1.")) + String(_currentVersion) + ' ';
|
||||
response += String(code);
|
||||
response += ' ';
|
||||
response += _responseCodeToString(code);
|
||||
response += "\r\n";
|
||||
|
||||
using namespace mime;
|
||||
if (!content_type) {
|
||||
content_type = mimeTable[html].mimeType;
|
||||
}
|
||||
|
||||
sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true);
|
||||
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(contentLength));
|
||||
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
|
||||
sendHeader(String(FPSTR(Content_Length)), String(_contentLength));
|
||||
} else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion) { //HTTP/1.1 or above client
|
||||
//let's do chunked
|
||||
_chunked = true;
|
||||
sendHeader(String(F("Accept-Ranges")), String(F("none")));
|
||||
sendHeader(String(F("Transfer-Encoding")), String(F("chunked")));
|
||||
}
|
||||
if (_corsEnabled) {
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
|
||||
}
|
||||
sendHeader(String(F("Connection")), String(F("close")));
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = "";
|
||||
}
|
||||
|
||||
void HTTPServer::send(int code, const char* content_type, const String& content) {
|
||||
String header;
|
||||
// Can we assume the following?
|
||||
//if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
|
||||
// _contentLength = CONTENT_LENGTH_UNKNOWN;
|
||||
if (content.length() == 0) {
|
||||
log_w("content length is zero");
|
||||
}
|
||||
_prepareHeader(header, code, content_type, content.length());
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
if (content.length()) {
|
||||
sendContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::send(int code, char* content_type, const String& content) {
|
||||
send(code, (const char*)content_type, content);
|
||||
}
|
||||
|
||||
void HTTPServer::send(int code, const String& content_type, const String& content) {
|
||||
send(code, (const char*)content_type.c_str(), content);
|
||||
}
|
||||
|
||||
void HTTPServer::send(int code, const char* content_type, const char* content) {
|
||||
const String passStr = (String)content;
|
||||
if (strlen(content) != passStr.length()) {
|
||||
log_e("String cast failed. Use send_P for long arrays");
|
||||
}
|
||||
send(code, content_type, passStr);
|
||||
}
|
||||
|
||||
void HTTPServer::send(int code, const char* content_type, const char* content, size_t contentLength) {
|
||||
String header;
|
||||
_prepareHeader(header, code, content_type, contentLength);
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
if (contentLength) {
|
||||
sendContent(content, contentLength);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::send_P(int code, PGM_P content_type, PGM_P content) {
|
||||
size_t contentLength = 0;
|
||||
|
||||
if (content != NULL) {
|
||||
contentLength = strlen_P(content);
|
||||
}
|
||||
|
||||
String header;
|
||||
char type[64];
|
||||
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
|
||||
_prepareHeader(header, code, (const char*)type, contentLength);
|
||||
_currentClientWrite(header.c_str(), header.length());
|
||||
sendContent_P(content);
|
||||
}
|
||||
|
||||
void HTTPServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
|
||||
String header;
|
||||
char type[64];
|
||||
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
|
||||
_prepareHeader(header, code, (const char*)type, contentLength);
|
||||
sendContent(header);
|
||||
sendContent_P(content, contentLength);
|
||||
}
|
||||
|
||||
void HTTPServer::sendContent(const String& content) {
|
||||
sendContent(content.c_str(), content.length());
|
||||
}
|
||||
|
||||
void HTTPServer::sendContent(const char* content, size_t contentLength) {
|
||||
const char * footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char * chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", contentLength, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite(content, contentLength);
|
||||
if (_chunked) {
|
||||
_currentClient->write(footer, 2);
|
||||
if (contentLength == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::sendContent_P(PGM_P content) {
|
||||
sendContent_P(content, strlen_P(content));
|
||||
}
|
||||
|
||||
void HTTPServer::sendContent_P(PGM_P content, size_t size) {
|
||||
const char * footer = "\r\n";
|
||||
if (_chunked) {
|
||||
char * chunkSize = (char *)malloc(11);
|
||||
if (chunkSize) {
|
||||
sprintf(chunkSize, "%x%s", size, footer);
|
||||
_currentClientWrite(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClientWrite_P(content, size);
|
||||
if (_chunked) {
|
||||
_currentClient->write(footer, 2);
|
||||
if (size == 0) {
|
||||
_chunked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HTTPServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType, const int code) {
|
||||
using namespace mime;
|
||||
setContentLength(fileSize);
|
||||
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) &&
|
||||
contentType != String(FPSTR(mimeTable[gz].mimeType)) &&
|
||||
contentType != String(FPSTR(mimeTable[none].mimeType))) {
|
||||
sendHeader(F("Content-Encoding"), F("gzip"));
|
||||
}
|
||||
send(code, contentType, "");
|
||||
}
|
||||
|
||||
String HTTPServer::pathArg(unsigned int i) {
|
||||
if (_currentHandler != nullptr) {
|
||||
return _currentHandler->pathArg(i);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String HTTPServer::arg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name) {
|
||||
return _postArgs[j].value;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name) {
|
||||
return _currentArgs[i].value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String HTTPServer::arg(int i) {
|
||||
if (i < _currentArgCount) {
|
||||
return _currentArgs[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String HTTPServer::argName(int i) {
|
||||
if (i < _currentArgCount) {
|
||||
return _currentArgs[i].key;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int HTTPServer::args() {
|
||||
return _currentArgCount;
|
||||
}
|
||||
|
||||
bool HTTPServer::hasArg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String HTTPServer::header(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(name)) {
|
||||
return _currentHeaders[i].value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void HTTPServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
|
||||
_headerKeysCount = headerKeysCount + 1;
|
||||
if (_currentHeaders) {
|
||||
delete[]_currentHeaders;
|
||||
}
|
||||
_currentHeaders = new RequestArgument[_headerKeysCount];
|
||||
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER);
|
||||
for (int i = 1; i < _headerKeysCount; i++) {
|
||||
_currentHeaders[i].key = headerKeys[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
String HTTPServer::header(int i) {
|
||||
if (i < _headerKeysCount) {
|
||||
return _currentHeaders[i].value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String HTTPServer::headerName(int i) {
|
||||
if (i < _headerKeysCount) {
|
||||
return _currentHeaders[i].key;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int HTTPServer::headers() {
|
||||
return _headerKeysCount;
|
||||
}
|
||||
|
||||
bool HTTPServer::hasHeader(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String HTTPServer::hostHeader() {
|
||||
return _hostHeader;
|
||||
}
|
||||
|
||||
void HTTPServer::onFileUpload(THandlerFunction fn) {
|
||||
_fileUploadHandler = fn;
|
||||
}
|
||||
|
||||
void HTTPServer::onNotFound(THandlerFunction fn) {
|
||||
_notFoundHandler = fn;
|
||||
}
|
||||
|
||||
void HTTPServer::_handleRequest() {
|
||||
bool handled = false;
|
||||
if (!_currentHandler) {
|
||||
log_e("request handler not found");
|
||||
} else {
|
||||
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
||||
if (!handled) {
|
||||
log_e("request handler failed to handle request");
|
||||
}
|
||||
}
|
||||
if (!handled && _notFoundHandler) {
|
||||
_notFoundHandler();
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
using namespace mime;
|
||||
send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri);
|
||||
handled = true;
|
||||
}
|
||||
if (handled) {
|
||||
_finalizeResponse();
|
||||
}
|
||||
_currentUri = "";
|
||||
}
|
||||
|
||||
|
||||
void HTTPServer::_finalizeResponse() {
|
||||
if (_chunked) {
|
||||
sendContent("");
|
||||
}
|
||||
}
|
||||
|
||||
String HTTPServer::_responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100: return F("Continue");
|
||||
case 101: return F("Switching Protocols");
|
||||
case 200: return F("OK");
|
||||
case 201: return F("Created");
|
||||
case 202: return F("Accepted");
|
||||
case 203: return F("Non-Authoritative Information");
|
||||
case 204: return F("No Content");
|
||||
case 205: return F("Reset Content");
|
||||
case 206: return F("Partial Content");
|
||||
case 300: return F("Multiple Choices");
|
||||
case 301: return F("Moved Permanently");
|
||||
case 302: return F("Found");
|
||||
case 303: return F("See Other");
|
||||
case 304: return F("Not Modified");
|
||||
case 305: return F("Use Proxy");
|
||||
case 307: return F("Temporary Redirect");
|
||||
case 400: return F("Bad Request");
|
||||
case 401: return F("Unauthorized");
|
||||
case 402: return F("Payment Required");
|
||||
case 403: return F("Forbidden");
|
||||
case 404: return F("Not Found");
|
||||
case 405: return F("Method Not Allowed");
|
||||
case 406: return F("Not Acceptable");
|
||||
case 407: return F("Proxy Authentication Required");
|
||||
case 408: return F("Request Time-out");
|
||||
case 409: return F("Conflict");
|
||||
case 410: return F("Gone");
|
||||
case 411: return F("Length Required");
|
||||
case 412: return F("Precondition Failed");
|
||||
case 413: return F("Request Entity Too Large");
|
||||
case 414: return F("Request-URI Too Large");
|
||||
case 415: return F("Unsupported Media Type");
|
||||
case 416: return F("Requested range not satisfiable");
|
||||
case 417: return F("Expectation Failed");
|
||||
case 500: return F("Internal Server Error");
|
||||
case 501: return F("Not Implemented");
|
||||
case 502: return F("Bad Gateway");
|
||||
case 503: return F("Service Unavailable");
|
||||
case 504: return F("Gateway Time-out");
|
||||
case 505: return F("HTTP Version not supported");
|
||||
default: return F("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
HTTPServer.h - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <WiFi.h>
|
||||
#include "HTTP_Method.h"
|
||||
#include "Uri.h"
|
||||
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||
UPLOAD_FILE_ABORTED
|
||||
};
|
||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1436
|
||||
|
||||
#ifndef HTTP_UPLOAD_BUFLEN
|
||||
#define HTTP_UPLOAD_BUFLEN 1436
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
|
||||
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||
|
||||
class HTTPServer;
|
||||
|
||||
typedef struct {
|
||||
HTTPUploadStatus status;
|
||||
String filename;
|
||||
String name;
|
||||
String type;
|
||||
size_t totalSize; // file size
|
||||
size_t currentSize; // size of data currently in buf
|
||||
uint8_t buf[HTTP_UPLOAD_BUFLEN];
|
||||
} HTTPUpload;
|
||||
|
||||
#include "detail/RequestHandler.h"
|
||||
|
||||
namespace fs {
|
||||
class FS;
|
||||
}
|
||||
|
||||
class HTTPServer {
|
||||
public:
|
||||
HTTPServer();
|
||||
virtual ~HTTPServer();
|
||||
|
||||
virtual void httpClose();
|
||||
virtual void httpHandleClient();
|
||||
|
||||
bool authenticate(const char * username, const char * password);
|
||||
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String(""));
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const Uri &uri, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads
|
||||
void addHandler(RequestHandler* handler);
|
||||
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL);
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction ufn); //handle file uploads
|
||||
|
||||
String uri() {
|
||||
return _currentUri;
|
||||
}
|
||||
HTTPMethod method() {
|
||||
return _currentMethod;
|
||||
}
|
||||
HTTPUpload& upload() {
|
||||
return *_currentUpload;
|
||||
}
|
||||
|
||||
String pathArg(unsigned int i); // get request path argument by number
|
||||
String arg(String name); // get request argument value by name
|
||||
String arg(int i); // get request argument value by number
|
||||
String argName(int i); // get request argument name by number
|
||||
int args(); // get arguments count
|
||||
bool hasArg(String name); // check if argument exists
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
|
||||
template<typename... Args>
|
||||
void collectHeaders(const Args&... args) { // set the request headers to collect (variadic template version)
|
||||
if (_currentHeaders) {
|
||||
delete[] _currentHeaders;
|
||||
}
|
||||
_headerKeysCount = sizeof...(args) + 1;
|
||||
_currentHeaders = new RequestArgument[_headerKeysCount] {
|
||||
{ .key = "Authorization", .value = "" },
|
||||
{ .key = String(args), .value = "" } ...
|
||||
};
|
||||
}
|
||||
|
||||
String header(String name); // get request header value by name
|
||||
String header(int i); // get request header value by number
|
||||
String headerName(int i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(String name); // check if header exists
|
||||
|
||||
int clientContentLength() {
|
||||
return _clientContentLength; // return "content-length" of incoming HTTP header from "_currentClient"
|
||||
}
|
||||
|
||||
String hostHeader(); // get request host header if available or empty String if not
|
||||
|
||||
// send response to the client
|
||||
// code - HTTP response code, can be 200 or 404
|
||||
// content_type - HTTP content type, like "text/plain" or "image/png"
|
||||
// content - actual content body
|
||||
void send(int code, const char* content_type = NULL, const String& content = String(""));
|
||||
void send(int code, char* content_type, const String& content);
|
||||
void send(int code, const String& content_type, const String& content);
|
||||
void send(int code, const char* content_type, const char* content);
|
||||
void send(int code, const char* content_type, const char* content, size_t contentLength);
|
||||
|
||||
void send_P(int code, PGM_P content_type, PGM_P content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
template<typename TypeName>
|
||||
void send(int code, PGM_P content_type, TypeName content, size_t contentLength) {
|
||||
send(code, content_type, (const char *)content, contentLength);
|
||||
}
|
||||
|
||||
void enableDelay(boolean value);
|
||||
void enableCORS(boolean value = true);
|
||||
void enableCrossOrigin(boolean value = true);
|
||||
|
||||
void setContentLength(const size_t contentLength);
|
||||
void sendHeader(const String& name, const String& value, bool first = false);
|
||||
void sendContent(const String& content);
|
||||
void sendContent(const char* content, size_t contentLength);
|
||||
void sendContent_P(PGM_P content);
|
||||
void sendContent_P(PGM_P content, size_t size);
|
||||
|
||||
bool chunkedResponseModeStart_P(int code, PGM_P content_type) {
|
||||
if (_currentVersion == 0)
|
||||
// no chunk mode in HTTP/1.0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
send(code, content_type, "");
|
||||
return true;
|
||||
}
|
||||
bool chunkedResponseModeStart(int code, const char* content_type) {
|
||||
return chunkedResponseModeStart_P(code, content_type);
|
||||
}
|
||||
bool chunkedResponseModeStart(int code, const String& content_type) {
|
||||
return chunkedResponseModeStart_P(code, content_type.c_str());
|
||||
}
|
||||
void chunkedResponseFinalize() {
|
||||
sendContent("");
|
||||
}
|
||||
|
||||
static String urlDecode(const String& text);
|
||||
|
||||
template<typename T>
|
||||
size_t streamFile(T &file, const String& contentType, const int code = 200) {
|
||||
_streamFileCore(file.size(), file.name(), contentType, code);
|
||||
return _currentClient->write(file);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual size_t _currentClientWrite(const char* b, size_t l) {
|
||||
return _currentClient->write(b, l);
|
||||
}
|
||||
virtual size_t _currentClientWrite_P(PGM_P b, size_t l) {
|
||||
return _currentClient->write(b, l);
|
||||
}
|
||||
void _addRequestHandler(RequestHandler* handler);
|
||||
void _handleRequest();
|
||||
void _finalizeResponse();
|
||||
bool _parseRequest(WiFiClient* client);
|
||||
void _parseArguments(String data);
|
||||
static String _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient* client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
int _uploadReadByte(WiFiClient* client);
|
||||
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
|
||||
bool _collectHeader(const char* headerName, const char* headerValue);
|
||||
|
||||
void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType, const int code = 200);
|
||||
|
||||
String _getRandomHexString();
|
||||
// for extracting Auth parameters
|
||||
String _extractParam(String& authReq, const String& param, const char delimit = '"');
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
boolean _corsEnabled;
|
||||
|
||||
WiFiClient *_currentClient;
|
||||
HTTPMethod _currentMethod;
|
||||
String _currentUri;
|
||||
uint8_t _currentVersion;
|
||||
HTTPClientStatus _currentStatus;
|
||||
unsigned long _statusChange;
|
||||
boolean _nullDelay;
|
||||
|
||||
RequestHandler* _currentHandler;
|
||||
RequestHandler* _firstHandler;
|
||||
RequestHandler* _lastHandler;
|
||||
THandlerFunction _notFoundHandler;
|
||||
THandlerFunction _fileUploadHandler;
|
||||
|
||||
int _currentArgCount;
|
||||
RequestArgument* _currentArgs;
|
||||
int _postArgsLen;
|
||||
RequestArgument* _postArgs;
|
||||
|
||||
std::unique_ptr<HTTPUpload> _currentUpload;
|
||||
|
||||
int _headerKeysCount;
|
||||
RequestArgument* _currentHeaders;
|
||||
size_t _contentLength;
|
||||
int _clientContentLength; // "Content-Length" from header of incoming POST or GET request
|
||||
String _responseHeaders;
|
||||
|
||||
String _hostHeader;
|
||||
bool _chunked;
|
||||
|
||||
String _snonce; // Store noance and opaque for future comparison
|
||||
String _sopaque;
|
||||
String _srealm; // Store the Auth realm between Calls
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "http_parser.h"
|
||||
|
||||
typedef enum http_method HTTPMethod;
|
||||
#define HTTP_ANY (HTTPMethod)(255)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user