mirror of
https://github.com/0xFEEDC0DE64/arduino-esp32.git
synced 2025-06-25 18:01:33 +02:00
Compare commits
161 Commits
Author | SHA1 | Date | |
---|---|---|---|
fa61b3bffe | |||
452c27a74a | |||
2fd39b1aff | |||
ff18a211e4 | |||
a6e3b29004 | |||
812d131663 | |||
00e69a28bc | |||
229d9b7366 | |||
6dd8be3262 | |||
28ea39cf05 | |||
f49c854ff3 | |||
879388e170 | |||
1085e9a8f0 | |||
bed9c96f41 | |||
5af139bb74 | |||
4f9a90fa0e | |||
310e78e6fd | |||
c827bb4177 | |||
1628f533a1 | |||
3e66aeff84 | |||
66d33f792c | |||
0de0d3f79a | |||
512d0d088f | |||
77810471ce | |||
be081ac026 | |||
4d3f6caa0d | |||
2db811f7f3 | |||
39836f12df | |||
25fd2d0f3b | |||
278fa0d87a | |||
b37f4069e4 | |||
e602145c2b | |||
6f6ee98188 | |||
1289f4be4b | |||
70f000da71 | |||
884e417a49 | |||
bb7dea1566 | |||
bff9f0b6b1 | |||
72803703fd | |||
0596a2ac86 | |||
fe1fdd2790 | |||
af7e489f01 | |||
5cfff190e9 | |||
7a332864ab | |||
8aa6e2e143 | |||
b5f317010c | |||
f644d9d157 | |||
a15b7e9088 | |||
ce340faf94 | |||
b69b04cb2b | |||
0cbba8a71d | |||
9e1f8cc457 | |||
cfe7e01158 | |||
fcd734a13c | |||
aa030e044c | |||
95d417cd93 | |||
df4eeb3005 | |||
a360064768 | |||
bfde8daf75 | |||
e583a0e879 | |||
7e9afe8c5e | |||
dcb007a485 | |||
bec6f87235 | |||
4ae64c541e | |||
46257c03b3 | |||
0640964879 | |||
e609c78f20 | |||
04963009ee | |||
c3ec91f968 | |||
a30005949a | |||
acefd4bf2e | |||
c700e5694f | |||
0d564d7b1d | |||
44ca2ee976 | |||
af79e18ecb | |||
273196d7e6 | |||
5d2460c74a | |||
259ff80d60 | |||
3902aa4019 | |||
f9d1b24c01 | |||
c7fa251d78 | |||
7b811f9b3a | |||
1fdc660641 | |||
a53c9de09d | |||
b58a3509b8 | |||
01d22c8807 | |||
b70737d276 | |||
233d31bed2 | |||
65c861ad4c | |||
f6a71da378 | |||
b8c9819e7f | |||
1bc1e8c602 | |||
2132d9f809 | |||
14ff311479 | |||
a3ed511884 | |||
9e2888392e | |||
a43682596f | |||
825b6ea79e | |||
deaf339bde | |||
f12df4c719 | |||
85032b226c | |||
e5ea089a7f | |||
96822d783f | |||
4e96bffe0e | |||
ea61563c69 | |||
5be3078b76 | |||
7206b2f397 | |||
3028ec42c7 | |||
145904fb9c | |||
cb8d72fdc7 | |||
1e4bf14a3e | |||
f9f995b283 | |||
c8fe873965 | |||
18d260ec6e | |||
3a8ac27a86 | |||
a6a9a518a7 | |||
02ee799f35 | |||
ce61074802 | |||
339618f8ef | |||
6e4e4c96ee | |||
a0f0bd930c | |||
80c110ece7 | |||
65511b23d3 | |||
14d6f6e7e6 | |||
9db207afbe | |||
a989853d4a | |||
172802b18e | |||
fff1783046 | |||
cb53ec4891 | |||
7d3a67ada0 | |||
d057e544e0 | |||
b05430cfd9 | |||
e346f20aa9 | |||
bdc45e39ef | |||
a7ddf39521 | |||
abb8ea99d5 | |||
30b3eebabc | |||
3222e6490a | |||
2fba81223e | |||
e51f7a5b87 | |||
7d2560cbbf | |||
17065dfd3a | |||
2f5b3c0c56 | |||
1fe3ee87b6 | |||
328523f5e3 | |||
7761ebd9f2 | |||
f9a382ab9f | |||
d854dc1bf6 | |||
f1f8d7e306 | |||
da798c7db0 | |||
8d7fb58672 | |||
2fda054bea | |||
e157ec06a7 | |||
05d72f963d | |||
b14f82b65f | |||
c830511f01 | |||
cbd4dc53a6 | |||
44f5a4dbc8 | |||
e63aa40650 | |||
28a410dd50 | |||
ddfeae90d0 |
@ -7,6 +7,9 @@ python:
|
||||
os:
|
||||
- linux
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "l/4Dt+KQ/mACtGAHDUsPr66fUte840PZoQ4xpPikqWZI0uARu4l+Ym7+sHinnT6fBqrj8AJeBYGz4nFa8NK4LutZn9mSD40w+sxl0wSV4oHV8rzKe3Cd8+sMG3+o33yWoikMNjSvqa73Q0rm+SgrlInNdZbuAyixL+a2alaWSnGPm4F2xwUGj+S33TOy5P/Xp77CYtCV5S8vzyk/eEdNhoF0GYePJVdfuzCOUjXMyT5OWxORkzzQ7Hnn/Ka/RDfV8Si4HgujLQBrK5q6iPnNBFqBSqilYBepSMn4opnOBpIm0SCgePz7XQEFC83buA7GUcnCnfg38bf+dCwHaODf1d1PmqVRYt2QmfinexXtM4afAtL0iBUDtvrfnXHzwW9w82VeZhpbJSVh9DUQvB0IlsZeCz9J9PUBAi3N+SMX+9l+BomYwRUlPuKY+Ef2JKk9q6mxtUkky5R0daAlVxEhpVdQks1rT+T+NMoDMemxQ3SKEiqAHh6EgHecruszffmZ71uLX9MpERpew0qN+UFiafws+jkTjx+3yF9yut0Hf9sMbeAYzzkGzRqJTUEBJ6B29Cql8M0yRXCNN/8wuuTHhG8esstozga4ZQoIVrq7mEAgup376PTcNfr1+imbbWVQ7lJdYIuDe6OS5V3OX6np11vgK/DbhfyzvQv9Z1zAGnM="
|
||||
|
@ -3,6 +3,7 @@ set(CORE_SRCS
|
||||
cores/esp32/cbuf.cpp
|
||||
cores/esp32/esp32-hal-adc.c
|
||||
cores/esp32/esp32-hal-bt.c
|
||||
cores/esp32/esp32-hal-cpu.c
|
||||
cores/esp32/esp32-hal-dac.c
|
||||
cores/esp32/esp32-hal-gpio.c
|
||||
cores/esp32/esp32-hal-i2c.c
|
||||
@ -16,12 +17,15 @@ set(CORE_SRCS
|
||||
cores/esp32/esp32-hal-timer.c
|
||||
cores/esp32/esp32-hal-touch.c
|
||||
cores/esp32/esp32-hal-uart.c
|
||||
cores/esp32/esp32-hal-rmt.c
|
||||
cores/esp32/Esp.cpp
|
||||
cores/esp32/FunctionalInterrupt.cpp
|
||||
cores/esp32/HardwareSerial.cpp
|
||||
cores/esp32/IPAddress.cpp
|
||||
cores/esp32/IPv6Address.cpp
|
||||
cores/esp32/libb64/cdecode.c
|
||||
cores/esp32/libb64/cencode.c
|
||||
cores/esp32/main.cpp
|
||||
cores/esp32/MD5Builder.cpp
|
||||
cores/esp32/Print.cpp
|
||||
cores/esp32/stdlib_noniso.c
|
||||
@ -40,9 +44,11 @@ set(LIBRARY_SRCS
|
||||
libraries/DNSServer/src/DNSServer.cpp
|
||||
libraries/EEPROM/EEPROM.cpp
|
||||
libraries/ESPmDNS/src/ESPmDNS.cpp
|
||||
libraries/FFat/src/FFat.cpp
|
||||
libraries/FS/src/FS.cpp
|
||||
libraries/FS/src/vfs_api.cpp
|
||||
libraries/HTTPClient/src/HTTPClient.cpp
|
||||
libraries/HTTPUpdate/src/HTTPUpdate.cpp
|
||||
libraries/NetBIOS/src/NetBIOS.cpp
|
||||
libraries/Preferences/src/Preferences.cpp
|
||||
libraries/SD_MMC/src/SD_MMC.cpp
|
||||
@ -145,6 +151,8 @@ set(BLE_SRCS
|
||||
libraries/BLE/src/BLEDescriptor.cpp
|
||||
libraries/BLE/src/BLEDescriptorMap.cpp
|
||||
libraries/BLE/src/BLEDevice.cpp
|
||||
libraries/BLE/src/BLEEddystoneTLM.cpp
|
||||
libraries/BLE/src/BLEEddystoneURL.cpp
|
||||
libraries/BLE/src/BLEExceptions.cpp
|
||||
libraries/BLE/src/BLEHIDDevice.cpp
|
||||
libraries/BLE/src/BLERemoteCharacteristic.cpp
|
||||
@ -175,8 +183,10 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
||||
libraries/DNSServer/src
|
||||
libraries/ESP32/src
|
||||
libraries/ESPmDNS/src
|
||||
libraries/FFat/src
|
||||
libraries/FS/src
|
||||
libraries/HTTPClient/src
|
||||
libraries/HTTPUpdate/src
|
||||
libraries/NetBIOS/src
|
||||
libraries/Preferences/src
|
||||
libraries/SD_MMC/src
|
||||
@ -195,6 +205,6 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS cores/esp32/libb64)
|
||||
|
||||
set(COMPONENT_REQUIRES spi_flash mbedtls mdns ethernet)
|
||||
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl)
|
||||
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl bt)
|
||||
|
||||
register_component()
|
||||
|
@ -106,8 +106,147 @@ config AUTOCONNECT_WIFI
|
||||
bool "Autoconnect WiFi on boot"
|
||||
default "n"
|
||||
depends on AUTOSTART_ARDUINO
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
help
|
||||
If enabled, WiFi will connect to the last used SSID (if station was enabled),
|
||||
else connection will be started only after calling WiFi.begin(ssid, password)
|
||||
|
||||
config ARDUINO_SELECTIVE_COMPILATION
|
||||
bool "Include only specific Arduino libraries"
|
||||
default n
|
||||
|
||||
config ARDUINO_SELECTIVE_ArduinoOTA
|
||||
bool "Enable ArduinoOTA"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
select ARDUINO_SELECTIVE_ESPmDNS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_AsyncUDP
|
||||
bool "Enable AsyncUDP"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_AzureIoT
|
||||
bool "Enable AzureIoT"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_HTTPClient
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_BLE
|
||||
bool "Enable BLE"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_BluetoothSerial
|
||||
bool "Enable BluetoothSerial"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_DNSServer
|
||||
bool "Enable DNSServer"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_EEPROM
|
||||
bool "Enable EEPROM"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_ESP32
|
||||
bool "Enable ESP32"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_ESPmDNS
|
||||
bool "Enable ESPmDNS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_FS
|
||||
bool "Enable FS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_HTTPClient
|
||||
bool "Enable HTTPClient"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
select ARDUINO_SELECTIVE_WiFiClientSecure
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_NetBIOS
|
||||
bool "Enable NetBIOS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Preferences
|
||||
bool "Enable Preferences"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SD
|
||||
bool "Enable SD"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SD_MMC
|
||||
bool "Enable SD_MMC"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SimpleBLE
|
||||
bool "Enable SimpleBLE"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SPI
|
||||
bool "Enable SPI"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SPIFFS
|
||||
bool "Enable SPIFFS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Ticker
|
||||
bool "Enable Ticker"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Update
|
||||
bool "Enable Update"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_WebServer
|
||||
bool "Enable WebServer"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
select ARDUINO_SELECTIVE_FS
|
||||
|
||||
config ARDUINO_SELECTIVE_WiFi
|
||||
bool "Enable WiFi"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_WiFiClientSecure
|
||||
bool "Enable WiFiClientSecure"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Wire
|
||||
bool "Enable Wire"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
|
||||
endmenu
|
||||
|
@ -1,17 +1,7 @@
|
||||
BOOT_APP_BIN_OFFSET := 0xe000
|
||||
BOOT_APP_BIN_ROOT := $(call dequote,$(COMPONENT_PATH))
|
||||
BOOT_APP_BIN_PATH := $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/boot_app0.bin)))
|
||||
|
||||
ifndef CONFIG_PARTITION_TABLE_CUSTOM
|
||||
PARTITION_TABLE_CSV_PATH = $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/$(CONFIG_ARDUHAL_PARTITION_SCHEME).csv)))
|
||||
endif
|
||||
|
||||
BOOT_APP_BIN_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
|
||||
ESPTOOL_ALL_FLASH_ARGS += $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
|
||||
|
||||
CPPFLAGS += -DARDUINO=10800 -DESP32=1 -DARDUINO_ARCH_ESP32=1
|
||||
|
||||
boot-app0:
|
||||
@echo "Rebooting to APP0"
|
||||
$(BOOT_APP_BIN_FLASH_CMD)
|
||||
|
||||
CPPFLAGS += -DARDUINO=10800 -DESP32=1 -DARDUINO_ARCH_ESP32=1 -DBOARD_HAS_PSRAM
|
||||
|
13
README.md
13
README.md
@ -1,6 +1,4 @@
|
||||
# Arduino core for ESP32 WiFi chip
|
||||
|
||||
[](https://travis-ci.org/espressif/arduino-esp32)
|
||||
# Arduino core for ESP32 WiFi chip [](https://travis-ci.org/espressif/arduino-esp32)
|
||||
|
||||
### Need help or have a question? Join the chat at [](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
@ -12,14 +10,19 @@
|
||||
- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap)
|
||||
|
||||
## Development Status
|
||||
[Latest stable release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
[Latest development release  ](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use:
|
||||
- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM
|
||||
- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation
|
||||
- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output
|
||||
|
||||
## Installation Instructions
|
||||
|
||||
- Using Arduino IDE
|
||||
- Using Arduino IDE Boards Manager (preferred)
|
||||
+ [Instructions for Boards Manager](docs/arduino-ide/boards_manager.md)
|
||||
- Using Arduino IDE with the development repository
|
||||
+ [Instructions for Windows](docs/arduino-ide/windows.md)
|
||||
+ [Instructions for Mac](docs/arduino-ide/mac.md)
|
||||
+ [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md)
|
||||
|
1169
boards.txt
1169
boards.txt
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,6 @@
|
||||
ARDUINO_CORE_LIBS := $(patsubst $(COMPONENT_PATH)/%,%,$(sort $(dir $(wildcard $(COMPONENT_PATH)/libraries/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/*/*/*/))))
|
||||
ARDUINO_LIBRARIES_LIST := $(patsubst $(COMPONENT_PATH)/libraries/%,%,$(wildcard $(COMPONENT_PATH)/libraries/*))
|
||||
ARDUINO_SINGLE_LIBRARY_FILES = $(patsubst $(COMPONENT_PATH)/%,%,$(sort $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/*/*/))))
|
||||
ARDUINO_CORE_LIBS := $(foreach MODULE,$(ARDUINO_LIBRARIES_LIST),$(if $(CONFIG_ARDUINO_SELECTIVE_COMPILATION),$(if $(CONFIG_ARDUINO_SELECTIVE_$(MODULE)),$(ARDUINO_SINGLE_LIBRARY_FILES)),$(ARDUINO_SINGLE_LIBRARY_FILES)))
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 variants/esp32 $(ARDUINO_CORE_LIBS)
|
||||
COMPONENT_PRIV_INCLUDEDIRS := cores/esp32/libb64
|
||||
|
@ -75,7 +75,6 @@
|
||||
|
||||
#define abs(x) ((x)>0?(x):-(x))
|
||||
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
#define radians(deg) ((deg)*DEG_TO_RAD)
|
||||
#define degrees(rad) ((rad)*RAD_TO_DEG)
|
||||
#define sq(x) ((x)*(x))
|
||||
@ -146,6 +145,9 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "WCharacter.h"
|
||||
#include "WString.h"
|
||||
#include "Stream.h"
|
||||
@ -158,6 +160,12 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||
#include "HardwareSerial.h"
|
||||
#include "Esp.h"
|
||||
|
||||
using std::isinf;
|
||||
using std::isnan;
|
||||
using std::max;
|
||||
using std::min;
|
||||
using ::round;
|
||||
|
||||
uint16_t makeWord(uint16_t w);
|
||||
uint16_t makeWord(byte h, byte l);
|
||||
|
||||
@ -176,12 +184,6 @@ extern "C" void configTzTime(const char* tz,
|
||||
long random(long);
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef _GLIBCXX_VECTOR
|
||||
// arduino is not compatible with std::vector
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#define _min(a,b) ((a)<(b)?(a):(b))
|
||||
#define _max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
|
@ -25,20 +25,12 @@
|
||||
#include <memory>
|
||||
#include <soc/soc.h>
|
||||
#include <soc/efuse_reg.h>
|
||||
|
||||
/* Main header of binary image */
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
uint8_t segment_count;
|
||||
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
|
||||
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
|
||||
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
|
||||
uint32_t entry_addr;
|
||||
uint8_t encrypt_flag; /* encrypt flag */
|
||||
uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */
|
||||
} esp_image_header_t;
|
||||
|
||||
#define ESP_IMAGE_HEADER_MAGIC 0xE9
|
||||
#include <esp_partition.h>
|
||||
extern "C" {
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
}
|
||||
#include <MD5Builder.h>
|
||||
|
||||
/**
|
||||
* User-defined Literals
|
||||
@ -112,9 +104,118 @@ void EspClass::restart(void)
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
uint32_t EspClass::getHeapSize(void)
|
||||
{
|
||||
multi_heap_info_t info;
|
||||
heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
|
||||
return info.total_free_bytes + info.total_allocated_bytes;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeHeap(void)
|
||||
{
|
||||
return esp_get_free_heap_size();
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreeHeap(void)
|
||||
{
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocHeap(void)
|
||||
{
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getPsramSize(void)
|
||||
{
|
||||
multi_heap_info_t info;
|
||||
heap_caps_get_info(&info, MALLOC_CAP_SPIRAM);
|
||||
return info.total_free_bytes + info.total_allocated_bytes;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreePsram(void)
|
||||
{
|
||||
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreePsram(void)
|
||||
{
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocPsram(void)
|
||||
{
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
static uint32_t sketchSize(sketchSize_t response) {
|
||||
esp_image_metadata_t data;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) return 0;
|
||||
const esp_partition_pos_t running_pos = {
|
||||
.offset = running->address,
|
||||
.size = running->size,
|
||||
};
|
||||
data.start_addr = running_pos.offset;
|
||||
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
|
||||
if (response) {
|
||||
return running_pos.size - data.image_len;
|
||||
} else {
|
||||
return data.image_len;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::getSketchSize () {
|
||||
return sketchSize(SKETCH_SIZE_TOTAL);
|
||||
}
|
||||
|
||||
String EspClass::getSketchMD5()
|
||||
{
|
||||
static String result;
|
||||
if (result.length()) {
|
||||
return result;
|
||||
}
|
||||
uint32_t lengthLeft = getSketchSize();
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) {
|
||||
log_e("Partition could not be found");
|
||||
|
||||
return String();
|
||||
}
|
||||
const size_t bufSize = SPI_FLASH_SEC_SIZE;
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
|
||||
uint32_t offset = 0;
|
||||
if(!buf.get()) {
|
||||
log_e("Not enough memory to allocate buffer");
|
||||
|
||||
return String();
|
||||
}
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
while( lengthLeft > 0) {
|
||||
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
|
||||
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
|
||||
log_e("Could not read buffer from flash");
|
||||
|
||||
return String();
|
||||
}
|
||||
md5.add(buf.get(), readBytes);
|
||||
lengthLeft -= readBytes;
|
||||
offset += readBytes;
|
||||
}
|
||||
md5.calculate();
|
||||
result = md5.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeSketchSpace () {
|
||||
const esp_partition_t* _partition = esp_ota_get_next_update_partition(NULL);
|
||||
if(!_partition){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _partition->size;
|
||||
}
|
||||
|
||||
uint8_t EspClass::getChipRevision(void)
|
||||
|
@ -50,13 +50,30 @@ typedef enum {
|
||||
FM_UNKNOWN = 0xff
|
||||
} FlashMode_t;
|
||||
|
||||
typedef enum {
|
||||
SKETCH_SIZE_TOTAL = 0,
|
||||
SKETCH_SIZE_FREE = 1
|
||||
} sketchSize_t;
|
||||
|
||||
class EspClass
|
||||
{
|
||||
public:
|
||||
EspClass() {}
|
||||
~EspClass() {}
|
||||
void restart();
|
||||
uint32_t getFreeHeap();
|
||||
|
||||
//Internal RAM
|
||||
uint32_t getHeapSize(); //total heap size
|
||||
uint32_t getFreeHeap(); //available heap
|
||||
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
|
||||
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once
|
||||
|
||||
//SPI RAM
|
||||
uint32_t getPsramSize();
|
||||
uint32_t getFreePsram();
|
||||
uint32_t getMinFreePsram();
|
||||
uint32_t getMaxAllocPsram();
|
||||
|
||||
uint8_t getChipRevision();
|
||||
uint8_t getCpuFreqMHz(){ return CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; }
|
||||
uint32_t getCycleCount();
|
||||
@ -72,6 +89,10 @@ public:
|
||||
uint32_t magicFlashChipSpeed(uint8_t byte);
|
||||
FlashMode_t magicFlashChipMode(uint8_t byte);
|
||||
|
||||
uint32_t getSketchSize();
|
||||
String getSketchMD5();
|
||||
uint32_t getFreeSketchSpace();
|
||||
|
||||
bool flashEraseSector(uint32_t sector);
|
||||
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
|
||||
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
|
||||
|
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* FunctionalInterrupt.cpp
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#include "FunctionalInterrupt.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional);
|
||||
}
|
||||
|
||||
void IRAM_ATTR interruptFunctional(void* arg)
|
||||
{
|
||||
InterruptArgStructure* localArg = (InterruptArgStructure*)arg;
|
||||
if (localArg->interruptFunction)
|
||||
{
|
||||
localArg->interruptFunction();
|
||||
}
|
||||
}
|
||||
|
||||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)
|
||||
{
|
||||
// use the local interrupt routine which takes the ArgStructure as argument
|
||||
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void cleanupFunctional(void* arg)
|
||||
{
|
||||
delete (InterruptArgStructure*)arg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
20
cores/esp32/FunctionalInterrupt.h
Normal file
20
cores/esp32/FunctionalInterrupt.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* FunctionalInterrupt.h
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct InterruptArgStructure {
|
||||
std::function<void(void)> interruptFunction;
|
||||
};
|
||||
|
||||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
|
||||
|
||||
|
||||
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */
|
@ -3,8 +3,25 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "HardwareSerial.h"
|
||||
|
||||
#ifndef RX1
|
||||
#define RX1 9
|
||||
#endif
|
||||
|
||||
#ifndef TX1
|
||||
#define TX1 10
|
||||
#endif
|
||||
|
||||
#ifndef RX2
|
||||
#define RX2 16
|
||||
#endif
|
||||
|
||||
#ifndef TX2
|
||||
#define TX2 17
|
||||
#endif
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
HardwareSerial Serial(0);
|
||||
HardwareSerial Serial1(1);
|
||||
@ -13,7 +30,7 @@ HardwareSerial Serial2(2);
|
||||
|
||||
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
|
||||
|
||||
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert)
|
||||
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
|
||||
{
|
||||
if(0 > _uart_nr || _uart_nr > 2) {
|
||||
log_e("Serial number is invalid, please use 0, 1 or 2");
|
||||
@ -27,14 +44,38 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
|
||||
txPin = 1;
|
||||
}
|
||||
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = 9;
|
||||
txPin = 10;
|
||||
rxPin = RX1;
|
||||
txPin = TX1;
|
||||
}
|
||||
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = 16;
|
||||
txPin = 17;
|
||||
rxPin = RX2;
|
||||
txPin = TX2;
|
||||
}
|
||||
_uart = uartBegin(_uart_nr, baud, config, rxPin, txPin, 256, invert);
|
||||
|
||||
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert);
|
||||
|
||||
if(!baud) {
|
||||
time_t startMillis = millis();
|
||||
unsigned long detectedBaudRate = 0;
|
||||
while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
|
||||
yield();
|
||||
}
|
||||
|
||||
end();
|
||||
|
||||
if(detectedBaudRate) {
|
||||
delay(100); // Give some time...
|
||||
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, 256, invert);
|
||||
} else {
|
||||
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible");
|
||||
_uart = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareSerial::updateBaudRate(unsigned long baud)
|
||||
{
|
||||
uartSetBaudRate(_uart, baud);
|
||||
}
|
||||
|
||||
void HardwareSerial::end()
|
||||
@ -46,6 +87,10 @@ void HardwareSerial::end()
|
||||
_uart = 0;
|
||||
}
|
||||
|
||||
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
|
||||
return uartResizeRxBuffer(_uart, new_size);
|
||||
}
|
||||
|
||||
void HardwareSerial::setDebugOutput(bool en)
|
||||
{
|
||||
if(_uart == 0) {
|
||||
|
@ -22,6 +22,24 @@
|
||||
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support)
|
||||
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
|
||||
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
|
||||
Modified 13 October 2018 by Jeroen Döll (add baudrate detection)
|
||||
Baudrate detection example usage (detection on Serial1):
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
Serial.println();
|
||||
|
||||
Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms
|
||||
|
||||
unsigned long detectedBaudRate = Serial1.baudRate();
|
||||
if(detectedBaudRate) {
|
||||
Serial.printf("Detected baudrate is %lu\n", detectedBaudRate);
|
||||
} else {
|
||||
Serial.println("No baudrate detected, Serial1 will not work!");
|
||||
}
|
||||
}
|
||||
|
||||
Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201
|
||||
*/
|
||||
|
||||
#ifndef HardwareSerial_h
|
||||
@ -37,8 +55,9 @@ class HardwareSerial: public Stream
|
||||
public:
|
||||
HardwareSerial(int uart_nr);
|
||||
|
||||
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false);
|
||||
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL);
|
||||
void end();
|
||||
void updateBaudRate(unsigned long baud);
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
int peek(void);
|
||||
@ -70,6 +89,7 @@ public:
|
||||
uint32_t baudRate();
|
||||
operator bool() const;
|
||||
|
||||
size_t setRxBufferSize(size_t);
|
||||
void setDebugOutput(bool);
|
||||
|
||||
protected:
|
||||
|
@ -63,7 +63,7 @@ size_t Print::printf(const char *format, ...)
|
||||
len = vsnprintf(temp, len+1, format, arg);
|
||||
write((uint8_t*)temp, len);
|
||||
va_end(arg);
|
||||
if(len > 64){
|
||||
if(len >= sizeof(loc_buf)){
|
||||
delete[] temp;
|
||||
}
|
||||
return len;
|
||||
|
@ -882,6 +882,12 @@ float String::toFloat(void) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
double String::toDouble(void) const {
|
||||
if (buffer) {
|
||||
return atof(buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char String::equalsConstantTime(const String &s2) const {
|
||||
// To avoid possible time-based attacks present function
|
||||
|
@ -260,6 +260,7 @@ public:
|
||||
// parsing/conversion
|
||||
long toInt(void) const;
|
||||
float toFloat(void) const;
|
||||
double toDouble(void) const;
|
||||
|
||||
protected:
|
||||
char *buffer; // the actual char array
|
||||
|
1
cores/esp32/apps/sntp/sntp.h
Normal file
1
cores/esp32/apps/sntp/sntp.h
Normal file
@ -0,0 +1 @@
|
||||
#include "lwip/apps/sntp.h"
|
@ -37,8 +37,7 @@ extern "C" {
|
||||
*/
|
||||
String base64::encode(uint8_t * data, size_t length)
|
||||
{
|
||||
// base64 needs more size then the source data
|
||||
size_t size = ((length * 1.6f) + 1);
|
||||
size_t size = base64_encode_expected_len(length) + 1;
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
|
211
cores/esp32/esp32-hal-cpu.c
Normal file
211
cores/esp32/esp32-hal-cpu.c
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/xtensa_timer.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "soc/apb_ctrl_reg.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-cpu.h"
|
||||
|
||||
typedef struct apb_change_cb_s {
|
||||
struct apb_change_cb_s * next;
|
||||
void * arg;
|
||||
apb_change_cb_t cb;
|
||||
} apb_change_t;
|
||||
|
||||
const uint32_t MHZ = 1000000;
|
||||
|
||||
static apb_change_t * apb_change_callbacks = NULL;
|
||||
static xSemaphoreHandle apb_change_lock = NULL;
|
||||
|
||||
static void initApbChangeCallback(){
|
||||
static volatile bool initialized = false;
|
||||
if(!initialized){
|
||||
initialized = true;
|
||||
apb_change_lock = xSemaphoreCreateMutex();
|
||||
if(!apb_change_lock){
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
initApbChangeCallback();
|
||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||
apb_change_t * r = apb_change_callbacks;
|
||||
while(r != NULL){
|
||||
r->cb(r->arg, ev_type, old_apb, new_apb);
|
||||
r=r->next;
|
||||
}
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
}
|
||||
|
||||
bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
|
||||
initApbChangeCallback();
|
||||
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
|
||||
if(!c){
|
||||
log_e("Callback Object Malloc Failed");
|
||||
return false;
|
||||
}
|
||||
c->next = NULL;
|
||||
c->arg = arg;
|
||||
c->cb = cb;
|
||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||
if(apb_change_callbacks == NULL){
|
||||
apb_change_callbacks = c;
|
||||
} else {
|
||||
apb_change_t * r = apb_change_callbacks;
|
||||
if(r->cb != cb || r->arg != arg){
|
||||
while(r->next){
|
||||
r = r->next;
|
||||
if(r->cb == cb && r->arg == arg){
|
||||
free(c);
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
}
|
||||
r->next = c;
|
||||
}
|
||||
}
|
||||
unlock_and_exit:
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){
|
||||
initApbChangeCallback();
|
||||
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
|
||||
apb_change_t * r = apb_change_callbacks;
|
||||
if(r == NULL){
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return false;
|
||||
}
|
||||
if(r->cb == cb && r->arg == arg){
|
||||
apb_change_callbacks = r->next;
|
||||
free(r);
|
||||
} else {
|
||||
while(r->next && (r->next->cb != cb || r->next->arg != arg)){
|
||||
r = r->next;
|
||||
}
|
||||
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return false;
|
||||
}
|
||||
apb_change_t * c = r->next;
|
||||
r->next = c->next;
|
||||
free(c);
|
||||
}
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){
|
||||
if(conf->freq_mhz >= 80){
|
||||
return 80 * MHZ;
|
||||
}
|
||||
return (conf->source_freq_mhz * MHZ) / conf->div;
|
||||
}
|
||||
|
||||
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
|
||||
|
||||
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){
|
||||
rtc_cpu_freq_config_t conf, cconf;
|
||||
uint32_t capb, apb;
|
||||
//Get XTAL Frequency and calculate min CPU MHz
|
||||
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get();
|
||||
uint32_t min_cpu_mhz = 10;
|
||||
if(xtal > RTC_XTAL_FREQ_AUTO){
|
||||
if(xtal < RTC_XTAL_FREQ_40M) {
|
||||
min_cpu_mhz = xtal / 2; //13Mhz for 26Mhz XTAL
|
||||
if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){
|
||||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
|
||||
return false;
|
||||
}
|
||||
} else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){
|
||||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){
|
||||
if(xtal >= RTC_XTAL_FREQ_40M){
|
||||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
|
||||
} else {
|
||||
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//Get current CPU clock configuration
|
||||
rtc_clk_cpu_freq_get_config(&cconf);
|
||||
//return if frequency has not changed
|
||||
if(cconf.freq_mhz == cpu_freq_mhz){
|
||||
return true;
|
||||
}
|
||||
//Get configuration for the new CPU frequency
|
||||
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
|
||||
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
|
||||
return false;
|
||||
}
|
||||
//Current APB
|
||||
capb = calculateApb(&cconf);
|
||||
//New APB
|
||||
apb = calculateApb(&conf);
|
||||
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb);
|
||||
//Call peripheral functions before the APB change
|
||||
if(apb_change_callbacks){
|
||||
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
|
||||
}
|
||||
//Make the frequency change
|
||||
rtc_clk_cpu_freq_set_config_fast(&conf);
|
||||
if(capb != apb){
|
||||
//Update REF_TICK (uncomment if REF_TICK is different than 1MHz)
|
||||
//if(conf.freq_mhz < 80){
|
||||
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1;
|
||||
//}
|
||||
//Update APB Freq REG
|
||||
rtc_clk_apb_freq_update(apb);
|
||||
//Update esp_timer divisor
|
||||
esp_timer_impl_update_apb_freq(apb / MHZ);
|
||||
}
|
||||
//Update FreeRTOS Tick Divisor
|
||||
uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb);
|
||||
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
|
||||
//Call peripheral functions after the APB change
|
||||
if(apb_change_callbacks){
|
||||
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t getCpuFrequencyMhz(){
|
||||
rtc_cpu_freq_config_t conf;
|
||||
rtc_clk_cpu_freq_get_config(&conf);
|
||||
return conf.freq_mhz;
|
||||
}
|
||||
|
||||
uint32_t getXtalFrequencyMhz(){
|
||||
return rtc_clk_xtal_freq_get();
|
||||
}
|
||||
|
||||
uint32_t getApbFrequency(){
|
||||
rtc_cpu_freq_config_t conf;
|
||||
rtc_clk_cpu_freq_get_config(&conf);
|
||||
return calculateApb(&conf);
|
||||
}
|
48
cores/esp32/esp32-hal-cpu.h
Normal file
48
cores/esp32/esp32-hal-cpu.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _ESP32_HAL_CPU_H_
|
||||
#define _ESP32_HAL_CPU_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;
|
||||
|
||||
typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
|
||||
|
||||
bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
|
||||
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);
|
||||
|
||||
//function takes the following frequencies as valid values:
|
||||
// 240, 160, 80 <<< For all XTAL types
|
||||
// 40, 20, 10 <<< For 40MHz XTAL
|
||||
// 26, 13 <<< For 26MHz XTAL
|
||||
// 24, 12 <<< For 24MHz XTAL
|
||||
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);
|
||||
|
||||
uint32_t getCpuFrequencyMhz(); // In MHz
|
||||
uint32_t getXtalFrequencyMhz(); // In MHz
|
||||
uint32_t getApbFrequency(); // In Hz
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP32_HAL_CPU_H_ */
|
@ -74,6 +74,7 @@ typedef void (*voidFuncPtrArg)(void*);
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void* arg;
|
||||
bool functional;
|
||||
} InterruptHandle_t;
|
||||
static InterruptHandle_t __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,};
|
||||
|
||||
@ -238,16 +239,26 @@ static void IRAM_ATTR __onPinInterrupt()
|
||||
}
|
||||
}
|
||||
|
||||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
|
||||
extern void cleanupFunctional(void* arg);
|
||||
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional)
|
||||
{
|
||||
static bool interrupt_initialized = false;
|
||||
|
||||
|
||||
if(!interrupt_initialized) {
|
||||
interrupt_initialized = true;
|
||||
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);
|
||||
}
|
||||
|
||||
// if new attach without detach remove old info
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
|
||||
{
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc;
|
||||
__pinInterruptHandlers[pin].arg = arg;
|
||||
__pinInterruptHandlers[pin].functional = functional;
|
||||
|
||||
esp_intr_disable(gpio_intr_handle);
|
||||
if(esp_intr_get_cpu(gpio_intr_handle)) { //APP_CPU
|
||||
GPIO.pin[pin].int_ena = 1;
|
||||
@ -258,15 +269,26 @@ extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * ar
|
||||
esp_intr_enable(gpio_intr_handle);
|
||||
}
|
||||
|
||||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
|
||||
{
|
||||
__attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) {
|
||||
__attachInterruptArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type);
|
||||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __detachInterrupt(uint8_t pin)
|
||||
{
|
||||
esp_intr_disable(gpio_intr_handle);
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
|
||||
{
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = NULL;
|
||||
__pinInterruptHandlers[pin].arg = NULL;
|
||||
__pinInterruptHandlers[pin].functional = false;
|
||||
|
||||
GPIO.pin[pin].int_ena = 0;
|
||||
GPIO.pin[pin].int_type = 0;
|
||||
esp_intr_enable(gpio_intr_handle);
|
||||
@ -277,6 +299,6 @@ extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pi
|
||||
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
|
||||
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")));
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtr handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
|
||||
|
||||
|
@ -79,7 +79,7 @@ void digitalWrite(uint8_t pin, uint8_t val);
|
||||
int digitalRead(uint8_t pin);
|
||||
|
||||
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void), void * arg, int mode);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode);
|
||||
void detachInterrupt(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -48,6 +48,7 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b
|
||||
i2c_err_t i2cFlush(i2c_t *i2c);
|
||||
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
|
||||
uint32_t i2cGetFrequency(i2c_t * i2c);
|
||||
uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral
|
||||
|
||||
//Functions below should be used only if well understood
|
||||
//Might be deprecated and removed in future
|
||||
@ -62,8 +63,17 @@ i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr,
|
||||
i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
|
||||
|
||||
//stickbreaker debug support
|
||||
void i2cDumpInts(uint8_t num);
|
||||
void i2cDumpI2c(i2c_t *i2c);
|
||||
uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits);
|
||||
// Debug actions have 3 currently defined locus
|
||||
// 0xXX------ : at entry of ProcQueue
|
||||
// 0x--XX---- : at exit of ProcQueue
|
||||
// 0x------XX : at entry of Flush
|
||||
//
|
||||
// bit 0 causes DumpI2c to execute
|
||||
// bit 1 causes DumpInts to execute
|
||||
// bit 2 causes DumpCmdqueue to execute
|
||||
// bit 3 causes DumpStatus to execute
|
||||
// bit 4 causes DumpFifo to execute
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -53,6 +53,33 @@ xSemaphoreHandle _ledc_sys_lock;
|
||||
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
|
||||
#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)]
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
|
||||
uint32_t iarg = (uint32_t)arg;
|
||||
uint8_t chan = iarg;
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
if(LEDC_TIMER(group, timer).conf.tick_sel){
|
||||
LEDC_MUTEX_LOCK();
|
||||
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
|
||||
uint32_t div_num = (new_apb * old_div) / old_apb;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
|
||||
new_apb = REF_CLK_FREQ / 1000000;
|
||||
div_num = (new_apb * old_div) / old_apb;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
||||
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
|
||||
}
|
||||
LEDC_TIMER(group, timer).conf.tick_sel = 0;
|
||||
} else if(div_num < 256) {
|
||||
div_num = 256;//highest clock possible
|
||||
}
|
||||
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num));
|
||||
static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk)
|
||||
{
|
||||
@ -78,13 +105,15 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
|
||||
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
|
||||
LEDC_TIMER(group, timer).conf.rst = 0;
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
uint32_t iarg = chan;
|
||||
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||
}
|
||||
|
||||
//max div_num 0x3FFFF (262143)
|
||||
//max bit_num 0x1F (31)
|
||||
static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num)
|
||||
{
|
||||
uint64_t clk_freq = APB_CLK_FREQ;
|
||||
uint64_t clk_freq = getApbFrequency();
|
||||
clk_freq <<= 8;//div_num is 8 bit decimal
|
||||
uint32_t div_num = (clk_freq >> bit_num) / freq;
|
||||
bool apb_clk = true;
|
||||
@ -117,7 +146,7 @@ static double _ledcTimerRead(uint8_t chan)
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
uint64_t clk_freq = 1000000;
|
||||
if(apb_clk) {
|
||||
clk_freq *= 80;
|
||||
clk_freq = getApbFrequency();
|
||||
}
|
||||
clk_freq <<= 8;//div_num is 8 bit decimal
|
||||
return (clk_freq >> bit_num) / (double)div_num;
|
||||
|
@ -79,38 +79,50 @@ int log_printf(const char *fmt, ...);
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_v(format, ...)
|
||||
#define isr_log_v(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_d(format, ...)
|
||||
#define isr_log_d(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
||||
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_i(format, ...)
|
||||
#define isr_log_i(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
|
||||
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_w(format, ...)
|
||||
#define isr_log_w(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_e(format, ...)
|
||||
#define isr_log_e(format, ...)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
|
||||
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_n(format, ...)
|
||||
#define isr_log_n(format, ...)
|
||||
#endif
|
||||
|
||||
#include "esp_log.h"
|
||||
@ -121,12 +133,22 @@ int log_printf(const char *fmt, ...);
|
||||
#undef ESP_LOGI
|
||||
#undef ESP_LOGD
|
||||
#undef ESP_LOGV
|
||||
#undef ESP_EARLY_LOGE
|
||||
#undef ESP_EARLY_LOGW
|
||||
#undef ESP_EARLY_LOGI
|
||||
#undef ESP_EARLY_LOGD
|
||||
#undef ESP_EARLY_LOGV
|
||||
|
||||
#define ESP_LOGE(tag, ...) log_e(__VA_ARGS__)
|
||||
#define ESP_LOGW(tag, ...) log_w(__VA_ARGS__)
|
||||
#define ESP_LOGI(tag, ...) log_i(__VA_ARGS__)
|
||||
#define ESP_LOGD(tag, ...) log_d(__VA_ARGS__)
|
||||
#define ESP_LOGV(tag, ...) log_v(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGE(tag, ...) isr_log_e(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGW(tag, ...) isr_log_w(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGI(tag, ...) isr_log_i(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGD(tag, ...) isr_log_d(__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGV(tag, ...) isr_log_v(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -21,8 +21,15 @@
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
#include "esp_bt.h"
|
||||
#endif //CONFIG_BT_ENABLED
|
||||
#include <sys/time.h>
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/apb_ctrl_reg.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
//Undocumented!!! Get chip temperature in Farenheit
|
||||
@ -39,9 +46,64 @@ void yield()
|
||||
vPortYield();
|
||||
}
|
||||
|
||||
#if CONFIG_AUTOSTART_ARDUINO
|
||||
|
||||
extern TaskHandle_t loopTaskHandle;
|
||||
extern bool loopTaskWDTEnabled;
|
||||
|
||||
void enableLoopWDT(){
|
||||
if(loopTaskHandle != NULL){
|
||||
if(esp_task_wdt_add(loopTaskHandle) != ESP_OK){
|
||||
log_e("Failed to add loop task to WDT");
|
||||
} else {
|
||||
loopTaskWDTEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disableLoopWDT(){
|
||||
if(loopTaskHandle != NULL && loopTaskWDTEnabled){
|
||||
loopTaskWDTEnabled = false;
|
||||
if(esp_task_wdt_delete(loopTaskHandle) != ESP_OK){
|
||||
log_e("Failed to remove loop task from WDT");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void enableCore0WDT(){
|
||||
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
|
||||
if(idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK){
|
||||
log_e("Failed to add Core 0 IDLE task to WDT");
|
||||
}
|
||||
}
|
||||
|
||||
void disableCore0WDT(){
|
||||
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
|
||||
if(idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK){
|
||||
log_e("Failed to remove Core 0 IDLE task from WDT");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
void enableCore1WDT(){
|
||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
||||
if(idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK){
|
||||
log_e("Failed to add Core 1 IDLE task to WDT");
|
||||
}
|
||||
}
|
||||
|
||||
void disableCore1WDT(){
|
||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
||||
if(idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK){
|
||||
log_e("Failed to remove Core 1 IDLE task from WDT");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long IRAM_ATTR micros()
|
||||
{
|
||||
return (unsigned long) esp_timer_get_time();
|
||||
return (unsigned long) (esp_timer_get_time());
|
||||
}
|
||||
|
||||
unsigned long IRAM_ATTR millis()
|
||||
@ -84,6 +146,11 @@ bool btInUse(){ return false; }
|
||||
|
||||
void initArduino()
|
||||
{
|
||||
//init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz)
|
||||
//ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1;
|
||||
#ifdef F_CPU
|
||||
setCpuFrequencyMhz(F_CPU/1000000);
|
||||
#endif
|
||||
#if CONFIG_SPIRAM_SUPPORT
|
||||
psramInit();
|
||||
#endif
|
||||
|
821
cores/esp32/esp32-hal-rmt.c
Normal file
821
cores/esp32/esp32-hal-rmt.c
Normal file
@ -0,0 +1,821 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp8266-compat.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
#include "esp32-hal-rmt.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
|
||||
#include "soc/rmt_struct.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
/**
|
||||
* Internal macros
|
||||
*/
|
||||
#define MAX_CHANNELS 8
|
||||
#define MAX_DATA_PER_CHANNEL 64
|
||||
#define MAX_DATA_PER_ITTERATION 62
|
||||
#define _ABS(a) (a>0?a:-a)
|
||||
#define _LIMIT(a,b) (a>b?b:a)
|
||||
#define __INT_TX_END (1)
|
||||
#define __INT_RX_END (2)
|
||||
#define __INT_ERROR (4)
|
||||
#define __INT_THR_EVNT (1<<24)
|
||||
|
||||
#define _INT_TX_END(channel) (__INT_TX_END<<(channel*3))
|
||||
#define _INT_RX_END(channel) (__INT_RX_END<<(channel*3))
|
||||
#define _INT_ERROR(channel) (__INT_ERROR<<(channel*3))
|
||||
#define _INT_THR_EVNT(channel) ((__INT_THR_EVNT)<<(channel))
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
# define RMT_MUTEX_LOCK(channel)
|
||||
# define RMT_MUTEX_UNLOCK(channel)
|
||||
#else
|
||||
# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS)
|
||||
# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel])
|
||||
#endif /* CONFIG_DISABLE_HAL_LOCKS */
|
||||
|
||||
#define _RMT_INTERNAL_DEBUG
|
||||
#ifdef _RMT_INTERNAL_DEBUG
|
||||
# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1);
|
||||
# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0);
|
||||
#else
|
||||
# define DEBUG_INTERRUPT_START(pin)
|
||||
# define DEBUG_INTERRUPT_END(pin)
|
||||
#endif /* _RMT_INTERNAL_DEBUG */
|
||||
|
||||
/**
|
||||
* Typedefs for internal stuctures, enums
|
||||
*/
|
||||
typedef enum {
|
||||
E_NO_INTR = 0,
|
||||
E_TX_INTR = 1,
|
||||
E_TXTHR_INTR = 2,
|
||||
E_RX_INTR = 4,
|
||||
} intr_mode_t;
|
||||
|
||||
typedef enum {
|
||||
E_INACTIVE = 0,
|
||||
E_FIRST_HALF = 1,
|
||||
E_LAST_DATA = 2,
|
||||
E_END_TRANS = 4,
|
||||
E_SET_CONTI = 8,
|
||||
} transaction_state_t;
|
||||
|
||||
struct rmt_obj_s
|
||||
{
|
||||
bool allocated;
|
||||
EventGroupHandle_t events;
|
||||
int pin;
|
||||
int channel;
|
||||
bool tx_not_rx;
|
||||
int buffers;
|
||||
int data_size;
|
||||
uint32_t* data_ptr;
|
||||
intr_mode_t intr_mode;
|
||||
transaction_state_t tx_state;
|
||||
rmt_rx_data_cb_t cb;
|
||||
bool data_alloc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal variables for channel descriptors
|
||||
*/
|
||||
static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = {
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal variables for driver data
|
||||
*/
|
||||
static intr_handle_t intr_handle;
|
||||
|
||||
static bool periph_enabled = false;
|
||||
|
||||
static xSemaphoreHandle g_rmt_block_lock = NULL;
|
||||
|
||||
/**
|
||||
* Internal method (private) declarations
|
||||
*/
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx);
|
||||
|
||||
static bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
static void IRAM_ATTR _rmt_isr(void* arg);
|
||||
|
||||
static rmt_obj_t* _rmtAllocate(int pin, int from, int size);
|
||||
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx);
|
||||
|
||||
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel);
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch);
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch);
|
||||
|
||||
|
||||
/**
|
||||
* Public method definitions
|
||||
*/
|
||||
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high)
|
||||
{
|
||||
if (!rmt || low > 0xFFFF || high > 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
RMT.carrier_duty_ch[channel].low = low;
|
||||
RMT.carrier_duty_ch[channel].low = high;
|
||||
RMT.conf_ch[channel].conf0.carrier_en = carrier_en;
|
||||
RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level)
|
||||
{
|
||||
if (!rmt || filter_level > 0xFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_filter_thres = filter_level;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_en = filter_en;
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value)
|
||||
{
|
||||
if (!rmt || value > 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
RMT.conf_ch[channel].conf0.idle_thres = value;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool rmtDeinit(rmt_obj_t *rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if (rmt != &(g_rmt_objects[rmt->channel])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t from = rmt->channel;
|
||||
size_t to = rmt->buffers + rmt->channel;
|
||||
size_t i;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(g_rmt_objlocks[from] != NULL) {
|
||||
vSemaphoreDelete(g_rmt_objlocks[from]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_rmt_objects[from].data_alloc) {
|
||||
free(g_rmt_objects[from].data_ptr);
|
||||
}
|
||||
|
||||
for (i = from; i < to; i++) {
|
||||
g_rmt_objects[i].allocated = false;
|
||||
}
|
||||
|
||||
g_rmt_objects[from].channel = 0;
|
||||
g_rmt_objects[from].buffers = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int channel = rmt->channel;
|
||||
int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers;
|
||||
|
||||
if (size > allocated_size) {
|
||||
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
// setup interrupt handler if not yet installed for half and full tx
|
||||
if (!intr_handle) {
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
|
||||
}
|
||||
|
||||
rmt->data_size = size - MAX_DATA_PER_ITTERATION;
|
||||
rmt->data_ptr = ((uint32_t*)data) + MAX_DATA_PER_ITTERATION;
|
||||
rmt->intr_mode = E_TX_INTR | E_TXTHR_INTR;
|
||||
rmt->tx_state = E_SET_CONTI | E_FIRST_HALF;
|
||||
|
||||
// init the tx limit for interruption
|
||||
RMT.tx_lim_ch[channel].limit = half_tx_nr+2;
|
||||
// reset memory pointer
|
||||
RMT.conf_ch[channel].conf1.apb_mem_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.apb_mem_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 0;
|
||||
|
||||
// set the tx end mark
|
||||
RMTMEM.chan[channel].data32[MAX_DATA_PER_ITTERATION].val = 0;
|
||||
|
||||
// clear and enable both Tx completed and half tx event
|
||||
RMT.int_clr.val = _INT_TX_END(channel);
|
||||
RMT.int_clr.val = _INT_THR_EVNT(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_TX_END(channel);
|
||||
RMT.int_ena.val |= _INT_THR_EVNT(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
// start the transation
|
||||
return _rmtSendOnce(rmt, data, MAX_DATA_PER_ITTERATION);
|
||||
} else {
|
||||
// use one-go mode if data fits one buffer
|
||||
return _rmtSendOnce(rmt, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
|
||||
for (i=0; i<size; i++) {
|
||||
data[i] = *rmt_mem_ptr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtBeginReceive(rmt_obj_t* rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtReceiveCompleted(rmt_obj_t* rmt)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (RMT.int_raw.val&_INT_RX_END(channel)) {
|
||||
// RX end flag
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb)
|
||||
{
|
||||
if (!rmt && !cb) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
rmt->intr_mode = E_RX_INTR;
|
||||
rmt->tx_state = E_FIRST_HALF;
|
||||
rmt->cb = cb;
|
||||
// allocate internally two buffers which would alternate
|
||||
if (!rmt->data_alloc) {
|
||||
rmt->data_ptr = (uint32_t*)malloc(2*MAX_DATA_PER_CHANNEL*(rmt->buffers)*sizeof(uint32_t));
|
||||
rmt->data_size = MAX_DATA_PER_CHANNEL*rmt->buffers;
|
||||
rmt->data_alloc = true;
|
||||
}
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_RX_END(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
|
||||
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventFlag) {
|
||||
xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL);
|
||||
rmt->events = eventFlag;
|
||||
}
|
||||
|
||||
if (data && size>0) {
|
||||
rmt->data_ptr = (uint32_t*)data;
|
||||
rmt->data_size = size;
|
||||
}
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
rmt->intr_mode = E_RX_INTR;
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
|
||||
RMT.int_clr.val = _INT_RX_END(channel);
|
||||
RMT.int_clr.val = _INT_ERROR(channel);
|
||||
|
||||
RMT.int_ena.val |= _INT_RX_END(channel);
|
||||
RMT.int_ena.val |= _INT_ERROR(channel);
|
||||
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
|
||||
RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
// wait for data if requested so
|
||||
if (waitForData && eventFlag) {
|
||||
uint32_t flags = xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL,
|
||||
pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout);
|
||||
if (flags & RMT_FLAG_ERROR) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float rmtSetTick(rmt_obj_t* rmt, float tick)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
divider field span from 1 (smallest), 2, 3, ... , 0xFF, 0x00 (highest)
|
||||
* rmt tick from 1/80M -> 12.5ns (1x) div_cnt = 0x01
|
||||
3.2 us (256x) div_cnt = 0x00
|
||||
* rmt tick for 1 MHz -> 1us (1x) div_cnt = 0x01
|
||||
256us (256x) div_cnt = 0x00
|
||||
*/
|
||||
int apb_div = _LIMIT(tick/12.5, 256);
|
||||
int ref_div = _LIMIT(tick/1000, 256);
|
||||
|
||||
float apb_tick = 12.5 * apb_div;
|
||||
float ref_tick = 1000.0 * ref_div;
|
||||
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) {
|
||||
RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF;
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 1;
|
||||
return apb_tick;
|
||||
} else {
|
||||
RMT.conf_ch[channel].conf0.div_cnt = ref_div & 0xFF;
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 0;
|
||||
return ref_tick;
|
||||
}
|
||||
}
|
||||
|
||||
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize)
|
||||
{
|
||||
int buffers = memsize;
|
||||
rmt_obj_t* rmt;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
// create common block mutex for protecting allocs from multiple threads
|
||||
if (!g_rmt_block_lock) {
|
||||
g_rmt_block_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
// lock
|
||||
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
|
||||
|
||||
for (i=0; i<MAX_CHANNELS; i++) {
|
||||
for (j=0; j<buffers && i+j < MAX_CHANNELS; j++) {
|
||||
// if the space is ocupied break and continue on other channel
|
||||
if (g_rmt_objects[i+j].allocated) {
|
||||
i += j; // continue searching from latter channel
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == buffers) {
|
||||
// found a space in channel descriptors
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAX_CHANNELS || i+j >= MAX_CHANNELS || j != buffers) {
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
return NULL;
|
||||
}
|
||||
rmt = _rmtAllocate(pin, i, buffers);
|
||||
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
|
||||
size_t channel = i;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(g_rmt_objlocks[channel] == NULL) {
|
||||
g_rmt_objlocks[channel] = xSemaphoreCreateMutex();
|
||||
if(g_rmt_objlocks[channel] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
|
||||
rmt->pin = pin;
|
||||
rmt->tx_not_rx = tx_not_rx;
|
||||
rmt->buffers =buffers;
|
||||
rmt->channel = channel;
|
||||
_initPin(pin, channel, tx_not_rx);
|
||||
|
||||
// Initialize the registers in default mode:
|
||||
// - no carrier, filter
|
||||
// - timebase tick of 1us
|
||||
// - idle threshold set to 0x8000 (max pulse width + 1)
|
||||
RMT.conf_ch[channel].conf0.div_cnt = 1;
|
||||
RMT.conf_ch[channel].conf0.mem_size = buffers;
|
||||
RMT.conf_ch[channel].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[channel].conf0.carrier_out_lv = 0;
|
||||
RMT.conf_ch[channel].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[channel].conf0.idle_thres = 0x80;
|
||||
RMT.conf_ch[channel].conf1.rx_en = 0;
|
||||
RMT.conf_ch[channel].conf1.tx_conti_mode = 0;
|
||||
RMT.conf_ch[channel].conf1.ref_cnt_rst = 0;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_en = 0;
|
||||
RMT.conf_ch[channel].conf1.rx_filter_thres = 0;
|
||||
RMT.conf_ch[channel].conf1.idle_out_lv = 0; // signal level for idle
|
||||
RMT.conf_ch[channel].conf1.idle_out_en = 1; // enable idle
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 0; // base clock
|
||||
RMT.apb_conf.fifo_mask = 1;
|
||||
|
||||
if (tx_not_rx) {
|
||||
// RMT.conf_ch[channel].conf1.rx_en = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
} else {
|
||||
// RMT.conf_ch[channel].conf1.rx_en = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
|
||||
}
|
||||
|
||||
// install interrupt if at least one channel is active
|
||||
if (!intr_handle) {
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
|
||||
}
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return rmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private methods definitions
|
||||
*/
|
||||
bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||
{
|
||||
if (!rmt) {
|
||||
return false;
|
||||
}
|
||||
int channel = rmt->channel;
|
||||
RMT.apb_conf.fifo_mask = 1;
|
||||
if (data && size>0) {
|
||||
size_t i;
|
||||
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
|
||||
for (i = 0; i < size; i++) {
|
||||
*rmt_mem_ptr++ = data[i].val;
|
||||
}
|
||||
// tx end mark
|
||||
RMTMEM.chan[channel].data32[size].val = 0;
|
||||
}
|
||||
|
||||
RMT_MUTEX_LOCK(channel);
|
||||
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[channel].conf1.tx_start = 1;
|
||||
RMT_MUTEX_UNLOCK(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static rmt_obj_t* _rmtAllocate(int pin, int from, int size)
|
||||
{
|
||||
size_t i;
|
||||
// setup how many buffers shall we use
|
||||
g_rmt_objects[from].buffers = size;
|
||||
|
||||
for (i=0; i<size; i++) {
|
||||
// mark the block of channels as used
|
||||
g_rmt_objects[i+from].allocated = true;
|
||||
}
|
||||
return &(g_rmt_objects[from]);
|
||||
}
|
||||
|
||||
|
||||
static void _initPin(int pin, int channel, bool tx_not_rx)
|
||||
{
|
||||
if (!periph_enabled) {
|
||||
periph_enabled = true;
|
||||
periph_module_enable( PERIPH_RMT_MODULE );
|
||||
}
|
||||
if (tx_not_rx) {
|
||||
pinMode(pin, OUTPUT);
|
||||
pinMatrixOutAttach(pin, RMT_SIG_OUT0_IDX + channel, 0, 0);
|
||||
} else {
|
||||
pinMode(pin, INPUT);
|
||||
pinMatrixInAttach(pin, RMT_SIG_IN0_IDX + channel, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void IRAM_ATTR _rmt_isr(void* arg)
|
||||
{
|
||||
int intr_val = RMT.int_st.val;
|
||||
size_t ch;
|
||||
for (ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
|
||||
if (intr_val & _INT_RX_END(ch)) {
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_RX_END(ch);
|
||||
RMT.int_ena.val &= ~_INT_RX_END(ch);
|
||||
|
||||
if ((g_rmt_objects[ch].intr_mode) & E_RX_INTR) {
|
||||
if (g_rmt_objects[ch].events) {
|
||||
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_RX_DONE);
|
||||
}
|
||||
if (g_rmt_objects[ch].data_ptr && g_rmt_objects[ch].data_size > 0) {
|
||||
size_t i;
|
||||
uint32_t * data = g_rmt_objects[ch].data_ptr;
|
||||
// in case of callback, provide switching between memories
|
||||
if (g_rmt_objects[ch].cb) {
|
||||
if (g_rmt_objects[ch].tx_state & E_FIRST_HALF) {
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
} else {
|
||||
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
|
||||
data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) {
|
||||
*data++ = RMTMEM.chan[ch].data32[i].val;
|
||||
}
|
||||
if (g_rmt_objects[ch].cb) {
|
||||
// actually received data ptr
|
||||
uint32_t * data = g_rmt_objects[ch].data_ptr;
|
||||
(g_rmt_objects[ch].cb)(data, _rmt_get_mem_len(ch));
|
||||
|
||||
// restart the reception
|
||||
RMT.conf_ch[ch].conf1.mem_owner = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.rx_en = 1;
|
||||
RMT.int_ena.val |= _INT_RX_END(ch);
|
||||
} else {
|
||||
// if not callback provide, expect only one Rx
|
||||
g_rmt_objects[ch].intr_mode &= ~E_RX_INTR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Report error and disable Rx interrupt
|
||||
log_e("Unexpected Rx interrupt!\n"); // TODO: eplace messages with log_X
|
||||
RMT.int_ena.val &= ~_INT_RX_END(ch);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (intr_val & _INT_ERROR(ch)) {
|
||||
digitalWrite(2, 1);
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_ERROR(ch);
|
||||
RMT.int_ena.val &= ~_INT_ERROR(ch);
|
||||
// report error
|
||||
log_e("RMT Error %d!\n", ch);
|
||||
if (g_rmt_objects[ch].events) {
|
||||
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_ERROR);
|
||||
}
|
||||
// reset memory
|
||||
RMT.conf_ch[ch].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_rd_rst = 0;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
|
||||
RMT.conf_ch[ch].conf1.mem_wr_rst = 0;
|
||||
}
|
||||
|
||||
if (intr_val & _INT_TX_END(ch)) {
|
||||
|
||||
RMT.int_clr.val = _INT_TX_END(ch);
|
||||
_rmt_tx_mem_second(ch);
|
||||
}
|
||||
|
||||
if (intr_val & _INT_THR_EVNT(ch)) {
|
||||
// clear the flag
|
||||
RMT.int_clr.val = _INT_THR_EVNT(ch);
|
||||
|
||||
// initial setup of continuous mode
|
||||
if (g_rmt_objects[ch].tx_state & E_SET_CONTI) {
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 1;
|
||||
g_rmt_objects[ch].intr_mode &= ~E_SET_CONTI;
|
||||
}
|
||||
_rmt_tx_mem_first(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch)
|
||||
{
|
||||
DEBUG_INTERRUPT_START(4)
|
||||
uint32_t* data = g_rmt_objects[ch].data_ptr;
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
int i;
|
||||
|
||||
RMT.tx_lim_ch[ch].limit = half_tx_nr+2;
|
||||
RMT.int_clr.val = _INT_THR_EVNT(ch);
|
||||
RMT.int_ena.val |= _INT_THR_EVNT(ch);
|
||||
|
||||
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
|
||||
|
||||
if (data) {
|
||||
int remaining_size = g_rmt_objects[ch].data_size;
|
||||
// will the remaining data occupy the entire halfbuffer
|
||||
if (remaining_size > half_tx_nr) {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
|
||||
}
|
||||
g_rmt_objects[ch].data_size -= half_tx_nr;
|
||||
g_rmt_objects[ch].data_ptr += half_tx_nr;
|
||||
} else {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
if (i < remaining_size) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
|
||||
}
|
||||
}
|
||||
g_rmt_objects[ch].data_ptr = NULL;
|
||||
|
||||
}
|
||||
} else if ((!(g_rmt_objects[ch].tx_state & E_LAST_DATA)) &&
|
||||
(!(g_rmt_objects[ch].tx_state & E_END_TRANS))) {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
|
||||
}
|
||||
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0;
|
||||
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
} else {
|
||||
log_d("RMT Tx finished %d!\n", ch);
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
RMT.int_ena.val &= ~_INT_TX_END(ch);
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
g_rmt_objects[ch].intr_mode = E_NO_INTR;
|
||||
g_rmt_objects[ch].tx_state = E_INACTIVE;
|
||||
}
|
||||
DEBUG_INTERRUPT_END(4);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch)
|
||||
{
|
||||
DEBUG_INTERRUPT_START(2);
|
||||
uint32_t* data = g_rmt_objects[ch].data_ptr;
|
||||
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
|
||||
int i;
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
|
||||
if (data) {
|
||||
int remaining_size = g_rmt_objects[ch].data_size;
|
||||
|
||||
// will the remaining data occupy the entire halfbuffer
|
||||
if (remaining_size > half_tx_nr) {
|
||||
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
|
||||
for (i = 1; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[i].val = data[i];
|
||||
}
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
// turn off the treshold interrupt
|
||||
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
g_rmt_objects[ch].data_size -= half_tx_nr;
|
||||
g_rmt_objects[ch].data_ptr += half_tx_nr;
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
|
||||
for (i = 1; i < half_tx_nr; i++) {
|
||||
if (i < remaining_size) {
|
||||
RMTMEM.chan[ch].data32[i].val = data[i];
|
||||
} else {
|
||||
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
|
||||
}
|
||||
}
|
||||
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
g_rmt_objects[ch].data_ptr = NULL;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < half_tx_nr; i++) {
|
||||
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
|
||||
}
|
||||
RMTMEM.chan[ch].data32[i].val = 0;
|
||||
|
||||
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
|
||||
RMT.tx_lim_ch[ch].limit = 0;
|
||||
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
|
||||
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
|
||||
}
|
||||
DEBUG_INTERRUPT_END(2);
|
||||
}
|
||||
|
||||
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel)
|
||||
{
|
||||
int block_num = RMT.conf_ch[channel].conf0.mem_size;
|
||||
int item_block_len = block_num * 64;
|
||||
volatile rmt_item32_t* data = RMTMEM.chan[channel].data32;
|
||||
int idx;
|
||||
for(idx = 0; idx < item_block_len; idx++) {
|
||||
if(data[idx].duration0 == 0) {
|
||||
return idx;
|
||||
} else if(data[idx].duration1 == 0) {
|
||||
return idx + 1;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
137
cores/esp32/esp32-hal-rmt.h
Normal file
137
cores/esp32/esp32-hal-rmt.h
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef MAIN_ESP32_HAL_RMT_H_
|
||||
#define MAIN_ESP32_HAL_RMT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// notification flags
|
||||
#define RMT_FLAG_TX_DONE (1)
|
||||
#define RMT_FLAG_RX_DONE (2)
|
||||
#define RMT_FLAG_ERROR (4)
|
||||
#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR)
|
||||
|
||||
struct rmt_obj_s;
|
||||
|
||||
typedef enum {
|
||||
RMT_MEM_64 = 1,
|
||||
RMT_MEM_128 = 2,
|
||||
RMT_MEM_192 = 3,
|
||||
RMT_MEM_256 = 4,
|
||||
RMT_MEM_320 = 5,
|
||||
RMT_MEM_384 = 6,
|
||||
RMT_MEM_448 = 7,
|
||||
RMT_MEM_512 = 8,
|
||||
} rmt_reserve_memsize_t;
|
||||
|
||||
typedef struct rmt_obj_s rmt_obj_t;
|
||||
|
||||
typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len);
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t duration0 :15;
|
||||
uint32_t level0 :1;
|
||||
uint32_t duration1 :15;
|
||||
uint32_t level1 :1;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
} rmt_data_t;
|
||||
|
||||
/**
|
||||
* Initialize the object
|
||||
*
|
||||
*/
|
||||
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize);
|
||||
|
||||
/**
|
||||
* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds
|
||||
* return the real actual tick value in ns
|
||||
*/
|
||||
float rmtSetTick(rmt_obj_t* rmt, float tick);
|
||||
|
||||
/**
|
||||
* Sending data in one-go mode or continual mode
|
||||
* (more data being send while updating buffers in interrupts)
|
||||
*
|
||||
*/
|
||||
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Initiates async receive, event flag indicates data received
|
||||
*
|
||||
*/
|
||||
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* Initiates async receive with automatic buffering
|
||||
* and callback with data from ISR
|
||||
*
|
||||
*/
|
||||
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb);
|
||||
|
||||
|
||||
/* Additional interface */
|
||||
|
||||
/**
|
||||
* Start reception
|
||||
*
|
||||
*/
|
||||
bool rmtBeginReceive(rmt_obj_t* rmt);
|
||||
|
||||
/**
|
||||
* Checks if reception completes
|
||||
*
|
||||
*/
|
||||
bool rmtReceiveCompleted(rmt_obj_t* rmt);
|
||||
|
||||
/**
|
||||
* Reads the data for particular channel
|
||||
*
|
||||
*/
|
||||
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Setting threshold for Rx completed
|
||||
*/
|
||||
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value);
|
||||
|
||||
/**
|
||||
* Setting carrier
|
||||
*/
|
||||
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high);
|
||||
|
||||
/**
|
||||
* Setting input filter
|
||||
*/
|
||||
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level);
|
||||
|
||||
|
||||
// TODO:
|
||||
// * uninstall interrupt when all channels are deinit
|
||||
// * send only-conti mode with circular-buffer
|
||||
// * put sanity checks to some macro or inlines
|
||||
// * doxy comments
|
||||
// * error reporting
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAIN_ESP32_HAL_RMT_H_ */
|
@ -31,6 +31,26 @@
|
||||
xSemaphoreHandle _sd_sys_lock;
|
||||
#endif
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
if(old_apb == new_apb){
|
||||
return;
|
||||
}
|
||||
uint32_t iarg = (uint32_t)arg;
|
||||
uint8_t channel = iarg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
} else {
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
SD_MUTEX_LOCK();
|
||||
uint32_t old_prescale = SIGMADELTA.channel[channel].prescale + 1;
|
||||
SIGMADELTA.channel[channel].prescale = ((new_apb * old_prescale) / old_apb) - 1;
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
SIGMADELTA.cg.clk_en = 1;
|
||||
SD_MUTEX_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500
|
||||
{
|
||||
if(channel > 7) {
|
||||
@ -43,7 +63,8 @@ uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-31
|
||||
_sd_sys_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
#endif
|
||||
uint32_t prescale = (10000000/(freq*32)) - 1;
|
||||
uint32_t apb_freq = getApbFrequency();
|
||||
uint32_t prescale = (apb_freq/(freq*256)) - 1;
|
||||
if(prescale > 0xFF) {
|
||||
prescale = 0xFF;
|
||||
}
|
||||
@ -52,7 +73,9 @@ uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-31
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
SIGMADELTA.cg.clk_en = 1;
|
||||
SD_MUTEX_UNLOCK();
|
||||
return 10000000/((prescale + 1) * 32);
|
||||
uint32_t iarg = channel;
|
||||
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||
return apb_freq/((prescale + 1) * 256);
|
||||
}
|
||||
|
||||
void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-7 duty 8 bit
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_IDX:((p==1)?SPICLK_OUT_IDX:((p==2)?HSPICLK_OUT_IDX:((p==3)?VSPICLK_OUT_IDX:0))))
|
||||
#define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?SPIQ_OUT_IDX:((p==2)?HSPIQ_OUT_IDX:((p==3)?VSPIQ_OUT_IDX:0))))
|
||||
@ -371,6 +372,18 @@ void spiSetBitOrder(spi_t * spi, uint8_t bitOrder)
|
||||
SPI_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
|
||||
{
|
||||
spi_t * spi = (spi_t *)arg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
SPI_MUTEX_LOCK();
|
||||
while(spi->dev->cmd.usr);
|
||||
} else {
|
||||
spi->dev->clock.val = spiFrequencyToClockDiv(old_apb / ((spi->dev->clock.clkdiv_pre + 1) * (spi->dev->clock.clkcnt_n + 1)));
|
||||
SPI_MUTEX_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
||||
void spiStopBus(spi_t * spi)
|
||||
{
|
||||
if(!spi) {
|
||||
@ -387,6 +400,7 @@ void spiStopBus(spi_t * spi)
|
||||
spi->dev->ctrl2.val = 0;
|
||||
spi->dev->clock.val = 0;
|
||||
SPI_MUTEX_UNLOCK();
|
||||
removeApbChangeCallback(spi, _on_apb_change);
|
||||
}
|
||||
|
||||
spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder)
|
||||
@ -433,6 +447,7 @@ spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_
|
||||
}
|
||||
SPI_MUTEX_UNLOCK();
|
||||
|
||||
addApbChangeCallback(spi, _on_apb_change);
|
||||
return spi;
|
||||
}
|
||||
|
||||
@ -750,7 +765,7 @@ void spiEndTransaction(spi_t * spi)
|
||||
SPI_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
void spiWriteByteNL(spi_t * spi, uint8_t data)
|
||||
void IRAM_ATTR spiWriteByteNL(spi_t * spi, uint8_t data)
|
||||
{
|
||||
if(!spi) {
|
||||
return;
|
||||
@ -776,7 +791,7 @@ uint8_t spiTransferByteNL(spi_t * spi, uint8_t data)
|
||||
return data;
|
||||
}
|
||||
|
||||
void spiWriteShortNL(spi_t * spi, uint16_t data)
|
||||
void IRAM_ATTR spiWriteShortNL(spi_t * spi, uint16_t data)
|
||||
{
|
||||
if(!spi) {
|
||||
return;
|
||||
@ -811,7 +826,7 @@ uint16_t spiTransferShortNL(spi_t * spi, uint16_t data)
|
||||
return data;
|
||||
}
|
||||
|
||||
void spiWriteLongNL(spi_t * spi, uint32_t data)
|
||||
void IRAM_ATTR spiWriteLongNL(spi_t * spi, uint32_t data)
|
||||
{
|
||||
if(!spi) {
|
||||
return;
|
||||
@ -959,7 +974,7 @@ void spiTransferBitsNL(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits)
|
||||
}
|
||||
}
|
||||
|
||||
void spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
|
||||
void IRAM_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
|
||||
size_t longs = len >> 2;
|
||||
if(len & 3){
|
||||
longs++;
|
||||
@ -1007,35 +1022,37 @@ void spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
|
||||
* */
|
||||
|
||||
typedef union {
|
||||
uint32_t regValue;
|
||||
uint32_t value;
|
||||
struct {
|
||||
unsigned regL :6;
|
||||
unsigned regH :6;
|
||||
unsigned regN :6;
|
||||
unsigned regPre :13;
|
||||
unsigned regEQU :1;
|
||||
uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/
|
||||
uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/
|
||||
uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/
|
||||
uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/
|
||||
uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/
|
||||
};
|
||||
} spiClk_t;
|
||||
|
||||
#define ClkRegToFreq(reg) (CPU_CLK_FREQ / (((reg)->regPre + 1) * ((reg)->regN + 1)))
|
||||
#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1)))
|
||||
|
||||
uint32_t spiClockDivToFrequency(uint32_t clockDiv)
|
||||
{
|
||||
uint32_t apb_freq = getApbFrequency();
|
||||
spiClk_t reg = { clockDiv };
|
||||
return ClkRegToFreq(®);
|
||||
}
|
||||
|
||||
uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
||||
{
|
||||
uint32_t apb_freq = getApbFrequency();
|
||||
|
||||
if(freq >= CPU_CLK_FREQ) {
|
||||
if(freq >= apb_freq) {
|
||||
return SPI_CLK_EQU_SYSCLK;
|
||||
}
|
||||
|
||||
const spiClk_t minFreqReg = { 0x7FFFF000 };
|
||||
uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg);
|
||||
if(freq < minFreq) {
|
||||
return minFreqReg.regValue;
|
||||
return minFreqReg.value;
|
||||
}
|
||||
|
||||
uint8_t calN = 1;
|
||||
@ -1048,18 +1065,18 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
||||
int32_t calPre;
|
||||
int8_t calPreVari = -2;
|
||||
|
||||
reg.regN = calN;
|
||||
reg.clkcnt_n = calN;
|
||||
|
||||
while(calPreVari++ <= 1) {
|
||||
calPre = (((CPU_CLK_FREQ / (reg.regN + 1)) / freq) - 1) + calPreVari;
|
||||
calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari;
|
||||
if(calPre > 0x1FFF) {
|
||||
reg.regPre = 0x1FFF;
|
||||
reg.clkdiv_pre = 0x1FFF;
|
||||
} else if(calPre <= 0) {
|
||||
reg.regPre = 0;
|
||||
reg.clkdiv_pre = 0;
|
||||
} else {
|
||||
reg.regPre = calPre;
|
||||
reg.clkdiv_pre = calPre;
|
||||
}
|
||||
reg.regL = ((reg.regN + 1) / 2);
|
||||
reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2);
|
||||
calFreq = ClkRegToFreq(®);
|
||||
if(calFreq == (int32_t) freq) {
|
||||
memcpy(&bestReg, ®, sizeof(bestReg));
|
||||
@ -1076,6 +1093,6 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
|
||||
}
|
||||
calN++;
|
||||
}
|
||||
return bestReg.regValue;
|
||||
return bestReg.value;
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,13 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "apps/sntp/sntp.h"
|
||||
#include "lwip/apps/sntp.h"
|
||||
|
||||
static void setTimeZone(long offset, int daylight)
|
||||
{
|
||||
char cst[16] = {0};
|
||||
char cdt[16] = "DST";
|
||||
char tz[32] = {0};
|
||||
char cst[17] = {0};
|
||||
char cdt[17] = "DST";
|
||||
char tz[33] = {0};
|
||||
|
||||
if(offset % 3600){
|
||||
sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
|
||||
|
@ -84,7 +84,7 @@ void IRAM_ATTR __timerISR(void * arg){
|
||||
i = 4;
|
||||
//call callbacks
|
||||
while(i--){
|
||||
if(__timerInterruptHandlers[i] && status & (1 << i)){
|
||||
if(__timerInterruptHandlers[i] && (status & (1 << i))){
|
||||
__timerInterruptHandlers[i]();
|
||||
}
|
||||
}
|
||||
@ -184,6 +184,18 @@ bool timerAlarmEnabled(hw_timer_t *timer){
|
||||
return timer->dev->config.alarm_en;
|
||||
}
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
hw_timer_t * timer = (hw_timer_t *)arg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
timer->dev->config.enable = 0;
|
||||
} else {
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb;
|
||||
timer->dev->config.enable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||
if(num > 3){
|
||||
return NULL;
|
||||
@ -205,12 +217,14 @@ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||
timerAttachInterrupt(timer, NULL, false);
|
||||
timerWrite(timer, 0);
|
||||
timer->dev->config.enable = 1;
|
||||
addApbChangeCallback(timer, _on_apb_change);
|
||||
return timer;
|
||||
}
|
||||
|
||||
void timerEnd(hw_timer_t *timer){
|
||||
timer->dev->config.enable = 0;
|
||||
timerAttachInterrupt(timer, NULL, false);
|
||||
removeApbChangeCallback(timer, _on_apb_change);
|
||||
}
|
||||
|
||||
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
||||
@ -271,23 +285,23 @@ void timerDetachInterrupt(hw_timer_t *timer){
|
||||
uint64_t timerReadMicros(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return timer_val * div / 80;
|
||||
return timer_val * div / (getApbFrequency() / 1000000);
|
||||
}
|
||||
|
||||
double timerReadSeconds(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return (double)timer_val * div / 80000000;
|
||||
return (double)timer_val * div / getApbFrequency();
|
||||
}
|
||||
|
||||
uint64_t timerAlarmReadMicros(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerAlarmRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return timer_val * div / 80;
|
||||
return timer_val * div / (getApbFrequency() / 1000000);
|
||||
}
|
||||
|
||||
double timerAlarmReadSeconds(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerAlarmRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return (double)timer_val * div / 80000000;
|
||||
return (double)timer_val * div / getApbFrequency();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0)))
|
||||
@ -66,6 +67,8 @@ static uart_t _uart_bus_array[3] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
|
||||
|
||||
static void IRAM_ATTR _uart_isr(void *arg)
|
||||
{
|
||||
uint8_t i, c;
|
||||
@ -80,7 +83,7 @@ static void IRAM_ATTR _uart_isr(void *arg)
|
||||
uart->dev->int_clr.rxfifo_full = 1;
|
||||
uart->dev->int_clr.frm_err = 1;
|
||||
uart->dev->int_clr.rxfifo_tout = 1;
|
||||
while(uart->dev->status.rxfifo_cnt) {
|
||||
while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
|
||||
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
||||
@ -215,6 +218,7 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
|
||||
uartAttachTx(uart, txPin, inverted);
|
||||
}
|
||||
|
||||
addApbChangeCallback(uart, uart_on_apb_change);
|
||||
return uart;
|
||||
}
|
||||
|
||||
@ -223,11 +227,10 @@ void uartEnd(uart_t* uart)
|
||||
if(uart == NULL) {
|
||||
return;
|
||||
}
|
||||
removeApbChangeCallback(uart, uart_on_apb_change);
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
if(uart->queue != NULL) {
|
||||
uint8_t c;
|
||||
while(xQueueReceive(uart->queue, &c, 0));
|
||||
vQueueDelete(uart->queue);
|
||||
uart->queue = NULL;
|
||||
}
|
||||
@ -240,6 +243,24 @@ void uartEnd(uart_t* uart)
|
||||
uartDetachTx(uart);
|
||||
}
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
if(uart->queue != NULL) {
|
||||
vQueueDelete(uart->queue);
|
||||
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
|
||||
if(uart->queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
return new_size;
|
||||
}
|
||||
|
||||
uint32_t uartAvailable(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->queue == NULL) {
|
||||
@ -298,10 +319,9 @@ void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
|
||||
}
|
||||
UART_MUTEX_LOCK();
|
||||
while(len) {
|
||||
while(len && uart->dev->status.txfifo_cnt < 0x7F) {
|
||||
uart->dev->fifo.rw_byte = *data++;
|
||||
len--;
|
||||
}
|
||||
while(uart->dev->status.txfifo_cnt == 0x7F);
|
||||
uart->dev->fifo.rw_byte = *data++;
|
||||
len--;
|
||||
}
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
@ -313,7 +333,7 @@ void uartFlush(uart_t* uart)
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
while(uart->dev->status.txfifo_cnt);
|
||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||
|
||||
//Due to hardware issue, we can not use fifo_rst to reset uart fifo.
|
||||
//See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
|
||||
@ -332,19 +352,55 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
|
||||
return;
|
||||
}
|
||||
UART_MUTEX_LOCK();
|
||||
uint32_t clk_div = ((UART_CLK_FREQ<<4)/baud_rate);
|
||||
uint32_t clk_div = ((getApbFrequency()<<4)/baud_rate);
|
||||
uart->dev->clk_div.div_int = clk_div>>4 ;
|
||||
uart->dev->clk_div.div_frag = clk_div & 0xf;
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
|
||||
{
|
||||
uart_t* uart = (uart_t*)arg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
UART_MUTEX_LOCK();
|
||||
//disabple interrupt
|
||||
uart->dev->int_ena.val = 0;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
// read RX fifo
|
||||
uint8_t c;
|
||||
BaseType_t xHigherPriorityTaskWoken;
|
||||
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
|
||||
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
// wait TX empty
|
||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||
} else {
|
||||
//todo:
|
||||
// set baudrate
|
||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
||||
uint32_t baud_rate = ((old_apb<<4)/clk_div);
|
||||
clk_div = ((new_apb<<4)/baud_rate);
|
||||
uart->dev->clk_div.div_int = clk_div>>4 ;
|
||||
uart->dev->clk_div.div_frag = clk_div & 0xf;
|
||||
//enable interrupts
|
||||
uart->dev->int_ena.rxfifo_full = 1;
|
||||
uart->dev->int_ena.frm_err = 1;
|
||||
uart->dev->int_ena.rxfifo_tout = 1;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t uartGetBaudRate(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
||||
return ((UART_CLK_FREQ<<4)/clk_div);
|
||||
return ((getApbFrequency()<<4)/clk_div);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR uart0_write_char(char c)
|
||||
@ -365,17 +421,8 @@ static void IRAM_ATTR uart2_write_char(char c)
|
||||
ESP_REG(DR_REG_UART2_BASE) = c;
|
||||
}
|
||||
|
||||
void uartSetDebug(uart_t* uart)
|
||||
void uart_install_putc()
|
||||
{
|
||||
if(uart == NULL || uart->num > 2) {
|
||||
s_uart_debug_nr = -1;
|
||||
ets_install_putc1(NULL);
|
||||
return;
|
||||
}
|
||||
if(s_uart_debug_nr == uart->num) {
|
||||
return;
|
||||
}
|
||||
s_uart_debug_nr = uart->num;
|
||||
switch(s_uart_debug_nr) {
|
||||
case 0:
|
||||
ets_install_putc1((void (*)(char)) &uart0_write_char);
|
||||
@ -392,6 +439,20 @@ void uartSetDebug(uart_t* uart)
|
||||
}
|
||||
}
|
||||
|
||||
void uartSetDebug(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->num > 2) {
|
||||
s_uart_debug_nr = -1;
|
||||
//ets_install_putc1(NULL);
|
||||
//return;
|
||||
} else
|
||||
if(s_uart_debug_nr == uart->num) {
|
||||
return;
|
||||
} else
|
||||
s_uart_debug_nr = uart->num;
|
||||
uart_install_putc();
|
||||
}
|
||||
|
||||
int uartGetDebug()
|
||||
{
|
||||
return s_uart_debug_nr;
|
||||
@ -430,8 +491,70 @@ int log_printf(const char *format, ...)
|
||||
ets_printf("%s", temp);
|
||||
#endif
|
||||
va_end(arg);
|
||||
if(len > 64){
|
||||
if(len > sizeof(loc_buf)){
|
||||
free(temp);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two.
|
||||
* This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
|
||||
*/
|
||||
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
|
||||
{
|
||||
while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
|
||||
if(flg) return 0;
|
||||
ets_delay_us(1000);
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is
|
||||
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
|
||||
* rounded to the closed real baudrate.
|
||||
*/
|
||||
unsigned long
|
||||
uartDetectBaudrate(uart_t *uart)
|
||||
{
|
||||
static bool uartStateDetectingBaudrate = false;
|
||||
|
||||
if(!uartStateDetectingBaudrate) {
|
||||
uart->dev->auto_baud.glitch_filt = 0x08;
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uart->dev->auto_baud.en = 1;
|
||||
uartStateDetectingBaudrate = true;
|
||||
}
|
||||
|
||||
unsigned long divisor = uartBaudrateDetect(uart, true);
|
||||
if (!divisor) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uartStateDetectingBaudrate = false; // Initialize for the next round
|
||||
|
||||
unsigned long baudrate = getApbFrequency() / divisor;
|
||||
|
||||
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
|
||||
|
||||
size_t i;
|
||||
for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate
|
||||
{
|
||||
if (baudrate <= default_rates[i])
|
||||
{
|
||||
if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) {
|
||||
i--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return default_rates[i];
|
||||
}
|
||||
|
@ -67,9 +67,13 @@ void uartFlush(uart_t* uart);
|
||||
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
|
||||
uint32_t uartGetBaudRate(uart_t* uart);
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
|
||||
|
||||
void uartSetDebug(uart_t* uart);
|
||||
int uartGetDebug();
|
||||
|
||||
unsigned long uartDetectBaudrate(uart_t *uart);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -57,10 +57,12 @@ void yield(void);
|
||||
#include "esp32-hal-spi.h"
|
||||
#include "esp32-hal-i2c.h"
|
||||
#include "esp32-hal-ledc.h"
|
||||
#include "esp32-hal-rmt.h"
|
||||
#include "esp32-hal-sigmadelta.h"
|
||||
#include "esp32-hal-timer.h"
|
||||
#include "esp32-hal-bt.h"
|
||||
#include "esp32-hal-psram.h"
|
||||
#include "esp32-hal-cpu.h"
|
||||
|
||||
#ifndef BOARD_HAS_PSRAM
|
||||
#ifdef CONFIG_SPIRAM_SUPPORT
|
||||
@ -71,6 +73,21 @@ void yield(void);
|
||||
//returns chip temperature in Celsius
|
||||
float temperatureRead();
|
||||
|
||||
#if CONFIG_AUTOSTART_ARDUINO
|
||||
//enable/disable WDT for Arduino's setup and loop functions
|
||||
void enableLoopWDT();
|
||||
void disableLoopWDT();
|
||||
#endif
|
||||
|
||||
//enable/disable WDT for the IDLE task on Core 0 (SYSTEM)
|
||||
void enableCore0WDT();
|
||||
void disableCore0WDT();
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
//enable/disable WDT for the IDLE task on Core 1 (Arduino)
|
||||
void enableCore1WDT();
|
||||
void disableCore1WDT();
|
||||
#endif
|
||||
|
||||
unsigned long micros();
|
||||
unsigned long millis();
|
||||
void delay(uint32_t);
|
||||
|
@ -7,13 +7,10 @@ For details, see http://sourceforge.net/projects/libb64
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
@ -68,12 +65,6 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4) {
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
|
@ -1,7 +1,10 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
TaskHandle_t loopTaskHandle = NULL;
|
||||
|
||||
#if CONFIG_AUTOSTART_ARDUINO
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
@ -10,18 +13,24 @@
|
||||
#define ARDUINO_RUNNING_CORE 1
|
||||
#endif
|
||||
|
||||
bool loopTaskWDTEnabled;
|
||||
|
||||
void loopTask(void *pvParameters)
|
||||
{
|
||||
setup();
|
||||
for(;;) {
|
||||
if(loopTaskWDTEnabled){
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
loopTaskWDTEnabled = false;
|
||||
initArduino();
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, ARDUINO_RUNNING_CORE);
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,24 +1,32 @@
|
||||
Please fill the info fields, it helps to get you faster support ;)
|
||||
Make your question, not a Statement, inclusive. Include all pertinent information:
|
||||
|
||||
If you have a Guru Meditation Error, please decode it:
|
||||
What you are trying to do.
|
||||
Describe your system( Hardware, computer, O/S, core version, environment)
|
||||
Describe what is failing
|
||||
Show the shortest possible code that will duplicate the error
|
||||
Show the EXACT error message(it doesn't work is not enough)
|
||||
Then if someone is interested and knowledgeable you might get a answer. All of this work on your part shows us that you have worked to solve YOUR problem. The more complete your issue posting is, the more likely someone will volunteer their time to help you.
|
||||
|
||||
If you have a Guru Meditation Error or Backtrace, ***please decode it***:
|
||||
https://github.com/me-no-dev/EspExceptionDecoder
|
||||
|
||||
----------------------------- Remove above -----------------------------
|
||||
|
||||
|
||||
### Hardware:
|
||||
Board: ?ESP32 Dev Module?
|
||||
Board: ?ESP32 Dev Module? ?node32? ?ttgo_lora?
|
||||
Core Installation/update date: ?11/jul/2017?
|
||||
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
|
||||
Flash Frequency: ?40Mhz?
|
||||
PSRAM enabled: ?no?
|
||||
Upload Speed: ?115200?
|
||||
|
||||
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
|
||||
|
||||
### Description:
|
||||
Describe your problem here
|
||||
|
||||
|
||||
### Sketch:
|
||||
### Sketch: (leave the backquotes for [code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/))
|
||||
```cpp
|
||||
|
||||
//Change the code below by your sketch
|
||||
|
13
docs/arduino-ide/boards_manager.md
Normal file
13
docs/arduino-ide/boards_manager.md
Normal file
@ -0,0 +1,13 @@
|
||||
Installation instructions using Arduino IDE Boards Manager
|
||||
==========================================================
|
||||
|
||||
Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit).
|
||||
|
||||
- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is at the [Arduino website](http://www.arduino.cc/en/main/software).
|
||||
- Start Arduino and open Preferences window.
|
||||
- Enter ```https://dl.espressif.com/dl/package_esp32_index.json``` into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
|
||||
- Open Boards Manager from Tools > Board menu and install *esp32* platform (and don't forget to select your ESP32 board from Tools > Board menu after installation).
|
||||
|
||||
Stable release link: `https://dl.espressif.com/dl/package_esp32_index.json`
|
||||
|
||||
Development release link: `https://dl.espressif.com/dl/package_esp32_dev_index.json`
|
@ -22,14 +22,15 @@ Installation instructions for Debian / Ubuntu OS
|
||||
|
||||
|
||||
|
||||
- If you have Arduino.app installed to /Applications/, modify the installation as follows, beginning at `mkdir -p ~/Arduino...`:
|
||||
- If you have Arduino installed to ~/, modify the installation as follows, beginning at `mkdir -p ~/Arduino/hardware`:
|
||||
|
||||
```bash
|
||||
cd /Applications/Arduino_*/Contents/java/hardware/
|
||||
```bash
|
||||
cd ~/Arduino/hardware
|
||||
mkdir -p espressif && \
|
||||
cd espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
python2 get.py```
|
||||
python2 get.py
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ Installation instructions for Mac OS
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
python get.py
|
||||
python get.py
|
||||
```
|
||||
Where `~/Documents/Arduino` represents your sketch book location as per "Arduino" > "Preferences" > "Sketchbook location" (in the IDE once started). Adjust the command above accordingly if necessary!
|
||||
|
||||
@ -20,6 +20,8 @@ Installation instructions for Mac OS
|
||||
```xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun```
|
||||
|
||||
```xcode-select --install```
|
||||
|
||||
- Try `python3` instead of `python` if you get the error: `IOError: [Errno socket error] [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)` when running `python get.py`
|
||||
|
||||
- Restart Arduino IDE
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "Update.h"
|
||||
|
||||
|
||||
//#define OTA_DEBUG Serial
|
||||
// #define OTA_DEBUG Serial
|
||||
|
||||
ArduinoOTAClass::ArduinoOTAClass()
|
||||
: _port(0)
|
||||
@ -20,6 +20,7 @@ ArduinoOTAClass::ArduinoOTAClass()
|
||||
, _size(0)
|
||||
, _cmd(0)
|
||||
, _ota_port(0)
|
||||
, _ota_timeout(1000)
|
||||
, _start_callback(NULL)
|
||||
, _end_callback(NULL)
|
||||
, _error_callback(NULL)
|
||||
@ -260,8 +261,9 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
}
|
||||
|
||||
uint32_t written = 0, total = 0, tried = 0;
|
||||
|
||||
while (!Update.isFinished() && client.connected()) {
|
||||
size_t waited = 1000;
|
||||
size_t waited = _ota_timeout;
|
||||
size_t available = client.available();
|
||||
while (!available && waited){
|
||||
delay(1);
|
||||
@ -387,6 +389,10 @@ int ArduinoOTAClass::getCommand() {
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setTimeout(int timeoutInMillis) {
|
||||
_ota_timeout = timeoutInMillis;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA)
|
||||
ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
#endif
|
@ -7,7 +7,6 @@
|
||||
|
||||
#define INT_BUFFER_SIZE 16
|
||||
|
||||
|
||||
typedef enum {
|
||||
OTA_IDLE,
|
||||
OTA_WAITAUTH,
|
||||
@ -25,9 +24,9 @@ typedef enum {
|
||||
class ArduinoOTAClass
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<void(ota_error_t)> THandlerFunction_Error;
|
||||
typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress;
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<void(ota_error_t)> THandlerFunction_Error;
|
||||
typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress;
|
||||
|
||||
ArduinoOTAClass();
|
||||
~ArduinoOTAClass();
|
||||
@ -75,6 +74,8 @@ class ArduinoOTAClass
|
||||
//Gets update command type after OTA has started. Either U_FLASH or U_SPIFFS
|
||||
int getCommand();
|
||||
|
||||
void setTimeout(int timeoutInMillis);
|
||||
|
||||
private:
|
||||
int _port;
|
||||
String _password;
|
||||
@ -88,6 +89,7 @@ class ArduinoOTAClass
|
||||
int _size;
|
||||
int _cmd;
|
||||
int _ota_port;
|
||||
int _ota_timeout;
|
||||
IPAddress _ota_ip;
|
||||
String _md5;
|
||||
|
||||
@ -106,4 +108,4 @@ class ArduinoOTAClass
|
||||
extern ArduinoOTAClass ArduinoOTA;
|
||||
#endif
|
||||
|
||||
#endif /* __ARDUINO_OTA_H */
|
||||
#endif /* __ARDUINO_OTA_H */
|
@ -8,6 +8,7 @@ extern "C" {
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/prot/ethernet.h"
|
||||
#include <esp_err.h>
|
||||
#include <esp_wifi.h>
|
||||
}
|
||||
@ -15,7 +16,7 @@ extern "C" {
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
typedef struct {
|
||||
struct tcpip_api_call call;
|
||||
struct tcpip_api_call_data call;
|
||||
udp_pcb * pcb;
|
||||
const ip_addr_t *addr;
|
||||
uint16_t port;
|
||||
@ -24,7 +25,7 @@ typedef struct {
|
||||
err_t err;
|
||||
} udp_api_call_t;
|
||||
|
||||
static err_t _udp_connect_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_connect_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_connect(msg->pcb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -35,11 +36,11 @@ static err_t _udp_connect(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port
|
||||
msg.pcb = pcb;
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_disconnect_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = 0;
|
||||
udp_disconnect(msg->pcb);
|
||||
@ -49,10 +50,10 @@ static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
|
||||
static void _udp_disconnect(struct udp_pcb *pcb){
|
||||
udp_api_call_t msg;
|
||||
msg.pcb = pcb;
|
||||
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call_data*)&msg);
|
||||
}
|
||||
|
||||
static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_remove_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = 0;
|
||||
udp_remove(msg->pcb);
|
||||
@ -62,10 +63,10 @@ static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
|
||||
static void _udp_remove(struct udp_pcb *pcb){
|
||||
udp_api_call_t msg;
|
||||
msg.pcb = pcb;
|
||||
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call_data*)&msg);
|
||||
}
|
||||
|
||||
static err_t _udp_bind_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_bind_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_bind(msg->pcb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -76,11 +77,11 @@ static err_t _udp_bind(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port){
|
||||
msg.pcb = pcb;
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_sendto_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_sendto_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_sendto(msg->pcb, msg->pb, msg->addr, msg->port);
|
||||
return msg->err;
|
||||
@ -92,11 +93,11 @@ static err_t _udp_sendto(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *
|
||||
msg.addr = addr;
|
||||
msg.port = port;
|
||||
msg.pb = pb;
|
||||
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _udp_sendto_if_api(struct tcpip_api_call *api_call_msg){
|
||||
static err_t _udp_sendto_if_api(struct tcpip_api_call_data *api_call_msg){
|
||||
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
|
||||
msg->err = udp_sendto_if(msg->pcb, msg->pb, msg->addr, msg->port, msg->netif);
|
||||
return msg->err;
|
||||
@ -109,7 +110,7 @@ static err_t _udp_sendto_if(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_
|
||||
msg.port = port;
|
||||
msg.pb = pb;
|
||||
msg.netif = netif;
|
||||
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call*)&msg);
|
||||
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call_data*)&msg);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ static void _udp_task(void *pvParameters){
|
||||
if(xQueueReceive(_udp_queue, &e, portMAX_DELAY) == pdTRUE){
|
||||
if(!e->pb){
|
||||
free((void*)(e));
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
AsyncUDP::_s_recv(e->arg, e->pcb, e->pb, e->addr, e->port, e->netif);
|
||||
free((void*)(e));
|
||||
@ -286,23 +287,29 @@ AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr,
|
||||
_len = pb->len;
|
||||
_index = 0;
|
||||
|
||||
pbuf_ref(_pb);
|
||||
|
||||
//memcpy(&_remoteIp, raddr, sizeof(ip_addr_t));
|
||||
_remoteIp.type = raddr->type;
|
||||
_localIp.type = _remoteIp.type;
|
||||
|
||||
eth_hdr* eth = NULL;
|
||||
udp_hdr* udphdr = reinterpret_cast<udp_hdr*>(_data - UDP_HLEN);
|
||||
_localPort = ntohs(udphdr->dest);
|
||||
_remotePort = ntohs(udphdr->src);
|
||||
|
||||
|
||||
if (_remoteIp.type == IPADDR_TYPE_V4) {
|
||||
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN - SIZEOF_ETH_HDR);
|
||||
struct ip_hdr * iphdr = (struct ip_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN);
|
||||
_localIp.u_addr.ip4.addr = iphdr->dest.addr;
|
||||
_remoteIp.u_addr.ip4.addr = iphdr->src.addr;
|
||||
} else {
|
||||
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN - SIZEOF_ETH_HDR);
|
||||
struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN);
|
||||
memcpy(&_localIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
|
||||
memcpy(&_remoteIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->src.addr, 16);
|
||||
}
|
||||
memcpy(_remoteMac, eth->src.addr, 6);
|
||||
|
||||
struct netif * netif = NULL;
|
||||
void * nif = NULL;
|
||||
@ -413,6 +420,11 @@ uint16_t AsyncUDPPacket::remotePort()
|
||||
return _remotePort;
|
||||
}
|
||||
|
||||
void AsyncUDPPacket::remoteMac(uint8_t * mac)
|
||||
{
|
||||
memcpy(mac, _remoteMac, 6);
|
||||
}
|
||||
|
||||
bool AsyncUDPPacket::isIPv6()
|
||||
{
|
||||
return _localIp.type == IPADDR_TYPE_V6;
|
||||
@ -450,16 +462,24 @@ size_t AsyncUDPPacket::send(AsyncUDPMessage &message)
|
||||
return write(message.data(), message.length());
|
||||
}
|
||||
|
||||
AsyncUDP::AsyncUDP()
|
||||
{
|
||||
bool AsyncUDP::_init(){
|
||||
if(_pcb){
|
||||
return true;
|
||||
}
|
||||
_pcb = udp_new();
|
||||
_connected = false;
|
||||
_handler = NULL;
|
||||
if(!_pcb){
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
//_lock = xSemaphoreCreateMutex();
|
||||
udp_recv(_pcb, &_udp_recv, (void *) this);
|
||||
return true;
|
||||
}
|
||||
|
||||
AsyncUDP::AsyncUDP()
|
||||
{
|
||||
_pcb = NULL;
|
||||
_connected = false;
|
||||
_handler = NULL;
|
||||
}
|
||||
|
||||
AsyncUDP::~AsyncUDP()
|
||||
@ -481,8 +501,7 @@ void AsyncUDP::close()
|
||||
_udp_disconnect(_pcb);
|
||||
}
|
||||
_connected = false;
|
||||
_pcb->multicast_ip.type = IPADDR_TYPE_V4;
|
||||
_pcb->multicast_ip.u_addr.ip4.addr = 0;
|
||||
//todo: unjoin multicast group
|
||||
}
|
||||
UDP_MUTEX_UNLOCK();
|
||||
}
|
||||
@ -493,7 +512,7 @@ bool AsyncUDP::connect(const ip_addr_t *addr, uint16_t port)
|
||||
log_e("failed to start task");
|
||||
return false;
|
||||
}
|
||||
if(_pcb == NULL) {
|
||||
if(!_init()) {
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
@ -514,7 +533,7 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
|
||||
log_e("failed to start task");
|
||||
return false;
|
||||
}
|
||||
if(_pcb == NULL) {
|
||||
if(!_init()) {
|
||||
return false;
|
||||
}
|
||||
close();
|
||||
@ -532,57 +551,75 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX)
|
||||
{
|
||||
struct netif * netif = NULL;
|
||||
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
|
||||
void * nif = NULL;
|
||||
esp_err_t err = tcpip_adapter_get_netif(tcpip_if, &nif);
|
||||
if (err) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
netif = (struct netif *)nif;
|
||||
|
||||
if (addr->type == IPADDR_TYPE_V4) {
|
||||
if(join){
|
||||
if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(join){
|
||||
if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (addr->type == IPADDR_TYPE_V4) {
|
||||
if(join){
|
||||
if (igmp_joingroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (igmp_leavegroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(join){
|
||||
if (mld6_joingroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
if (mld6_leavegroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if)
|
||||
{
|
||||
if(!ip_addr_ismulticast(addr)) {
|
||||
return false;
|
||||
}
|
||||
ip_addr_t multicast_if_addr;
|
||||
uint8_t mode;
|
||||
if(esp_wifi_get_mode((wifi_mode_t*)&mode)){
|
||||
mode = WIFI_MODE_NULL;
|
||||
}
|
||||
|
||||
if(addr->type == IPADDR_TYPE_V6){
|
||||
multicast_if_addr.type = IPADDR_TYPE_V6;
|
||||
|
||||
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
|
||||
if(tcpip_adapter_get_ip6_linklocal(tcpip_if, &multicast_if_addr.u_addr.ip6)){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mld6_joingroup(&(multicast_if_addr.u_addr.ip6), &(addr->u_addr.ip6))) {
|
||||
return false;
|
||||
}
|
||||
} else if(addr->type == IPADDR_TYPE_V4){
|
||||
tcpip_adapter_ip_info_t ifIpInfo;
|
||||
|
||||
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|
||||
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
|
||||
if(tcpip_adapter_get_ip_info(tcpip_if, &ifIpInfo)){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
multicast_if_addr.type = IPADDR_TYPE_V4;
|
||||
multicast_if_addr.u_addr.ip4.addr = ifIpInfo.ip.addr;
|
||||
|
||||
if (igmp_joingroup((const ip4_addr *)&multicast_if_addr.u_addr.ip4, (const ip4_addr *)&addr->u_addr.ip4)!= ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (joinMulticastGroup(addr, true, tcpip_if)!= ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!listen(&multicast_if_addr, port)) {
|
||||
if(!listen(NULL, port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -590,9 +627,7 @@ bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl
|
||||
_pcb->mcast_ttl = ttl;
|
||||
_pcb->remote_port = port;
|
||||
ip_addr_copy(_pcb->remote_ip, *addr);
|
||||
if(addr->type == IPADDR_TYPE_V4){
|
||||
ip_addr_copy(_pcb->multicast_ip, multicast_if_addr);
|
||||
}
|
||||
//ip_addr_copy(_pcb->remote_ip, ip_addr_any_type);
|
||||
UDP_MUTEX_UNLOCK();
|
||||
|
||||
return true;
|
||||
@ -602,7 +637,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
|
||||
{
|
||||
if(!_pcb) {
|
||||
UDP_MUTEX_LOCK();
|
||||
_pcb = udp_new_ip_type(addr->type);
|
||||
_pcb = udp_new();
|
||||
UDP_MUTEX_UNLOCK();
|
||||
if(_pcb == NULL) {
|
||||
return 0;
|
||||
@ -617,7 +652,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload);
|
||||
memcpy(dst, data, len);
|
||||
UDP_MUTEX_LOCK();
|
||||
if(tcpip_if != TCPIP_ADAPTER_IF_MAX){
|
||||
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
|
||||
void * nif = NULL;
|
||||
tcpip_adapter_get_netif((tcpip_adapter_if_t)tcpip_if, &nif);
|
||||
if(!nif){
|
||||
|
@ -53,6 +53,7 @@ protected:
|
||||
uint16_t _localPort;
|
||||
ip_addr_t _remoteIp;
|
||||
uint16_t _remotePort;
|
||||
uint8_t _remoteMac[6];
|
||||
uint8_t *_data;
|
||||
size_t _len;
|
||||
size_t _index;
|
||||
@ -74,6 +75,7 @@ public:
|
||||
IPAddress remoteIP();
|
||||
IPv6Address remoteIPv6();
|
||||
uint16_t remotePort();
|
||||
void remoteMac(uint8_t * mac);
|
||||
|
||||
size_t send(AsyncUDPMessage &message);
|
||||
|
||||
@ -95,6 +97,7 @@ protected:
|
||||
bool _connected;
|
||||
AuPacketHandlerFunction _handler;
|
||||
|
||||
bool _init();
|
||||
void _recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif);
|
||||
|
||||
public:
|
||||
@ -109,9 +112,9 @@ public:
|
||||
bool listen(const IPv6Address addr, uint16_t port);
|
||||
bool listen(uint16_t port);
|
||||
|
||||
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
|
||||
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
|
||||
|
||||
bool connect(const ip_addr_t *addr, uint16_t port);
|
||||
bool connect(const IPAddress addr, uint16_t port);
|
||||
|
Submodule libraries/BLE updated: 7951347ed6...b232e7f5f0
@ -40,11 +40,117 @@
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
const char * _spp_server_name = "ESP32_SPP_SERVER";
|
||||
const char * _spp_server_name = "ESP32SPP";
|
||||
|
||||
#define QUEUE_SIZE 256
|
||||
#define RX_QUEUE_SIZE 512
|
||||
#define TX_QUEUE_SIZE 32
|
||||
static uint32_t _spp_client = 0;
|
||||
static xQueueHandle _spp_queue = NULL;
|
||||
static xQueueHandle _spp_rx_queue = NULL;
|
||||
static xQueueHandle _spp_tx_queue = NULL;
|
||||
static SemaphoreHandle_t _spp_tx_done = NULL;
|
||||
static TaskHandle_t _spp_task_handle = NULL;
|
||||
static EventGroupHandle_t _spp_event_group = NULL;
|
||||
static boolean secondConnectionAttempt;
|
||||
static esp_spp_cb_t * custom_spp_callback = NULL;
|
||||
|
||||
#define SPP_RUNNING 0x01
|
||||
#define SPP_CONNECTED 0x02
|
||||
#define SPP_CONGESTED 0x04
|
||||
|
||||
typedef struct {
|
||||
size_t len;
|
||||
uint8_t data[];
|
||||
} spp_packet_t;
|
||||
|
||||
static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){
|
||||
if(!data || !len){
|
||||
log_w("No data provided");
|
||||
return ESP_OK;
|
||||
}
|
||||
spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t) + len);
|
||||
if(!packet){
|
||||
log_e("SPP TX Packet Malloc Failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
packet->len = len;
|
||||
memcpy(packet->data, data, len);
|
||||
if (xQueueSend(_spp_tx_queue, &packet, portMAX_DELAY) != pdPASS) {
|
||||
log_e("SPP TX Queue Send Failed!");
|
||||
free(packet);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const uint16_t SPP_TX_MAX = 330;
|
||||
static uint8_t _spp_tx_buffer[SPP_TX_MAX];
|
||||
static uint16_t _spp_tx_buffer_len = 0;
|
||||
|
||||
static bool _spp_send_buffer(){
|
||||
if((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED)){
|
||||
esp_err_t err = esp_spp_write(_spp_client, _spp_tx_buffer_len, _spp_tx_buffer);
|
||||
if(err != ESP_OK){
|
||||
log_e("SPP Write Failed! [0x%X]", err);
|
||||
return false;
|
||||
}
|
||||
_spp_tx_buffer_len = 0;
|
||||
if(xSemaphoreTake(_spp_tx_done, portMAX_DELAY) != pdTRUE){
|
||||
log_e("SPP Ack Failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _spp_tx_task(void * arg){
|
||||
spp_packet_t *packet = NULL;
|
||||
size_t len = 0, to_send = 0;
|
||||
uint8_t * data = NULL;
|
||||
for (;;) {
|
||||
if(_spp_tx_queue && xQueueReceive(_spp_tx_queue, &packet, portMAX_DELAY) == pdTRUE && packet){
|
||||
if(packet->len <= (SPP_TX_MAX - _spp_tx_buffer_len)){
|
||||
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, packet->data, packet->len);
|
||||
_spp_tx_buffer_len+=packet->len;
|
||||
free(packet);
|
||||
packet = NULL;
|
||||
if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){
|
||||
_spp_send_buffer();
|
||||
}
|
||||
} else {
|
||||
len = packet->len;
|
||||
data = packet->data;
|
||||
to_send = SPP_TX_MAX - _spp_tx_buffer_len;
|
||||
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, data, to_send);
|
||||
_spp_tx_buffer_len = SPP_TX_MAX;
|
||||
data += to_send;
|
||||
len -= to_send;
|
||||
_spp_send_buffer();
|
||||
while(len >= SPP_TX_MAX){
|
||||
memcpy(_spp_tx_buffer, data, SPP_TX_MAX);
|
||||
_spp_tx_buffer_len = SPP_TX_MAX;
|
||||
data += SPP_TX_MAX;
|
||||
len -= SPP_TX_MAX;
|
||||
_spp_send_buffer();
|
||||
}
|
||||
if(len){
|
||||
memcpy(_spp_tx_buffer, data, len);
|
||||
_spp_tx_buffer_len += len;
|
||||
free(packet);
|
||||
packet = NULL;
|
||||
if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){
|
||||
_spp_send_buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_e("Something went horribly wrong");
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
_spp_task_handle = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
|
||||
{
|
||||
@ -54,86 +160,153 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
|
||||
log_i("ESP_SPP_INIT_EVT");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name);
|
||||
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
|
||||
break;
|
||||
|
||||
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
||||
if (!_spp_client){
|
||||
_spp_client = param->open.handle;
|
||||
} else {
|
||||
secondConnectionAttempt = true;
|
||||
esp_spp_disconnect(param->open.handle);
|
||||
}
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
|
||||
log_i("ESP_SPP_SRV_OPEN_EVT");
|
||||
break;
|
||||
|
||||
case ESP_SPP_CLOSE_EVT://Client connection closed
|
||||
if(secondConnectionAttempt) {
|
||||
secondConnectionAttempt = false;
|
||||
} else {
|
||||
_spp_client = 0;
|
||||
}
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
|
||||
log_i("ESP_SPP_CLOSE_EVT");
|
||||
break;
|
||||
|
||||
case ESP_SPP_CONG_EVT://connection congestion status changed
|
||||
if(param->cong.cong){
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
|
||||
} else {
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE");
|
||||
break;
|
||||
|
||||
case ESP_SPP_WRITE_EVT://write operation completed
|
||||
if(param->write.cong){
|
||||
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
xSemaphoreGive(_spp_tx_done);//we can try to send another packet
|
||||
log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"FREE");
|
||||
break;
|
||||
|
||||
case ESP_SPP_DATA_IND_EVT://connection received data
|
||||
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
|
||||
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
|
||||
//ets_printf("r:%u\n", param->data_ind.len);
|
||||
|
||||
if (_spp_rx_queue != NULL){
|
||||
for (int i = 0; i < param->data_ind.len; i++){
|
||||
if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){
|
||||
log_e("RX Full! Discarding %u bytes", param->data_ind.len - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
//should maybe delete those.
|
||||
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
|
||||
log_i("ESP_SPP_DISCOVERY_COMP_EVT");
|
||||
break;
|
||||
case ESP_SPP_OPEN_EVT://Client connection open
|
||||
log_i("ESP_SPP_OPEN_EVT");
|
||||
break;
|
||||
case ESP_SPP_CLOSE_EVT://Client connection closed
|
||||
_spp_client = 0;
|
||||
log_i("ESP_SPP_CLOSE_EVT");
|
||||
break;
|
||||
case ESP_SPP_START_EVT://server started
|
||||
log_i("ESP_SPP_START_EVT");
|
||||
break;
|
||||
case ESP_SPP_CL_INIT_EVT://client initiated a connection
|
||||
log_i("ESP_SPP_CL_INIT_EVT");
|
||||
break;
|
||||
case ESP_SPP_DATA_IND_EVT://connection received data
|
||||
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
|
||||
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
|
||||
|
||||
if (_spp_queue != NULL){
|
||||
for (int i = 0; i < param->data_ind.len; i++)
|
||||
xQueueSend(_spp_queue, param->data_ind.data + i, (TickType_t)0);
|
||||
} else {
|
||||
log_e("SerialQueueBT ERROR");
|
||||
}
|
||||
break;
|
||||
case ESP_SPP_CONG_EVT://connection congestion status changed
|
||||
log_i("ESP_SPP_CONG_EVT");
|
||||
break;
|
||||
case ESP_SPP_WRITE_EVT://write operation completed
|
||||
log_v("ESP_SPP_WRITE_EVT");
|
||||
break;
|
||||
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
||||
_spp_client = param->open.handle;
|
||||
log_i("ESP_SPP_SRV_OPEN_EVT");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(custom_spp_callback)(*custom_spp_callback)(event, param);
|
||||
}
|
||||
|
||||
static bool _init_bt(const char *deviceName)
|
||||
{
|
||||
if(!_spp_event_group){
|
||||
_spp_event_group = xEventGroupCreate();
|
||||
if(!_spp_event_group){
|
||||
log_e("SPP Event Group Create Failed!");
|
||||
return false;
|
||||
}
|
||||
xEventGroupClearBits(_spp_event_group, 0xFFFFFF);
|
||||
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
||||
}
|
||||
if (_spp_rx_queue == NULL){
|
||||
_spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
|
||||
if (_spp_rx_queue == NULL){
|
||||
log_e("RX Queue Create Failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_spp_tx_queue == NULL){
|
||||
_spp_tx_queue = xQueueCreate(TX_QUEUE_SIZE, sizeof(spp_packet_t*)); //initialize the queue
|
||||
if (_spp_tx_queue == NULL){
|
||||
log_e("TX Queue Create Failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(_spp_tx_done == NULL){
|
||||
_spp_tx_done = xSemaphoreCreateBinary();
|
||||
if (_spp_tx_done == NULL){
|
||||
log_e("TX Semaphore Create Failed");
|
||||
return false;
|
||||
}
|
||||
xSemaphoreTake(_spp_tx_done, 0);
|
||||
}
|
||||
|
||||
if(!_spp_task_handle){
|
||||
xTaskCreate(_spp_tx_task, "spp_tx", 4096, NULL, 2, &_spp_task_handle);
|
||||
if(!_spp_task_handle){
|
||||
log_e("Network Event Task Start Failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!btStarted() && !btStart()){
|
||||
log_e("%s initialize controller failed\n", __func__);
|
||||
log_e("initialize controller failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
|
||||
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){
|
||||
if (esp_bluedroid_init()) {
|
||||
log_e("%s initialize bluedroid failed\n", __func__);
|
||||
log_e("initialize bluedroid failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){
|
||||
if (esp_bluedroid_enable()) {
|
||||
log_e("%s enable bluedroid failed\n", __func__);
|
||||
log_e("enable bluedroid failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){
|
||||
log_e("%s spp register failed\n", __func__);
|
||||
log_e("spp register failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK){
|
||||
log_e("%s spp init failed\n", __func__);
|
||||
log_e("spp init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
_spp_queue = xQueueCreate(QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
|
||||
if (_spp_queue == NULL){
|
||||
log_e("%s Queue creation error\n", __func__);
|
||||
return false;
|
||||
}
|
||||
esp_bt_dev_set_device_name(deviceName);
|
||||
|
||||
// the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack
|
||||
@ -142,7 +315,7 @@ static bool _init_bt(const char *deviceName)
|
||||
cod.minor = 0b000100;
|
||||
cod.service = 0b00000010110;
|
||||
if (esp_bt_gap_set_cod(cod, ESP_BT_INIT_COD) != ESP_OK) {
|
||||
log_e("%s set cod failed\n", __func__);
|
||||
log_e("set cod failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -152,10 +325,39 @@ static bool _init_bt(const char *deviceName)
|
||||
static bool _stop_bt()
|
||||
{
|
||||
if (btStarted()){
|
||||
if(_spp_client)
|
||||
esp_spp_disconnect(_spp_client);
|
||||
esp_spp_deinit();
|
||||
esp_bluedroid_disable();
|
||||
esp_bluedroid_deinit();
|
||||
btStop();
|
||||
}
|
||||
_spp_client = 0;
|
||||
if(_spp_task_handle){
|
||||
vTaskDelete(_spp_task_handle);
|
||||
_spp_task_handle = NULL;
|
||||
}
|
||||
if(_spp_event_group){
|
||||
vEventGroupDelete(_spp_event_group);
|
||||
_spp_event_group = NULL;
|
||||
}
|
||||
if(_spp_rx_queue){
|
||||
vQueueDelete(_spp_rx_queue);
|
||||
//ToDo: clear RX queue when in packet mode
|
||||
_spp_rx_queue = NULL;
|
||||
}
|
||||
if(_spp_tx_queue){
|
||||
spp_packet_t *packet = NULL;
|
||||
while(xQueueReceive(_spp_tx_queue, &packet, 0) == pdTRUE){
|
||||
free(packet);
|
||||
}
|
||||
vQueueDelete(_spp_tx_queue);
|
||||
_spp_tx_queue = NULL;
|
||||
}
|
||||
if (_spp_tx_done) {
|
||||
vSemaphoreDelete(_spp_tx_done);
|
||||
_spp_tx_done = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -184,60 +386,39 @@ bool BluetoothSerial::begin(String localName)
|
||||
|
||||
int BluetoothSerial::available(void)
|
||||
{
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
if (_spp_rx_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
return uxQueueMessagesWaiting(_spp_queue);
|
||||
return uxQueueMessagesWaiting(_spp_rx_queue);
|
||||
}
|
||||
|
||||
int BluetoothSerial::peek(void)
|
||||
{
|
||||
if (available()){
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
if (xQueuePeek(_spp_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
uint8_t c;
|
||||
if (_spp_rx_queue && xQueuePeek(_spp_rx_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool BluetoothSerial::hasClient(void)
|
||||
{
|
||||
if (_spp_client)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return _spp_client > 0;
|
||||
}
|
||||
|
||||
int BluetoothSerial::read(void)
|
||||
{
|
||||
if (available()){
|
||||
if (!_spp_client || _spp_queue == NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
if (xQueueReceive(_spp_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
if (_spp_rx_queue && xQueueReceive(_spp_rx_queue, &c, 0)){
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t BluetoothSerial::write(uint8_t c)
|
||||
{
|
||||
if (!_spp_client){
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buffer[1];
|
||||
buffer[0] = c;
|
||||
esp_err_t err = esp_spp_write(_spp_client, 1, buffer);
|
||||
return (err == ESP_OK) ? 1 : 0;
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
|
||||
@ -245,18 +426,12 @@ size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
|
||||
if (!_spp_client){
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_spp_write(_spp_client, size, (uint8_t *)buffer);
|
||||
return (err == ESP_OK) ? size : 0;
|
||||
return (_spp_queue_packet((uint8_t *)buffer, size) == ESP_OK) ? size : 0;
|
||||
}
|
||||
|
||||
void BluetoothSerial::flush()
|
||||
{
|
||||
if (_spp_client){
|
||||
int qsize = available();
|
||||
uint8_t buffer[qsize];
|
||||
esp_spp_write(_spp_client, qsize, buffer);
|
||||
}
|
||||
while(read() >= 0){}
|
||||
}
|
||||
|
||||
void BluetoothSerial::end()
|
||||
@ -264,4 +439,10 @@ void BluetoothSerial::end()
|
||||
_stop_bt();
|
||||
}
|
||||
|
||||
esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback)
|
||||
{
|
||||
custom_spp_callback = callback;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Stream.h"
|
||||
#include <esp_spp_api.h>
|
||||
|
||||
class BluetoothSerial: public Stream
|
||||
{
|
||||
@ -38,6 +39,7 @@ class BluetoothSerial: public Stream
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
void flush();
|
||||
void end(void);
|
||||
esp_err_t register_callback(esp_spp_cb_t * callback);
|
||||
|
||||
private:
|
||||
String local_name;
|
||||
|
@ -24,7 +24,6 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "EEPROM.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
@ -29,6 +29,7 @@
|
||||
#ifndef EEPROM_FLASH_PARTITION_NAME
|
||||
#define EEPROM_FLASH_PARTITION_NAME "eeprom"
|
||||
#endif
|
||||
#include <Arduino.h>
|
||||
extern "C" {
|
||||
|
||||
#include <stddef.h>
|
||||
|
@ -0,0 +1,148 @@
|
||||
#include "esp_camera.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
//
|
||||
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
|
||||
// or another board which has PSRAM enabled
|
||||
//
|
||||
|
||||
// Select camera model
|
||||
#define CAMERA_MODEL_WROVER_KIT
|
||||
//#define CAMERA_MODEL_M5STACK_PSRAM
|
||||
//#define CAMERA_MODEL_AI_THINKER
|
||||
|
||||
const char* ssid = "*********";
|
||||
const char* password = "*********";
|
||||
|
||||
|
||||
#if defined(CAMERA_MODEL_WROVER_KIT)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 21
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 19
|
||||
#define Y4_GPIO_NUM 18
|
||||
#define Y3_GPIO_NUM 5
|
||||
#define Y2_GPIO_NUM 4
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM 15
|
||||
#define XCLK_GPIO_NUM 27
|
||||
#define SIOD_GPIO_NUM 25
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 19
|
||||
#define Y8_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 5
|
||||
#define Y4_GPIO_NUM 34
|
||||
#define Y3_GPIO_NUM 35
|
||||
#define Y2_GPIO_NUM 32
|
||||
#define VSYNC_GPIO_NUM 22
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1
|
||||
#define XCLK_GPIO_NUM 0
|
||||
#define SIOD_GPIO_NUM 26
|
||||
#define SIOC_GPIO_NUM 27
|
||||
|
||||
#define Y9_GPIO_NUM 35
|
||||
#define Y8_GPIO_NUM 34
|
||||
#define Y7_GPIO_NUM 39
|
||||
#define Y6_GPIO_NUM 36
|
||||
#define Y5_GPIO_NUM 21
|
||||
#define Y4_GPIO_NUM 19
|
||||
#define Y3_GPIO_NUM 18
|
||||
#define Y2_GPIO_NUM 5
|
||||
#define VSYNC_GPIO_NUM 25
|
||||
#define HREF_GPIO_NUM 23
|
||||
#define PCLK_GPIO_NUM 22
|
||||
|
||||
#else
|
||||
#error "Camera model not selected"
|
||||
#endif
|
||||
|
||||
void startCameraServer();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
Serial.println();
|
||||
|
||||
camera_config_t config;
|
||||
config.ledc_channel = LEDC_CHANNEL_0;
|
||||
config.ledc_timer = LEDC_TIMER_0;
|
||||
config.pin_d0 = Y2_GPIO_NUM;
|
||||
config.pin_d1 = Y3_GPIO_NUM;
|
||||
config.pin_d2 = Y4_GPIO_NUM;
|
||||
config.pin_d3 = Y5_GPIO_NUM;
|
||||
config.pin_d4 = Y6_GPIO_NUM;
|
||||
config.pin_d5 = Y7_GPIO_NUM;
|
||||
config.pin_d6 = Y8_GPIO_NUM;
|
||||
config.pin_d7 = Y9_GPIO_NUM;
|
||||
config.pin_xclk = XCLK_GPIO_NUM;
|
||||
config.pin_pclk = PCLK_GPIO_NUM;
|
||||
config.pin_vsync = VSYNC_GPIO_NUM;
|
||||
config.pin_href = HREF_GPIO_NUM;
|
||||
config.pin_sscb_sda = SIOD_GPIO_NUM;
|
||||
config.pin_sscb_scl = SIOC_GPIO_NUM;
|
||||
config.pin_pwdn = PWDN_GPIO_NUM;
|
||||
config.pin_reset = RESET_GPIO_NUM;
|
||||
config.xclk_freq_hz = 20000000;
|
||||
config.pixel_format = PIXFORMAT_JPEG;
|
||||
//init with high specs to pre-allocate larger buffers
|
||||
if(psramFound()){
|
||||
config.frame_size = FRAMESIZE_UXGA;
|
||||
config.jpeg_quality = 10;
|
||||
config.fb_count = 2;
|
||||
} else {
|
||||
config.frame_size = FRAMESIZE_SVGA;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
}
|
||||
|
||||
// camera init
|
||||
esp_err_t err = esp_camera_init(&config);
|
||||
if (err != ESP_OK) {
|
||||
Serial.printf("Camera init failed with error 0x%x", err);
|
||||
return;
|
||||
}
|
||||
|
||||
//drop down frame size for higher initial frame rate
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
s->set_framesize(s, FRAMESIZE_QVGA);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
|
||||
startCameraServer();
|
||||
|
||||
Serial.print("Camera Ready! Use 'http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("' to connect");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(10000);
|
||||
}
|
650
libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp
Normal file
650
libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp
Normal file
@ -0,0 +1,650 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_camera.h"
|
||||
#include "img_converters.h"
|
||||
#include "camera_index.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "fb_gfx.h"
|
||||
#include "fd_forward.h"
|
||||
#include "dl_lib.h"
|
||||
#include "fr_forward.h"
|
||||
|
||||
#define ENROLL_CONFIRM_TIMES 5
|
||||
#define FACE_ID_SAVE_NUMBER 7
|
||||
|
||||
#define FACE_COLOR_WHITE 0x00FFFFFF
|
||||
#define FACE_COLOR_BLACK 0x00000000
|
||||
#define FACE_COLOR_RED 0x000000FF
|
||||
#define FACE_COLOR_GREEN 0x0000FF00
|
||||
#define FACE_COLOR_BLUE 0x00FF0000
|
||||
#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
|
||||
#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
|
||||
#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
|
||||
|
||||
typedef struct {
|
||||
size_t size; //number of values used for filtering
|
||||
size_t index; //current value index
|
||||
size_t count; //value count
|
||||
int sum;
|
||||
int * values; //array to be filled with values
|
||||
} ra_filter_t;
|
||||
|
||||
typedef struct {
|
||||
httpd_req_t *req;
|
||||
size_t len;
|
||||
} jpg_chunking_t;
|
||||
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
|
||||
|
||||
static ra_filter_t ra_filter;
|
||||
httpd_handle_t stream_httpd = NULL;
|
||||
httpd_handle_t camera_httpd = NULL;
|
||||
|
||||
static mtmn_config_t mtmn_config = {0};
|
||||
static int8_t detection_enabled = 0;
|
||||
static int8_t recognition_enabled = 0;
|
||||
static int8_t is_enrolling = 0;
|
||||
static face_id_list id_list = {0};
|
||||
|
||||
static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){
|
||||
memset(filter, 0, sizeof(ra_filter_t));
|
||||
|
||||
filter->values = (int *)malloc(sample_size * sizeof(int));
|
||||
if(!filter->values){
|
||||
return NULL;
|
||||
}
|
||||
memset(filter->values, 0, sample_size * sizeof(int));
|
||||
|
||||
filter->size = sample_size;
|
||||
return filter;
|
||||
}
|
||||
|
||||
static int ra_filter_run(ra_filter_t * filter, int value){
|
||||
if(!filter->values){
|
||||
return value;
|
||||
}
|
||||
filter->sum -= filter->values[filter->index];
|
||||
filter->values[filter->index] = value;
|
||||
filter->sum += filter->values[filter->index];
|
||||
filter->index++;
|
||||
filter->index = filter->index % filter->size;
|
||||
if (filter->count < filter->size) {
|
||||
filter->count++;
|
||||
}
|
||||
return filter->sum / filter->count;
|
||||
}
|
||||
|
||||
static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){
|
||||
fb_data_t fb;
|
||||
fb.width = image_matrix->w;
|
||||
fb.height = image_matrix->h;
|
||||
fb.data = image_matrix->item;
|
||||
fb.bytes_per_pixel = 3;
|
||||
fb.format = FB_BGR888;
|
||||
fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str);
|
||||
}
|
||||
|
||||
static int rgb_printf(dl_matrix3du_t *image_matrix, uint32_t color, const char *format, ...){
|
||||
char loc_buf[64];
|
||||
char * temp = loc_buf;
|
||||
int len;
|
||||
va_list arg;
|
||||
va_list copy;
|
||||
va_start(arg, format);
|
||||
va_copy(copy, arg);
|
||||
len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg);
|
||||
va_end(copy);
|
||||
if(len >= sizeof(loc_buf)){
|
||||
temp = (char*)malloc(len+1);
|
||||
if(temp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
vsnprintf(temp, len+1, format, arg);
|
||||
va_end(arg);
|
||||
rgb_print(image_matrix, color, temp);
|
||||
if(len > 64){
|
||||
free(temp);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){
|
||||
int x, y, w, h, i;
|
||||
uint32_t color = FACE_COLOR_YELLOW;
|
||||
if(face_id < 0){
|
||||
color = FACE_COLOR_RED;
|
||||
} else if(face_id > 0){
|
||||
color = FACE_COLOR_GREEN;
|
||||
}
|
||||
fb_data_t fb;
|
||||
fb.width = image_matrix->w;
|
||||
fb.height = image_matrix->h;
|
||||
fb.data = image_matrix->item;
|
||||
fb.bytes_per_pixel = 3;
|
||||
fb.format = FB_BGR888;
|
||||
for (i = 0; i < boxes->len; i++){
|
||||
// rectangle box
|
||||
x = (int)boxes->box[i].box_p[0];
|
||||
y = (int)boxes->box[i].box_p[1];
|
||||
w = (int)boxes->box[i].box_p[2] - x + 1;
|
||||
h = (int)boxes->box[i].box_p[3] - y + 1;
|
||||
fb_gfx_drawFastHLine(&fb, x, y, w, color);
|
||||
fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color);
|
||||
fb_gfx_drawFastVLine(&fb, x, y, h, color);
|
||||
fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color);
|
||||
#if 0
|
||||
// landmark
|
||||
int x0, y0, j;
|
||||
for (j = 0; j < 10; j+=2) {
|
||||
x0 = (int)boxes->landmark[i].landmark_p[j];
|
||||
y0 = (int)boxes->landmark[i].landmark_p[j+1];
|
||||
fb_gfx_fillRect(&fb, x0, y0, 3, 3, color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){
|
||||
dl_matrix3du_t *aligned_face = NULL;
|
||||
int matched_id = 0;
|
||||
|
||||
aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
|
||||
if(!aligned_face){
|
||||
Serial.println("Could not allocate face recognition buffer");
|
||||
return matched_id;
|
||||
}
|
||||
if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){
|
||||
if (is_enrolling == 1){
|
||||
int8_t left_sample_face = enroll_face(&id_list, aligned_face);
|
||||
|
||||
if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){
|
||||
Serial.printf("Enrolling Face ID: %d\n", id_list.tail);
|
||||
}
|
||||
Serial.printf("Enrolling Face ID: %d sample %d\n", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
|
||||
rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
|
||||
if (left_sample_face == 0){
|
||||
is_enrolling = 0;
|
||||
Serial.printf("Enrolled Face ID: %d\n", id_list.tail);
|
||||
}
|
||||
} else {
|
||||
matched_id = recognize_face(&id_list, aligned_face);
|
||||
if (matched_id >= 0) {
|
||||
Serial.printf("Match Face ID: %u\n", matched_id);
|
||||
rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id);
|
||||
} else {
|
||||
Serial.println("No Match Found");
|
||||
rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!");
|
||||
matched_id = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println("Face Not Aligned");
|
||||
//rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected");
|
||||
}
|
||||
|
||||
dl_matrix3du_free(aligned_face);
|
||||
return matched_id;
|
||||
}
|
||||
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
|
||||
jpg_chunking_t *j = (jpg_chunking_t *)arg;
|
||||
if(!index){
|
||||
j->len = 0;
|
||||
}
|
||||
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
|
||||
return 0;
|
||||
}
|
||||
j->len += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static esp_err_t capture_handler(httpd_req_t *req){
|
||||
camera_fb_t * fb = NULL;
|
||||
esp_err_t res = ESP_OK;
|
||||
int64_t fr_start = esp_timer_get_time();
|
||||
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
Serial.println("Camera capture failed");
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
|
||||
|
||||
size_t out_len, out_width, out_height;
|
||||
uint8_t * out_buf;
|
||||
bool s;
|
||||
bool detected = false;
|
||||
int face_id = 0;
|
||||
if(!detection_enabled || fb->width > 400){
|
||||
size_t fb_len = 0;
|
||||
if(fb->format == PIXFORMAT_JPEG){
|
||||
fb_len = fb->len;
|
||||
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
||||
} else {
|
||||
jpg_chunking_t jchunk = {req, 0};
|
||||
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
fb_len = jchunk.len;
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
|
||||
return res;
|
||||
}
|
||||
|
||||
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
|
||||
if (!image_matrix) {
|
||||
esp_camera_fb_return(fb);
|
||||
Serial.println("dl_matrix3du_alloc failed");
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
out_buf = image_matrix->item;
|
||||
out_len = fb->width * fb->height * 3;
|
||||
out_width = fb->width;
|
||||
out_height = fb->height;
|
||||
|
||||
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
|
||||
esp_camera_fb_return(fb);
|
||||
if(!s){
|
||||
dl_matrix3du_free(image_matrix);
|
||||
Serial.println("to rgb888 failed");
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config);
|
||||
|
||||
if (net_boxes){
|
||||
detected = true;
|
||||
if(recognition_enabled){
|
||||
face_id = run_face_recognition(image_matrix, net_boxes);
|
||||
}
|
||||
draw_face_boxes(image_matrix, net_boxes, face_id);
|
||||
free(net_boxes->box);
|
||||
free(net_boxes->landmark);
|
||||
free(net_boxes);
|
||||
}
|
||||
|
||||
jpg_chunking_t jchunk = {req, 0};
|
||||
s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
|
||||
dl_matrix3du_free(image_matrix);
|
||||
if(!s){
|
||||
Serial.println("JPEG compression failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
Serial.printf("FACE: %uB %ums %s%d\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
static esp_err_t stream_handler(httpd_req_t *req){
|
||||
camera_fb_t * fb = NULL;
|
||||
esp_err_t res = ESP_OK;
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t * _jpg_buf = NULL;
|
||||
char * part_buf[64];
|
||||
dl_matrix3du_t *image_matrix = NULL;
|
||||
bool detected = false;
|
||||
int face_id = 0;
|
||||
int64_t fr_start = 0;
|
||||
int64_t fr_ready = 0;
|
||||
int64_t fr_face = 0;
|
||||
int64_t fr_recognize = 0;
|
||||
int64_t fr_encode = 0;
|
||||
|
||||
static int64_t last_frame = 0;
|
||||
if(!last_frame) {
|
||||
last_frame = esp_timer_get_time();
|
||||
}
|
||||
|
||||
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
|
||||
if(res != ESP_OK){
|
||||
return res;
|
||||
}
|
||||
|
||||
while(true){
|
||||
detected = false;
|
||||
face_id = 0;
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
Serial.println("Camera capture failed");
|
||||
res = ESP_FAIL;
|
||||
} else {
|
||||
fr_start = esp_timer_get_time();
|
||||
fr_ready = fr_start;
|
||||
fr_face = fr_start;
|
||||
fr_encode = fr_start;
|
||||
fr_recognize = fr_start;
|
||||
if(!detection_enabled || fb->width > 400){
|
||||
if(fb->format != PIXFORMAT_JPEG){
|
||||
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
if(!jpeg_converted){
|
||||
Serial.println("JPEG compression failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
_jpg_buf_len = fb->len;
|
||||
_jpg_buf = fb->buf;
|
||||
}
|
||||
} else {
|
||||
|
||||
image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
|
||||
|
||||
if (!image_matrix) {
|
||||
Serial.println("dl_matrix3du_alloc failed");
|
||||
res = ESP_FAIL;
|
||||
} else {
|
||||
if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){
|
||||
Serial.println("fmt2rgb888 failed");
|
||||
res = ESP_FAIL;
|
||||
} else {
|
||||
fr_ready = esp_timer_get_time();
|
||||
box_array_t *net_boxes = NULL;
|
||||
if(detection_enabled){
|
||||
net_boxes = face_detect(image_matrix, &mtmn_config);
|
||||
}
|
||||
fr_face = esp_timer_get_time();
|
||||
fr_recognize = fr_face;
|
||||
if (net_boxes || fb->format != PIXFORMAT_JPEG){
|
||||
if(net_boxes){
|
||||
detected = true;
|
||||
if(recognition_enabled){
|
||||
face_id = run_face_recognition(image_matrix, net_boxes);
|
||||
}
|
||||
fr_recognize = esp_timer_get_time();
|
||||
draw_face_boxes(image_matrix, net_boxes, face_id);
|
||||
free(net_boxes->box);
|
||||
free(net_boxes->landmark);
|
||||
free(net_boxes);
|
||||
}
|
||||
if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){
|
||||
Serial.println("fmt2jpg failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
} else {
|
||||
_jpg_buf = fb->buf;
|
||||
_jpg_buf_len = fb->len;
|
||||
}
|
||||
fr_encode = esp_timer_get_time();
|
||||
}
|
||||
dl_matrix3du_free(image_matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(res == ESP_OK){
|
||||
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
|
||||
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
|
||||
}
|
||||
if(res == ESP_OK){
|
||||
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
|
||||
}
|
||||
if(res == ESP_OK){
|
||||
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
|
||||
}
|
||||
if(fb){
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
_jpg_buf = NULL;
|
||||
} else if(_jpg_buf){
|
||||
free(_jpg_buf);
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
if(res != ESP_OK){
|
||||
break;
|
||||
}
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
|
||||
int64_t ready_time = (fr_ready - fr_start)/1000;
|
||||
int64_t face_time = (fr_face - fr_ready)/1000;
|
||||
int64_t recognize_time = (fr_recognize - fr_face)/1000;
|
||||
int64_t encode_time = (fr_encode - fr_recognize)/1000;
|
||||
int64_t process_time = (fr_encode - fr_start)/1000;
|
||||
|
||||
int64_t frame_time = fr_end - last_frame;
|
||||
last_frame = fr_end;
|
||||
frame_time /= 1000;
|
||||
uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
|
||||
Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n",
|
||||
(uint32_t)(_jpg_buf_len),
|
||||
(uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
|
||||
avg_frame_time, 1000.0 / avg_frame_time,
|
||||
(uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time,
|
||||
(detected)?"DETECTED ":"", face_id
|
||||
);
|
||||
}
|
||||
|
||||
last_frame = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static esp_err_t cmd_handler(httpd_req_t *req){
|
||||
char* buf;
|
||||
size_t buf_len;
|
||||
char variable[32] = {0,};
|
||||
char value[32] = {0,};
|
||||
|
||||
buf_len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = (char*)malloc(buf_len);
|
||||
if(!buf){
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||
if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
|
||||
httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
|
||||
} else {
|
||||
free(buf);
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
free(buf);
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
} else {
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int val = atoi(value);
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
int res = 0;
|
||||
|
||||
if(!strcmp(variable, "framesize")) {
|
||||
if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
|
||||
}
|
||||
else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
|
||||
else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
|
||||
else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
|
||||
else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
|
||||
else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
|
||||
else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
|
||||
else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
|
||||
else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
|
||||
else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
|
||||
else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
|
||||
else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
|
||||
else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
|
||||
else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
|
||||
else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
|
||||
else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
|
||||
else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
|
||||
else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
|
||||
else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
|
||||
else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
|
||||
else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
|
||||
else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
|
||||
else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
|
||||
else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
|
||||
else if(!strcmp(variable, "face_detect")) {
|
||||
detection_enabled = val;
|
||||
if(!detection_enabled) {
|
||||
recognition_enabled = 0;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(variable, "face_enroll")) is_enrolling = val;
|
||||
else if(!strcmp(variable, "face_recognize")) {
|
||||
recognition_enabled = val;
|
||||
if(recognition_enabled){
|
||||
detection_enabled = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if(res){
|
||||
return httpd_resp_send_500(req);
|
||||
}
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
}
|
||||
|
||||
static esp_err_t status_handler(httpd_req_t *req){
|
||||
static char json_response[1024];
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
char * p = json_response;
|
||||
*p++ = '{';
|
||||
|
||||
p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
|
||||
p+=sprintf(p, "\"quality\":%u,", s->status.quality);
|
||||
p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
|
||||
p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
|
||||
p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
|
||||
p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
|
||||
p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
|
||||
p+=sprintf(p, "\"awb\":%u,", s->status.awb);
|
||||
p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
|
||||
p+=sprintf(p, "\"aec\":%u,", s->status.aec);
|
||||
p+=sprintf(p, "\"aec2\":%u,", s->status.aec2);
|
||||
p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
|
||||
p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
|
||||
p+=sprintf(p, "\"agc\":%u,", s->status.agc);
|
||||
p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
|
||||
p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
|
||||
p+=sprintf(p, "\"bpc\":%u,", s->status.bpc);
|
||||
p+=sprintf(p, "\"wpc\":%u,", s->status.wpc);
|
||||
p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma);
|
||||
p+=sprintf(p, "\"lenc\":%u,", s->status.lenc);
|
||||
p+=sprintf(p, "\"vflip\":%u,", s->status.vflip);
|
||||
p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
|
||||
p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
|
||||
p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
|
||||
p+=sprintf(p, "\"face_detect\":%u,", detection_enabled);
|
||||
p+=sprintf(p, "\"face_enroll\":%u,", is_enrolling);
|
||||
p+=sprintf(p, "\"face_recognize\":%u", recognition_enabled);
|
||||
*p++ = '}';
|
||||
*p++ = 0;
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
return httpd_resp_send(req, json_response, strlen(json_response));
|
||||
}
|
||||
|
||||
static esp_err_t index_handler(httpd_req_t *req){
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
|
||||
return httpd_resp_send(req, (const char *)index_html_gz, index_html_gz_len);
|
||||
}
|
||||
|
||||
void startCameraServer(){
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
|
||||
httpd_uri_t index_uri = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = index_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
httpd_uri_t status_uri = {
|
||||
.uri = "/status",
|
||||
.method = HTTP_GET,
|
||||
.handler = status_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
httpd_uri_t cmd_uri = {
|
||||
.uri = "/control",
|
||||
.method = HTTP_GET,
|
||||
.handler = cmd_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
httpd_uri_t capture_uri = {
|
||||
.uri = "/capture",
|
||||
.method = HTTP_GET,
|
||||
.handler = capture_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
httpd_uri_t stream_uri = {
|
||||
.uri = "/stream",
|
||||
.method = HTTP_GET,
|
||||
.handler = stream_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
|
||||
ra_filter_init(&ra_filter, 20);
|
||||
|
||||
mtmn_config.min_face = 80;
|
||||
mtmn_config.pyramid = 0.7;
|
||||
mtmn_config.p_threshold.score = 0.6;
|
||||
mtmn_config.p_threshold.nms = 0.7;
|
||||
mtmn_config.r_threshold.score = 0.7;
|
||||
mtmn_config.r_threshold.nms = 0.7;
|
||||
mtmn_config.r_threshold.candidate_number = 4;
|
||||
mtmn_config.o_threshold.score = 0.7;
|
||||
mtmn_config.o_threshold.nms = 0.4;
|
||||
mtmn_config.o_threshold.candidate_number = 1;
|
||||
|
||||
face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
|
||||
|
||||
Serial.printf("Starting web server on port: '%d'\n", config.server_port);
|
||||
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
|
||||
httpd_register_uri_handler(camera_httpd, &index_uri);
|
||||
httpd_register_uri_handler(camera_httpd, &cmd_uri);
|
||||
httpd_register_uri_handler(camera_httpd, &status_uri);
|
||||
httpd_register_uri_handler(camera_httpd, &capture_uri);
|
||||
}
|
||||
|
||||
config.server_port += 1;
|
||||
config.ctrl_port += 1;
|
||||
Serial.printf("Starting stream server on port: '%d'\n", config.server_port);
|
||||
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
|
||||
httpd_register_uri_handler(stream_httpd, &stream_uri);
|
||||
}
|
||||
}
|
233
libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h
Normal file
233
libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h
Normal file
@ -0,0 +1,233 @@
|
||||
//File: index.html.gz, Size: 3663
|
||||
#define index_html_gz_len 3663
|
||||
const uint8_t index_html_gz[] = {
|
||||
0x1F, 0x8B, 0x08, 0x08, 0x60, 0x15, 0x36, 0x5C, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E,
|
||||
0x68, 0x74, 0x6D, 0x6C, 0x00, 0xDD, 0x5C, 0xEB, 0x72, 0x9B, 0xC8, 0x12, 0xFE, 0x7F, 0x9E, 0x02,
|
||||
0x93, 0xDD, 0x08, 0x6A, 0x91, 0x2C, 0xC9, 0x8A, 0xE3, 0x20, 0x0B, 0x1F, 0x5B, 0x76, 0x92, 0xAD,
|
||||
0xCA, 0x6D, 0xE3, 0x3D, 0xBB, 0x5B, 0xB5, 0xB5, 0x95, 0x8C, 0x60, 0x90, 0x26, 0x46, 0x8C, 0x02,
|
||||
0x83, 0x2E, 0xD1, 0xF2, 0x1C, 0xE7, 0x81, 0xCE, 0x8B, 0x9D, 0x9E, 0x19, 0x40, 0xA0, 0x8B, 0x65,
|
||||
0x49, 0x89, 0x94, 0x5A, 0xBB, 0xCA, 0x1A, 0xA0, 0xBB, 0xA7, 0xBB, 0xBF, 0xBE, 0x0C, 0x88, 0xF1,
|
||||
0xF9, 0x91, 0x43, 0x6D, 0x36, 0x19, 0x60, 0xA5, 0xC7, 0xFA, 0x9E, 0xF5, 0xAF, 0x73, 0xF9, 0xA1,
|
||||
0xC0, 0xCF, 0x79, 0x0F, 0x23, 0x47, 0x0E, 0xC5, 0x61, 0x1F, 0x33, 0xA4, 0xD8, 0x3D, 0x14, 0x84,
|
||||
0x98, 0xB5, 0xD4, 0x88, 0xB9, 0xE5, 0x33, 0x75, 0xFE, 0xB2, 0x8F, 0xFA, 0xB8, 0xA5, 0x0E, 0x09,
|
||||
0x1E, 0x0D, 0x68, 0xC0, 0x54, 0xC5, 0xA6, 0x3E, 0xC3, 0x3E, 0x90, 0x8F, 0x88, 0xC3, 0x7A, 0x2D,
|
||||
0x07, 0x0F, 0x89, 0x8D, 0xCB, 0xE2, 0xC0, 0x20, 0x3E, 0x61, 0x04, 0x79, 0xE5, 0xD0, 0x46, 0x1E,
|
||||
0x6E, 0xD5, 0xF2, 0xB2, 0x18, 0x61, 0x1E, 0xB6, 0x6E, 0x6E, 0xDF, 0x9D, 0xD4, 0x95, 0xB7, 0xBF,
|
||||
0xD5, 0x1B, 0xA7, 0xD5, 0xF3, 0x63, 0x79, 0x6E, 0x46, 0x13, 0xB2, 0x09, 0x3F, 0xEE, 0x50, 0x67,
|
||||
0x32, 0x75, 0x61, 0x9A, 0xB2, 0x8B, 0xFA, 0xC4, 0x9B, 0x98, 0x97, 0x01, 0x08, 0x35, 0x5E, 0x62,
|
||||
0x6F, 0x88, 0x19, 0xB1, 0x91, 0x11, 0x22, 0x3F, 0x2C, 0x87, 0x38, 0x20, 0x6E, 0xB3, 0x83, 0xEC,
|
||||
0xBB, 0x6E, 0x40, 0x23, 0xDF, 0x31, 0x1F, 0xD5, 0xCE, 0xF8, 0x6F, 0xD3, 0xA6, 0x1E, 0x0D, 0xCC,
|
||||
0x47, 0x37, 0xCF, 0xF9, 0x6F, 0x53, 0xC8, 0x09, 0xC9, 0x17, 0x6C, 0xD6, 0x4E, 0x07, 0xE3, 0xB8,
|
||||
0x57, 0x9F, 0xE6, 0xCE, 0x9C, 0xC1, 0x99, 0x10, 0xDB, 0x8C, 0x50, 0xBF, 0xD2, 0x47, 0xC4, 0x9F,
|
||||
0x3A, 0x24, 0x1C, 0x78, 0x68, 0x62, 0xBA, 0x1E, 0x1E, 0xC7, 0x8F, 0xFA, 0xD8, 0x8F, 0x8C, 0xC2,
|
||||
0x75, 0x7E, 0xBE, 0xEC, 0x90, 0x40, 0x9E, 0x33, 0x61, 0xAA, 0xA8, 0xEF, 0x4B, 0xC2, 0x8C, 0xD7,
|
||||
0xA7, 0x3E, 0x6E, 0x0A, 0xC2, 0x51, 0x80, 0x06, 0x70, 0xC8, 0x3F, 0x9A, 0x7D, 0xE2, 0x4B, 0x27,
|
||||
0x99, 0x27, 0x8D, 0xEA, 0x60, 0x5C, 0x50, 0xFC, 0xE4, 0x94, 0xFF, 0x36, 0x07, 0xC8, 0x71, 0x88,
|
||||
0xDF, 0x35, 0xCF, 0xF8, 0x65, 0x1A, 0x38, 0x38, 0x28, 0x07, 0xC8, 0x21, 0x51, 0x68, 0x36, 0xE0,
|
||||
0x4C, 0x1F, 0x05, 0x5D, 0x90, 0xC1, 0xE8, 0xC0, 0x2C, 0xD7, 0xAA, 0xB3, 0x13, 0x01, 0xE9, 0xF6,
|
||||
0x98, 0xC9, 0xCF, 0xC4, 0x8F, 0x12, 0x6C, 0x0A, 0x66, 0xE4, 0x54, 0x11, 0x8A, 0x20, 0x8F, 0x74,
|
||||
0xFD, 0x32, 0x61, 0xB8, 0x1F, 0x9A, 0x21, 0x0B, 0x30, 0xB3, 0x7B, 0xB1, 0x4B, 0xBA, 0x51, 0x80,
|
||||
0xA7, 0xA9, 0x02, 0xD5, 0x44, 0x36, 0x0C, 0xCA, 0x23, 0xDC, 0xB9, 0x23, 0xAC, 0x9C, 0x4C, 0xD6,
|
||||
0xC1, 0x2E, 0x0D, 0x70, 0x46, 0x50, 0xEE, 0x78, 0xD4, 0xBE, 0x2B, 0x87, 0x0C, 0x05, 0x6C, 0x91,
|
||||
0x18, 0xB9, 0x0C, 0x07, 0xF3, 0xB4, 0x18, 0x0C, 0x5E, 0xA0, 0x4C, 0x05, 0x24, 0x87, 0xC4, 0xF7,
|
||||
0x88, 0x8F, 0x57, 0x89, 0x95, 0x12, 0x8A, 0xA4, 0xE2, 0x5C, 0x62, 0x86, 0x42, 0xFA, 0xDD, 0xCC,
|
||||
0x03, 0x62, 0xD2, 0xA6, 0x74, 0x7C, 0xAD, 0x5A, 0xFD, 0xB1, 0xD9, 0xC3, 0xC2, 0x5F, 0x28, 0x62,
|
||||
0xF4, 0x7E, 0x27, 0xF3, 0xD8, 0xF8, 0x77, 0x1F, 0x3B, 0x04, 0x29, 0xDA, 0x0C, 0x3C, 0xE5, 0xAC,
|
||||
0x0A, 0x9E, 0xD6, 0x15, 0xE4, 0x3B, 0x8A, 0x46, 0x03, 0x02, 0xDE, 0x46, 0x22, 0x14, 0x3C, 0x38,
|
||||
0x03, 0x61, 0x3F, 0xC0, 0xFA, 0x74, 0x1D, 0x0C, 0x49, 0x44, 0xAC, 0x06, 0x62, 0x89, 0x05, 0x7D,
|
||||
0x34, 0x2E, 0xE7, 0xAC, 0xE0, 0x87, 0x89, 0x25, 0x90, 0x6A, 0xB6, 0x06, 0x27, 0x87, 0x3D, 0xA5,
|
||||
0xAC, 0xF0, 0xD0, 0xD2, 0x13, 0x73, 0x85, 0x89, 0x39, 0x73, 0xFF, 0x29, 0x28, 0xA7, 0x19, 0xFB,
|
||||
0xA8, 0x13, 0x31, 0x46, 0xFD, 0x70, 0x8D, 0x9B, 0x3F, 0x45, 0x21, 0x23, 0xEE, 0xA4, 0x9C, 0x80,
|
||||
0x62, 0x86, 0x03, 0x04, 0xF5, 0xAA, 0x83, 0xD9, 0x08, 0x63, 0x48, 0x5D, 0x1F, 0x0D, 0x01, 0xEE,
|
||||
0x6E, 0xD7, 0xC3, 0x53, 0x3B, 0x0A, 0x42, 0xA8, 0x1C, 0x03, 0x4A, 0x80, 0x32, 0x68, 0x16, 0x00,
|
||||
0xC8, 0x13, 0x96, 0xED, 0xCE, 0x94, 0x46, 0x8C, 0xAB, 0x04, 0x2A, 0x52, 0x90, 0x47, 0xD8, 0x04,
|
||||
0x46, 0xD2, 0xED, 0xD5, 0xD4, 0xE7, 0xD5, 0x39, 0x1E, 0xD3, 0xEE, 0x61, 0xFB, 0x0E, 0x3B, 0x3F,
|
||||
0x15, 0xCB, 0x85, 0x28, 0x35, 0x15, 0xE2, 0x0F, 0x22, 0x56, 0xE6, 0x05, 0x61, 0xB0, 0xC6, 0x1E,
|
||||
0xE1, 0x89, 0x64, 0x8A, 0x7A, 0x3D, 0x8B, 0x59, 0xF3, 0xC9, 0x60, 0xAC, 0x54, 0x0B, 0x82, 0x2C,
|
||||
0x0F, 0x75, 0xB0, 0x97, 0x89, 0x4B, 0x9C, 0x28, 0xE3, 0x29, 0x09, 0x82, 0x5C, 0xF5, 0xC8, 0x55,
|
||||
0xA8, 0xC6, 0xD3, 0x1F, 0x0B, 0x82, 0x14, 0x31, 0x36, 0x0A, 0xA7, 0x42, 0xEC, 0x01, 0x0C, 0xB2,
|
||||
0x20, 0xC2, 0x99, 0x91, 0x59, 0x8B, 0x2B, 0x01, 0xF2, 0xBB, 0x18, 0x00, 0x1C, 0x1B, 0xE9, 0x30,
|
||||
0x57, 0x52, 0x97, 0x4D, 0x6F, 0x56, 0x15, 0x50, 0x3B, 0x96, 0x40, 0x2E, 0x44, 0x7C, 0x6A, 0x56,
|
||||
0x8E, 0xBA, 0x56, 0xCF, 0x6A, 0x23, 0x38, 0xBA, 0xE0, 0x0A, 0x5E, 0x35, 0xE7, 0x10, 0x4C, 0x3A,
|
||||
0x81, 0xEB, 0x16, 0xFB, 0x84, 0xEB, 0x9E, 0x54, 0x4F, 0x1A, 0x73, 0xD9, 0xCF, 0xE7, 0x29, 0xF6,
|
||||
0x8A, 0x66, 0x86, 0x71, 0xA2, 0xA0, 0xD9, 0xA3, 0x43, 0x1C, 0x4C, 0x8B, 0xA2, 0x1A, 0xCF, 0x1A,
|
||||
0x4E, 0x7A, 0x1D, 0x41, 0x5C, 0x0E, 0x71, 0x91, 0xA0, 0x5E, 0xB3, 0xEB, 0xB5, 0x84, 0xA0, 0x02,
|
||||
0x16, 0xA2, 0x8E, 0x87, 0x9D, 0x34, 0xD4, 0x1C, 0xEC, 0xA2, 0xC8, 0x63, 0x05, 0xED, 0x50, 0x95,
|
||||
0xFF, 0xC6, 0xC2, 0xD7, 0x7F, 0xF2, 0x36, 0xDE, 0x12, 0xBE, 0xFC, 0x6B, 0x9A, 0x26, 0x08, 0x1A,
|
||||
0x0C, 0x30, 0x82, 0x73, 0x36, 0x96, 0xAD, 0x66, 0xB1, 0xB8, 0x89, 0xB0, 0x58, 0xD2, 0x60, 0xE6,
|
||||
0xDC, 0x93, 0xA6, 0xFF, 0xE2, 0x5C, 0xA6, 0x4B, 0xED, 0x28, 0x9C, 0x05, 0xF9, 0x12, 0x0A, 0x33,
|
||||
0x55, 0x27, 0xF4, 0x88, 0x70, 0x63, 0xE4, 0xFB, 0xDC, 0xB6, 0x32, 0x0B, 0x60, 0xE2, 0xE9, 0x12,
|
||||
0xA5, 0x16, 0xF1, 0xC9, 0xAB, 0x98, 0xB4, 0xEB, 0x22, 0x28, 0xD5, 0x0C, 0x6B, 0x25, 0xA4, 0x30,
|
||||
0x8F, 0x92, 0x90, 0x3D, 0x40, 0x1F, 0xD6, 0x8B, 0xFA, 0x9D, 0x69, 0xC2, 0x5E, 0x83, 0xDC, 0x90,
|
||||
0x02, 0x82, 0x6E, 0x07, 0x69, 0x55, 0xA3, 0x6A, 0x9C, 0xC0, 0x1F, 0xBD, 0xE0, 0x30, 0xA9, 0x72,
|
||||
0xBD, 0xBE, 0xD0, 0x7D, 0x9F, 0xCC, 0xF7, 0xEB, 0x24, 0x80, 0xE6, 0xAC, 0x59, 0x85, 0x4F, 0xA1,
|
||||
0x71, 0xD7, 0x2A, 0x3C, 0xE0, 0x57, 0x38, 0x7C, 0x9D, 0x53, 0x17, 0xFD, 0xB5, 0xD4, 0x11, 0x7D,
|
||||
0xFA, 0xA5, 0x2C, 0xF3, 0xEF, 0x60, 0x58, 0xE4, 0x54, 0xD8, 0x37, 0x0E, 0xCB, 0xF5, 0x09, 0xB7,
|
||||
0xF4, 0x45, 0x55, 0x49, 0xED, 0x2E, 0xCB, 0x6A, 0x02, 0x62, 0x7C, 0x68, 0x21, 0x01, 0xB4, 0x92,
|
||||
0xE6, 0xC2, 0x99, 0x55, 0x73, 0xBB, 0xC4, 0xF3, 0xCA, 0x1E, 0x1D, 0xCD, 0x55, 0x8F, 0x82, 0x9F,
|
||||
0xE7, 0xFD, 0x3A, 0xEF, 0xFE, 0x7B, 0x65, 0x47, 0x10, 0x73, 0xDF, 0x40, 0xF6, 0xFE, 0x93, 0x68,
|
||||
0x06, 0xCA, 0x3D, 0x49, 0xB2, 0xCE, 0xA3, 0x0F, 0x60, 0x5D, 0x74, 0x98, 0xAC, 0x91, 0x71, 0x25,
|
||||
0x1C, 0x11, 0x58, 0x89, 0xCD, 0x35, 0xA3, 0x01, 0x0D, 0x89, 0x58, 0xE6, 0x05, 0xD8, 0x43, 0xBC,
|
||||
0xC8, 0x2F, 0xB6, 0xE1, 0xB9, 0xE6, 0x91, 0xBB, 0x94, 0xCA, 0x94, 0x6D, 0xF4, 0x61, 0x4B, 0x87,
|
||||
0x8A, 0xAC, 0x00, 0x49, 0xBC, 0x0A, 0xE7, 0x15, 0x8A, 0x7B, 0xC1, 0xB7, 0xF5, 0x7B, 0x63, 0x38,
|
||||
0x09, 0xDC, 0x6E, 0x80, 0x27, 0xA9, 0x58, 0x23, 0xF9, 0x34, 0xE5, 0x4A, 0x6F, 0x79, 0x8F, 0x16,
|
||||
0x71, 0x2D, 0xAD, 0xAE, 0x34, 0xC2, 0x78, 0x8E, 0x65, 0xD1, 0x23, 0xE9, 0x02, 0x4B, 0x55, 0x17,
|
||||
0xA0, 0xCF, 0x92, 0x4D, 0xB8, 0x26, 0xC9, 0x41, 0x3E, 0xF4, 0xB0, 0xCB, 0xC4, 0xC2, 0x9B, 0x57,
|
||||
0xC7, 0x93, 0x42, 0x84, 0x94, 0x67, 0xDD, 0x5B, 0xE2, 0x99, 0xAD, 0x9F, 0x52, 0xDF, 0x2C, 0xA3,
|
||||
0xE5, 0x31, 0xB5, 0x9C, 0x3C, 0x55, 0x3C, 0x2D, 0xB1, 0xC2, 0x3C, 0x38, 0xD3, 0x97, 0x09, 0x0C,
|
||||
0x46, 0xE0, 0x3F, 0xB4, 0xFA, 0x29, 0x5F, 0x3F, 0xAF, 0xBE, 0x14, 0x27, 0xCB, 0x9E, 0x85, 0x94,
|
||||
0x48, 0x5B, 0x6C, 0x2E, 0x0A, 0x1A, 0x73, 0x98, 0xCD, 0x70, 0x5F, 0x58, 0x79, 0xC0, 0x6A, 0xAB,
|
||||
0x8F, 0xA0, 0x58, 0x72, 0x17, 0xC2, 0x6D, 0x26, 0xD8, 0xB6, 0xE8, 0xDE, 0xD9, 0xF2, 0xAC, 0x76,
|
||||
0xCA, 0x6F, 0xF6, 0x2A, 0xB6, 0x47, 0xC3, 0x1C, 0x0E, 0xA8, 0x03, 0x9A, 0x44, 0x0C, 0x37, 0xE5,
|
||||
0x92, 0xEE, 0x49, 0xE2, 0xD4, 0x27, 0xCB, 0xD3, 0x2E, 0x87, 0x41, 0x1E, 0x9A, 0xA2, 0x66, 0x35,
|
||||
0x7E, 0xAF, 0x93, 0x5F, 0x45, 0x31, 0x3C, 0x86, 0xFE, 0xC6, 0xEF, 0x5B, 0x4C, 0x1B, 0x8B, 0x30,
|
||||
0xCB, 0xA7, 0x41, 0x6D, 0x71, 0x09, 0x16, 0x57, 0x7A, 0xC4, 0x71, 0xB0, 0x5F, 0xB8, 0x39, 0x8E,
|
||||
0x67, 0x77, 0xFC, 0xC7, 0xC9, 0x2D, 0xBF, 0x3C, 0x98, 0x3D, 0x9D, 0x38, 0xE7, 0xCF, 0x00, 0xF2,
|
||||
0x4F, 0x06, 0xE4, 0x92, 0x5F, 0xB1, 0x3D, 0x14, 0x86, 0x2D, 0x95, 0xDF, 0x8B, 0xE7, 0x1E, 0x2E,
|
||||
0x08, 0x12, 0x87, 0x0C, 0x15, 0xE2, 0xB4, 0x54, 0x8F, 0x76, 0xE9, 0xDC, 0x35, 0x71, 0x5D, 0x2C,
|
||||
0x86, 0x15, 0x40, 0xB5, 0xA5, 0x16, 0x96, 0xE5, 0xAA, 0xE0, 0x9A, 0x9D, 0x52, 0xAD, 0xC7, 0x8F,
|
||||
0x9E, 0x3D, 0x7D, 0x7A, 0xDA, 0x7C, 0xEC, 0x77, 0xC2, 0x41, 0xF2, 0xF7, 0x57, 0x71, 0x09, 0x16,
|
||||
0xBD, 0x8C, 0xC1, 0x42, 0x34, 0x3C, 0x3F, 0x16, 0xD2, 0xE6, 0x34, 0x38, 0x06, 0x15, 0x56, 0x28,
|
||||
0x95, 0xE4, 0xC6, 0x32, 0xBD, 0x52, 0x92, 0x10, 0x82, 0xB4, 0x83, 0x82, 0x25, 0x24, 0x82, 0x4C,
|
||||
0xC4, 0xB4, 0x22, 0x4A, 0x9A, 0x2A, 0x22, 0xBB, 0x43, 0xC7, 0xF3, 0xAA, 0x0B, 0x6B, 0x92, 0xB0,
|
||||
0x4F, 0xA8, 0xB0, 0xB3, 0x4A, 0x20, 0xB0, 0x09, 0x76, 0x7E, 0x33, 0xB2, 0x82, 0x26, 0xD3, 0x2F,
|
||||
0x71, 0x7B, 0x6E, 0xFD, 0x2F, 0xA7, 0x76, 0x03, 0xD4, 0xC7, 0x3C, 0xDA, 0x93, 0x93, 0xAB, 0xC5,
|
||||
0xCC, 0x43, 0x90, 0x71, 0xAA, 0xD6, 0x7B, 0x2C, 0x02, 0x17, 0xE0, 0x5D, 0xEA, 0xD6, 0x05, 0x29,
|
||||
0x32, 0x05, 0x8B, 0xF3, 0xAB, 0xA9, 0x8A, 0xC9, 0x8A, 0xBA, 0x8C, 0x44, 0xBC, 0xAC, 0x51, 0x48,
|
||||
0x88, 0xA3, 0x03, 0x11, 0x59, 0x43, 0xE4, 0x45, 0xE0, 0xDA, 0x5A, 0x55, 0xB5, 0xFE, 0xF3, 0xC7,
|
||||
0x8B, 0x4B, 0x0D, 0x92, 0xAC, 0x3A, 0xAE, 0xD5, 0xAB, 0x55, 0xFD, 0xFC, 0x58, 0x92, 0x6C, 0x2C,
|
||||
0xEB, 0x99, 0x6A, 0xDD, 0x0A, 0x51, 0xF5, 0x33, 0x10, 0x55, 0xAD, 0x37, 0xB6, 0x17, 0x75, 0xA6,
|
||||
0x5A, 0x42, 0x12, 0x08, 0x19, 0x3F, 0x3D, 0x3D, 0xDB, 0x5E, 0xD0, 0x53, 0xD0, 0xE9, 0x37, 0x90,
|
||||
0x74, 0x06, 0xD6, 0x9D, 0xEE, 0x62, 0xDC, 0xA9, 0x6A, 0x71, 0x39, 0xA7, 0x8D, 0xEA, 0xB8, 0x71,
|
||||
0xB6, 0x83, 0x9C, 0x27, 0x6A, 0x72, 0x2B, 0xC9, 0x43, 0x36, 0x1D, 0xA9, 0x56, 0xFB, 0xE7, 0xE7,
|
||||
0x5A, 0x03, 0x74, 0xAC, 0x3F, 0x3B, 0xDD, 0x5E, 0x76, 0x43, 0xB5, 0x7E, 0xE1, 0x4A, 0x9E, 0xD4,
|
||||
0x41, 0x50, 0x63, 0x07, 0x25, 0x4F, 0x54, 0xEB, 0xA5, 0x90, 0x04, 0x52, 0xC6, 0xB5, 0xA7, 0x3B,
|
||||
0xA8, 0x04, 0xE1, 0xF5, 0x8B, 0x90, 0x04, 0xF1, 0xC5, 0xC3, 0xEB, 0x81, 0x92, 0xA0, 0x50, 0x0A,
|
||||
0xD7, 0xDC, 0x93, 0xA7, 0x8B, 0xD5, 0xA7, 0x70, 0xF9, 0xBE, 0x34, 0xFE, 0x1C, 0x41, 0x4D, 0x67,
|
||||
0x93, 0x8D, 0x93, 0x38, 0xE1, 0x03, 0x93, 0xE4, 0xE0, 0x61, 0xF9, 0x9B, 0xD3, 0x24, 0x7B, 0x4A,
|
||||
0xA0, 0x5A, 0xB5, 0xEA, 0x1A, 0x0B, 0x04, 0x6F, 0xBE, 0x0A, 0x0A, 0xE6, 0x82, 0x01, 0xAA, 0x02,
|
||||
0xA2, 0x44, 0x0E, 0x2B, 0x7D, 0x34, 0x86, 0x18, 0x3D, 0x51, 0x73, 0x79, 0xBD, 0x55, 0x89, 0x58,
|
||||
0xA2, 0x2D, 0x1A, 0xAB, 0xD6, 0xE9, 0xC9, 0x3A, 0x7F, 0xEF, 0x00, 0x47, 0x47, 0x74, 0x70, 0x1F,
|
||||
0x87, 0xE1, 0xC6, 0x88, 0xCC, 0x58, 0x55, 0xEB, 0x2A, 0x1B, 0xEF, 0x82, 0x4B, 0xB9, 0xBE, 0x03,
|
||||
0x2E, 0x39, 0x75, 0x24, 0x34, 0xE5, 0x7A, 0x02, 0x4D, 0x5D, 0x9D, 0x65, 0xC4, 0xD7, 0x04, 0x66,
|
||||
0x9D, 0xB6, 0xBB, 0xE0, 0xC2, 0x9B, 0x78, 0x80, 0x42, 0xB6, 0x31, 0x2A, 0x29, 0x23, 0x94, 0xB5,
|
||||
0x64, 0x74, 0x30, 0x44, 0x32, 0x55, 0xFE, 0x01, 0x78, 0x84, 0x88, 0x45, 0x81, 0x78, 0xFA, 0xBE,
|
||||
0x31, 0x22, 0x33, 0x56, 0xE8, 0x87, 0xD9, 0xF8, 0x60, 0xA8, 0xE4, 0xD4, 0xF9, 0x27, 0xE0, 0x32,
|
||||
0xC0, 0x36, 0x41, 0xDE, 0x07, 0xEC, 0xBA, 0xD0, 0xB2, 0x36, 0xC7, 0xA6, 0xC0, 0x0E, 0xF8, 0xC8,
|
||||
0x63, 0xE5, 0x46, 0x1C, 0x6F, 0xBC, 0x46, 0x9C, 0x13, 0xF7, 0xB5, 0x16, 0x8A, 0xD5, 0xE5, 0xEB,
|
||||
0x96, 0x37, 0x34, 0xD3, 0x73, 0xCB, 0x15, 0x42, 0x0D, 0x84, 0xE0, 0xAE, 0xB8, 0xE7, 0xDB, 0x5A,
|
||||
0x46, 0x5D, 0xB5, 0x5E, 0x04, 0x68, 0x22, 0xBE, 0x86, 0xDD, 0x65, 0xD1, 0xF3, 0x1E, 0x3B, 0xCA,
|
||||
0xAF, 0x70, 0x23, 0xB7, 0xCB, 0x0A, 0xEC, 0x45, 0x80, 0xB1, 0xBF, 0x9B, 0x94, 0x27, 0xD0, 0xCC,
|
||||
0x60, 0xB0, 0x9B, 0x10, 0x58, 0xB0, 0xDE, 0xE2, 0x01, 0x41, 0xDF, 0xC3, 0x82, 0x0B, 0x8D, 0x3A,
|
||||
0x1B, 0xA7, 0x05, 0xF0, 0xA8, 0xD6, 0xE5, 0xEF, 0x57, 0x1B, 0x17, 0x29, 0xF9, 0xF0, 0xE9, 0x21,
|
||||
0x11, 0x2E, 0xAB, 0x53, 0xA2, 0xA0, 0xBA, 0x70, 0xB3, 0xB9, 0x3C, 0x73, 0x1E, 0x7A, 0xC3, 0xB9,
|
||||
0xC4, 0xAE, 0x54, 0x41, 0xF1, 0x7C, 0x46, 0xCD, 0x99, 0xF9, 0x30, 0x1B, 0xBF, 0x5D, 0x05, 0x03,
|
||||
0x25, 0x3E, 0x74, 0x11, 0xD9, 0xBC, 0xAF, 0xA4, 0x8C, 0x02, 0x29, 0xE5, 0x05, 0x8C, 0xF6, 0x05,
|
||||
0x97, 0x9C, 0xF6, 0x60, 0x98, 0x25, 0x56, 0x1F, 0x1A, 0x38, 0x50, 0xA4, 0x4F, 0x9D, 0xCD, 0x1F,
|
||||
0x47, 0x24, 0x7C, 0xAA, 0x05, 0xA8, 0xBD, 0x86, 0xC1, 0xC6, 0x5D, 0x26, 0x15, 0xF0, 0x8D, 0xDB,
|
||||
0xCB, 0x65, 0xC4, 0xE8, 0x2E, 0x9D, 0xE5, 0x36, 0xF2, 0xFD, 0xC9, 0x2E, 0x6D, 0xA5, 0xED, 0xD1,
|
||||
0xC8, 0xD9, 0x5E, 0x02, 0xF4, 0x94, 0xB7, 0xAE, 0x4B, 0xEC, 0xED, 0xBB, 0x12, 0x74, 0x94, 0x97,
|
||||
0xB4, 0xFF, 0x40, 0xFE, 0x6F, 0x5C, 0xC5, 0xB1, 0xBD, 0x79, 0x81, 0xC0, 0x36, 0xA0, 0x78, 0xD3,
|
||||
0x56, 0x6E, 0x6F, 0xDE, 0xDC, 0xBE, 0x7D, 0xBF, 0x9F, 0xEA, 0x00, 0x73, 0x1E, 0xA8, 0x30, 0x70,
|
||||
0x6B, 0x0F, 0x5D, 0x13, 0x40, 0x89, 0xFA, 0x36, 0x38, 0xD5, 0x25, 0x50, 0xD7, 0xB7, 0xEF, 0xF6,
|
||||
0x85, 0x52, 0xFD, 0x70, 0x30, 0xD5, 0xBF, 0x07, 0x9C, 0x3E, 0x78, 0x78, 0x88, 0xBD, 0x2D, 0xB0,
|
||||
0x92, 0x8C, 0x1C, 0x2F, 0xE5, 0x15, 0x1F, 0x1D, 0xEC, 0x46, 0x2E, 0x53, 0xE5, 0x1F, 0x70, 0x1B,
|
||||
0x07, 0x51, 0xF1, 0x41, 0x28, 0xBD, 0x4D, 0xF2, 0x48, 0x4E, 0xD5, 0xBA, 0x19, 0x0F, 0x68, 0x18,
|
||||
0x05, 0x0F, 0x6C, 0xA8, 0xCB, 0x11, 0xD9, 0xE5, 0xC9, 0xE0, 0x4C, 0x15, 0x89, 0x48, 0xFA, 0x68,
|
||||
0x90, 0x3F, 0xD9, 0xCF, 0x30, 0xA9, 0x57, 0x1B, 0x5F, 0x15, 0x15, 0x2E, 0xFC, 0x5B, 0x02, 0xD3,
|
||||
0xDD, 0xA2, 0xEF, 0x74, 0x79, 0xDF, 0x79, 0xD1, 0xDE, 0x4F, 0x29, 0xEB, 0x1E, 0xAC, 0xE1, 0x74,
|
||||
0x0F, 0xDA, 0x70, 0x14, 0xF9, 0x6D, 0x67, 0x06, 0xD3, 0x96, 0x37, 0x11, 0x09, 0x23, 0xDC, 0x3B,
|
||||
0x6F, 0x73, 0x03, 0x91, 0x7F, 0xA8, 0x3E, 0xDE, 0x25, 0x75, 0x52, 0x35, 0x8A, 0x99, 0x73, 0x32,
|
||||
0xCB, 0x9B, 0x27, 0x5F, 0x35, 0x6B, 0x4E, 0xD6, 0x6A, 0xBB, 0x4B, 0xD2, 0x70, 0x4B, 0x6C, 0x4C,
|
||||
0x3C, 0xFE, 0xD2, 0xE3, 0xA6, 0x80, 0xE4, 0x78, 0x25, 0x26, 0x4A, 0x5B, 0x1E, 0xED, 0x82, 0x4D,
|
||||
0x7D, 0x17, 0x6C, 0xF2, 0x1A, 0x15, 0xE1, 0x39, 0xFD, 0x46, 0x9D, 0xA6, 0x56, 0x3F, 0xFB, 0x96,
|
||||
0xF0, 0x74, 0x06, 0x9B, 0xD7, 0x34, 0xE0, 0x51, 0xAD, 0xAB, 0x77, 0xFB, 0xA9, 0x69, 0x7C, 0xB2,
|
||||
0x07, 0xD6, 0xB4, 0x9D, 0x2A, 0x98, 0x30, 0xEA, 0xD0, 0x4B, 0xB1, 0xD1, 0x16, 0x68, 0x8C, 0xB8,
|
||||
0xE2, 0xBF, 0xEF, 0x09, 0x8D, 0xD1, 0xC3, 0xD1, 0xF8, 0xCA, 0x1D, 0x66, 0xF4, 0x3D, 0xE0, 0x13,
|
||||
0xA0, 0xD1, 0x87, 0x6E, 0x1F, 0x6D, 0x8C, 0x51, 0xC2, 0xA7, 0x5A, 0xEF, 0xD1, 0x48, 0x79, 0xF1,
|
||||
0xFA, 0x72, 0x2F, 0x58, 0xA5, 0x93, 0x1E, 0x06, 0xAF, 0xCC, 0xE4, 0x43, 0x63, 0xE6, 0x61, 0x7F,
|
||||
0xF3, 0xA4, 0xE2, 0x4C, 0xAA, 0xF5, 0x0A, 0xFB, 0xA1, 0xD2, 0xA6, 0x41, 0xB2, 0xED, 0x68, 0x2F,
|
||||
0xA8, 0x89, 0x99, 0x0F, 0x03, 0x99, 0x34, 0xFA, 0xD0, 0x78, 0xF5, 0xFA, 0x24, 0x08, 0x68, 0xB0,
|
||||
0x31, 0x64, 0x09, 0x9F, 0x6A, 0xBD, 0x2C, 0xBF, 0x16, 0xA3, 0xBD, 0xC0, 0x95, 0xCE, 0x7A, 0x18,
|
||||
0xC4, 0x32, 0x9B, 0x0F, 0x0D, 0xDA, 0xD0, 0xF5, 0xC8, 0x60, 0x63, 0xC8, 0x04, 0x97, 0x6A, 0xFD,
|
||||
0x56, 0x7E, 0x0E, 0x9F, 0x7B, 0x81, 0x4B, 0xCE, 0x78, 0x18, 0xB0, 0x12, 0x6B, 0x0F, 0x0D, 0x95,
|
||||
0x63, 0x8F, 0x36, 0x06, 0x0A, 0x78, 0x54, 0xEB, 0xBA, 0xFD, 0xBB, 0xA2, 0x5D, 0xD3, 0x91, 0xCF,
|
||||
0x5F, 0xFC, 0x53, 0x6E, 0xDE, 0xE8, 0x7B, 0x41, 0x8C, 0x4F, 0x7D, 0x18, 0xBC, 0x84, 0xD1, 0x87,
|
||||
0x46, 0x4B, 0xBC, 0x04, 0xDC, 0x41, 0x9B, 0x97, 0xC3, 0x94, 0x91, 0xBF, 0xFB, 0x02, 0x23, 0xE5,
|
||||
0x0A, 0xED, 0xA7, 0x20, 0x66, 0xF3, 0xEE, 0x63, 0xD1, 0x3E, 0x33, 0xF2, 0xD0, 0x38, 0xB9, 0xC8,
|
||||
0xC6, 0x1F, 0x1C, 0xCC, 0xB6, 0x79, 0xF1, 0x22, 0xC7, 0xAB, 0x5A, 0xCF, 0xE1, 0x40, 0xB9, 0x16,
|
||||
0x07, 0xFB, 0x5A, 0x72, 0xE4, 0xE7, 0xDF, 0x07, 0x6A, 0x05, 0x7B, 0xBF, 0x0B, 0xE0, 0x60, 0x81,
|
||||
0x47, 0xBB, 0xFE, 0x56, 0xEF, 0x53, 0x17, 0xD8, 0x13, 0xF8, 0xDE, 0xCB, 0xE3, 0xFD, 0x02, 0x38,
|
||||
0x53, 0x62, 0x6F, 0x18, 0xE6, 0xEC, 0xDE, 0x07, 0x8C, 0xE9, 0x66, 0x04, 0xF1, 0x58, 0x40, 0xEE,
|
||||
0x41, 0x5E, 0x87, 0x94, 0x24, 0x93, 0x8F, 0x6E, 0x30, 0x2B, 0x87, 0x8C, 0x78, 0x9E, 0x6A, 0xBD,
|
||||
0xC0, 0x4C, 0xB9, 0xE5, 0xC3, 0xF3, 0x63, 0x49, 0xF0, 0x70, 0x29, 0xC9, 0x0B, 0xFF, 0x7C, 0xDF,
|
||||
0x38, 0xEA, 0xAB, 0xD6, 0x2D, 0xDF, 0x44, 0x0D, 0xB2, 0xF8, 0xD1, 0xE6, 0xC2, 0x84, 0x13, 0xB1,
|
||||
0x1F, 0x50, 0x50, 0x2A, 0x03, 0x29, 0xD9, 0xAA, 0xAA, 0x2A, 0xE9, 0x28, 0x77, 0xCE, 0xBA, 0x11,
|
||||
0xC4, 0x0A, 0x8F, 0xB2, 0xF5, 0xD3, 0xF1, 0x6F, 0x61, 0xED, 0xD5, 0x5F, 0xD6, 0x9E, 0x1F, 0xFB,
|
||||
0x68, 0x89, 0xBB, 0x57, 0xA0, 0x70, 0x2E, 0x77, 0xB1, 0xAF, 0x10, 0x95, 0x6D, 0xA6, 0x10, 0x9E,
|
||||
0x98, 0xED, 0xA7, 0xC9, 0xCC, 0x9A, 0xDB, 0x67, 0x93, 0x3E, 0xB0, 0x7D, 0x58, 0xD2, 0x8A, 0x1D,
|
||||
0x37, 0x49, 0x3F, 0xE4, 0xC3, 0xCC, 0xFD, 0xFF, 0xFB, 0xEF, 0xBA, 0x98, 0x21, 0xFD, 0x6E, 0x4E,
|
||||
0x31, 0x55, 0x09, 0x03, 0xBB, 0xA5, 0xAE, 0xDA, 0x9A, 0xB1, 0xC2, 0xF2, 0xE3, 0x65, 0xA6, 0xCF,
|
||||
0x11, 0x2F, 0xF1, 0xF5, 0x79, 0x68, 0x07, 0x64, 0xC0, 0xAC, 0x7F, 0x39, 0xD4, 0x8E, 0xFA, 0xD8,
|
||||
0x67, 0x15, 0xE4, 0x38, 0x37, 0x43, 0x18, 0xBC, 0x22, 0x21, 0xC3, 0xE0, 0x05, 0xAD, 0x74, 0xFD,
|
||||
0xF6, 0x75, 0x5B, 0x6E, 0x51, 0x79, 0x45, 0x91, 0x83, 0x9D, 0x92, 0xE1, 0x46, 0xBE, 0x90, 0xA3,
|
||||
0xE9, 0xD3, 0x74, 0xA8, 0x74, 0xB4, 0x2B, 0x7D, 0xEA, 0x41, 0xD0, 0xB6, 0x9B, 0xB2, 0x3C, 0x68,
|
||||
0x57, 0x15, 0x9E, 0xE3, 0xFA, 0xD4, 0x46, 0x21, 0x2E, 0xA5, 0x89, 0x5E, 0x32, 0xDB, 0xAD, 0xAB,
|
||||
0x4A, 0xB2, 0xF6, 0xB9, 0xA8, 0xF1, 0x0D, 0x4F, 0x60, 0xF4, 0x5D, 0x53, 0x10, 0x89, 0x47, 0x8A,
|
||||
0x25, 0x53, 0x8C, 0xE5, 0x97, 0xF3, 0x65, 0xEA, 0x63, 0xC9, 0x22, 0x1E, 0x5C, 0xE6, 0x89, 0x65,
|
||||
0x64, 0xA5, 0xD4, 0x51, 0xA7, 0x4F, 0x18, 0xA7, 0x2C, 0xD5, 0x4A, 0x09, 0x55, 0x52, 0x4A, 0xCC,
|
||||
0x00, 0xB3, 0x28, 0xF0, 0x9B, 0x31, 0x00, 0x1B, 0x32, 0xE5, 0xBA, 0xF5, 0xF1, 0x87, 0xA9, 0x1D,
|
||||
0x1F, 0x8B, 0x97, 0x5D, 0xA9, 0x77, 0x31, 0x44, 0x41, 0xEB, 0x87, 0xE9, 0x55, 0x85, 0x38, 0xF1,
|
||||
0x63, 0x98, 0x03, 0xC6, 0xED, 0xF8, 0x63, 0xD3, 0xE5, 0xFF, 0x71, 0x41, 0xBB, 0xD6, 0x2B, 0xAC,
|
||||
0x87, 0x7D, 0xED, 0xA6, 0x65, 0x4D, 0x39, 0x37, 0xF5, 0x70, 0xC5, 0xA3, 0x5D, 0xED, 0x63, 0x80,
|
||||
0x3F, 0x47, 0x18, 0x84, 0x31, 0xAA, 0xFC, 0x30, 0xBD, 0x8E, 0x15, 0x97, 0xF8, 0x24, 0xEC, 0x61,
|
||||
0xC7, 0x50, 0x42, 0x86, 0x58, 0x14, 0x9A, 0x70, 0xFA, 0xA6, 0x22, 0xC7, 0xF1, 0x47, 0x3D, 0xD6,
|
||||
0x63, 0x98, 0x46, 0xB1, 0x5B, 0x99, 0x97, 0x3D, 0x6A, 0x8B, 0x57, 0x3A, 0x2B, 0x34, 0x20, 0x5D,
|
||||
0xE2, 0x37, 0xA5, 0x6E, 0xB8, 0x75, 0x05, 0x33, 0x81, 0x7B, 0x78, 0x48, 0x71, 0x00, 0x38, 0x1A,
|
||||
0x5A, 0x49, 0xC6, 0x61, 0x49, 0x8F, 0x0D, 0x77, 0x81, 0x20, 0xC0, 0x7D, 0x3A, 0xC4, 0x79, 0x9A,
|
||||
0xEE, 0x72, 0x21, 0x69, 0x7E, 0x96, 0x74, 0xE3, 0x2A, 0xDB, 0x6B, 0xDE, 0x3A, 0xAA, 0xC6, 0x46,
|
||||
0x6F, 0xA5, 0xD0, 0x15, 0x3C, 0xB5, 0xD8, 0x20, 0x2D, 0xED, 0xCA, 0x68, 0x1B, 0xD7, 0x3A, 0x70,
|
||||
0x5E, 0xB7, 0x8E, 0x34, 0x3F, 0xF2, 0xBC, 0xA3, 0xD6, 0xB5, 0xFE, 0xF7, 0xDF, 0xD7, 0x4D, 0x1E,
|
||||
0x04, 0x37, 0xCD, 0x19, 0xE2, 0xAD, 0x56, 0x4B, 0x86, 0xC2, 0x05, 0x38, 0x32, 0xC3, 0xDE, 0x68,
|
||||
0xB7, 0x8E, 0x8E, 0xDA, 0x46, 0x76, 0xDC, 0x6A, 0xEB, 0xA6, 0xB8, 0x2E, 0x80, 0x36, 0x92, 0x4F,
|
||||
0x38, 0x6B, 0x5C, 0x3F, 0x7E, 0x7C, 0x73, 0xD4, 0x6A, 0xB5, 0x2F, 0x78, 0x88, 0x99, 0x47, 0x70,
|
||||
0xA8, 0x95, 0x10, 0xB6, 0xA5, 0x5C, 0xE2, 0x5C, 0xB4, 0x2F, 0xB0, 0x36, 0xD4, 0x4D, 0x97, 0xFF,
|
||||
0x29, 0xA1, 0x6E, 0xFE, 0x82, 0xE6, 0x6A, 0x4C, 0x37, 0xB0, 0x16, 0xEA, 0x20, 0x1C, 0xF3, 0xB1,
|
||||
0x2B, 0xC6, 0xA5, 0xF4, 0xAD, 0xA4, 0x1C, 0xAD, 0xAB, 0x8D, 0x75, 0x13, 0xF3, 0x3F, 0xA5, 0x62,
|
||||
0xE3, 0x48, 0x69, 0x60, 0xDE, 0xF6, 0x45, 0x4F, 0xF3, 0x75, 0xB3, 0x0B, 0x7F, 0x74, 0x3D, 0x6E,
|
||||
0x66, 0x70, 0x42, 0x34, 0x04, 0x93, 0x5B, 0x11, 0xB1, 0x34, 0xB8, 0xF4, 0x3C, 0xAD, 0x24, 0x77,
|
||||
0xE0, 0x95, 0xF4, 0x0A, 0x74, 0xA2, 0x1B, 0xC4, 0xB3, 0x41, 0xF8, 0x98, 0xFA, 0xB6, 0x47, 0xEC,
|
||||
0xBB, 0x96, 0xC6, 0x1D, 0x87, 0x21, 0x45, 0xE4, 0xDE, 0xE0, 0x37, 0xD4, 0xC1, 0x7A, 0x1C, 0x83,
|
||||
0x7A, 0x22, 0xEE, 0x64, 0x84, 0xCA, 0xF0, 0xF9, 0x98, 0xC4, 0x60, 0x96, 0x73, 0x90, 0x66, 0x32,
|
||||
0xA2, 0x95, 0xAB, 0xCA, 0xA7, 0x90, 0x27, 0x61, 0xBC, 0x84, 0xE4, 0x3E, 0xD5, 0x8A, 0x3D, 0x36,
|
||||
0xA7, 0x63, 0x1B, 0x94, 0x22, 0x1A, 0x80, 0xF2, 0x67, 0x1B, 0xEC, 0xFD, 0xCB, 0x38, 0xAA, 0xF1,
|
||||
0xD0, 0xD5, 0x93, 0xE8, 0xFC, 0x34, 0x0B, 0x5F, 0xE8, 0x53, 0x37, 0x1E, 0xE6, 0xC3, 0xAB, 0xC9,
|
||||
0xCF, 0x10, 0x5C, 0xB2, 0x72, 0x41, 0x98, 0xDC, 0xAD, 0xA3, 0x99, 0x95, 0x57, 0xA0, 0xF6, 0x56,
|
||||
0x53, 0x67, 0x9D, 0x10, 0xC8, 0xFA, 0xAB, 0xC9, 0x0A, 0xAD, 0x0E, 0x48, 0xFD, 0xD5, 0xA4, 0xB9,
|
||||
0x46, 0x06, 0x84, 0x74, 0x35, 0x61, 0xBE, 0x7C, 0x03, 0xE5, 0x40, 0x82, 0x35, 0x22, 0xBE, 0x43,
|
||||
0x47, 0x90, 0xD3, 0x74, 0xA0, 0x81, 0x4A, 0x15, 0xE2, 0x83, 0x0D, 0x2F, 0x7F, 0x7D, 0xFD, 0xAA,
|
||||
0x55, 0xCA, 0x37, 0xD8, 0x52, 0x6C, 0x7C, 0x96, 0x0C, 0x9F, 0x2A, 0xBC, 0x8E, 0x73, 0x28, 0x7F,
|
||||
0x2A, 0x99, 0x67, 0xB5, 0x12, 0x07, 0x94, 0x53, 0x7C, 0x84, 0x18, 0xBC, 0x5B, 0x90, 0x40, 0x07,
|
||||
0x99, 0x80, 0xA6, 0x57, 0x0C, 0x13, 0x3E, 0xDF, 0x4C, 0x18, 0x54, 0x2E, 0x34, 0x00, 0xF8, 0xF1,
|
||||
0xC5, 0x07, 0xBB, 0x03, 0xD5, 0xEA, 0x1A, 0x31, 0x5C, 0xF1, 0xE9, 0x08, 0xC2, 0x40, 0x4A, 0x8E,
|
||||
0x0D, 0xBA, 0xC8, 0x8F, 0xC5, 0x85, 0x7E, 0xF1, 0x82, 0x84, 0xF5, 0xAA, 0x38, 0x3D, 0x04, 0x7B,
|
||||
0x4E, 0xB5, 0xE6, 0xD5, 0x05, 0xB0, 0x9B, 0x9F, 0x41, 0xBA, 0xE1, 0x17, 0xB9, 0x3B, 0x90, 0x04,
|
||||
0xB1, 0xB1, 0x55, 0x9C, 0x65, 0xB9, 0xD0, 0xE3, 0x05, 0x5F, 0x88, 0xE3, 0xB9, 0x9D, 0x45, 0x5A,
|
||||
0xB0, 0x1A, 0x1C, 0x9E, 0xDF, 0xBA, 0x11, 0xDE, 0x4B, 0x90, 0xFB, 0x66, 0x15, 0x68, 0xD9, 0x3D,
|
||||
0x41, 0x36, 0xFF, 0xBD, 0x5F, 0x49, 0x6F, 0x06, 0x45, 0xBD, 0xC0, 0xCC, 0x40, 0x37, 0x82, 0xAC,
|
||||
0x63, 0xAD, 0xA8, 0x28, 0x71, 0xA2, 0x79, 0x74, 0x8F, 0x62, 0x98, 0x6B, 0x3E, 0xBC, 0x97, 0x20,
|
||||
0xFF, 0x4E, 0x05, 0xE8, 0x12, 0x2D, 0xE8, 0x12, 0xE9, 0x46, 0x94, 0xE9, 0x92, 0x95, 0xBD, 0x74,
|
||||
0xF6, 0xD1, 0x3D, 0xC2, 0xD3, 0x82, 0xA7, 0x1B, 0xE3, 0xD5, 0x54, 0x85, 0x57, 0x24, 0x41, 0x81,
|
||||
0xD1, 0x82, 0x02, 0x23, 0xDD, 0x18, 0x65, 0x0A, 0x64, 0x25, 0x33, 0x55, 0x60, 0xB2, 0x26, 0xFD,
|
||||
0xE4, 0x0D, 0x15, 0xE8, 0xF0, 0x65, 0x0D, 0xE1, 0xAC, 0xF8, 0xEA, 0xC6, 0xE5, 0x3D, 0xB4, 0xE9,
|
||||
0x1E, 0x4F, 0xD0, 0xF5, 0x72, 0x41, 0xD7, 0x4B, 0xDD, 0x78, 0x72, 0x7E, 0x29, 0x1B, 0x09, 0x14,
|
||||
0x6F, 0xA2, 0x4D, 0x78, 0x45, 0x33, 0x88, 0xF6, 0x85, 0x7F, 0x42, 0xF0, 0x4E, 0xE6, 0x58, 0x92,
|
||||
0xBA, 0x9A, 0x31, 0x5D, 0x68, 0xC8, 0xC3, 0x01, 0xD3, 0x4A, 0xEF, 0x3C, 0x0C, 0xAB, 0x8C, 0xE4,
|
||||
0xAD, 0x4B, 0xA5, 0xFD, 0xF3, 0x73, 0x85, 0x06, 0x8A, 0xF8, 0x0F, 0x03, 0x4A, 0x90, 0xED, 0x50,
|
||||
0x55, 0xE4, 0x26, 0x72, 0x05, 0xF3, 0x7F, 0xCB, 0x01, 0x21, 0xA5, 0xB0, 0x1E, 0x09, 0x15, 0x17,
|
||||
0xF3, 0xFD, 0x1B, 0xF8, 0x88, 0x63, 0x4F, 0x89, 0xA3, 0x24, 0x5A, 0xE8, 0x26, 0x3F, 0xD2, 0x3A,
|
||||
0xDA, 0x44, 0x37, 0x8E, 0x26, 0xA9, 0x47, 0x41, 0x4B, 0xDE, 0x5B, 0x32, 0x15, 0x41, 0xC7, 0x2F,
|
||||
0x07, 0xD1, 0xF1, 0x4B, 0x41, 0xC7, 0x2F, 0x00, 0xD8, 0x2C, 0x03, 0x7A, 0x52, 0x43, 0x30, 0xA3,
|
||||
0xAA, 0x27, 0xBD, 0x10, 0x5A, 0x57, 0x33, 0xBF, 0xCC, 0x4C, 0x16, 0x95, 0xF2, 0x48, 0x6E, 0xD7,
|
||||
0x3E, 0x3F, 0x16, 0xFF, 0x6A, 0xEE, 0xFF, 0x81, 0x09, 0x07, 0x8B, 0x81, 0x4E, 0x00, 0x00
|
||||
};
|
@ -36,12 +36,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,7 @@ void setup(){
|
||||
reset occurs.
|
||||
*/
|
||||
Serial.println("Going to sleep now");
|
||||
Serial.flush();
|
||||
esp_deep_sleep_start();
|
||||
Serial.println("This will never be printed");
|
||||
}
|
||||
|
@ -26,12 +26,12 @@ void print_wakeup_reason(){
|
||||
|
||||
switch(wakeup_reason)
|
||||
{
|
||||
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case 3 : Serial.println("Wakeup caused by timer"); break;
|
||||
case 4 : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case 5 : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.println("Wakeup was not caused by deep sleep"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
|
||||
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
|
||||
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
|
||||
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
|
||||
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
97
libraries/ESP32/examples/FreeRTOS/FreeRTOS.ino
Normal file
97
libraries/ESP32/examples/FreeRTOS/FreeRTOS.ino
Normal file
@ -0,0 +1,97 @@
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define ARDUINO_RUNNING_CORE 0
|
||||
#else
|
||||
#define ARDUINO_RUNNING_CORE 1
|
||||
#endif
|
||||
|
||||
#ifndef LED_BUILTIN
|
||||
#define LED_BUILTIN 13
|
||||
#endif
|
||||
|
||||
// define two tasks for Blink & AnalogRead
|
||||
void TaskBlink( void *pvParameters );
|
||||
void TaskAnalogReadA3( void *pvParameters );
|
||||
|
||||
// the setup function runs once when you press reset or power the board
|
||||
void setup() {
|
||||
|
||||
// initialize serial communication at 115200 bits per second:
|
||||
Serial.begin(115200);
|
||||
|
||||
// Now set up two tasks to run independently.
|
||||
xTaskCreatePinnedToCore(
|
||||
TaskBlink
|
||||
, "TaskBlink" // A name just for humans
|
||||
, 1024 // This stack size can be checked & adjusted by reading the Stack Highwater
|
||||
, NULL
|
||||
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
|
||||
, NULL
|
||||
, ARDUINO_RUNNING_CORE);
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
TaskAnalogReadA3
|
||||
, "AnalogReadA3"
|
||||
, 1024 // Stack size
|
||||
, NULL
|
||||
, 1 // Priority
|
||||
, NULL
|
||||
, ARDUINO_RUNNING_CORE);
|
||||
|
||||
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Empty. Things are done in Tasks.
|
||||
}
|
||||
|
||||
/*--------------------------------------------------*/
|
||||
/*---------------------- Tasks ---------------------*/
|
||||
/*--------------------------------------------------*/
|
||||
|
||||
void TaskBlink(void *pvParameters) // This is a task.
|
||||
{
|
||||
(void) pvParameters;
|
||||
|
||||
/*
|
||||
Blink
|
||||
Turns on an LED on for one second, then off for one second, repeatedly.
|
||||
|
||||
If you want to know what pin the on-board LED is connected to on your ESP32 model, check
|
||||
the Technical Specs of your board.
|
||||
*/
|
||||
|
||||
// initialize digital LED_BUILTIN on pin 13 as an output.
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
for (;;) // A Task shall never return or exit.
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
|
||||
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
|
||||
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
|
||||
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
|
||||
}
|
||||
}
|
||||
|
||||
void TaskAnalogReadA3(void *pvParameters) // This is a task.
|
||||
{
|
||||
(void) pvParameters;
|
||||
|
||||
/*
|
||||
AnalogReadSerial
|
||||
Reads an analog input on pin A3, prints the result to the serial monitor.
|
||||
Graphical representation is available using serial plotter (Tools > Serial Plotter menu)
|
||||
Attach the center pin of a potentiometer to pin A3, and the outside pins to +5V and ground.
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// read the input on analog pin A3:
|
||||
int sensorValueA3 = analogRead(A3);
|
||||
// print out the value you read:
|
||||
Serial.println(sensorValueA3);
|
||||
vTaskDelay(10); // one tick delay (15ms) in between reads for stability
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#include <Arduino.h>
|
||||
#include <FunctionalInterrupt.h>
|
||||
|
||||
#define BUTTON1 16
|
||||
#define BUTTON2 17
|
||||
|
||||
class Button
|
||||
{
|
||||
public:
|
||||
Button(uint8_t reqPin) : PIN(reqPin){
|
||||
pinMode(PIN, INPUT_PULLUP);
|
||||
attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
|
||||
};
|
||||
~Button() {
|
||||
detachInterrupt(PIN);
|
||||
}
|
||||
|
||||
void IRAM_ATTR isr() {
|
||||
numberKeyPresses += 1;
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
void checkPressed() {
|
||||
if (pressed) {
|
||||
Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses);
|
||||
pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t PIN;
|
||||
volatile uint32_t numberKeyPresses;
|
||||
volatile bool pressed;
|
||||
};
|
||||
|
||||
Button button1(BUTTON1);
|
||||
Button button2(BUTTON2);
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
button1.checkPressed();
|
||||
button2.checkPressed();
|
||||
}
|
61
libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino
Normal file
61
libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino
Normal file
@ -0,0 +1,61 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
rmt_data_t my_data[256];
|
||||
rmt_data_t data[256];
|
||||
|
||||
rmt_obj_t* rmt_send = NULL;
|
||||
rmt_obj_t* rmt_recv = NULL;
|
||||
|
||||
static EventGroupHandle_t events;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
|
||||
{
|
||||
Serial.println("init sender failed\n");
|
||||
}
|
||||
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
|
||||
{
|
||||
Serial.println("init receiver failed\n");
|
||||
}
|
||||
|
||||
float realTick = rmtSetTick(rmt_send, 100);
|
||||
printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Init data
|
||||
int i;
|
||||
for (i=0; i<255; i++) {
|
||||
data[i].val = 0x80010001 + ((i%13)<<16) + 13-(i%13);
|
||||
}
|
||||
data[255].val = 0;
|
||||
|
||||
// Start receiving
|
||||
rmtReadAsync(rmt_recv, my_data, 100, events, false, 0);
|
||||
|
||||
// Send in continous mode
|
||||
rmtWrite(rmt_send, data, 100);
|
||||
|
||||
// Wait for data
|
||||
xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY);
|
||||
|
||||
// Printout the received data plus the original values
|
||||
for (i=0; i<60; i++)
|
||||
{
|
||||
Serial.printf("%08x=%08x ", my_data[i], data[i] );
|
||||
if (!((i+1)%4)) Serial.println("\n");
|
||||
}
|
||||
Serial.println("\n");
|
||||
|
||||
delay(2000);
|
||||
}
|
204
libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino
Normal file
204
libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino
Normal file
@ -0,0 +1,204 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
//
|
||||
// Note: This example uses a FrSKY device communication
|
||||
// using XJT D12 protocol
|
||||
//
|
||||
// ; 0 bit = 6us low/10us high
|
||||
// ; 1 bit = 14us low/10us high
|
||||
// ;
|
||||
// ; --------+ +----------+ +----------+
|
||||
// ; | | | | |
|
||||
// ; | 0 | | 1 | |
|
||||
// ; | | | | |
|
||||
// ; | | | | |
|
||||
// ; +-------+ +-----------------+ +---------
|
||||
// ;
|
||||
// ; | 6us 10us | 14us 10us |
|
||||
// ; |-------|----------|-----------------|----------|--------
|
||||
// ; | 16us | 24us |
|
||||
|
||||
// Typedef of received frame
|
||||
//
|
||||
// ; 0x00 - Sync, 0x7E (sync header ID)
|
||||
// ; 0x01 - Rx ID, 0x?? (receiver ID number, 0x00-0x??)
|
||||
// ; 0x02 - Flags 1, 0x?? (used for failsafe and binding)
|
||||
// ; 0x03 - Flags 2, 0x00 (reserved)
|
||||
// ; 0x04-0x06, Channels 1/9 and 2/10
|
||||
// ; 0x07-0x09, Channels 3/11 and 4/12
|
||||
// ; 0x0A-0x0C, Channels 5/13 and 6/14
|
||||
// ; 0x0D-0x0F, Channels 7/15 and 8/16
|
||||
// ; 0x10 - 0x00, always zero
|
||||
// ; 0x11 - CRC-16 High
|
||||
// ; 0x12 - CRC-16 Low
|
||||
// ; 0x13 - Tail, 0x7E (tail ID)
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t head;//0x7E
|
||||
uint8_t rxid;//Receiver Number
|
||||
uint8_t flags;//Range:0x20, Bind:0x01
|
||||
uint8_t reserved0;//0x00
|
||||
union {
|
||||
struct {
|
||||
uint8_t ch0_l;
|
||||
uint8_t ch0_h:4;
|
||||
uint8_t ch1_l:4;
|
||||
uint8_t ch1_h;
|
||||
};
|
||||
uint8_t bytes[3];
|
||||
} channels[4];
|
||||
uint8_t reserved1;//0x00
|
||||
uint8_t crc_h;
|
||||
uint8_t crc_l;
|
||||
uint8_t tail;//0x7E
|
||||
};
|
||||
uint8_t buffer[20];
|
||||
} xjt_packet_t;
|
||||
|
||||
#define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11)
|
||||
|
||||
rmt_obj_t* rmt_recv = NULL;
|
||||
|
||||
static uint32_t *s_channels;
|
||||
static uint32_t channels[16];
|
||||
static uint8_t xjt_flags = 0x0;
|
||||
static uint8_t xjt_rxid = 0x0;
|
||||
|
||||
static bool xjtReceiveBit(size_t index, bool bit){
|
||||
static xjt_packet_t xjt;
|
||||
static uint8_t xjt_bit_index = 8;
|
||||
static uint8_t xht_byte_index = 0;
|
||||
static uint8_t xht_ones = 0;
|
||||
|
||||
if(!index){
|
||||
xjt_bit_index = 8;
|
||||
xht_byte_index = 0;
|
||||
xht_ones = 0;
|
||||
}
|
||||
|
||||
if(xht_byte_index > 19){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
if(bit){
|
||||
xht_ones++;
|
||||
if(xht_ones > 5 && xht_byte_index && xht_byte_index < 19){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
//add bit
|
||||
xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index);
|
||||
} else if(xht_ones == 5 && xht_byte_index && xht_byte_index < 19){
|
||||
xht_ones = 0;
|
||||
//skip bit
|
||||
return true;
|
||||
} else {
|
||||
xht_ones = 0;
|
||||
//add bit
|
||||
xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index);
|
||||
}
|
||||
if ((!xjt_bit_index) || (xjt_bit_index==1 && xht_byte_index==19) ) {
|
||||
xjt_bit_index = 8;
|
||||
if(!xht_byte_index && xjt.buffer[0] != 0x7E){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
xht_byte_index++;
|
||||
if(xht_byte_index == 20){
|
||||
//done
|
||||
if(xjt.buffer[19] != 0x7E){
|
||||
//fail!
|
||||
return false;
|
||||
}
|
||||
//check crc?
|
||||
|
||||
xjt_flags = xjt.flags;
|
||||
xjt_rxid = xjt.rxid;
|
||||
for(int i=0; i<4; i++){
|
||||
uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8);
|
||||
ch0 = ((ch0 * 2) + 2452) / 3;
|
||||
uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4);
|
||||
ch1 = ((ch1 * 2) + 2452) / 3;
|
||||
uint8_t c0n = i*2;
|
||||
if(xjt.channels[i].ch0_h & 0x8){
|
||||
c0n += 8;
|
||||
}
|
||||
uint8_t c1n = i*2+1;
|
||||
if(xjt.channels[i].ch1_h & 0x80){
|
||||
c1n += 8;
|
||||
}
|
||||
s_channels[c0n] = ch0;
|
||||
s_channels[c1n] = ch1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels){
|
||||
size_t chan = 0;
|
||||
bool valid = true;
|
||||
rmt_data_t* it = NULL;
|
||||
|
||||
if (!channels) {
|
||||
log_e("Please provide data block for storing channel info");
|
||||
return;
|
||||
}
|
||||
s_channels = channels;
|
||||
|
||||
it = &items[0];
|
||||
for(size_t i = 0; i<len; i++){
|
||||
|
||||
if(!valid){
|
||||
break;
|
||||
}
|
||||
it = &items[i];
|
||||
if(XJT_VALID(it)){
|
||||
if(it->duration1 >= 5 && it->duration1 <= 8){
|
||||
valid = xjtReceiveBit(i, false);
|
||||
} else if(it->duration1 >= 13 && it->duration1 <= 16){
|
||||
valid = xjtReceiveBit(i, true);
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
} else if(!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) {
|
||||
valid = xjtReceiveBit(i, false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void receive_data(uint32_t *data, size_t len)
|
||||
{
|
||||
parseRmt((rmt_data_t*) data, len, channels);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize the channel to capture up to 192 items
|
||||
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
|
||||
{
|
||||
Serial.println("init receiver failed\n");
|
||||
}
|
||||
|
||||
// Setup 1us tick
|
||||
float realTick = rmtSetTick(rmt_recv, 1000);
|
||||
Serial.printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
// Ask to start reading
|
||||
rmtRead(rmt_recv, receive_data);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// printout some of the channels
|
||||
Serial.printf("%04x %04x %04x %04x\n", channels[0], channels[1], channels[2], channels[3]);
|
||||
delay(500);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#define NR_OF_LEDS 8*4
|
||||
#define NR_OF_ALL_BITS 24*NR_OF_LEDS
|
||||
|
||||
//
|
||||
// Note: This example uses Neopixel LED board, 32 LEDs chained one
|
||||
// after another, each RGB LED has its 24 bit value
|
||||
// for color configuration (8b for each color)
|
||||
//
|
||||
// Bits encoded as pulses as follows:
|
||||
//
|
||||
// "0":
|
||||
// +-------+ +--
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// ---| |--------------|
|
||||
// + + +
|
||||
// | 0.4us | 0.85 0us |
|
||||
//
|
||||
// "1":
|
||||
// +-------------+ +--
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// ---+ +-------+
|
||||
// | 0.8us | 0.4us |
|
||||
|
||||
rmt_data_t led_data[NR_OF_ALL_BITS];
|
||||
|
||||
rmt_obj_t* rmt_send = NULL;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
|
||||
{
|
||||
Serial.println("init sender failed\n");
|
||||
}
|
||||
|
||||
float realTick = rmtSetTick(rmt_send, 100);
|
||||
Serial.printf("real tick set to: %fns\n", realTick);
|
||||
|
||||
}
|
||||
|
||||
int color[] = { 0x55, 0x11, 0x77 }; // RGB value
|
||||
int led_index = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Init data with only one led ON
|
||||
int led, col, bit;
|
||||
int i=0;
|
||||
for (led=0; led<NR_OF_LEDS; led++) {
|
||||
for (col=0; col<3; col++ ) {
|
||||
for (bit=0; bit<8; bit++){
|
||||
if ( (color[col] & (1<<(8-bit))) && (led == led_index) ) {
|
||||
led_data[i].level0 = 1;
|
||||
led_data[i].duration0 = 8;
|
||||
led_data[i].level1 = 0;
|
||||
led_data[i].duration1 = 4;
|
||||
} else {
|
||||
led_data[i].level0 = 1;
|
||||
led_data[i].duration0 = 4;
|
||||
led_data[i].level1 = 0;
|
||||
led_data[i].duration1 = 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// make the led travel in the pannel
|
||||
if ((++led_index)>=NR_OF_LEDS) {
|
||||
led_index = 0;
|
||||
}
|
||||
|
||||
// Send the data
|
||||
rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS);
|
||||
|
||||
delay(100);
|
||||
}
|
@ -6,7 +6,7 @@ hw_timer_t *timer = NULL;
|
||||
|
||||
void IRAM_ATTR resetModule() {
|
||||
ets_printf("reboot\n");
|
||||
esp_restart_noos();
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
@ -43,6 +43,14 @@ License (MIT license):
|
||||
#include <functional>
|
||||
#include "esp_wifi.h"
|
||||
|
||||
// Add quotes around defined value
|
||||
#ifdef __IN_ECLIPSE__
|
||||
#define STR_EXPAND(tok) #tok
|
||||
#define STR(tok) STR_EXPAND(tok)
|
||||
#else
|
||||
#define STR(tok) tok
|
||||
#endif
|
||||
|
||||
static void _on_sys_event(system_event_t *event){
|
||||
mdns_handle_system_event(NULL, event);
|
||||
}
|
||||
@ -82,7 +90,7 @@ void MDNSResponder::setInstanceName(String name) {
|
||||
|
||||
void MDNSResponder::enableArduino(uint16_t port, bool auth){
|
||||
mdns_txt_item_t arduTxtData[4] = {
|
||||
{(char*)"board" ,(char*)ARDUINO_VARIANT},
|
||||
{(char*)"board" ,(char*)STR(ARDUINO_VARIANT)},
|
||||
{(char*)"tcp_check" ,(char*)"no"},
|
||||
{(char*)"ssh_upload" ,(char*)"no"},
|
||||
{(char*)"auth_upload" ,(char*)"no"}
|
||||
|
181
libraries/FFat/examples/FFat_Test/FFat_Test.ino
Normal file
181
libraries/FFat/examples/FFat_Test/FFat_Test.ino
Normal file
@ -0,0 +1,181 @@
|
||||
#include "FS.h"
|
||||
#include "FFat.h"
|
||||
|
||||
// You only need to format FFat the first time you run a test
|
||||
#define FORMAT_FFAT true
|
||||
|
||||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if(!root){
|
||||
Serial.println("- failed to open directory");
|
||||
return;
|
||||
}
|
||||
if(!root.isDirectory()){
|
||||
Serial.println(" - not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
if(file.isDirectory()){
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if(levels){
|
||||
listDir(fs, file.name(), levels -1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char * path){
|
||||
Serial.printf("Reading file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if(!file || file.isDirectory()){
|
||||
Serial.println("- failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("- read from file:");
|
||||
while(file.available()){
|
||||
Serial.write(file.read());
|
||||
}
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char * path, const char * message){
|
||||
Serial.printf("Writing file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if(file.print(message)){
|
||||
Serial.println("- file written");
|
||||
} else {
|
||||
Serial.println("- frite failed");
|
||||
}
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char * path, const char * message){
|
||||
Serial.printf("Appending to file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if(file.print(message)){
|
||||
Serial.println("- message appended");
|
||||
} else {
|
||||
Serial.println("- append failed");
|
||||
}
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char * path1, const char * path2){
|
||||
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("- file renamed");
|
||||
} else {
|
||||
Serial.println("- rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char * path){
|
||||
Serial.printf("Deleting file: %s\r\n", path);
|
||||
if(fs.remove(path)){
|
||||
Serial.println("- file deleted");
|
||||
} else {
|
||||
Serial.println("- delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char * path){
|
||||
Serial.printf("Testing file I/O with %s\r\n", path);
|
||||
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if(!file){
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
Serial.print("- writing" );
|
||||
uint32_t start = millis();
|
||||
for(i=0; i<2048; i++){
|
||||
if ((i & 0x001F) == 0x001F){
|
||||
Serial.print(".");
|
||||
}
|
||||
file.write(buf, 512);
|
||||
}
|
||||
Serial.println("");
|
||||
uint32_t end = millis() - start;
|
||||
Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
|
||||
file.close();
|
||||
|
||||
file = fs.open(path);
|
||||
start = millis();
|
||||
end = start;
|
||||
i = 0;
|
||||
if(file && !file.isDirectory()){
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
Serial.print("- reading" );
|
||||
while(len){
|
||||
size_t toRead = len;
|
||||
if(toRead > 512){
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
if ((i++ & 0x001F) == 0x001F){
|
||||
Serial.print(".");
|
||||
}
|
||||
len -= toRead;
|
||||
}
|
||||
Serial.println("");
|
||||
end = millis() - start;
|
||||
Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("- failed to open file for reading");
|
||||
}
|
||||
}
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
if (FORMAT_FFAT) FFat.format();
|
||||
if(!FFat.begin()){
|
||||
Serial.println("FFat Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("Total space: %10lu\n", FFat.totalBytes());
|
||||
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
|
||||
listDir(FFat, "/", 0);
|
||||
writeFile(FFat, "/hello.txt", "Hello ");
|
||||
appendFile(FFat, "/hello.txt", "World!\r\n");
|
||||
readFile(FFat, "/hello.txt");
|
||||
renameFile(FFat, "/hello.txt", "/foo.txt");
|
||||
readFile(FFat, "/foo.txt");
|
||||
deleteFile(FFat, "/foo.txt");
|
||||
testFileIO(FFat, "/test.txt");
|
||||
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
|
||||
deleteFile(FFat, "/test.txt");
|
||||
Serial.println( "Test complete" );
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
}
|
9
libraries/FFat/library.properties
Normal file
9
libraries/FFat/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=FFat
|
||||
version=1.0
|
||||
author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=ESP32 FAT on Flash File System
|
||||
paragraph=
|
||||
category=Data Storage
|
||||
url=
|
||||
architectures=esp32
|
144
libraries/FFat/src/FFat.cpp
Normal file
144
libraries/FFat/src/FFat.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "vfs_api.h"
|
||||
extern "C" {
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "diskio.h"
|
||||
#include "diskio_wl.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
}
|
||||
#include "FFat.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
F_Fat::F_Fat(FSImplPtr impl)
|
||||
: FS(impl)
|
||||
{}
|
||||
|
||||
const esp_partition_t *check_ffat_partition(const char* label)
|
||||
{
|
||||
const esp_partition_t* ck_part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, label);
|
||||
if (!ck_part) {
|
||||
log_e("No FAT partition found with label %s", label);
|
||||
return NULL;
|
||||
}
|
||||
return ck_part;
|
||||
}
|
||||
|
||||
bool F_Fat::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles, const char * partitionLabel)
|
||||
{
|
||||
if(_wl_handle){
|
||||
log_w("Already Mounted!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!check_ffat_partition(partitionLabel)) return false;
|
||||
|
||||
esp_vfs_fat_mount_config_t conf = {
|
||||
.format_if_mount_failed = formatOnFail,
|
||||
.max_files = maxOpenFiles
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount(basePath, partitionLabel, &conf, &_wl_handle);
|
||||
if(err){
|
||||
log_e("Mounting FFat partition failed! Error: %d", err);
|
||||
return false;
|
||||
}
|
||||
_impl->mountpoint(basePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
void F_Fat::end()
|
||||
{
|
||||
if(_wl_handle){
|
||||
esp_err_t err = esp_vfs_fat_spiflash_unmount(_impl->mountpoint(), _wl_handle);
|
||||
if(err){
|
||||
log_e("Unmounting FFat partition failed! Error: %d", err);
|
||||
return;
|
||||
}
|
||||
_wl_handle = NULL;
|
||||
_impl->mountpoint(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool F_Fat::format(bool full_wipe, char* partitionLabel)
|
||||
{
|
||||
esp_err_t result;
|
||||
if(_wl_handle){
|
||||
log_w("Already Mounted!");
|
||||
return false;
|
||||
}
|
||||
wl_handle_t temp_handle;
|
||||
// Attempt to mount to see if there is already data
|
||||
const esp_partition_t *ffat_partition = check_ffat_partition(partitionLabel);
|
||||
if (!ffat_partition) return false;
|
||||
result = wl_mount(ffat_partition, &temp_handle);
|
||||
|
||||
if (result == ESP_OK) {
|
||||
// Wipe disk- quick just wipes the FAT. Full zeroes the whole disk
|
||||
uint32_t wipe_size = full_wipe ? wl_size(temp_handle) : 16384;
|
||||
wl_erase_range(temp_handle, 0, wipe_size);
|
||||
wl_unmount(temp_handle);
|
||||
}
|
||||
// Now do a mount with format_if_fail (which it will)
|
||||
esp_vfs_fat_mount_config_t conf = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 1
|
||||
};
|
||||
result = esp_vfs_fat_spiflash_mount("/format_ffat", partitionLabel, &conf, &temp_handle);
|
||||
esp_vfs_fat_spiflash_unmount("/format_ffat", temp_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t F_Fat::totalBytes()
|
||||
{
|
||||
FATFS *fs;
|
||||
DWORD free_clust, tot_sect, sect_size;
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
|
||||
char drv[3] = {(char)(48+pdrv), ':', 0};
|
||||
FRESULT res = f_getfree(drv, &free_clust, &fs);
|
||||
tot_sect = (fs->n_fatent - 2) * fs->csize;
|
||||
sect_size = CONFIG_WL_SECTOR_SIZE;
|
||||
return tot_sect * sect_size;
|
||||
}
|
||||
|
||||
size_t F_Fat::freeBytes()
|
||||
{
|
||||
|
||||
FATFS *fs;
|
||||
DWORD free_clust, free_sect, sect_size;
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
|
||||
char drv[3] = {(char)(48+pdrv), ':', 0};
|
||||
FRESULT res = f_getfree(drv, &free_clust, &fs);
|
||||
free_sect = free_clust * fs->csize;
|
||||
sect_size = CONFIG_WL_SECTOR_SIZE;
|
||||
return free_sect * sect_size;
|
||||
}
|
||||
|
||||
bool F_Fat::exists(const char* path)
|
||||
{
|
||||
File f = open(path, "r");
|
||||
return (f == true) && !f.isDirectory();
|
||||
}
|
||||
|
||||
bool F_Fat::exists(const String& path)
|
||||
{
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
|
||||
F_Fat FFat = F_Fat(FSImplPtr(new VFSImpl()));
|
47
libraries/FFat/src/FFat.h
Normal file
47
libraries/FFat/src/FFat.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _FFAT_H_
|
||||
#define _FFAT_H_
|
||||
|
||||
#include "FS.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#define FFAT_WIPE_QUICK 0
|
||||
#define FFAT_WIPE_FULL 1
|
||||
#define FFAT_PARTITION_LABEL "ffat"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
class F_Fat : public FS
|
||||
{
|
||||
public:
|
||||
F_Fat(FSImplPtr impl);
|
||||
bool begin(bool formatOnFail=false, const char * basePath="/ffat", uint8_t maxOpenFiles=10, const char * partitionLabel = (char*)FFAT_PARTITION_LABEL);
|
||||
bool format(bool full_wipe = FFAT_WIPE_QUICK, char* partitionLabel = (char*)FFAT_PARTITION_LABEL);
|
||||
size_t totalBytes();
|
||||
size_t freeBytes();
|
||||
void end();
|
||||
bool exists(const char* path);
|
||||
bool exists(const String& path);
|
||||
|
||||
private:
|
||||
wl_handle_t _wl_handle;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern fs::F_Fat FFat;
|
||||
|
||||
#endif /* _FFAT_H_ */
|
@ -0,0 +1,147 @@
|
||||
/**
|
||||
BasicHTTPSClient.ino
|
||||
|
||||
Created on: 14.10.2018
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
// This is GandiStandardSSLCA2.pem, the root Certificate Authority that signed
|
||||
// the server certifcate for the demo server https://jigsaw.w3.org in this
|
||||
// example. This certificate is valid until Sep 11 23:59:59 2024 GMT
|
||||
const char* rootCACertificate = \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIF6TCCA9GgAwIBAgIQBeTcO5Q4qzuFl8umoZhQ4zANBgkqhkiG9w0BAQwFADCB\n" \
|
||||
"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \
|
||||
"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \
|
||||
"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQw\n" \
|
||||
"OTEyMDAwMDAwWhcNMjQwOTExMjM1OTU5WjBfMQswCQYDVQQGEwJGUjEOMAwGA1UE\n" \
|
||||
"CBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMQ4wDAYDVQQKEwVHYW5kaTEgMB4GA1UE\n" \
|
||||
"AxMXR2FuZGkgU3RhbmRhcmQgU1NMIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" \
|
||||
"DwAwggEKAoIBAQCUBC2meZV0/9UAPPWu2JSxKXzAjwsLibmCg5duNyj1ohrP0pIL\n" \
|
||||
"m6jTh5RzhBCf3DXLwi2SrCG5yzv8QMHBgyHwv/j2nPqcghDA0I5O5Q1MsJFckLSk\n" \
|
||||
"QFEW2uSEEi0FXKEfFxkkUap66uEHG4aNAXLy59SDIzme4OFMH2sio7QQZrDtgpbX\n" \
|
||||
"bmq08j+1QvzdirWrui0dOnWbMdw+naxb00ENbLAb9Tr1eeohovj0M1JLJC0epJmx\n" \
|
||||
"bUi8uBL+cnB89/sCdfSN3tbawKAyGlLfOGsuRTg/PwSWAP2h9KK71RfWJ3wbWFmV\n" \
|
||||
"XooS/ZyrgT5SKEhRhWvzkbKGPym1bgNi7tYFAgMBAAGjggF1MIIBcTAfBgNVHSME\n" \
|
||||
"GDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUs5Cn2MmvTs1hPJ98\n" \
|
||||
"rV1/Qf1pMOowDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD\n" \
|
||||
"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCIGA1UdIAQbMBkwDQYLKwYBBAGy\n" \
|
||||
"MQECAhowCAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNl\n" \
|
||||
"cnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNy\n" \
|
||||
"bDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRy\n" \
|
||||
"dXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZ\n" \
|
||||
"aHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAWGf9\n" \
|
||||
"crJq13xhlhl+2UNG0SZ9yFP6ZrBrLafTqlb3OojQO3LJUP33WbKqaPWMcwO7lWUX\n" \
|
||||
"zi8c3ZgTopHJ7qFAbjyY1lzzsiI8Le4bpOHeICQW8owRc5E69vrOJAKHypPstLbI\n" \
|
||||
"FhfFcvwnQPYT/pOmnVHvPCvYd1ebjGU6NSU2t7WKY28HJ5OxYI2A25bUeo8tqxyI\n" \
|
||||
"yW5+1mUfr13KFj8oRtygNeX56eXVlogMT8a3d2dIhCe2H7Bo26y/d7CQuKLJHDJd\n" \
|
||||
"ArolQ4FCR7vY4Y8MDEZf7kYzawMUgtN+zY+vkNaOJH1AQrRqahfGlZfh8jjNp+20\n" \
|
||||
"J0CT33KpuMZmYzc4ZCIwojvxuch7yPspOqsactIGEk72gtQjbz7Dk+XYtsDe3CMW\n" \
|
||||
"1hMwt6CaDixVBgBwAc/qOR2A24j3pSC4W/0xJmmPLQphgzpHphNULB7j7UTKvGof\n" \
|
||||
"KA5R2d4On3XNDgOVyvnFqSot/kGkoUeuDcL5OWYzSlvhhChZbH2UF3bkRYKtcCD9\n" \
|
||||
"0m9jqNf6oDP6N8v3smWe2lBvP+Sn845dWDKXcCMu5/3EFZucJ48y7RetWIExKREa\n" \
|
||||
"m9T8bJUox04FB6b9HbwZ4ui3uRGKLXASUoWNjDNKD/yZkuBjcNqllEdjB+dYxzFf\n" \
|
||||
"BT02Vf6Dsuimrdfp5gJ0iHRc2jTbkNJtUQoj1iM=\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
// Not sure if WiFiClientSecure checks the validity date of the certificate.
|
||||
// Setting clock just to be sure...
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Serial.print(F("Waiting for NTP time sync: "));
|
||||
time_t nowSecs = time(nullptr);
|
||||
while (nowSecs < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
Serial.print(F("."));
|
||||
yield();
|
||||
nowSecs = time(nullptr);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&nowSecs, &timeinfo);
|
||||
Serial.print(F("Current time: "));
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
// wait for WiFi connection
|
||||
Serial.print("Waiting for WiFi to connect...");
|
||||
while ((WiFiMulti.run() != WL_CONNECTED)) {
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println(" connected");
|
||||
|
||||
setClock();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
WiFiClientSecure *client = new WiFiClientSecure;
|
||||
if(client) {
|
||||
client -> setCACert(rootCACertificate);
|
||||
|
||||
{
|
||||
// Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is
|
||||
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");
|
||||
}
|
||||
|
||||
// End extra scoping block
|
||||
}
|
||||
|
||||
delete client;
|
||||
} else {
|
||||
Serial.println("Unable to create client");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Waiting 10s before the next round...");
|
||||
delay(10000);
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*|----------------------------------------------------------|*/
|
||||
/*|WORKING EXAMPLE FOR HTTP/HTTPS CONNECTION |*/
|
||||
/*|TESTED BOARDS: Devkit v1 DOIT, Devkitc v4 |*/
|
||||
/*|CORE: June 2018 |*/
|
||||
/*|----------------------------------------------------------|*/
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "esp_wpa2.h"
|
||||
#include <Wire.h>
|
||||
#define EAP_IDENTITY "identity" //if connecting from another corporation, use identity@organisation.domain in Eduroam
|
||||
#define EAP_PASSWORD "password" //your Eduroam password
|
||||
const char* ssid = "eduroam"; // Eduroam SSID
|
||||
int counter = 0;
|
||||
const char* test_root_ca= \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
|
||||
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
|
||||
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
|
||||
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
|
||||
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
|
||||
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
|
||||
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
|
||||
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
|
||||
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
|
||||
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
|
||||
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
|
||||
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
|
||||
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
|
||||
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
Serial.println();
|
||||
Serial.print("Connecting to network: ");
|
||||
Serial.println(ssid);
|
||||
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
|
||||
WiFi.mode(WIFI_STA); //init wifi mode
|
||||
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
|
||||
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
|
||||
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
|
||||
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
|
||||
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
|
||||
WiFi.begin(ssid); //connect to wifi
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //after 30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address set: ");
|
||||
Serial.println(WiFi.localIP()); //print LAN IP
|
||||
}
|
||||
void loop() {
|
||||
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
|
||||
counter = 0; //reset counter
|
||||
Serial.println("Wifi is still connected with IP: ");
|
||||
Serial.println(WiFi.localIP()); //inform user about his IP address
|
||||
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
|
||||
WiFi.begin(ssid);
|
||||
}
|
||||
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
counter++;
|
||||
if(counter>=60){ //30 seconds timeout - reset board
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.print("Connecting to website: ");
|
||||
HTTPClient http;
|
||||
http.begin("https://arduino.php5.sk/rele/rele1.txt", test_root_ca); //HTTPS example connection
|
||||
//http.begin("http://www.arduino.php5.sk/rele/rele1.txt"); //HTTP example connection
|
||||
//if uncomment HTTP example, you can comment root CA certificate too!
|
||||
int httpCode = http.GET();
|
||||
if(httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
//file found at server --> on unsucessful connection code will be -1
|
||||
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(2000);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
name=HTTPClient
|
||||
version=1.1
|
||||
version=1.2
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=http Client for ESP32
|
||||
|
@ -22,17 +22,23 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Adapted in October 2018
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
|
||||
#include <StreamString.h>
|
||||
#include <base64.h>
|
||||
|
||||
#include "HTTPClient.h"
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits
|
||||
{
|
||||
public:
|
||||
@ -78,6 +84,7 @@ protected:
|
||||
const char* _clicert;
|
||||
const char* _clikey;
|
||||
};
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
/**
|
||||
* constructor
|
||||
@ -91,8 +98,8 @@ HTTPClient::HTTPClient()
|
||||
*/
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
if(_tcp) {
|
||||
_tcp->stop();
|
||||
if(_client) {
|
||||
_client->stop();
|
||||
}
|
||||
if(_currentHeaders) {
|
||||
delete[] _currentHeaders;
|
||||
@ -107,9 +114,81 @@ void HTTPClient::clear()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* parsing the url for all needed parameters
|
||||
* @param client Client&
|
||||
* @param url String
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String url) {
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
#endif
|
||||
|
||||
_client = &client;
|
||||
|
||||
// check for : (http: or https:)
|
||||
int index = url.indexOf(':');
|
||||
if(index < 0) {
|
||||
log_d("failed to parse protocol");
|
||||
return false;
|
||||
}
|
||||
|
||||
String protocol = url.substring(0, index);
|
||||
if(protocol != "http" && protocol != "https") {
|
||||
log_d("unknown protocol '%s'", protocol.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_port = (protocol == "https" ? 443 : 80);
|
||||
return beginInternal(url, protocol.c_str());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* directly supply all needed parameters
|
||||
* @param client Client&
|
||||
* @param host String
|
||||
* @param port uint16_t
|
||||
* @param uri String
|
||||
* @param https bool
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https)
|
||||
{
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
#endif
|
||||
|
||||
_client = &client;
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
_uri = uri;
|
||||
_protocol = (https ? "https" : "http");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool HTTPClient::begin(String url, const char* CAcert)
|
||||
{
|
||||
_transportTraits.reset(nullptr);
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
_port = 443;
|
||||
if (!beginInternal(url, "https")) {
|
||||
return false;
|
||||
@ -125,8 +204,12 @@ bool HTTPClient::begin(String url, const char* CAcert)
|
||||
*/
|
||||
bool HTTPClient::begin(String url)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
_transportTraits.reset(nullptr);
|
||||
_port = 80;
|
||||
if (!beginInternal(url, "http")) {
|
||||
return begin(url, (const char*)NULL);
|
||||
@ -134,6 +217,7 @@ bool HTTPClient::begin(String url)
|
||||
_transportTraits = TransportTraitsPtr(new TransportTraits());
|
||||
return true;
|
||||
}
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
{
|
||||
@ -182,8 +266,15 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -195,6 +286,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri)
|
||||
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -210,6 +307,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
|
||||
|
||||
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key)
|
||||
{
|
||||
if(_client && !_tcpDeprecated) {
|
||||
log_d("mix up of new and deprecated api");
|
||||
_canReuse = false;
|
||||
end();
|
||||
}
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
@ -222,37 +325,60 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
|
||||
_transportTraits = TransportTraitsPtr(new TLSTraits(CAcert, cli_cert, cli_key));
|
||||
return true;
|
||||
}
|
||||
#endif // HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
/**
|
||||
* end
|
||||
* called after the payload is handled
|
||||
*/
|
||||
void HTTPClient::end(void)
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* disconnect
|
||||
* close the TCP socket
|
||||
*/
|
||||
void HTTPClient::disconnect()
|
||||
{
|
||||
if(connected()) {
|
||||
if(_tcp->available() > 0) {
|
||||
log_d("still data in buffer (%d), clean up.", _tcp->available());
|
||||
_tcp->flush();
|
||||
if(_client->available() > 0) {
|
||||
log_d("still data in buffer (%d), clean up.\n", _client->available());
|
||||
while(_client->available() > 0) {
|
||||
_client->read();
|
||||
}
|
||||
}
|
||||
|
||||
if(_reuse && _canReuse) {
|
||||
log_d("tcp keep open for reuse");
|
||||
log_d("tcp keep open for reuse\n");
|
||||
} else {
|
||||
log_d("tcp stop");
|
||||
_tcp->stop();
|
||||
log_d("tcp stop\n");
|
||||
_client->stop();
|
||||
_client = nullptr;
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(_tcpDeprecated) {
|
||||
_transportTraits.reset(nullptr);
|
||||
_tcpDeprecated.reset(nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
log_v("tcp is closed");
|
||||
log_d("tcp is closed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* connected
|
||||
* @return connected status
|
||||
*/
|
||||
bool HTTPClient::connected()
|
||||
{
|
||||
if(_tcp) {
|
||||
return ((_tcp->available() > 0) || _tcp->connected());
|
||||
if(_client) {
|
||||
return ((_client->available() > 0) || _client->connected());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -309,8 +435,8 @@ void HTTPClient::setAuthorization(const char * auth)
|
||||
void HTTPClient::setTimeout(uint16_t timeout)
|
||||
{
|
||||
_tcpTimeout = timeout;
|
||||
if(connected() && !_secure) {
|
||||
_tcp->setTimeout(timeout);
|
||||
if(connected()) {
|
||||
_client->setTimeout((timeout + 500) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +524,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
|
||||
|
||||
// send Payload if needed
|
||||
if(payload && size > 0) {
|
||||
if(_tcp->write(&payload[0], size) != size) {
|
||||
if(_client->write(&payload[0], size) != size) {
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
@ -477,7 +603,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
int bytesRead = stream->readBytes(buff, readBytes);
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = _tcp->write((const uint8_t *) buff, bytesRead);
|
||||
int bytesWrite = _client->write((const uint8_t *) buff, bytesRead);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
// are all Bytes a writen to stream ?
|
||||
@ -485,11 +611,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
log_d("short write, asked for %d but got %d retry...", bytesRead, bytesWrite);
|
||||
|
||||
// check for write error
|
||||
if(_tcp->getWriteError()) {
|
||||
log_d("stream write error %d", _tcp->getWriteError());
|
||||
if(_client->getWriteError()) {
|
||||
log_d("stream write error %d", _client->getWriteError());
|
||||
|
||||
//reset write error for retry
|
||||
_tcp->clearWriteError();
|
||||
_client->clearWriteError();
|
||||
}
|
||||
|
||||
// some time for the stream
|
||||
@ -498,7 +624,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
int leftBytes = (readBytes - bytesWrite);
|
||||
|
||||
// retry to send the missed bytes
|
||||
bytesWrite = _tcp->write((const uint8_t *) (buff + bytesWrite), leftBytes);
|
||||
bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
if(bytesWrite != leftBytes) {
|
||||
@ -510,8 +636,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
}
|
||||
|
||||
// check for write error
|
||||
if(_tcp->getWriteError()) {
|
||||
log_d("stream write error %d", _tcp->getWriteError());
|
||||
if(_client->getWriteError()) {
|
||||
log_d("stream write error %d", _client->getWriteError());
|
||||
free(buff);
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
@ -561,8 +687,8 @@ int HTTPClient::getSize(void)
|
||||
*/
|
||||
WiFiClient& HTTPClient::getStream(void)
|
||||
{
|
||||
if (connected() && !_secure) {
|
||||
return *_tcp;
|
||||
if (connected()) {
|
||||
return *_client;
|
||||
}
|
||||
|
||||
log_w("getStream: not connected");
|
||||
@ -577,7 +703,7 @@ WiFiClient& HTTPClient::getStream(void)
|
||||
WiFiClient* HTTPClient::getStreamPtr(void)
|
||||
{
|
||||
if(connected()) {
|
||||
return _tcp.get();
|
||||
return _client;
|
||||
}
|
||||
|
||||
log_w("getStreamPtr: not connected");
|
||||
@ -617,7 +743,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
if(!connected()) {
|
||||
return returnError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
}
|
||||
String chunkHeader = _tcp->readStringUntil('\n');
|
||||
String chunkHeader = _client->readStringUntil('\n');
|
||||
|
||||
if(chunkHeader.length() <= 0) {
|
||||
return returnError(HTTPC_ERROR_READ_TIMEOUT);
|
||||
@ -654,7 +780,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
|
||||
// read trailing \r\n at the end of the chunk
|
||||
char buf[2];
|
||||
auto trailing_seq_len = _tcp->readBytes((uint8_t*)buf, 2);
|
||||
auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2);
|
||||
if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') {
|
||||
return returnError(HTTPC_ERROR_READ_TIMEOUT);
|
||||
}
|
||||
@ -822,38 +948,46 @@ bool HTTPClient::connect(void)
|
||||
|
||||
if(connected()) {
|
||||
log_d("already connected, try reuse!");
|
||||
while(_tcp->available() > 0) {
|
||||
_tcp->read();
|
||||
while(_client->available() > 0) {
|
||||
_client->read();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_transportTraits) {
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if(!_client) {
|
||||
_tcpDeprecated = _transportTraits->create();
|
||||
_client = _tcpDeprecated.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_client) {
|
||||
log_d("HTTPClient::begin was not called or returned error");
|
||||
return false;
|
||||
}
|
||||
|
||||
_tcp = _transportTraits->create();
|
||||
|
||||
|
||||
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
|
||||
log_d("transport level verify failed");
|
||||
_tcp->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_tcp->connect(_host.c_str(), _port)) {
|
||||
if(!_client->connect(_host.c_str(), _port)) {
|
||||
log_d("failed connect to %s:%u", _host.c_str(), _port);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set Timeout for WiFiClient and for Stream::readBytesUntil() and Stream::readStringUntil()
|
||||
_client->setTimeout((_tcpTimeout + 500) / 1000);
|
||||
|
||||
log_d(" connected to %s:%u", _host.c_str(), _port);
|
||||
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
setTimeout(_tcpTimeout);
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
if (_tcpDeprecated && !_transportTraits->verify(*_client, _host.c_str())) {
|
||||
log_d("transport level verify failed");
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
#ifdef ESP8266
|
||||
_tcp->setNoDelay(true);
|
||||
_client->setNoDelay(true);
|
||||
#endif
|
||||
*/
|
||||
return connected();
|
||||
@ -907,7 +1041,7 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
|
||||
header += _headers + "\r\n";
|
||||
|
||||
return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length());
|
||||
return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -928,9 +1062,9 @@ int HTTPClient::handleHeaderResponse()
|
||||
unsigned long lastDataTime = millis();
|
||||
|
||||
while(connected()) {
|
||||
size_t len = _tcp->available();
|
||||
size_t len = _client->available();
|
||||
if(len > 0) {
|
||||
String headerLine = _tcp->readStringUntil('\n');
|
||||
String headerLine = _client->readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
lastDataTime = millis();
|
||||
@ -1026,7 +1160,7 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
|
||||
while(connected() && (len > 0 || len == -1)) {
|
||||
|
||||
// get available data size
|
||||
size_t sizeAvailable = _tcp->available();
|
||||
size_t sizeAvailable = _client->available();
|
||||
|
||||
if(sizeAvailable) {
|
||||
|
||||
@ -1041,9 +1175,13 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
|
||||
if(readBytes > buff_size) {
|
||||
readBytes = buff_size;
|
||||
}
|
||||
|
||||
// stop if no more reading
|
||||
if (readBytes == 0)
|
||||
break;
|
||||
|
||||
// read data
|
||||
int bytesRead = _tcp->readBytes(buff, readBytes);
|
||||
int bytesRead = _client->readBytes(buff, readBytes);
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = stream->write(buff, bytesRead);
|
||||
@ -1124,7 +1262,7 @@ int HTTPClient::returnError(int error)
|
||||
log_w("error(%d): %s", error, errorToString(error).c_str());
|
||||
if(connected()) {
|
||||
log_d("tcp stop");
|
||||
_tcp->stop();
|
||||
_client->stop();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
|
@ -27,6 +27,8 @@
|
||||
#ifndef HTTPClient_H_
|
||||
#define HTTPClient_H_
|
||||
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
@ -117,8 +119,10 @@ typedef enum {
|
||||
HTTPC_TE_CHUNKED
|
||||
} transferEncoding_t;
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
#endif
|
||||
|
||||
class HTTPClient
|
||||
{
|
||||
@ -126,11 +130,20 @@ public:
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClient
|
||||
*/
|
||||
bool begin(WiFiClient &client, String url);
|
||||
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool begin(String url);
|
||||
bool begin(String url, const char* CAcert);
|
||||
bool begin(String host, uint16_t port, String uri = "/");
|
||||
bool begin(String host, uint16_t port, String uri, const char* CAcert);
|
||||
bool begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key);
|
||||
#endif
|
||||
|
||||
void end(void);
|
||||
|
||||
@ -181,6 +194,7 @@ protected:
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char* expectedProtocol);
|
||||
void disconnect();
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
@ -189,8 +203,12 @@ protected:
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcp;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient* _client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
|
72
libraries/HTTPUpdate/examples/httpUpdate/httpUpdate.ino
Normal file
72
libraries/HTTPUpdate/examples/httpUpdate/httpUpdate.ino
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
httpUpdate.ino
|
||||
|
||||
Created on: 27.11.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
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", "PASSWORD");
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, "http://server/file.bin");
|
||||
// Or:
|
||||
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED 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,75 @@
|
||||
/**
|
||||
httpUpdateSPIFFS.ino
|
||||
|
||||
Created on: 05.12.2015
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
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", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
Serial.println("Update SPIFFS...");
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.updateSpiffs(client, "http://server/spiffs.bin");
|
||||
if (ret == HTTP_UPDATE_OK) {
|
||||
Serial.println("Update sketch...");
|
||||
ret = httpUpdate.update(client, "http://server/file.bin");
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", 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,128 @@
|
||||
/**
|
||||
httpUpdateSecure.ino
|
||||
|
||||
Created on: 16.10.2018 as an adaptation of the ESP8266 version of httpUpdate.ino
|
||||
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
|
||||
|
||||
Serial.print(F("Waiting for NTP time sync: "));
|
||||
time_t now = time(nullptr);
|
||||
while (now < 8 * 3600 * 2) {
|
||||
yield();
|
||||
delay(500);
|
||||
Serial.print(F("."));
|
||||
now = time(nullptr);
|
||||
}
|
||||
|
||||
Serial.println(F(""));
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&now, &timeinfo);
|
||||
Serial.print(F("Current time: "));
|
||||
Serial.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is lets-encrypt-x3-cross-signed.pem
|
||||
*/
|
||||
const char* rootCACertificate = \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
|
||||
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
|
||||
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
|
||||
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
|
||||
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
|
||||
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
|
||||
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
|
||||
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
|
||||
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
|
||||
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
|
||||
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
|
||||
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
|
||||
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
|
||||
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
|
||||
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
|
||||
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
|
||||
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
|
||||
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
|
||||
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
|
||||
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
|
||||
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
|
||||
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
|
||||
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
|
||||
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
|
||||
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
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", "PASSWORD");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
setClock();
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setCACert(rootCACertificate);
|
||||
|
||||
// Reading data over SSL may be slow, use an adequate timeout
|
||||
client.setTimeout(12000);
|
||||
|
||||
// The line below is optional. It can be used to blink the LED on the board during flashing
|
||||
// The LED will be on during download of one buffer of data from the network. The LED will
|
||||
// be off during writing that buffer to flash
|
||||
// On a good connection the LED should flash regularly. On a bad connection the LED will be
|
||||
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
|
||||
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
|
||||
// httpUpdate.setLedPin(LED_BUILTIN, HIGH);
|
||||
|
||||
t_httpUpdate_return ret = httpUpdate.update(client, "https://server/file.bin");
|
||||
// Or:
|
||||
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 443, "file.bin");
|
||||
|
||||
|
||||
switch (ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("HTTP_UPDATE_FAILED 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;
|
||||
}
|
||||
}
|
||||
}
|
42
libraries/HTTPUpdate/keywords.txt
Normal file
42
libraries/HTTPUpdate/keywords.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESP8266httpUpdate
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
ESP8266httpUpdate 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
|
||||
|
||||
#######################################
|
||||
# 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_UPDATE_FAILED LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_NO_UPDATES LITERAL1 RESERVED_WORD_2
|
||||
HTTP_UPDATE_OK LITERAL1 RESERVED_WORD_2
|
9
libraries/HTTPUpdate/library.properties
Normal file
9
libraries/HTTPUpdate/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=HTTPUpdate
|
||||
version=1.3
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=Http Update for ESP32
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=https://github.com/Links2004/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/ESP8266httpUpdate
|
||||
architectures=esp32
|
417
libraries/HTTPUpdate/src/HTTPUpdate.cpp
Normal file
417
libraries/HTTPUpdate/src/HTTPUpdate.cpp
Normal file
@ -0,0 +1,417 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.cpp based om ESP8266HTTPUpdate.cpp
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 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>
|
||||
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h> // get running partition
|
||||
|
||||
// To do extern "C" uint32_t _SPIFFS_start;
|
||||
// To do extern "C" uint32_t _SPIFFS_end;
|
||||
|
||||
HTTPUpdate::HTTPUpdate(void)
|
||||
: _httpClientTimeout(8000), _ledPin(-1)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdate::HTTPUpdate(int httpClientTimeout)
|
||||
: _httpClientTimeout(httpClientTimeout), _ledPin(-1)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdate::~HTTPUpdate(void)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, url))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, url))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, true);
|
||||
}
|
||||
|
||||
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
|
||||
const String& currentVersion)
|
||||
{
|
||||
HTTPClient http;
|
||||
if(!http.begin(client, host, port, uri))
|
||||
{
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
return handleUpdate(http, currentVersion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("Update error: ") + error;
|
||||
}
|
||||
|
||||
// error from http client
|
||||
if(_lastError > -100) {
|
||||
return String("HTTP error: ") + HTTPClient::errorToString(_lastError);
|
||||
}
|
||||
|
||||
switch(_lastError) {
|
||||
case HTTP_UE_TOO_LESS_SPACE:
|
||||
return "Not Enough space";
|
||||
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
|
||||
return "Server Did Not Report Size";
|
||||
case HTTP_UE_SERVER_FILE_NOT_FOUND:
|
||||
return "File Not Found (404)";
|
||||
case HTTP_UE_SERVER_FORBIDDEN:
|
||||
return "Forbidden (403)";
|
||||
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
|
||||
return "Wrong HTTP Code";
|
||||
case HTTP_UE_SERVER_FAULTY_MD5:
|
||||
return "Wrong MD5";
|
||||
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
|
||||
return "Verify Bin Header Failed";
|
||||
case HTTP_UE_BIN_FOR_WRONG_FLASH:
|
||||
return "New Binary Does Not Fit Flash Size";
|
||||
case HTTP_UE_NO_PARTITION:
|
||||
return "Partition Could Not be Found";
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
|
||||
String getSketchSHA256() {
|
||||
const size_t HASH_LEN = 32; // SHA-256 digest length
|
||||
|
||||
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
|
||||
// get sha256 digest for running partition
|
||||
if(esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256) == 0) {
|
||||
char buffer[2 * HASH_LEN + 1];
|
||||
|
||||
for(size_t index = 0; index < HASH_LEN; index++) {
|
||||
uint8_t nibble = (sha_256[index] & 0xf0) >> 4;
|
||||
buffer[2 * index] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
|
||||
nibble = sha_256[index] & 0x0f;
|
||||
buffer[2 * index + 1] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
|
||||
}
|
||||
|
||||
buffer[2 * HASH_LEN] = '\0';
|
||||
|
||||
return String(buffer);
|
||||
} else {
|
||||
|
||||
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.setUserAgent("ESP32-http-Update");
|
||||
http.addHeader("Cache-Control", "no-cache");
|
||||
http.addHeader("x-ESP32-STA-MAC", WiFi.macAddress());
|
||||
http.addHeader("x-ESP32-AP-MAC", WiFi.softAPmacAddress());
|
||||
http.addHeader("x-ESP32-free-space", String(ESP.getFreeSketchSpace()));
|
||||
http.addHeader("x-ESP32-sketch-size", String(ESP.getSketchSize()));
|
||||
String sketchMD5 = ESP.getSketchMD5();
|
||||
if(sketchMD5.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-md5", sketchMD5);
|
||||
}
|
||||
// Add also a SHA256
|
||||
String sketchSHA256 = getSketchSHA256();
|
||||
if(sketchSHA256.length() != 0) {
|
||||
http.addHeader("x-ESP32-sketch-sha256", sketchSHA256);
|
||||
}
|
||||
http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize()));
|
||||
http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion());
|
||||
|
||||
if(spiffs) {
|
||||
http.addHeader("x-ESP32-mode", "spiffs");
|
||||
} else {
|
||||
http.addHeader("x-ESP32-mode", "sketch");
|
||||
}
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
http.addHeader("x-ESP32-version", currentVersion);
|
||||
}
|
||||
|
||||
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) {
|
||||
log_e("HTTP error: %s\n", http.errorToString(code).c_str());
|
||||
_lastError = code;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
log_d("Header read fin.\n");
|
||||
log_d("Server header:\n");
|
||||
log_d(" - code: %d\n", code);
|
||||
log_d(" - len: %d\n", len);
|
||||
|
||||
if(http.hasHeader("x-MD5")) {
|
||||
log_d(" - MD5: %s\n", http.header("x-MD5").c_str());
|
||||
}
|
||||
|
||||
log_d("ESP32 info:\n");
|
||||
log_d(" - free Space: %d\n", ESP.getFreeSketchSpace());
|
||||
log_d(" - current Sketch Size: %d\n", ESP.getSketchSize());
|
||||
|
||||
if(currentVersion && currentVersion[0] != 0x00) {
|
||||
log_d(" - current version: %s\n", currentVersion.c_str() );
|
||||
}
|
||||
|
||||
switch(code) {
|
||||
case HTTP_CODE_OK: ///< OK (Start Update)
|
||||
if(len > 0) {
|
||||
bool startUpdate = true;
|
||||
if(spiffs) {
|
||||
const esp_partition_t* _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
|
||||
if(!_partition){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > _partition->size) {
|
||||
log_e("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
} else {
|
||||
int sketchFreeSpace = ESP.getFreeSketchSpace();
|
||||
if(!sketchFreeSpace){
|
||||
_lastError = HTTP_UE_NO_PARTITION;
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
|
||||
if(len > sketchFreeSpace) {
|
||||
log_e("FreeSketchSpace to low (%d) needed: %d\n", sketchFreeSpace, len);
|
||||
startUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!startUpdate) {
|
||||
_lastError = HTTP_UE_TOO_LESS_SPACE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
} else {
|
||||
|
||||
WiFiClient * tcp = http.getStreamPtr();
|
||||
|
||||
// To do? WiFiUDP::stopAll();
|
||||
// To do? WiFiClient::stopAllExcept(tcp);
|
||||
|
||||
delay(100);
|
||||
|
||||
int command;
|
||||
|
||||
if(spiffs) {
|
||||
command = U_SPIFFS;
|
||||
log_d("runUpdate spiffs...\n");
|
||||
} else {
|
||||
command = U_FLASH;
|
||||
log_d("runUpdate flash...\n");
|
||||
}
|
||||
|
||||
if(!spiffs) {
|
||||
/* To do
|
||||
uint8_t buf[4];
|
||||
if(tcp->peekBytes(&buf[0], 4) != 4) {
|
||||
log_e("peekBytes magic header failed\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
|
||||
// check for valid first magic byte
|
||||
// if(buf[0] != 0xE9) {
|
||||
if(tcp->peek() != 0xE9) {
|
||||
log_e("Magic header does not start with 0xE9\n");
|
||||
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
|
||||
}
|
||||
/* To do
|
||||
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
||||
|
||||
// check if new bin fits to SPI flash
|
||||
if(bin_flash_size > ESP.getFlashChipRealSize()) {
|
||||
log_e("New binary does not fit SPI Flash size\n");
|
||||
_lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
|
||||
http.end();
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
*/
|
||||
}
|
||||
if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
log_d("Update ok\n");
|
||||
http.end();
|
||||
|
||||
if(_rebootOnUpdate && !spiffs) {
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("Update failed\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("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:
|
||||
_lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
case HTTP_CODE_FORBIDDEN:
|
||||
_lastError = HTTP_UE_SERVER_FORBIDDEN;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
break;
|
||||
default:
|
||||
_lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
log_e("HTTP Code is (%d)\n", code);
|
||||
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, String md5, int command)
|
||||
{
|
||||
|
||||
StreamString error;
|
||||
|
||||
if(!Update.begin(size, command, _ledPin, _ledOn)) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.begin failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(md5.length()) {
|
||||
if(!Update.setMD5(md5.c_str())) {
|
||||
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
|
||||
log_e("Update.setMD5 failed! (%s)\n", md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// To do: the SHA256 could be checked if the server sends it
|
||||
|
||||
if(Update.writeStream(in) != size) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.writeStream failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Update.end()) {
|
||||
_lastError = Update.getError();
|
||||
Update.printError(error);
|
||||
error.trim(); // remove line ending
|
||||
log_e("Update.end failed! (%s)\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
HTTPUpdate httpUpdate;
|
||||
#endif
|
101
libraries/HTTPUpdate/src/HTTPUpdate.h
Normal file
101
libraries/HTTPUpdate/src/HTTPUpdate.h
Normal file
@ -0,0 +1,101 @@
|
||||
/**
|
||||
*
|
||||
* @file HTTPUpdate.h based on ESP8266HTTPUpdate.h
|
||||
* @date 16.10.2018
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP32 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ___HTTP_UPDATE_H___
|
||||
#define ___HTTP_UPDATE_H___
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Update.h>
|
||||
|
||||
/// note we use HTTP client errors too so we start at 100
|
||||
#define HTTP_UE_TOO_LESS_SPACE (-100)
|
||||
#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101)
|
||||
#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102)
|
||||
#define HTTP_UE_SERVER_FORBIDDEN (-103)
|
||||
#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104)
|
||||
#define HTTP_UE_SERVER_FAULTY_MD5 (-105)
|
||||
#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106)
|
||||
#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107)
|
||||
#define HTTP_UE_NO_PARTITION (-108)
|
||||
|
||||
enum HTTPUpdateResult {
|
||||
HTTP_UPDATE_FAILED,
|
||||
HTTP_UPDATE_NO_UPDATES,
|
||||
HTTP_UPDATE_OK
|
||||
};
|
||||
|
||||
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
|
||||
|
||||
class HTTPUpdate
|
||||
{
|
||||
public:
|
||||
HTTPUpdate(void);
|
||||
HTTPUpdate(int httpClientTimeout);
|
||||
~HTTPUpdate(void);
|
||||
|
||||
void rebootOnUpdate(bool reboot)
|
||||
{
|
||||
_rebootOnUpdate = reboot;
|
||||
}
|
||||
|
||||
void setLedPin(int ledPin = -1, uint8_t ledOn = HIGH)
|
||||
{
|
||||
_ledPin = ledPin;
|
||||
_ledOn = ledOn;
|
||||
}
|
||||
|
||||
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 updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||
|
||||
|
||||
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, String md5, int command = U_FLASH);
|
||||
|
||||
int _lastError;
|
||||
bool _rebootOnUpdate = true;
|
||||
private:
|
||||
int _httpClientTimeout;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
|
||||
extern HTTPUpdate httpUpdate;
|
||||
#endif
|
||||
|
||||
#endif /* ___HTTP_UPDATE_H___ */
|
@ -468,3 +468,13 @@ size_t Preferences::getBytes(const char* key, void * buf, size_t maxLen){
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Preferences::freeEntries() {
|
||||
nvs_stats_t nvs_stats;
|
||||
esp_err_t err = nvs_get_stats(NULL, &nvs_stats);
|
||||
if(err){
|
||||
log_e("Failed to get nvs statistics");
|
||||
return 0;
|
||||
}
|
||||
return nvs_stats.free_entries;
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ class Preferences {
|
||||
size_t getString(const char* key, char* value, size_t maxLen);
|
||||
String getString(const char* key, String defaultValue = String());
|
||||
size_t getBytes(const char* key, void * buf, size_t maxLen);
|
||||
size_t freeEntries();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@ extern "C" {
|
||||
#include "diskio.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
#include "esp_vfs.h"
|
||||
//#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
char CRC7(const char* data, int length);
|
||||
unsigned short CRC16(const char* data, int length);
|
||||
|
@ -50,12 +50,13 @@ bool SDMMCFS::begin(const char * mountpoint, bool mode1bit)
|
||||
.init = &sdmmc_host_init,
|
||||
.set_bus_width = &sdmmc_host_set_bus_width,
|
||||
.get_bus_width = &sdmmc_host_get_slot_width,
|
||||
.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode,
|
||||
.set_card_clk = &sdmmc_host_set_card_clk,
|
||||
.do_transaction = &sdmmc_host_do_transaction,
|
||||
.deinit = &sdmmc_host_deinit,
|
||||
.io_int_enable = sdmmc_host_io_int_enable,
|
||||
.io_int_wait = sdmmc_host_io_int_wait,
|
||||
.command_timeout_ms = 0,
|
||||
.io_int_enable = &sdmmc_host_io_int_enable,
|
||||
.io_int_wait = &sdmmc_host_io_int_wait,
|
||||
.command_timeout_ms = 0
|
||||
};
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#ifdef BOARD_HAS_1BIT_SDMMC
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
/* The ESP32 has four SPi buses, however as of right now only two of
|
||||
* them are available to use, HSPI and VSPI. Simply using the SPI API
|
||||
* as illustrated in Arduino examples will use HSPI, leaving VSPI unused.
|
||||
* as illustrated in Arduino examples will use VSPI, leaving HSPI unused.
|
||||
*
|
||||
* However if we simply intialise two instance of the SPI class for both
|
||||
* of these buses both can be used. However when just using these the Arduino
|
||||
|
@ -214,6 +214,11 @@ void SPIClass::writeBytes(uint8_t * data, uint32_t size)
|
||||
spiEndTransaction(_spi);
|
||||
}
|
||||
|
||||
void SPIClass::transfer(uint8_t * data, uint32_t size)
|
||||
{
|
||||
transferBytes(data, data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data void *
|
||||
* @param size uint32_t
|
||||
|
@ -65,10 +65,11 @@ public:
|
||||
|
||||
void beginTransaction(SPISettings settings);
|
||||
void endTransaction(void);
|
||||
|
||||
void transfer(uint8_t * data, uint32_t size);
|
||||
uint8_t transfer(uint8_t data);
|
||||
uint16_t transfer16(uint16_t data);
|
||||
uint32_t transfer32(uint32_t data);
|
||||
|
||||
void transferBytes(uint8_t * data, uint8_t * out, uint32_t size);
|
||||
void transferBits(uint32_t data, uint32_t * out, uint8_t bits);
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
#include "FS.h"
|
||||
#include "SPIFFS.h"
|
||||
|
||||
/* You only need to format SPIFFS the first time you run a
|
||||
test or else use the SPIFFS plugin to create a partition
|
||||
https://github.com/me-no-dev/arduino-esp32fs-plugin */
|
||||
#define FORMAT_SPIFFS_IF_FAILED true
|
||||
|
||||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
@ -151,7 +156,7 @@ void testFileIO(fs::FS &fs, const char * path){
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
if(!SPIFFS.begin()){
|
||||
if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){
|
||||
Serial.println("SPIFFS Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
@ -39,11 +39,16 @@ bool SPIFFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFi
|
||||
.base_path = basePath,
|
||||
.partition_label = NULL,
|
||||
.max_files = maxOpenFiles,
|
||||
.format_if_mount_failed = formatOnFail
|
||||
.format_if_mount_failed = false
|
||||
};
|
||||
|
||||
esp_err_t err = esp_vfs_spiffs_register(&conf);
|
||||
if(err){
|
||||
if(err == ESP_FAIL && formatOnFail){
|
||||
if(format()){
|
||||
err = esp_vfs_spiffs_register(&conf);
|
||||
}
|
||||
}
|
||||
if(err != ESP_OK){
|
||||
log_e("Mounting SPIFFS failed! Error: %d", err);
|
||||
return false;
|
||||
}
|
||||
@ -65,7 +70,9 @@ void SPIFFSFS::end()
|
||||
|
||||
bool SPIFFSFS::format()
|
||||
{
|
||||
disableCore0WDT();
|
||||
esp_err_t err = esp_spiffs_format(NULL);
|
||||
enableCore0WDT();
|
||||
if(err){
|
||||
log_e("Formatting SPIFFS failed! Error: %d", err);
|
||||
return false;
|
||||
|
@ -41,7 +41,7 @@ class UpdateClass {
|
||||
Call this to check the space needed for the update
|
||||
Will return false if there is not enough space
|
||||
*/
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW);
|
||||
|
||||
/*
|
||||
Writes a buffer to the flash and increments the address
|
||||
@ -174,6 +174,9 @@ class UpdateClass {
|
||||
|
||||
String _target_md5;
|
||||
MD5Builder _md5;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
extern UpdateClass Update;
|
||||
|
@ -88,6 +88,10 @@ void UpdateClass::_reset() {
|
||||
_progress = 0;
|
||||
_size = 0;
|
||||
_command = U_FLASH;
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // off
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateClass::canRollBack(){
|
||||
@ -106,12 +110,15 @@ bool UpdateClass::rollBack(){
|
||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||
}
|
||||
|
||||
bool UpdateClass::begin(size_t size, int command) {
|
||||
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
if(_size > 0){
|
||||
log_w("already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
_ledPin = ledPin;
|
||||
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||
|
||||
_reset();
|
||||
_error = 0;
|
||||
|
||||
@ -315,16 +322,32 @@ size_t UpdateClass::writeStream(Stream &data) {
|
||||
if (_progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
while(remaining()) {
|
||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||
}
|
||||
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
if(bytesToRead > remaining()) {
|
||||
bytesToRead = remaining();
|
||||
}
|
||||
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
delay(100);
|
||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) { //Timeout
|
||||
_abort(UPDATE_ERROR_STREAM);
|
||||
return written;
|
||||
}
|
||||
}
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||
}
|
||||
_bufferLen += toRead;
|
||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||
return written;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
FSWebServer - Example WebServer with SPIFFS backend for esp8266
|
||||
FSWebServer - Example WebServer with FS backend for esp8266/esp32
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the WebServer library for Arduino environment.
|
||||
|
||||
@ -26,14 +26,21 @@
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <SPIFFS.h>
|
||||
|
||||
#define FILESYSTEM SPIFFS
|
||||
#define FORMAT_FILESYSTEM true
|
||||
#define DBG_OUTPUT_PORT Serial
|
||||
|
||||
#if FILESYSTEM == FFat
|
||||
#include <FFat.h>
|
||||
#endif
|
||||
#if FILESYSTEM == SPIFFS
|
||||
#include <SPIFFS.h>
|
||||
#endif
|
||||
|
||||
const char* ssid = "wifi-ssid";
|
||||
const char* password = "wifi-password";
|
||||
const char* host = "esp32fs";
|
||||
|
||||
WebServer server(80);
|
||||
//holds the current upload
|
||||
File fsUploadFile;
|
||||
@ -84,7 +91,7 @@ String getContentType(String filename) {
|
||||
|
||||
bool exists(String path){
|
||||
bool yes = false;
|
||||
File file = SPIFFS.open(path, "r");
|
||||
File file = FILESYSTEM.open(path, "r");
|
||||
if(!file.isDirectory()){
|
||||
yes = true;
|
||||
}
|
||||
@ -103,7 +110,7 @@ bool handleFileRead(String path) {
|
||||
if (exists(pathWithGz)) {
|
||||
path += ".gz";
|
||||
}
|
||||
File file = SPIFFS.open(path, "r");
|
||||
File file = FILESYSTEM.open(path, "r");
|
||||
server.streamFile(file, contentType);
|
||||
file.close();
|
||||
return true;
|
||||
@ -122,7 +129,7 @@ void handleFileUpload() {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
|
||||
fsUploadFile = SPIFFS.open(filename, "w");
|
||||
fsUploadFile = FILESYSTEM.open(filename, "w");
|
||||
filename = String();
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
|
||||
@ -149,7 +156,7 @@ void handleFileDelete() {
|
||||
if (!exists(path)) {
|
||||
return server.send(404, "text/plain", "FileNotFound");
|
||||
}
|
||||
SPIFFS.remove(path);
|
||||
FILESYSTEM.remove(path);
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
}
|
||||
@ -166,7 +173,7 @@ void handleFileCreate() {
|
||||
if (exists(path)) {
|
||||
return server.send(500, "text/plain", "FILE EXISTS");
|
||||
}
|
||||
File file = SPIFFS.open(path, "w");
|
||||
File file = FILESYSTEM.open(path, "w");
|
||||
if (file) {
|
||||
file.close();
|
||||
} else {
|
||||
@ -186,7 +193,7 @@ void handleFileList() {
|
||||
DBG_OUTPUT_PORT.println("handleFileList: " + path);
|
||||
|
||||
|
||||
File root = SPIFFS.open(path);
|
||||
File root = FILESYSTEM.open(path);
|
||||
path = String();
|
||||
|
||||
String output = "[";
|
||||
@ -212,9 +219,10 @@ void setup(void) {
|
||||
DBG_OUTPUT_PORT.begin(115200);
|
||||
DBG_OUTPUT_PORT.print("\n");
|
||||
DBG_OUTPUT_PORT.setDebugOutput(true);
|
||||
SPIFFS.begin();
|
||||
if (FORMAT_FILESYSTEM) FILESYSTEM.format();
|
||||
FILESYSTEM.begin();
|
||||
{
|
||||
File root = SPIFFS.open("/");
|
||||
File root = FILESYSTEM.open("/");
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
String fileName = file.name();
|
||||
@ -267,7 +275,7 @@ void setup(void) {
|
||||
}, handleFileUpload);
|
||||
|
||||
//called when the url is not defined here
|
||||
//use it to load content from SPIFFS
|
||||
//use it to load content from FILESYSTEM
|
||||
server.onNotFound([]() {
|
||||
if (!handleFileRead(server.uri())) {
|
||||
server.send(404, "text/plain", "FileNotFound");
|
||||
|
53
libraries/WebServer/examples/PathArgServer/PathArgServer.ino
Normal file
53
libraries/WebServer/examples/PathArgServer/PathArgServer.ino
Normal file
@ -0,0 +1,53 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
const char *ssid = "........";
|
||||
const char *password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
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("esp32")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", []() {
|
||||
server.send(200, "text/plain", "hello from esp32!");
|
||||
});
|
||||
|
||||
server.on("/users/{}", []() {
|
||||
String user = server.pathArg(0);
|
||||
server.send(200, "text/plain", "User: '" + user + "'");
|
||||
});
|
||||
|
||||
server.on("/users/{}/devices/{}", []() {
|
||||
String user = server.pathArg(0);
|
||||
String device = server.pathArg(1);
|
||||
server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
}
|
@ -32,6 +32,10 @@
|
||||
#define DEBUG_OUTPUT Serial
|
||||
#endif
|
||||
|
||||
#ifndef WEBSERVER_MAX_POST_ARGS
|
||||
#define WEBSERVER_MAX_POST_ARGS 32
|
||||
#endif
|
||||
|
||||
static const char Content_Type[] PROGMEM = "Content-Type";
|
||||
static const char filename[] PROGMEM = "filename";
|
||||
|
||||
@ -355,14 +359,14 @@ void WebServer::_uploadWriteByte(uint8_t b){
|
||||
_currentUpload->buf[_currentUpload->currentSize++] = b;
|
||||
}
|
||||
|
||||
uint8_t WebServer::_uploadReadByte(WiFiClient& client){
|
||||
int WebServer::_uploadReadByte(WiFiClient& client){
|
||||
int res = client.read();
|
||||
if(res == -1){
|
||||
while(!client.available() && client.connected())
|
||||
delay(2);
|
||||
res = client.read();
|
||||
}
|
||||
return (uint8_t)res;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
@ -383,8 +387,9 @@ bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
client.readStringUntil('\n');
|
||||
//start reading the form
|
||||
if (line == ("--"+boundary)){
|
||||
RequestArgument* postArgs = new RequestArgument[32];
|
||||
int postArgsLen = 0;
|
||||
if(_postArgs) delete[] _postArgs;
|
||||
_postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS];
|
||||
_postArgsLen = 0;
|
||||
while(1){
|
||||
String argName;
|
||||
String argValue;
|
||||
@ -445,7 +450,7 @@ bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
DEBUG_OUTPUT.println();
|
||||
#endif
|
||||
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
RequestArgument& arg = _postArgs[_postArgsLen++];
|
||||
arg.key = argName;
|
||||
arg.value = argValue;
|
||||
|
||||
@ -472,19 +477,20 @@ bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||
uint8_t argByte = _uploadReadByte(client);
|
||||
int argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
|
||||
while(argByte != 0x0D){
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
if(argByte < 0) return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if(argByte < 0) return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A){
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if(argByte < 0) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
@ -492,7 +498,7 @@ readfile:
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if(argByte < 0) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
@ -552,22 +558,25 @@ readfile:
|
||||
}
|
||||
|
||||
int iarg;
|
||||
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
|
||||
int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount;
|
||||
for (iarg = 0; iarg < totalArgs; iarg++){
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
RequestArgument& arg = _postArgs[_postArgsLen++];
|
||||
arg.key = _currentArgs[iarg].key;
|
||||
arg.value = _currentArgs[iarg].value;
|
||||
}
|
||||
if (_currentArgs) delete[] _currentArgs;
|
||||
_currentArgs = new RequestArgument[postArgsLen];
|
||||
for (iarg = 0; iarg < postArgsLen; iarg++){
|
||||
_currentArgs = new RequestArgument[_postArgsLen];
|
||||
for (iarg = 0; iarg < _postArgsLen; iarg++){
|
||||
RequestArgument& arg = _currentArgs[iarg];
|
||||
arg.key = postArgs[iarg].key;
|
||||
arg.value = postArgs[iarg].value;
|
||||
arg.key = _postArgs[iarg].key;
|
||||
arg.value = _postArgs[iarg].value;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
if (postArgs)
|
||||
delete[] postArgs;
|
||||
if (_postArgs) {
|
||||
delete[] _postArgs;
|
||||
_postArgs=nullptr;
|
||||
_postArgsLen = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
|
@ -54,6 +54,8 @@ WebServer::WebServer(IPAddress addr, int port)
|
||||
, _lastHandler(nullptr)
|
||||
, _currentArgCount(0)
|
||||
, _currentArgs(nullptr)
|
||||
, _postArgsLen(0)
|
||||
, _postArgs(nullptr)
|
||||
, _headerKeysCount(0)
|
||||
, _currentHeaders(nullptr)
|
||||
, _contentLength(0)
|
||||
@ -72,6 +74,8 @@ WebServer::WebServer(int port)
|
||||
, _lastHandler(nullptr)
|
||||
, _currentArgCount(0)
|
||||
, _currentArgs(nullptr)
|
||||
, _postArgsLen(0)
|
||||
, _postArgs(nullptr)
|
||||
, _headerKeysCount(0)
|
||||
, _currentHeaders(nullptr)
|
||||
, _contentLength(0)
|
||||
@ -505,8 +509,17 @@ void WebServer::_streamFileCore(const size_t fileSize, const String & fileName,
|
||||
send(200, contentType, "");
|
||||
}
|
||||
|
||||
String WebServer::pathArg(unsigned int i) {
|
||||
if (_currentHandler != nullptr)
|
||||
return _currentHandler->pathArg(i);
|
||||
return "";
|
||||
}
|
||||
|
||||
String WebServer::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;
|
||||
@ -531,6 +544,10 @@ int WebServer::args() {
|
||||
}
|
||||
|
||||
bool WebServer::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;
|
||||
|
@ -97,6 +97,7 @@ public:
|
||||
virtual WiFiClient client() { return _currentClient; }
|
||||
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
|
||||
@ -147,7 +148,7 @@ protected:
|
||||
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
uint8_t _uploadReadByte(WiFiClient& client);
|
||||
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);
|
||||
|
||||
@ -179,6 +180,9 @@ protected:
|
||||
|
||||
int _currentArgCount;
|
||||
RequestArgument* _currentArgs;
|
||||
int _postArgsLen;
|
||||
RequestArgument* _postArgs;
|
||||
|
||||
std::unique_ptr<HTTPUpload> _currentUpload;
|
||||
|
||||
int _headerKeysCount;
|
||||
|
@ -1,6 +1,9 @@
|
||||
#ifndef REQUESTHANDLER_H
|
||||
#define REQUESTHANDLER_H
|
||||
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
virtual ~RequestHandler() { }
|
||||
@ -14,6 +17,15 @@ public:
|
||||
|
||||
private:
|
||||
RequestHandler* _next = nullptr;
|
||||
|
||||
protected:
|
||||
std::vector<String> pathArgs;
|
||||
|
||||
public:
|
||||
const String& pathArg(unsigned int i) {
|
||||
assert(i < pathArgs.size());
|
||||
return pathArgs[i];
|
||||
}
|
||||
};
|
||||
|
||||
#endif //REQUESTHANDLER_H
|
||||
|
@ -15,16 +15,55 @@ public:
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
{
|
||||
int numParams = 0, start = 0;
|
||||
do {
|
||||
start = _uri.indexOf("{}", start);
|
||||
if (start > 0) {
|
||||
numParams++;
|
||||
start += 2;
|
||||
}
|
||||
} while (start > 0);
|
||||
pathArgs.resize(numParams);
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
if (_uri == requestUri)
|
||||
return true;
|
||||
|
||||
return true;
|
||||
size_t uriLength = _uri.length();
|
||||
unsigned int pathArgIndex = 0;
|
||||
unsigned int requestUriIndex = 0;
|
||||
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
|
||||
char uriChar = _uri[i];
|
||||
char requestUriChar = requestUri[requestUriIndex];
|
||||
|
||||
if (uriChar == requestUriChar)
|
||||
continue;
|
||||
if (uriChar != '{')
|
||||
return false;
|
||||
|
||||
i += 2; // index of char after '}'
|
||||
if (i >= uriLength) {
|
||||
// there is no char after '}'
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex);
|
||||
return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/'
|
||||
}
|
||||
else
|
||||
{
|
||||
char charEnd = _uri[i];
|
||||
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
|
||||
if (uriIndex < 0)
|
||||
return false;
|
||||
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex);
|
||||
requestUriIndex = (unsigned int) uriIndex;
|
||||
}
|
||||
pathArgIndex++;
|
||||
}
|
||||
|
||||
return requestUriIndex >= requestUri.length();
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user