forked from espressif/arduino-esp32
Compare commits
255 Commits
Author | SHA1 | Date | |
---|---|---|---|
6dab3f6777 | |||
1efcd21ba9 | |||
7b5cd47d07 | |||
14126060a1 | |||
25c0b52212 | |||
e0beac88c9 | |||
a87b2ec690 | |||
6744565257 | |||
da8b7c1b80 | |||
91508030d8 | |||
a0c975dfbc | |||
0906bf580f | |||
53a4bf33b6 | |||
92220b7643 | |||
dd649808d1 | |||
67ee7c32e7 | |||
8091c2cac7 | |||
4930853edb | |||
8e8c5035ea | |||
1db72a27c4 | |||
e28dce7eb7 | |||
d5f71ce545 | |||
7df50a97d1 | |||
566f659e90 | |||
cb0a939a6f | |||
cebd8704c8 | |||
ff85f3e90e | |||
dffda0bd6e | |||
2ceab7c279 | |||
84e458a9e1 | |||
f3c1a91f8e | |||
5af0336177 | |||
628b8f0c29 | |||
2bb32bd4b0 | |||
89d6b895d1 | |||
010a7c60f7 | |||
c0345eafbf | |||
71ec3c3e31 | |||
8ec76405b9 | |||
8806acfbf9 | |||
2e9cb5945d | |||
c001996eef | |||
c1344ae094 | |||
f6b9e9c2ab | |||
fc737e08c6 | |||
e302a6848a | |||
aa2393b573 | |||
755cd938f7 | |||
86fdb5b23d | |||
ffd15e4637 | |||
29d59876b4 | |||
8cbc60edbc | |||
00a546ee06 | |||
2ba1e66060 | |||
489feb8727 | |||
3350476d1d | |||
fa6f75952d | |||
6718da0350 | |||
45fa0f8294 | |||
6cf307dbd1 | |||
81844f5360 | |||
f7cf583b87 | |||
e8a2c16c8f | |||
9a7946e685 | |||
566b69eda3 | |||
e544e67838 | |||
b0582e1ec8 | |||
a539257a38 | |||
70656aa129 | |||
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 | |||
ff90778173 | |||
4e9d1ee237 | |||
c1a94b5326 | |||
3e160587f3 | |||
79010b6498 | |||
9925772db0 | |||
c77aed4ac4 | |||
901a341949 | |||
9f6d0d2958 | |||
9efecc1be0 | |||
b0c6991bcf | |||
8afdd71b3a | |||
871dd183d8 | |||
05111bbde7 | |||
bad53905e8 | |||
95b87545e7 | |||
9f8f05735b | |||
a835bb26c4 | |||
5e46c9bae6 | |||
659c8ad528 | |||
2fe965259a | |||
0161e28614 | |||
4e5cbdaa7f | |||
12ca9e8b52 | |||
8b01b9e187 |
54
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
54
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Please fill in the bug report carefully
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Make your question, not a Statement, inclusive. Include all pertinent information:
|
||||
|
||||
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).
|
||||
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? ?node32? ?ttgo_lora?
|
||||
Core Installation version: ?1.0.0? ?1.0.1-rc4? ?1.0.1? ?1.0.1-git?
|
||||
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
|
||||
Flash Frequency: ?40Mhz?
|
||||
PSRAM enabled: ?no? ?yes?
|
||||
Upload Speed: ?115200?
|
||||
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
|
||||
|
||||
### Description:
|
||||
Describe your problem here
|
||||
|
||||
|
||||
### 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
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Messages:
|
||||
```
|
||||
Enable Core debug level: Debug on tools menu of Arduino IDE, then put the serial output here
|
||||
```
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "libraries/BLE"]
|
||||
path = libraries/BLE
|
||||
url = https://github.com/nkolban/ESP32_BLE_Arduino.git
|
||||
[submodule "libraries/AzureIoT"]
|
||||
path = libraries/AzureIoT
|
||||
url = https://github.com/VSChina/ESP32_AzureIoT_Arduino
|
||||
|
@ -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
|
||||
@ -38,11 +42,13 @@ set(LIBRARY_SRCS
|
||||
libraries/AsyncUDP/src/AsyncUDP.cpp
|
||||
libraries/BluetoothSerial/src/BluetoothSerial.cpp
|
||||
libraries/DNSServer/src/DNSServer.cpp
|
||||
libraries/EEPROM/EEPROM.cpp
|
||||
libraries/EEPROM/src/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
|
||||
@ -173,10 +181,13 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
||||
libraries/BLE/src
|
||||
libraries/BluetoothSerial/src
|
||||
libraries/DNSServer/src
|
||||
libraries/EEPROM/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 +206,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()
|
||||
|
@ -90,6 +90,8 @@ config ARDUHAL_PARTITION_SCHEME_MINIMAL
|
||||
bool "Minimal (for 2MB FLASH)"
|
||||
config ARDUHAL_PARTITION_SCHEME_NO_OTA
|
||||
bool "No OTA (for large apps)"
|
||||
config ARDUHAL_PARTITION_SCHEME_HUGE_APP
|
||||
bool "Huge App (for very large apps)"
|
||||
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
|
||||
bool "Minimal SPIFFS (for large apps with OTA)"
|
||||
endchoice
|
||||
@ -99,6 +101,7 @@ config ARDUHAL_PARTITION_SCHEME
|
||||
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT
|
||||
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL
|
||||
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA
|
||||
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP
|
||||
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
|
||||
|
||||
|
||||
@ -106,8 +109,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
|
||||
|
14
README.md
14
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)
|
||||
@ -28,6 +31,7 @@ Most of the framework is implemented. Most noticable is the missing analogWrite.
|
||||
- [Using PlatformIO](docs/platformio.md)
|
||||
- [Building with make](docs/make.md)
|
||||
- [Using as ESP-IDF component](docs/esp-idf_component.md)
|
||||
- [Using OTAWebUpdater](docs/OTAWebUpdate/OTAWebUpdate.md)
|
||||
|
||||
#### Decoding exceptions
|
||||
|
||||
|
1853
boards.txt
1853
boards.txt
File diff suppressed because it is too large
Load Diff
36
component.mk
36
component.mk
@ -1,6 +1,36 @@
|
||||
ARDUINO_CORE_LIBS := $(patsubst $(COMPONENT_PATH)/%,%,$(sort $(dir $(wildcard $(COMPONENT_PATH)/libraries/*/*/))))
|
||||
ARDUINO_ALL_LIBRARIES := $(patsubst $(COMPONENT_PATH)/libraries/%,%,$(wildcard $(COMPONENT_PATH)/libraries/*))
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 variants/esp32 $(ARDUINO_CORE_LIBS)
|
||||
# Macro returns non-empty if Arduino library $(1) should be included in the build
|
||||
# (either because selective compilation is of, or this library is enabled
|
||||
define ARDUINO_LIBRARY_ENABLED
|
||||
$(if $(CONFIG_ARDUINO_SELECTIVE_COMPILATION),$(CONFIG_ARDUINO_SELECTIVE_$(1)),y)
|
||||
endef
|
||||
|
||||
ARDUINO_ENABLED_LIBRARIES := $(foreach LIBRARY,$(sort $(ARDUINO_ALL_LIBRARIES)),$(if $(call ARDUINO_LIBRARY_ENABLED,$(LIBRARY)),$(LIBRARY)))
|
||||
|
||||
$(info Arduino libraries in build: $(ARDUINO_ENABLED_LIBRARIES))
|
||||
|
||||
# Expand all subdirs under $(1)
|
||||
define EXPAND_SUBDIRS
|
||||
$(sort $(dir $(wildcard $(1)/* $(1)/*/* $(1)/*/*/* $(1)/*/*/*/* $(1)/*/*/*/*/*)))
|
||||
endef
|
||||
|
||||
# Macro returns SRCDIRS for library
|
||||
define ARDUINO_LIBRARY_GET_SRCDIRS
|
||||
$(if $(wildcard $(COMPONENT_PATH)/libraries/$(1)/src/.), \
|
||||
$(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)/src), \
|
||||
$(filter-out $(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)/examples), \
|
||||
$(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)) \
|
||||
) \
|
||||
)
|
||||
endef
|
||||
|
||||
# Make a list of all srcdirs in enabled libraries
|
||||
ARDUINO_LIBRARY_SRCDIRS := $(patsubst $(COMPONENT_PATH)/%,%,$(foreach LIBRARY,$(ARDUINO_ENABLED_LIBRARIES),$(call ARDUINO_LIBRARY_GET_SRCDIRS,$(LIBRARY))))
|
||||
|
||||
#$(info Arduino libraries src dirs: $(ARDUINO_LIBRARY_SRCDIRS))
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 variants/esp32 $(ARDUINO_LIBRARY_SRCDIRS)
|
||||
COMPONENT_PRIV_INCLUDEDIRS := cores/esp32/libb64
|
||||
COMPONENT_SRCDIRS := cores/esp32/libb64 cores/esp32 variants/esp32 $(ARDUINO_CORE_LIBS)
|
||||
COMPONENT_SRCDIRS := cores/esp32/libb64 cores/esp32 variants/esp32 $(ARDUINO_LIBRARY_SRCDIRS)
|
||||
CXXFLAGS += -fno-rtti
|
||||
|
@ -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,15 +3,34 @@
|
||||
#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);
|
||||
HardwareSerial Serial2(2);
|
||||
#endif
|
||||
|
||||
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");
|
||||
@ -25,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()
|
||||
@ -44,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:
|
||||
@ -79,6 +99,8 @@ protected:
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
extern HardwareSerial Serial;
|
||||
extern HardwareSerial Serial1;
|
||||
extern HardwareSerial Serial2;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
@ -82,6 +82,9 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
unsigned long Stream::getTimeout(void) {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
// find returns true if the target string is found
|
||||
bool Stream::find(const char *target)
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
|
||||
unsigned long getTimeout(void);
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(uint8_t *target)
|
||||
{
|
||||
|
@ -37,10 +37,23 @@ void randomSeed(unsigned long seed)
|
||||
|
||||
long random(long howbig)
|
||||
{
|
||||
if(howbig == 0) {
|
||||
return 0;
|
||||
uint32_t x = esp_random();
|
||||
uint64_t m = uint64_t(x) * uint64_t(howbig);
|
||||
uint32_t l = uint32_t(m);
|
||||
if (l < howbig) {
|
||||
uint32_t t = -howbig;
|
||||
if (t >= howbig) {
|
||||
t -= howbig;
|
||||
if (t >= howbig)
|
||||
t %= howbig;
|
||||
}
|
||||
while (l < t) {
|
||||
x = esp_random();
|
||||
m = uint64_t(x) * uint64_t(howbig);
|
||||
l = uint32_t(m);
|
||||
}
|
||||
}
|
||||
return esp_random() % howbig;
|
||||
return m >> 32;
|
||||
}
|
||||
|
||||
long random(long howsmall, long howbig)
|
||||
|
@ -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;
|
||||
|
209
cores/esp32/esp32-hal-cpu.c
Normal file
209
cores/esp32/esp32-hal-cpu.c
Normal file
@ -0,0 +1,209 @@
|
||||
// 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();
|
||||
if(xtal > RTC_XTAL_FREQ_AUTO){
|
||||
if(xtal < RTC_XTAL_FREQ_40M) {
|
||||
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_ */
|
@ -70,7 +70,13 @@ const DRAM_ATTR esp32_gpioMux_t esp32_gpioMux[GPIO_PIN_COUNT]={
|
||||
};
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
static voidFuncPtr __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,};
|
||||
typedef void (*voidFuncPtrArg)(void*);
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void* arg;
|
||||
bool functional;
|
||||
} InterruptHandle_t;
|
||||
static InterruptHandle_t __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,};
|
||||
|
||||
#include "driver/rtc_io.h"
|
||||
|
||||
@ -193,7 +199,7 @@ extern int IRAM_ATTR __digitalRead(uint8_t pin)
|
||||
|
||||
static intr_handle_t gpio_intr_handle = NULL;
|
||||
|
||||
static void IRAM_ATTR __onPinInterrupt(void *arg)
|
||||
static void IRAM_ATTR __onPinInterrupt()
|
||||
{
|
||||
uint32_t gpio_intr_status_l=0;
|
||||
uint32_t gpio_intr_status_h=0;
|
||||
@ -207,8 +213,12 @@ static void IRAM_ATTR __onPinInterrupt(void *arg)
|
||||
if(gpio_intr_status_l) {
|
||||
do {
|
||||
if(gpio_intr_status_l & ((uint32_t)1 << pin)) {
|
||||
if(__pinInterruptHandlers[pin]) {
|
||||
__pinInterruptHandlers[pin]();
|
||||
if(__pinInterruptHandlers[pin].fn) {
|
||||
if(__pinInterruptHandlers[pin].arg){
|
||||
((voidFuncPtrArg)__pinInterruptHandlers[pin].fn)(__pinInterruptHandlers[pin].arg);
|
||||
} else {
|
||||
__pinInterruptHandlers[pin].fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(++pin<32);
|
||||
@ -217,23 +227,38 @@ static void IRAM_ATTR __onPinInterrupt(void *arg)
|
||||
pin=32;
|
||||
do {
|
||||
if(gpio_intr_status_h & ((uint32_t)1 << (pin - 32))) {
|
||||
if(__pinInterruptHandlers[pin]) {
|
||||
__pinInterruptHandlers[pin]();
|
||||
if(__pinInterruptHandlers[pin].fn) {
|
||||
if(__pinInterruptHandlers[pin].arg){
|
||||
((voidFuncPtrArg)__pinInterruptHandlers[pin].fn)(__pinInterruptHandlers[pin].arg);
|
||||
} else {
|
||||
__pinInterruptHandlers[pin].fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(++pin<GPIO_PIN_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, 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);
|
||||
}
|
||||
__pinInterruptHandlers[pin] = userFunc;
|
||||
|
||||
// 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;
|
||||
@ -244,10 +269,26 @@ extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type)
|
||||
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) {
|
||||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __detachInterrupt(uint8_t pin)
|
||||
{
|
||||
esp_intr_disable(gpio_intr_handle);
|
||||
__pinInterruptHandlers[pin] = NULL;
|
||||
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);
|
||||
@ -258,5 +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, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
|
||||
|
||||
|
@ -79,6 +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 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;
|
||||
@ -138,7 +167,7 @@ static void _ledcSetupChannel(uint8_t chan, uint8_t idle_level)
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
if(group) {
|
||||
LEDC_CHAN(group, channel).conf0.val &= ~BIT(4);
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 0;
|
||||
}
|
||||
@ -167,7 +196,7 @@ void ledcWrite(uint8_t chan, uint32_t duty)
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
if(group) {
|
||||
LEDC_CHAN(group, channel).conf0.val |= BIT(4);
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 1;
|
||||
}
|
||||
@ -175,7 +204,7 @@ void ledcWrite(uint8_t chan, uint32_t duty)
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
if(group) {
|
||||
LEDC_CHAN(group, channel).conf0.val &= ~BIT(4);
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 0;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@ -22,8 +21,19 @@
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#ifdef CONFIG_APP_ROLLBACK_ENABLE
|
||||
#include "esp_ota_ops.h"
|
||||
#endif //CONFIG_APP_ROLLBACK_ENABLE
|
||||
#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
|
||||
//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino
|
||||
@ -39,14 +49,76 @@ 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void feedLoopWDT(){
|
||||
esp_err_t err = esp_task_wdt_reset();
|
||||
if(err != ESP_OK){
|
||||
log_e("Failed to feed WDT! Error: %d", err);
|
||||
}
|
||||
}
|
||||
#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()
|
||||
{
|
||||
return (unsigned long) (esp_timer_get_time() / 1000);
|
||||
return (unsigned long) (esp_timer_get_time() / 1000ULL);
|
||||
}
|
||||
|
||||
void delay(uint32_t ms)
|
||||
@ -76,6 +148,9 @@ void initVariant() {}
|
||||
void init() __attribute__((weak));
|
||||
void init() {}
|
||||
|
||||
bool verifyOta() __attribute__((weak));
|
||||
bool verifyOta() { return true; }
|
||||
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
//overwritten in esp32-hal-bt.c
|
||||
bool btInUse() __attribute__((weak));
|
||||
@ -84,6 +159,25 @@ bool btInUse(){ return false; }
|
||||
|
||||
void initArduino()
|
||||
{
|
||||
#ifdef CONFIG_APP_ROLLBACK_ENABLE
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
if (verifyOta()) {
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
log_e("OTA verification failed! Start rollback to the previous version ...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//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
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_SPIRAM_SUPPORT
|
||||
#include "esp_spiram.h"
|
||||
@ -20,7 +19,7 @@ bool psramInit(){
|
||||
}
|
||||
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
|
||||
uint32_t pkg_ver = chip_ver & 0x7;
|
||||
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
|
||||
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
|
||||
spiramFailed = true;
|
||||
log_w("PSRAM not supported!");
|
||||
return false;
|
||||
|
@ -15,6 +15,10 @@
|
||||
#ifndef _ESP32_HAL_PSRAM_H_
|
||||
#define _ESP32_HAL_PSRAM_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool psramInit();
|
||||
bool psramFound();
|
||||
|
||||
@ -22,4 +26,8 @@ void *ps_malloc(size_t size);
|
||||
void *ps_calloc(size_t n, size_t size);
|
||||
void *ps_realloc(void *ptr, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP32_HAL_PSRAM_H_ */
|
||||
|
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].high = 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);
|
||||
}
|
||||
}
|
||||
uint32_t *data_received = data;
|
||||
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
|
||||
(g_rmt_objects[ch].cb)(data_received, _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;
|
||||
}
|
141
cores/esp32/esp32-hal-rmt.h
Normal file
141
cores/esp32/esp32-hal-rmt.h
Normal file
@ -0,0 +1,141 @@
|
||||
// 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);
|
||||
|
||||
/**
|
||||
* Deinitialize the driver
|
||||
*/
|
||||
bool rmtDeinit(rmt_obj_t *rmt);
|
||||
|
||||
// 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));
|
||||
@ -76,23 +76,15 @@ void configTzTime(const char* tz, const char* server1, const char* server2, cons
|
||||
|
||||
bool getLocalTime(struct tm * info, uint32_t ms)
|
||||
{
|
||||
uint32_t count = ms / 10;
|
||||
uint32_t start = millis();
|
||||
time_t now;
|
||||
|
||||
time(&now);
|
||||
localtime_r(&now, info);
|
||||
|
||||
if(info->tm_year > (2016 - 1900)){
|
||||
return true;
|
||||
}
|
||||
|
||||
while(count--) {
|
||||
delay(10);
|
||||
while((millis()-start) <= ms) {
|
||||
time(&now);
|
||||
localtime_r(&now, info);
|
||||
if(info->tm_year > (2016 - 1900)){
|
||||
return true;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -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,13 +333,16 @@ 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);
|
||||
|
||||
uart->dev->conf0.txfifo_rst = 1;
|
||||
uart->dev->conf0.txfifo_rst = 0;
|
||||
//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.
|
||||
|
||||
// we read the data out and make `fifo_len == 0 && rd_addr == wr_addr`.
|
||||
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
READ_PERI_REG(UART_FIFO_REG(uart->num));
|
||||
}
|
||||
|
||||
uart->dev->conf0.rxfifo_rst = 1;
|
||||
uart->dev->conf0.rxfifo_rst = 0;
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
@ -329,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)
|
||||
@ -362,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);
|
||||
@ -389,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;
|
||||
@ -417,7 +481,7 @@ int log_printf(const char *format, ...)
|
||||
vsnprintf(temp, len+1, format, arg);
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(_uart_bus_array[s_uart_debug_nr].lock){
|
||||
while (xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY) != pdPASS);
|
||||
xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY);
|
||||
ets_printf("%s", temp);
|
||||
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
|
||||
} else {
|
||||
@ -427,8 +491,77 @@ 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];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the status of the RX state machine, if the value is non-zero the state machine is active.
|
||||
*/
|
||||
bool uartRxActive(uart_t* uart) {
|
||||
return uart->dev->status.st_urx_out != 0;
|
||||
}
|
||||
|
@ -67,9 +67,15 @@ 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);
|
||||
|
||||
bool uartRxActive(uart_t* uart);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -33,6 +33,7 @@ extern "C" {
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000U)
|
||||
@ -56,15 +57,39 @@ 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 "esp_system.h"
|
||||
#include "esp32-hal-cpu.h"
|
||||
|
||||
#ifndef BOARD_HAS_PSRAM
|
||||
#ifdef CONFIG_SPIRAM_SUPPORT
|
||||
#undef CONFIG_SPIRAM_SUPPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//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();
|
||||
//feed WDT for the loop task
|
||||
void feedLoopWDT();
|
||||
#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
|
||||
|
@ -25,11 +25,12 @@ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < 8; ++i) {
|
||||
digitalWrite(clockPin, HIGH);
|
||||
//digitalWrite(clockPin, HIGH);
|
||||
if(bitOrder == LSBFIRST)
|
||||
value |= digitalRead(dataPin) << i;
|
||||
else
|
||||
value |= digitalRead(dataPin) << (7 - i);
|
||||
digitalWrite(clockPin, HIGH);
|
||||
digitalWrite(clockPin, LOW);
|
||||
}
|
||||
return value;
|
||||
|
@ -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
|
||||
|
59
docs/OTAWebUpdate/OTAWebUpdate.md
Normal file
59
docs/OTAWebUpdate/OTAWebUpdate.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Over the Air through Web browser
|
||||
OTAWebUpdate is done with a web browser that can be useful in the following typical scenarios:
|
||||
- Once the application developed and loading directly from Arduino IDE is inconvenient or not possible
|
||||
- after deployment if user is unable to expose Firmware for OTA from external update server
|
||||
- provide updates after deployment to small quantity of modules when setting an update server is not practicable
|
||||
|
||||
## Requirements
|
||||
- The ESP and the computer must be connected to the same network
|
||||
|
||||
## Implementation
|
||||
The sample implementation has been done using:
|
||||
- example sketch OTAWebUpdater.ino
|
||||
- ESP32 (Dev Module)
|
||||
|
||||
You can use another module also if it meets Flash chip size of the sketch
|
||||
|
||||
1-Before you begin, please make sure that you have the following software installed:
|
||||
- Arduino IDE
|
||||
- Host software depending on O/S you use:
|
||||
- Avahi http://avahi.org/ for Linux
|
||||
- Bonjour http://www.apple.com/support/bonjour/ for Windows
|
||||
- Mac OSX and iOS - support is already built in / no any extra s/w is required
|
||||
|
||||
Prepare the sketch and configuration for initial upload with a serial port
|
||||
- Start Arduino IDE and load sketch OTAWebUpdater.ino available under File > Examples > OTAWebUpdater.ino
|
||||
- Update ssid and pass in the sketch so the module can join your Wi-Fi network
|
||||
- Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option
|
||||
|
||||

|
||||
|
||||
- Upload sketch (Ctrl+U)
|
||||
- Now open web browser and enter the url, i.e. http://esp32.local. Once entered, browser should display a form
|
||||
|
||||

|
||||
|
||||
> username= admin
|
||||
|
||||
> password= admin
|
||||
|
||||
**Note**-*If entering “http://ESP32.local” does not work, try replacing “ESP32” with module’s IP address.This workaround is useful in case the host software installed does not work*.
|
||||
|
||||
Now click on Login button and browser will display a upload form
|
||||
|
||||

|
||||
|
||||
For Uploading the New Firmware you need to provide the Binary File of your Code.
|
||||
|
||||
Exporting Binary file of the Firmware (Code)
|
||||
- Open up the Arduino IDE
|
||||
- Open up the Code, for Exporting up Binary file
|
||||
- Now go to Sketch > export compiled Binary
|
||||

|
||||
|
||||
- Binary file is exported to the same Directory where your code is present
|
||||
|
||||
Once you are comfortable with this procedure go ahead and modify OTAWebUpdater.ino sketch to print some additional messages, compile it, Export new binary file and upload it using web browser to see entered changes on a Serial Monitor
|
||||
|
||||
|
||||
|
BIN
docs/OTAWebUpdate/esp32login.PNG
Normal file
BIN
docs/OTAWebUpdate/esp32login.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
docs/OTAWebUpdate/esp32upload.PNG
Normal file
BIN
docs/OTAWebUpdate/esp32upload.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
docs/OTAWebUpdate/esp32verbose.PNG
Normal file
BIN
docs/OTAWebUpdate/esp32verbose.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
docs/OTAWebUpdate/exportTobinary.PNG
Normal file
BIN
docs/OTAWebUpdate/exportTobinary.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
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`
|
@ -16,20 +16,21 @@ Installation instructions for Debian / Ubuntu OS
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
python2 get.py
|
||||
python3 get.py
|
||||
```
|
||||
- Restart Arduino IDE
|
||||
|
||||
|
||||
|
||||
- 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```
|
||||
python3 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
|
||||
|
||||
|
@ -4,93 +4,101 @@
|
||||
#include <ESPmDNS.h>
|
||||
#include <Update.h>
|
||||
|
||||
const char* host = "ESP32";
|
||||
const char* host = "esp32";
|
||||
const char* ssid = "xxx";
|
||||
const char* password = "xxxx";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const char* loginIndex = "<form name='loginForm'>"
|
||||
"<table width='20%' bgcolor='A09F9F' align='center'>"
|
||||
"<tr>"
|
||||
"<td colspan=2><center><font size=4><b>ESP32 Login Page</b></font></center>"
|
||||
"<br>"
|
||||
/*
|
||||
* Login page
|
||||
*/
|
||||
|
||||
"</td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
"<td>Username:</td>"
|
||||
"<td><input type='text' size=25 name='userid'><br></td>"
|
||||
"</tr>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"<tr>"
|
||||
"<td>Password:</td>"
|
||||
"<td><input type='Password' size=25 name='pwd'><br></td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
const char* loginIndex =
|
||||
"<form name='loginForm'>"
|
||||
"<table width='20%' bgcolor='A09F9F' align='center'>"
|
||||
"<tr>"
|
||||
"<td colspan=2>"
|
||||
"<center><font size=4><b>ESP32 Login Page</b></font></center>"
|
||||
"<br>"
|
||||
"</td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
"<td>Username:</td>"
|
||||
"<td><input type='text' size=25 name='userid'><br></td>"
|
||||
"</tr>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"<tr>"
|
||||
"<td>Password:</td>"
|
||||
"<td><input type='Password' size=25 name='pwd'><br></td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
"<tr>"
|
||||
"<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
|
||||
"</tr>"
|
||||
"</table>"
|
||||
"</form>"
|
||||
"<script>"
|
||||
"function check(form)"
|
||||
"{"
|
||||
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
|
||||
"{"
|
||||
"window.open('/serverIndex')"
|
||||
"}"
|
||||
"else"
|
||||
"{"
|
||||
" alert('Error Password or Username')/*displays error message*/"
|
||||
"}"
|
||||
"}"
|
||||
"</script>";
|
||||
|
||||
/*
|
||||
* Server Index Page
|
||||
*/
|
||||
|
||||
const char* serverIndex =
|
||||
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
|
||||
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
|
||||
"<input type='file' name='update'>"
|
||||
"<input type='submit' value='Update'>"
|
||||
"</form>"
|
||||
"<div id='prg'>progress: 0%</div>"
|
||||
"<script>"
|
||||
"$('form').submit(function(e){"
|
||||
"e.preventDefault();"
|
||||
"var form = $('#upload_form')[0];"
|
||||
"var data = new FormData(form);"
|
||||
" $.ajax({"
|
||||
"url: '/update',"
|
||||
"type: 'POST',"
|
||||
"data: data,"
|
||||
"contentType: false,"
|
||||
"processData:false,"
|
||||
"xhr: function() {"
|
||||
"var xhr = new window.XMLHttpRequest();"
|
||||
"xhr.upload.addEventListener('progress', function(evt) {"
|
||||
"if (evt.lengthComputable) {"
|
||||
"var per = evt.loaded / evt.total;"
|
||||
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
|
||||
"}"
|
||||
"}, false);"
|
||||
"return xhr;"
|
||||
"},"
|
||||
"success:function(d, s) {"
|
||||
"console.log('success!')"
|
||||
"},"
|
||||
"error: function (a, b, c) {"
|
||||
"}"
|
||||
"});"
|
||||
"});"
|
||||
"</script>";
|
||||
|
||||
"<tr>"
|
||||
"<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
|
||||
"</tr>"
|
||||
"</table>"
|
||||
"</form>"
|
||||
"<script>"
|
||||
"function check(form)"
|
||||
"{"
|
||||
"if(form.userid.value=='admin' && form.pwd.value=='admin')"
|
||||
"{"
|
||||
"window.open('/serverIndex')"
|
||||
"}"
|
||||
"else"
|
||||
"{"
|
||||
" alert('Error Password or Username')/*displays error message*/"
|
||||
|
||||
"}"
|
||||
"}"
|
||||
"</script>";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const char* serverIndex = "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
|
||||
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
|
||||
"<input type='file' name='update'>"
|
||||
"<input type='submit' value='Update'>"
|
||||
"</form>"
|
||||
"<div id='prg'>progress: 0%</div>"
|
||||
"<script>"
|
||||
"$('form').submit(function(e){"
|
||||
"e.preventDefault();"
|
||||
"var form = $('#upload_form')[0];"
|
||||
"var data = new FormData(form);"
|
||||
" $.ajax({"
|
||||
"url: '/update',"
|
||||
"type: 'POST',"
|
||||
"data: data,"
|
||||
"contentType: false,"
|
||||
"processData:false,"
|
||||
"xhr: function() {"
|
||||
"var xhr = new window.XMLHttpRequest();"
|
||||
"xhr.upload.addEventListener('progress', function(evt) {"
|
||||
"if (evt.lengthComputable) {"
|
||||
"var per = evt.loaded / evt.total;"
|
||||
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
|
||||
"}"
|
||||
"}, false);"
|
||||
"return xhr;"
|
||||
"},"
|
||||
"success:function(d, s) {"
|
||||
"console.log('success!')"
|
||||
"},"
|
||||
"error: function (a, b, c) {"
|
||||
"}"
|
||||
"});"
|
||||
"});"
|
||||
"</script>";
|
||||
/*
|
||||
* setup function
|
||||
*/
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
@ -110,7 +118,7 @@ void setup(void) {
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
/*use mdns for host name resolution*/
|
||||
if (!MDNS.begin(host)) { //http://esp32.local
|
||||
if (!MDNS.begin(host)) { //http://esp32.local
|
||||
Serial.println("Error setting up MDNS responder!");
|
||||
while (1) {
|
||||
delay(1000);
|
||||
|
@ -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){
|
||||
@ -647,9 +682,8 @@ void AsyncUDP::_recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t po
|
||||
if(_handler) {
|
||||
AsyncUDPPacket packet(this, this_pb, addr, port, netif);
|
||||
_handler(packet);
|
||||
} else {
|
||||
pbuf_free(this_pb);
|
||||
}
|
||||
pbuf_free(this_pb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 deleted from 7951347ed6
15
libraries/BLE/README.md
Normal file
15
libraries/BLE/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# ESP32 BLE for Arduino
|
||||
The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino.
|
||||
|
||||
The actual source of the project which is being maintained can be found here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets
|
||||
|
||||
Issues and questions should be raised here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets/issues
|
||||
|
||||
|
||||
Documentation for using the library can be found here:
|
||||
|
||||
https://github.com/nkolban/esp32-snippets/tree/master/Documentation
|
160
libraries/BLE/examples/BLE_client/BLE_client.ino
Normal file
160
libraries/BLE/examples/BLE_client/BLE_client.ino
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* A BLE client example that is rich in capabilities.
|
||||
* There is a lot new capabilities implemented.
|
||||
* author unknown
|
||||
* updated by chegewara
|
||||
*/
|
||||
|
||||
#include "BLEDevice.h"
|
||||
//#include "BLEScan.h"
|
||||
|
||||
// The remote service we wish to connect to.
|
||||
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
|
||||
// The characteristic of the remote service we are interested in.
|
||||
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
|
||||
|
||||
static boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLERemoteCharacteristic* pRemoteCharacteristic;
|
||||
static BLEAdvertisedDevice* myDevice;
|
||||
|
||||
static void notifyCallback(
|
||||
BLERemoteCharacteristic* pBLERemoteCharacteristic,
|
||||
uint8_t* pData,
|
||||
size_t length,
|
||||
bool isNotify) {
|
||||
Serial.print("Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("data: ");
|
||||
Serial.println((char*)pData);
|
||||
}
|
||||
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient* pclient) {
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient* pclient) {
|
||||
connected = false;
|
||||
Serial.println("onDisconnect");
|
||||
}
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Forming a connection to ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
BLEClient* pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
pClient->setClientCallbacks(new MyClientCallback());
|
||||
|
||||
// Connect to the remove BLE Server.
|
||||
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
|
||||
Serial.println(" - Connected to server");
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server.
|
||||
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
|
||||
// Obtain a reference to the characteristic in the service of the remote BLE server.
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic");
|
||||
|
||||
// Read the value of the characteristic.
|
||||
if(pRemoteCharacteristic->canRead()) {
|
||||
std::string value = pRemoteCharacteristic->readValue();
|
||||
Serial.print("The characteristic value was: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
if(pRemoteCharacteristic->canNotify())
|
||||
pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
/**
|
||||
* Scan for BLE servers and find the first one that advertises the service we are looking for.
|
||||
*/
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
/**
|
||||
* Called for each advertising BLE server.
|
||||
*/
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Advertised Device found: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// We have found a device, let us now see if it contains the service we are looking for.
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
|
||||
|
||||
BLEDevice::getScan()->stop();
|
||||
myDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
doScan = true;
|
||||
|
||||
} // Found our server
|
||||
} // onResult
|
||||
}; // MyAdvertisedDeviceCallbacks
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Arduino BLE Client application...");
|
||||
BLEDevice::init("");
|
||||
|
||||
// Retrieve a Scanner and set the callback we want to use to be informed when we
|
||||
// have detected a new device. Specify that we want active scanning and start the
|
||||
// scan to run for 5 seconds.
|
||||
BLEScan* pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
} // End of setup.
|
||||
|
||||
|
||||
// This is the Arduino main loop function.
|
||||
void loop() {
|
||||
|
||||
// If the flag "doConnect" is true then we have scanned for and found the desired
|
||||
// BLE Server with which we wish to connect. Now we connect to it. Once we are
|
||||
// connected we set the connected flag to be true.
|
||||
if (doConnect == true) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("We are now connected to the BLE Server.");
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
|
||||
// with the current time since boot.
|
||||
if (connected) {
|
||||
String newValue = "Time since boot: " + String(millis()/1000);
|
||||
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
|
||||
|
||||
// Set the characteristic's value to be the array of bytes that is actually a string.
|
||||
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
|
||||
}else if(doScan){
|
||||
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
|
||||
}
|
||||
|
||||
delay(1000); // Delay a second between loops.
|
||||
} // End of loop
|
103
libraries/BLE/examples/BLE_iBeacon/BLE_iBeacon.ino
Normal file
103
libraries/BLE/examples/BLE_iBeacon/BLE_iBeacon.ino
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by pcbreflux
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic iBeacon frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
#include "sys/time.h"
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEBeacon.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint8_t temprature_sens_read();
|
||||
//uint8_t g_phyFuns;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval now;
|
||||
|
||||
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
||||
|
||||
void setBeacon() {
|
||||
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
|
||||
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
|
||||
oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16);
|
||||
oBeacon.setMinor(bootcount&0xFFFF);
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
|
||||
|
||||
std::string strServiceData = "";
|
||||
|
||||
strServiceData += (char)26; // Len
|
||||
strServiceData += (char)0xFF; // Type
|
||||
strServiceData += oBeacon.getData();
|
||||
oAdvertisementData.addData(strServiceData);
|
||||
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
Serial.printf("start ESP32 %d\n",bootcount++);
|
||||
|
||||
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last);
|
||||
|
||||
last = now.tv_sec;
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("");
|
||||
|
||||
// Create the BLE Server
|
||||
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertizing started...");
|
||||
delay(100);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("enter deep sleep\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
Serial.printf("in deep sleep\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
110
libraries/BLE/examples/BLE_notify/BLE_notify.ino
Normal file
110
libraries/BLE/examples/BLE_notify/BLE_notify.ino
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect hander associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer* pServer = NULL;
|
||||
BLECharacteristic* pCharacteristic = NULL;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
40
libraries/BLE/examples/BLE_scan/BLE_scan.ino
Normal file
40
libraries/BLE/examples/BLE_scan/BLE_scan.ino
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan* pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices.getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
45
libraries/BLE/examples/BLE_server/BLE_server.ino
Normal file
45
libraries/BLE/examples/BLE_server/BLE_server.ino
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updates by chegewara
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
BLEDevice::init("Long name works now");
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pCharacteristic->setValue("Hello World says Neil");
|
||||
pService->start();
|
||||
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMinPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect hander associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer* pServer = NULL;
|
||||
BLECharacteristic* pCharacteristic = NULL;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
BLEDevice::startAdvertising();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t*)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
125
libraries/BLE/examples/BLE_uart/BLE_uart.ino
Normal file
125
libraries/BLE/examples/BLE_uart/BLE_uart.ino
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
|
||||
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
|
||||
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
In this example rxValue is the data received (only accessible inside that function).
|
||||
And txValue is the data to be sent, in this example just a byte incremented every second.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic * pTxCharacteristic;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint8_t txValue = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
|
||||
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer* pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("Received Value: ");
|
||||
for (int i = 0; i < rxValue.length(); i++)
|
||||
Serial.print(rxValue[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("UART Service");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pTxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_TX,
|
||||
BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
|
||||
pTxCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID_RX,
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pRxCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
pServer->getAdvertising()->start();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (deviceConnected) {
|
||||
pTxCharacteristic->setValue(&txValue, 1);
|
||||
pTxCharacteristic->notify();
|
||||
txValue++;
|
||||
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
|
||||
}
|
||||
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
65
libraries/BLE/examples/BLE_write/BLE_write.ino
Normal file
65
libraries/BLE/examples/BLE_write/BLE_write.ino
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
|
||||
class MyCallbacks: public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
std::string value = pCharacteristic->getValue();
|
||||
|
||||
if (value.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
Serial.print(value[i]);
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("1- Download and install an BLE scanner app in your phone");
|
||||
Serial.println("2- Scan for BLE devices in the app");
|
||||
Serial.println("3- Connect to MyESP32");
|
||||
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
|
||||
Serial.println("5- See the magic =)");
|
||||
|
||||
BLEDevice::init("MyESP32");
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE
|
||||
);
|
||||
|
||||
pCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
pCharacteristic->setValue("Hello World");
|
||||
pService->start();
|
||||
|
||||
BLEAdvertising *pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
10
libraries/BLE/library.properties
Normal file
10
libraries/BLE/library.properties
Normal file
@ -0,0 +1,10 @@
|
||||
name=ESP32 BLE Arduino
|
||||
version=1.0.1
|
||||
author=Neil Kolban <kolban1@kolban.com>
|
||||
maintainer=Dariusz Krempa <esp32@esp32.eu.org>
|
||||
sentence=BLE functions for ESP32
|
||||
paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform.
|
||||
category=Communication
|
||||
url=https://github.com/nkolban/ESP32_BLE_Arduino
|
||||
architectures=esp32
|
||||
includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h
|
62
libraries/BLE/src/BLE2902.cpp
Normal file
62
libraries/BLE/src/BLE2902.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* BLE2902.cpp
|
||||
*
|
||||
* Created on: Jun 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLE2902.h"
|
||||
|
||||
BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) {
|
||||
uint8_t data[2] = { 0, 0 };
|
||||
setValue(data, 2);
|
||||
} // BLE2902
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the notifications value.
|
||||
* @return The notifications value. True if notifications are enabled and false if not.
|
||||
*/
|
||||
bool BLE2902::getNotifications() {
|
||||
return (getValue()[0] & (1 << 0)) != 0;
|
||||
} // getNotifications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the indications value.
|
||||
* @return The indications value. True if indications are enabled and false if not.
|
||||
*/
|
||||
bool BLE2902::getIndications() {
|
||||
return (getValue()[0] & (1 << 1)) != 0;
|
||||
} // getIndications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the indications flag.
|
||||
* @param [in] flag The indications flag.
|
||||
*/
|
||||
void BLE2902::setIndications(bool flag) {
|
||||
uint8_t *pValue = getValue();
|
||||
if (flag) pValue[0] |= 1 << 1;
|
||||
else pValue[0] &= ~(1 << 1);
|
||||
} // setIndications
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the notifications flag.
|
||||
* @param [in] flag The notifications flag.
|
||||
*/
|
||||
void BLE2902::setNotifications(bool flag) {
|
||||
uint8_t *pValue = getValue();
|
||||
if (flag) pValue[0] |= 1 << 0;
|
||||
else pValue[0] &= ~(1 << 0);
|
||||
} // setNotifications
|
||||
|
||||
#endif
|
34
libraries/BLE/src/BLE2902.h
Normal file
34
libraries/BLE/src/BLE2902.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* BLE2902.h
|
||||
*
|
||||
* Created on: Jun 25, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLE2902_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEDescriptor.h"
|
||||
|
||||
/**
|
||||
* @brief Descriptor for Client Characteristic Configuration.
|
||||
*
|
||||
* This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
*/
|
||||
class BLE2902: public BLEDescriptor {
|
||||
public:
|
||||
BLE2902();
|
||||
bool getNotifications();
|
||||
bool getIndications();
|
||||
void setNotifications(bool flag);
|
||||
void setIndications(bool flag);
|
||||
|
||||
}; // BLE2902
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */
|
74
libraries/BLE/src/BLE2904.cpp
Normal file
74
libraries/BLE/src/BLE2904.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* BLE2904.cpp
|
||||
*
|
||||
* Created on: Dec 23, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLE2904.h"
|
||||
|
||||
|
||||
BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) {
|
||||
m_data.m_format = 0;
|
||||
m_data.m_exponent = 0;
|
||||
m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers
|
||||
m_data.m_unit = 0;
|
||||
m_data.m_description = 0;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // BLE2902
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the description.
|
||||
*/
|
||||
void BLE2904::setDescription(uint16_t description) {
|
||||
m_data.m_description = description;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the exponent.
|
||||
*/
|
||||
void BLE2904::setExponent(int8_t exponent) {
|
||||
m_data.m_exponent = exponent;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setExponent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the format.
|
||||
*/
|
||||
void BLE2904::setFormat(uint8_t format) {
|
||||
m_data.m_format = format;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setFormat
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the namespace.
|
||||
*/
|
||||
void BLE2904::setNamespace(uint8_t namespace_value) {
|
||||
m_data.m_namespace = namespace_value;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setNamespace
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the units for this value. It should be one of the encoded values defined here:
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/units
|
||||
* @param [in] unit The type of units of this characteristic as defined by assigned numbers.
|
||||
*/
|
||||
void BLE2904::setUnit(uint16_t unit) {
|
||||
m_data.m_unit = unit;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // setUnit
|
||||
|
||||
#endif
|
74
libraries/BLE/src/BLE2904.h
Normal file
74
libraries/BLE/src/BLE2904.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* BLE2904.h
|
||||
*
|
||||
* Created on: Dec 23, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLE2904_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEDescriptor.h"
|
||||
|
||||
struct BLE2904_Data {
|
||||
uint8_t m_format;
|
||||
int8_t m_exponent;
|
||||
uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units
|
||||
uint8_t m_namespace;
|
||||
uint16_t m_description;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Descriptor for Characteristic Presentation Format.
|
||||
*
|
||||
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
class BLE2904: public BLEDescriptor {
|
||||
public:
|
||||
BLE2904();
|
||||
static const uint8_t FORMAT_BOOLEAN = 1;
|
||||
static const uint8_t FORMAT_UINT2 = 2;
|
||||
static const uint8_t FORMAT_UINT4 = 3;
|
||||
static const uint8_t FORMAT_UINT8 = 4;
|
||||
static const uint8_t FORMAT_UINT12 = 5;
|
||||
static const uint8_t FORMAT_UINT16 = 6;
|
||||
static const uint8_t FORMAT_UINT24 = 7;
|
||||
static const uint8_t FORMAT_UINT32 = 8;
|
||||
static const uint8_t FORMAT_UINT48 = 9;
|
||||
static const uint8_t FORMAT_UINT64 = 10;
|
||||
static const uint8_t FORMAT_UINT128 = 11;
|
||||
static const uint8_t FORMAT_SINT8 = 12;
|
||||
static const uint8_t FORMAT_SINT12 = 13;
|
||||
static const uint8_t FORMAT_SINT16 = 14;
|
||||
static const uint8_t FORMAT_SINT24 = 15;
|
||||
static const uint8_t FORMAT_SINT32 = 16;
|
||||
static const uint8_t FORMAT_SINT48 = 17;
|
||||
static const uint8_t FORMAT_SINT64 = 18;
|
||||
static const uint8_t FORMAT_SINT128 = 19;
|
||||
static const uint8_t FORMAT_FLOAT32 = 20;
|
||||
static const uint8_t FORMAT_FLOAT64 = 21;
|
||||
static const uint8_t FORMAT_SFLOAT16 = 22;
|
||||
static const uint8_t FORMAT_SFLOAT32 = 23;
|
||||
static const uint8_t FORMAT_IEEE20601 = 24;
|
||||
static const uint8_t FORMAT_UTF8 = 25;
|
||||
static const uint8_t FORMAT_UTF16 = 26;
|
||||
static const uint8_t FORMAT_OPAQUE = 27;
|
||||
|
||||
void setDescription(uint16_t);
|
||||
void setExponent(int8_t exponent);
|
||||
void setFormat(uint8_t format);
|
||||
void setNamespace(uint8_t namespace_value);
|
||||
void setUnit(uint16_t unit);
|
||||
|
||||
private:
|
||||
BLE2904_Data m_data;
|
||||
}; // BLE2904
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */
|
95
libraries/BLE/src/BLEAddress.cpp
Normal file
95
libraries/BLE/src/BLEAddress.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* BLEAddress.cpp
|
||||
*
|
||||
* Created on: Jul 2, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "BLEAddress.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create an address from the native ESP32 representation.
|
||||
* @param [in] address The native representation.
|
||||
*/
|
||||
BLEAddress::BLEAddress(esp_bd_addr_t address) {
|
||||
memcpy(m_address, address, ESP_BD_ADDR_LEN);
|
||||
} // BLEAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create an address from a hex string
|
||||
*
|
||||
* A hex string is of the format:
|
||||
* ```
|
||||
* 00:00:00:00:00:00
|
||||
* ```
|
||||
* which is 17 characters in length.
|
||||
*
|
||||
* @param [in] stringAddress The hex representation of the address.
|
||||
*/
|
||||
BLEAddress::BLEAddress(std::string stringAddress) {
|
||||
if (stringAddress.length() != 17) return;
|
||||
|
||||
int data[6];
|
||||
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
|
||||
m_address[0] = (uint8_t) data[0];
|
||||
m_address[1] = (uint8_t) data[1];
|
||||
m_address[2] = (uint8_t) data[2];
|
||||
m_address[3] = (uint8_t) data[3];
|
||||
m_address[4] = (uint8_t) data[4];
|
||||
m_address[5] = (uint8_t) data[5];
|
||||
} // BLEAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Determine if this address equals another.
|
||||
* @param [in] otherAddress The other address to compare against.
|
||||
* @return True if the addresses are equal.
|
||||
*/
|
||||
bool BLEAddress::equals(BLEAddress otherAddress) {
|
||||
return memcmp(otherAddress.getNative(), m_address, 6) == 0;
|
||||
} // equals
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the native representation of the address.
|
||||
* @return The native representation of the address.
|
||||
*/
|
||||
esp_bd_addr_t *BLEAddress::getNative() {
|
||||
return &m_address;
|
||||
} // getNative
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a BLE address to a string.
|
||||
*
|
||||
* A string representation of an address is in the format:
|
||||
*
|
||||
* ```
|
||||
* xx:xx:xx:xx:xx:xx
|
||||
* ```
|
||||
*
|
||||
* @return The string representation of the address.
|
||||
*/
|
||||
std::string BLEAddress::toString() {
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[0] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[1] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[2] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[3] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[4] << ':';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[5];
|
||||
return stream.str();
|
||||
} // toString
|
||||
#endif
|
34
libraries/BLE/src/BLEAddress.h
Normal file
34
libraries/BLE/src/BLEAddress.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* BLEAddress.h
|
||||
*
|
||||
* Created on: Jul 2, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* @brief A %BLE device address.
|
||||
*
|
||||
* Every %BLE device has a unique address which can be used to identify it and form connections.
|
||||
*/
|
||||
class BLEAddress {
|
||||
public:
|
||||
BLEAddress(esp_bd_addr_t address);
|
||||
BLEAddress(std::string stringAddress);
|
||||
bool equals(BLEAddress otherAddress);
|
||||
esp_bd_addr_t* getNative();
|
||||
std::string toString();
|
||||
|
||||
private:
|
||||
esp_bd_addr_t m_address;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */
|
529
libraries/BLE/src/BLEAdvertisedDevice.cpp
Normal file
529
libraries/BLE/src/BLEAdvertisedDevice.cpp
Normal file
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* BLEAdvertisedDevice.cpp
|
||||
*
|
||||
* During the scanning procedure, we will be finding advertised BLE devices. This class
|
||||
* models a found device.
|
||||
*
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
|
||||
*
|
||||
* Created on: Jul 3, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG="BLEAdvertisedDevice";
|
||||
#endif
|
||||
|
||||
BLEAdvertisedDevice::BLEAdvertisedDevice() {
|
||||
m_adFlag = 0;
|
||||
m_appearance = 0;
|
||||
m_deviceType = 0;
|
||||
m_manufacturerData = "";
|
||||
m_name = "";
|
||||
m_rssi = -9999;
|
||||
m_serviceData = "";
|
||||
m_txPower = 0;
|
||||
m_pScan = nullptr;
|
||||
|
||||
m_haveAppearance = false;
|
||||
m_haveManufacturerData = false;
|
||||
m_haveName = false;
|
||||
m_haveRSSI = false;
|
||||
m_haveServiceData = false;
|
||||
m_haveServiceUUID = false;
|
||||
m_haveTXPower = false;
|
||||
|
||||
} // BLEAdvertisedDevice
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the address.
|
||||
*
|
||||
* Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
|
||||
* Call this function to obtain the address of the advertised device.
|
||||
*
|
||||
* @return The address of the advertised device.
|
||||
*/
|
||||
BLEAddress BLEAdvertisedDevice::getAddress() {
|
||||
return m_address;
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the appearance.
|
||||
*
|
||||
* A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user
|
||||
* typcially in the form of an icon.
|
||||
*
|
||||
* @return The appearance of the advertised device.
|
||||
*/
|
||||
uint16_t BLEAdvertisedDevice::getAppearance() {
|
||||
return m_appearance;
|
||||
} // getAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the manufacturer data.
|
||||
* @return The manufacturer data of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getManufacturerData() {
|
||||
return m_manufacturerData;
|
||||
} // getManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the name.
|
||||
* @return The name of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getName() {
|
||||
return m_name;
|
||||
} // getName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the RSSI.
|
||||
* @return The RSSI of the advertised device.
|
||||
*/
|
||||
int BLEAdvertisedDevice::getRSSI() {
|
||||
return m_rssi;
|
||||
} // getRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the scan object that created this advertisement.
|
||||
* @return The scan object.
|
||||
*/
|
||||
BLEScan* BLEAdvertisedDevice::getScan() {
|
||||
return m_pScan;
|
||||
} // getScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service data.
|
||||
* @return The ServiceData of the advertised device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::getServiceData() {
|
||||
return m_serviceData;
|
||||
} //getServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service data UUID.
|
||||
* @return The service data UUID.
|
||||
*/
|
||||
BLEUUID BLEAdvertisedDevice::getServiceDataUUID() {
|
||||
return m_serviceDataUUID;
|
||||
} // getServiceDataUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the Service UUID.
|
||||
* @return The Service UUID of the advertised device.
|
||||
*/
|
||||
BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
|
||||
return m_serviceUUIDs[0];
|
||||
} // getServiceUUID
|
||||
|
||||
/**
|
||||
* @brief Check advertised serviced for existence required UUID
|
||||
* @return Return true if service is advertised
|
||||
*/
|
||||
bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){
|
||||
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
|
||||
if (m_serviceUUIDs[i].equals(uuid)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the TX Power.
|
||||
* @return The TX Power of the advertised device.
|
||||
*/
|
||||
int8_t BLEAdvertisedDevice::getTXPower() {
|
||||
return m_txPower;
|
||||
} // getTXPower
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have an appearance value?
|
||||
* @return True if there is an appearance value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveAppearance() {
|
||||
return m_haveAppearance;
|
||||
} // haveAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have manufacturer data?
|
||||
* @return True if there is manufacturer data present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveManufacturerData() {
|
||||
return m_haveManufacturerData;
|
||||
} // haveManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a name value?
|
||||
* @return True if there is a name value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveName() {
|
||||
return m_haveName;
|
||||
} // haveName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a signal strength value?
|
||||
* @return True if there is a signal strength value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveRSSI() {
|
||||
return m_haveRSSI;
|
||||
} // haveRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service data value?
|
||||
* @return True if there is a service data value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveServiceData() {
|
||||
return m_haveServiceData;
|
||||
} // haveServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a service UUID value?
|
||||
* @return True if there is a service UUID value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveServiceUUID() {
|
||||
return m_haveServiceUUID;
|
||||
} // haveServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a transmission power value?
|
||||
* @return True if there is a transmission power value present.
|
||||
*/
|
||||
bool BLEAdvertisedDevice::haveTXPower() {
|
||||
return m_haveTXPower;
|
||||
} // haveTXPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parse the advertising pay load.
|
||||
*
|
||||
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by
|
||||
* a 0 length value. Each entry in the buffer has the format:
|
||||
* [length][type][data...]
|
||||
*
|
||||
* The length does not include itself but does include everything after it until the next record. A record
|
||||
* with a length value of 0 indicates a terminator.
|
||||
*
|
||||
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
|
||||
*/
|
||||
void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) {
|
||||
uint8_t length;
|
||||
uint8_t ad_type;
|
||||
uint8_t sizeConsumed = 0;
|
||||
bool finished = false;
|
||||
m_payload = payload;
|
||||
m_payloadLength = total_len;
|
||||
|
||||
while(!finished) {
|
||||
length = *payload; // Retrieve the length of the record.
|
||||
payload++; // Skip to type
|
||||
sizeConsumed += 1 + length; // increase the size consumed.
|
||||
|
||||
if (length != 0) { // A length of 0 indicates that we have reached the end.
|
||||
ad_type = *payload;
|
||||
payload++;
|
||||
length--;
|
||||
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, payload, length);
|
||||
ESP_LOGD(LOG_TAG, "Type: 0x%.2x (%s), length: %d, data: %s",
|
||||
ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
|
||||
free(pHex);
|
||||
|
||||
switch(ad_type) {
|
||||
case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
|
||||
setName(std::string(reinterpret_cast<char*>(payload), length));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_NAME_CMPL
|
||||
|
||||
case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
|
||||
setTXPower(*payload);
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_TX_PWR
|
||||
|
||||
case ESP_BLE_AD_TYPE_APPEARANCE: { // Adv Data Type: 0x19
|
||||
setAppearance(*reinterpret_cast<uint16_t*>(payload));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_APPEARANCE
|
||||
|
||||
case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
|
||||
setAdFlag(*payload);
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_FLAG
|
||||
|
||||
case ESP_BLE_AD_TYPE_16SRV_CMPL:
|
||||
case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
|
||||
for (int var = 0; var < length/2; ++var) {
|
||||
setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t*>(payload + var * 2)));
|
||||
}
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_16SRV_PART
|
||||
|
||||
case ESP_BLE_AD_TYPE_32SRV_CMPL:
|
||||
case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
|
||||
for (int var = 0; var < length/4; ++var) {
|
||||
setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t*>(payload + var * 4)));
|
||||
}
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_32SRV_PART
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SRV_CMPL: { // Adv Data Type: 0x07
|
||||
setServiceUUID(BLEUUID(payload, 16, false));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_128SRV_CMPL
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SRV_PART: { // Adv Data Type: 0x06
|
||||
setServiceUUID(BLEUUID(payload, 16, false));
|
||||
break;
|
||||
} // ESP_BLE_AD_TYPE_128SRV_PART
|
||||
|
||||
// See CSS Part A 1.4 Manufacturer Specific Data
|
||||
case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
|
||||
setManufacturerData(std::string(reinterpret_cast<char*>(payload), length));
|
||||
break;
|
||||
} // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE
|
||||
|
||||
case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID
|
||||
if (length < 2) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
uint16_t uuid = *(uint16_t*)payload;
|
||||
setServiceDataUUID(BLEUUID(uuid));
|
||||
if (length > 2) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 2), length - 2));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_SERVICE_DATA
|
||||
|
||||
case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID
|
||||
if (length < 4) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
uint32_t uuid = *(uint32_t*) payload;
|
||||
setServiceDataUUID(BLEUUID(uuid));
|
||||
if (length > 4) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 4), length - 4));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
|
||||
|
||||
case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID
|
||||
if (length < 16) {
|
||||
ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
|
||||
break;
|
||||
}
|
||||
|
||||
setServiceDataUUID(BLEUUID(payload, (size_t)16, false));
|
||||
if (length > 16) {
|
||||
setServiceData(std::string(reinterpret_cast<char*>(payload + 16), length - 16));
|
||||
}
|
||||
break;
|
||||
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
|
||||
|
||||
default: {
|
||||
ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type);
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
payload += length;
|
||||
} // Length <> 0
|
||||
|
||||
|
||||
if (sizeConsumed >= total_len)
|
||||
finished = true;
|
||||
|
||||
} // !finished
|
||||
} // parseAdvertisement
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the address of the advertised device.
|
||||
* @param [in] address The address of the advertised device.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAddress(BLEAddress address) {
|
||||
m_address = address;
|
||||
} // setAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the adFlag for this device.
|
||||
* @param [in] The discovered adFlag.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAdFlag(uint8_t adFlag) {
|
||||
m_adFlag = adFlag;
|
||||
} // setAdFlag
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the appearance for this device.
|
||||
* @param [in] The discovered appearance.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setAppearance(uint16_t appearance) {
|
||||
m_appearance = appearance;
|
||||
m_haveAppearance = true;
|
||||
ESP_LOGD(LOG_TAG, "- appearance: %d", m_appearance);
|
||||
} // setAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the manufacturer data for this device.
|
||||
* @param [in] The discovered manufacturer data.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
|
||||
m_manufacturerData = manufacturerData;
|
||||
m_haveManufacturerData = true;
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
|
||||
ESP_LOGD(LOG_TAG, "- manufacturer data: %s", pHex);
|
||||
free(pHex);
|
||||
} // setManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the name for this device.
|
||||
* @param [in] name The discovered name.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setName(std::string name) {
|
||||
m_name = name;
|
||||
m_haveName = true;
|
||||
ESP_LOGD(LOG_TAG, "- setName(): name: %s", m_name.c_str());
|
||||
} // setName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the RSSI for this device.
|
||||
* @param [in] rssi The discovered RSSI.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setRSSI(int rssi) {
|
||||
m_rssi = rssi;
|
||||
m_haveRSSI = true;
|
||||
ESP_LOGD(LOG_TAG, "- setRSSI(): rssi: %d", m_rssi);
|
||||
} // setRSSI
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Scan that created this advertised device.
|
||||
* @param pScan The Scan that created this advertised device.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setScan(BLEScan* pScan) {
|
||||
m_pScan = pScan;
|
||||
} // setScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Service UUID for this device.
|
||||
* @param [in] serviceUUID The discovered serviceUUID
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
|
||||
return setServiceUUID(BLEUUID(serviceUUID));
|
||||
} // setServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Service UUID for this device.
|
||||
* @param [in] serviceUUID The discovered serviceUUID
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
|
||||
m_serviceUUIDs.push_back(serviceUUID);
|
||||
m_haveServiceUUID = true;
|
||||
ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
|
||||
} // setServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the ServiceData value.
|
||||
* @param [in] data ServiceData value.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceData(std::string serviceData) {
|
||||
m_haveServiceData = true; // Set the flag that indicates we have service data.
|
||||
m_serviceData = serviceData; // Save the service data that we received.
|
||||
} //setServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the ServiceDataUUID value.
|
||||
* @param [in] data ServiceDataUUID value.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) {
|
||||
m_haveServiceData = true; // Set the flag that indicates we have service data.
|
||||
m_serviceDataUUID = uuid;
|
||||
} // setServiceDataUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the power level for this device.
|
||||
* @param [in] txPower The discovered power level.
|
||||
*/
|
||||
void BLEAdvertisedDevice::setTXPower(int8_t txPower) {
|
||||
m_txPower = txPower;
|
||||
m_haveTXPower = true;
|
||||
ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower);
|
||||
} // setTXPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a string representation of this device.
|
||||
* @return A string representation of this device.
|
||||
*/
|
||||
std::string BLEAdvertisedDevice::toString() {
|
||||
std::stringstream ss;
|
||||
ss << "Name: " << getName() << ", Address: " << getAddress().toString();
|
||||
if (haveAppearance()) {
|
||||
ss << ", appearance: " << getAppearance();
|
||||
}
|
||||
if (haveManufacturerData()) {
|
||||
char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length());
|
||||
ss << ", manufacturer data: " << pHex;
|
||||
free(pHex);
|
||||
}
|
||||
if (haveServiceUUID()) {
|
||||
ss << ", serviceUUID: " << getServiceUUID().toString();
|
||||
}
|
||||
if (haveTXPower()) {
|
||||
ss << ", txPower: " << (int)getTXPower();
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
uint8_t* BLEAdvertisedDevice::getPayload() {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() {
|
||||
return m_addressType;
|
||||
}
|
||||
|
||||
void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) {
|
||||
m_addressType = type;
|
||||
}
|
||||
|
||||
size_t BLEAdvertisedDevice::getPayloadLength() {
|
||||
return m_payloadLength;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
123
libraries/BLE/src/BLEAdvertisedDevice.h
Normal file
123
libraries/BLE/src/BLEAdvertisedDevice.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* BLEAdvertisedDevice.h
|
||||
*
|
||||
* Created on: Jul 3, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BLEAddress.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEUUID.h"
|
||||
|
||||
|
||||
class BLEScan;
|
||||
/**
|
||||
* @brief A representation of a %BLE advertised device found by a scan.
|
||||
*
|
||||
* When we perform a %BLE scan, the result will be a set of devices that are advertising. This
|
||||
* class provides a model of a detected device.
|
||||
*/
|
||||
class BLEAdvertisedDevice {
|
||||
public:
|
||||
BLEAdvertisedDevice();
|
||||
|
||||
BLEAddress getAddress();
|
||||
uint16_t getAppearance();
|
||||
std::string getManufacturerData();
|
||||
std::string getName();
|
||||
int getRSSI();
|
||||
BLEScan* getScan();
|
||||
std::string getServiceData();
|
||||
BLEUUID getServiceDataUUID();
|
||||
BLEUUID getServiceUUID();
|
||||
int8_t getTXPower();
|
||||
uint8_t* getPayload();
|
||||
size_t getPayloadLength();
|
||||
esp_ble_addr_type_t getAddressType();
|
||||
void setAddressType(esp_ble_addr_type_t type);
|
||||
|
||||
|
||||
bool isAdvertisingService(BLEUUID uuid);
|
||||
bool haveAppearance();
|
||||
bool haveManufacturerData();
|
||||
bool haveName();
|
||||
bool haveRSSI();
|
||||
bool haveServiceData();
|
||||
bool haveServiceUUID();
|
||||
bool haveTXPower();
|
||||
|
||||
std::string toString();
|
||||
|
||||
private:
|
||||
friend class BLEScan;
|
||||
|
||||
void parseAdvertisement(uint8_t* payload, size_t total_len=62);
|
||||
void setAddress(BLEAddress address);
|
||||
void setAdFlag(uint8_t adFlag);
|
||||
void setAdvertizementResult(uint8_t* payload);
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setManufacturerData(std::string manufacturerData);
|
||||
void setName(std::string name);
|
||||
void setRSSI(int rssi);
|
||||
void setScan(BLEScan* pScan);
|
||||
void setServiceData(std::string data);
|
||||
void setServiceDataUUID(BLEUUID uuid);
|
||||
void setServiceUUID(const char* serviceUUID);
|
||||
void setServiceUUID(BLEUUID serviceUUID);
|
||||
void setTXPower(int8_t txPower);
|
||||
|
||||
bool m_haveAppearance;
|
||||
bool m_haveManufacturerData;
|
||||
bool m_haveName;
|
||||
bool m_haveRSSI;
|
||||
bool m_haveServiceData;
|
||||
bool m_haveServiceUUID;
|
||||
bool m_haveTXPower;
|
||||
|
||||
|
||||
BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0");
|
||||
uint8_t m_adFlag;
|
||||
uint16_t m_appearance;
|
||||
int m_deviceType;
|
||||
std::string m_manufacturerData;
|
||||
std::string m_name;
|
||||
BLEScan* m_pScan;
|
||||
int m_rssi;
|
||||
std::vector<BLEUUID> m_serviceUUIDs;
|
||||
int8_t m_txPower;
|
||||
std::string m_serviceData;
|
||||
BLEUUID m_serviceDataUUID;
|
||||
uint8_t* m_payload;
|
||||
size_t m_payloadLength = 0;
|
||||
esp_ble_addr_type_t m_addressType;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A callback handler for callbacks associated device scanning.
|
||||
*
|
||||
* When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
|
||||
* has been found. This class can be sub-classed and registered such that when a scan is performed and
|
||||
* a new advertised device has been found, we will be called back to be notified.
|
||||
*/
|
||||
class BLEAdvertisedDeviceCallbacks {
|
||||
public:
|
||||
virtual ~BLEAdvertisedDeviceCallbacks() {}
|
||||
/**
|
||||
* @brief Called when a new scan result is detected.
|
||||
*
|
||||
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
|
||||
* device that was found. During any individual scan, a device will only be detected one time.
|
||||
*/
|
||||
virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */
|
505
libraries/BLE/src/BLEAdvertising.cpp
Normal file
505
libraries/BLE/src/BLEAdvertising.cpp
Normal file
@ -0,0 +1,505 @@
|
||||
/*
|
||||
* BLEAdvertising.cpp
|
||||
*
|
||||
* This class encapsulates advertising a BLE Server.
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*
|
||||
* The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set
|
||||
* of properties that are advertised and has built a data structure that can be populated by the programmer.
|
||||
* This means that the programmer doesn't have to "mess with" the low level construction of a low level
|
||||
* BLE advertising frame. Many of the fields are determined for us while others we can set before starting
|
||||
* to advertise.
|
||||
*
|
||||
* Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters
|
||||
* upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer
|
||||
* set in the data will be advertised.
|
||||
*
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include "BLEAdvertising.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEAdvertising";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a default advertising object.
|
||||
*
|
||||
*/
|
||||
BLEAdvertising::BLEAdvertising() {
|
||||
m_advData.set_scan_rsp = false;
|
||||
m_advData.include_name = true;
|
||||
m_advData.include_txpower = true;
|
||||
m_advData.min_interval = 0x20;
|
||||
m_advData.max_interval = 0x40;
|
||||
m_advData.appearance = 0x00;
|
||||
m_advData.manufacturer_len = 0;
|
||||
m_advData.p_manufacturer_data = nullptr;
|
||||
m_advData.service_data_len = 0;
|
||||
m_advData.p_service_data = nullptr;
|
||||
m_advData.service_uuid_len = 0;
|
||||
m_advData.p_service_uuid = nullptr;
|
||||
m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
|
||||
|
||||
m_advParams.adv_int_min = 0x20;
|
||||
m_advParams.adv_int_max = 0x40;
|
||||
m_advParams.adv_type = ADV_TYPE_IND;
|
||||
m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
m_advParams.channel_map = ADV_CHNL_ALL;
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
|
||||
m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
|
||||
m_customAdvData = false; // No custom advertising data
|
||||
m_customScanResponseData = false; // No custom scan response data
|
||||
} // BLEAdvertising
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a service uuid to exposed list of services.
|
||||
* @param [in] serviceUUID The UUID of the service to expose.
|
||||
*/
|
||||
void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) {
|
||||
m_serviceUUIDs.push_back(serviceUUID);
|
||||
} // addServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add a service uuid to exposed list of services.
|
||||
* @param [in] serviceUUID The string representation of the service to expose.
|
||||
*/
|
||||
void BLEAdvertising::addServiceUUID(const char* serviceUUID) {
|
||||
addServiceUUID(BLEUUID(serviceUUID));
|
||||
} // addServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the device appearance in the advertising data.
|
||||
* The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
|
||||
* @param [in] appearance The appearance of the device in the advertising data.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::setAppearance(uint16_t appearance) {
|
||||
m_advData.appearance = appearance;
|
||||
} // setAppearance
|
||||
|
||||
void BLEAdvertising::setMinInterval(uint16_t mininterval) {
|
||||
m_advParams.adv_int_min = mininterval;
|
||||
} // setMinInterval
|
||||
|
||||
void BLEAdvertising::setMaxInterval(uint16_t maxinterval) {
|
||||
m_advParams.adv_int_max = maxinterval;
|
||||
} // setMaxInterval
|
||||
|
||||
void BLEAdvertising::setMinPreferred(uint16_t mininterval) {
|
||||
m_advData.min_interval = mininterval;
|
||||
} //
|
||||
|
||||
void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
|
||||
m_advData.max_interval = maxinterval;
|
||||
} //
|
||||
|
||||
void BLEAdvertising::setScanResponse(bool set) {
|
||||
m_scanResp = set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the filtering for the scan filter.
|
||||
* @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
|
||||
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
|
||||
*/
|
||||
void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
|
||||
ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
|
||||
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
|
||||
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST;
|
||||
ESP_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
return;
|
||||
}
|
||||
} // setScanFilter
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement data that is to be published in a regular advertisement.
|
||||
* @param [in] advertisementData The data to be advertised.
|
||||
*/
|
||||
void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) {
|
||||
ESP_LOGD(LOG_TAG, ">> setAdvertisementData");
|
||||
esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw(
|
||||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
|
||||
ESP_LOGD(LOG_TAG, "<< setAdvertisementData");
|
||||
} // setAdvertisementData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement data that is to be published in a scan response.
|
||||
* @param [in] advertisementData The data to be advertised.
|
||||
*/
|
||||
void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) {
|
||||
ESP_LOGD(LOG_TAG, ">> setScanResponseData");
|
||||
esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw(
|
||||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
|
||||
ESP_LOGD(LOG_TAG, "<< setScanResponseData");
|
||||
} // setScanResponseData
|
||||
|
||||
/**
|
||||
* @brief Start advertising.
|
||||
* Start advertising.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::start() {
|
||||
ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
|
||||
|
||||
// We have a vector of service UUIDs that we wish to advertise. In order to use the
|
||||
// ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte)
|
||||
// representations. If we have 1 or more services to advertise then we allocate enough
|
||||
// storage to host them and then copy them in one at a time into the contiguous storage.
|
||||
int numServices = m_serviceUUIDs.size();
|
||||
if (numServices > 0) {
|
||||
m_advData.service_uuid_len = 16 * numServices;
|
||||
m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len];
|
||||
uint8_t* p = m_advData.p_service_uuid;
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str());
|
||||
BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128();
|
||||
memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16);
|
||||
p += 16;
|
||||
}
|
||||
} else {
|
||||
m_advData.service_uuid_len = 0;
|
||||
ESP_LOGD(LOG_TAG, "- no services advertised");
|
||||
}
|
||||
|
||||
esp_err_t errRc;
|
||||
|
||||
if (!m_customAdvData) {
|
||||
// Set the configuration for advertising.
|
||||
m_advData.set_scan_rsp = false;
|
||||
m_advData.include_name = !m_scanResp;
|
||||
m_advData.include_txpower = !m_scanResp;
|
||||
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_customScanResponseData && m_scanResp) {
|
||||
m_advData.set_scan_rsp = true;
|
||||
m_advData.include_name = m_scanResp;
|
||||
m_advData.include_txpower = m_scanResp;
|
||||
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we had services to advertise then we previously allocated some storage for them.
|
||||
// Here we release that storage.
|
||||
if (m_advData.service_uuid_len > 0) {
|
||||
delete[] m_advData.p_service_uuid;
|
||||
m_advData.p_service_uuid = nullptr;
|
||||
}
|
||||
|
||||
// Start advertising.
|
||||
errRc = ::esp_ble_gap_start_advertising(&m_advParams);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< start");
|
||||
} // start
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stop advertising.
|
||||
* Stop advertising.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::stop() {
|
||||
ESP_LOGD(LOG_TAG, ">> stop");
|
||||
esp_err_t errRc = ::esp_ble_gap_stop_advertising();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< stop");
|
||||
} // stop
|
||||
|
||||
/**
|
||||
* @brief Add data to the payload to be advertised.
|
||||
* @param [in] data The data to be added to the payload.
|
||||
*/
|
||||
void BLEAdvertisementData::addData(std::string data) {
|
||||
if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) {
|
||||
return;
|
||||
}
|
||||
m_payload.append(data);
|
||||
} // addData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the appearance.
|
||||
* @param [in] appearance The appearance code value.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
*/
|
||||
void BLEAdvertisementData::setAppearance(uint16_t appearance) {
|
||||
char cdata[2];
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19
|
||||
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
|
||||
} // setAppearance
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the complete services.
|
||||
* @param [in] uuid The single service to advertise.
|
||||
*/
|
||||
void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x02] [LL] [HH]
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x04] [LL] [LL] [HH] [HH]
|
||||
cdata[0] = 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x04] [0] [1] ... [15]
|
||||
cdata[0] = 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07
|
||||
addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setCompleteServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertisement flags.
|
||||
* @param [in] The flags to be set in the advertisement.
|
||||
*
|
||||
* * ESP_BLE_ADV_FLAG_LIMIT_DISC
|
||||
* * ESP_BLE_ADV_FLAG_GEN_DISC
|
||||
* * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
|
||||
* * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
|
||||
* * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
|
||||
* * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
|
||||
*/
|
||||
void BLEAdvertisementData::setFlags(uint8_t flag) {
|
||||
char cdata[3];
|
||||
cdata[0] = 2;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01
|
||||
cdata[2] = flag;
|
||||
addData(std::string(cdata, 3));
|
||||
} // setFlag
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set manufacturer specific data.
|
||||
* @param [in] data Manufacturer data.
|
||||
*/
|
||||
void BLEAdvertisementData::setManufacturerData(std::string data) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData");
|
||||
char cdata[2];
|
||||
cdata[0] = data.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff
|
||||
addData(std::string(cdata, 2) + data);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData");
|
||||
} // setManufacturerData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the name.
|
||||
* @param [in] The complete name of the device.
|
||||
*/
|
||||
void BLEAdvertisementData::setName(std::string name) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str());
|
||||
char cdata[2];
|
||||
cdata[0] = name.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09
|
||||
addData(std::string(cdata, 2) + name);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setName");
|
||||
} // setName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the partial services.
|
||||
* @param [in] uuid The single service to advertise.
|
||||
*/
|
||||
void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x02] [LL] [HH]
|
||||
cdata[0] = 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2));
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x04] [LL] [LL] [HH] [HH]
|
||||
cdata[0] = 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x04] [0] [1] ... [15]
|
||||
cdata[0] = 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06
|
||||
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setPartialServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the service data (UUID + data)
|
||||
* @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
|
||||
* @param [in] data The data to be associated with the service data advert.
|
||||
*/
|
||||
void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) {
|
||||
char cdata[2];
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
// [Len] [0x16] [UUID16] data
|
||||
cdata[0] = data.length() + 3;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: {
|
||||
// [Len] [0x20] [UUID32] data
|
||||
cdata[0] = data.length() + 5;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 128: {
|
||||
// [Len] [0x21] [UUID128] data
|
||||
cdata[0] = data.length() + 17;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21
|
||||
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} // setServiceData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the short name.
|
||||
* @param [in] The short name of the device.
|
||||
*/
|
||||
void BLEAdvertisementData::setShortName(std::string name) {
|
||||
ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str());
|
||||
char cdata[2];
|
||||
cdata[0] = name.length() + 1;
|
||||
cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08
|
||||
addData(std::string(cdata, 2) + name);
|
||||
ESP_LOGD("BLEAdvertisementData", "<< setShortName");
|
||||
} // setShortName
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the payload that is to be advertised.
|
||||
* @return The payload that is to be advertised.
|
||||
*/
|
||||
std::string BLEAdvertisementData::getPayload() {
|
||||
return m_payload;
|
||||
} // getPayload
|
||||
|
||||
void BLEAdvertising::handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event);
|
||||
|
||||
switch(event) {
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: {
|
||||
// m_semaphoreSetAdv.give();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: {
|
||||
ESP_LOGI(LOG_TAG, "STOP advertising");
|
||||
start();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
78
libraries/BLE/src/BLEAdvertising.h
Normal file
78
libraries/BLE/src/BLEAdvertising.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* BLEAdvertising.h
|
||||
*
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include "BLEUUID.h"
|
||||
#include <vector>
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
/**
|
||||
* @brief Advertisement data set by the programmer to be published by the %BLE server.
|
||||
*/
|
||||
class BLEAdvertisementData {
|
||||
// Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will
|
||||
// be exposed on demand/request or as time permits.
|
||||
//
|
||||
public:
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setCompleteServices(BLEUUID uuid);
|
||||
void setFlags(uint8_t);
|
||||
void setManufacturerData(std::string data);
|
||||
void setName(std::string name);
|
||||
void setPartialServices(BLEUUID uuid);
|
||||
void setServiceData(BLEUUID uuid, std::string data);
|
||||
void setShortName(std::string name);
|
||||
void addData(std::string data); // Add data to the payload.
|
||||
std::string getPayload(); // Retrieve the current advert payload.
|
||||
|
||||
private:
|
||||
friend class BLEAdvertising;
|
||||
std::string m_payload; // The payload of the advertisement.
|
||||
}; // BLEAdvertisementData
|
||||
|
||||
|
||||
/**
|
||||
* @brief Perform and manage %BLE advertising.
|
||||
*
|
||||
* A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
|
||||
*/
|
||||
class BLEAdvertising {
|
||||
public:
|
||||
BLEAdvertising();
|
||||
void addServiceUUID(BLEUUID serviceUUID);
|
||||
void addServiceUUID(const char* serviceUUID);
|
||||
void start();
|
||||
void stop();
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setMaxInterval(uint16_t maxinterval);
|
||||
void setMinInterval(uint16_t mininterval);
|
||||
void setAdvertisementData(BLEAdvertisementData& advertisementData);
|
||||
void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
|
||||
void setScanResponseData(BLEAdvertisementData& advertisementData);
|
||||
void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
|
||||
|
||||
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
|
||||
void setMinPreferred(uint16_t);
|
||||
void setMaxPreferred(uint16_t);
|
||||
void setScanResponse(bool);
|
||||
|
||||
private:
|
||||
esp_ble_adv_data_t m_advData;
|
||||
esp_ble_adv_params_t m_advParams;
|
||||
std::vector<BLEUUID> m_serviceUUIDs;
|
||||
bool m_customAdvData = false; // Are we using custom advertising data?
|
||||
bool m_customScanResponseData = false; // Are we using custom scan response data?
|
||||
FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert");
|
||||
bool m_scanResp = true;
|
||||
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */
|
89
libraries/BLE/src/BLEBeacon.cpp
Normal file
89
libraries/BLE/src/BLEBeacon.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* BLEBeacon.cpp
|
||||
*
|
||||
* Created on: Jan 4, 2018
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include "BLEBeacon.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEBeacon";
|
||||
#endif
|
||||
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
|
||||
|
||||
|
||||
BLEBeacon::BLEBeacon() {
|
||||
m_beaconData.manufacturerId = 0x4c00;
|
||||
m_beaconData.subType = 0x02;
|
||||
m_beaconData.subTypeLength = 0x15;
|
||||
m_beaconData.major = 0;
|
||||
m_beaconData.minor = 0;
|
||||
m_beaconData.signalPower = 0;
|
||||
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
|
||||
} // BLEBeacon
|
||||
|
||||
std::string BLEBeacon::getData() {
|
||||
return std::string((char*) &m_beaconData, sizeof(m_beaconData));
|
||||
} // getData
|
||||
|
||||
uint16_t BLEBeacon::getMajor() {
|
||||
return m_beaconData.major;
|
||||
}
|
||||
|
||||
uint16_t BLEBeacon::getManufacturerId() {
|
||||
return m_beaconData.manufacturerId;
|
||||
}
|
||||
|
||||
uint16_t BLEBeacon::getMinor() {
|
||||
return m_beaconData.minor;
|
||||
}
|
||||
|
||||
BLEUUID BLEBeacon::getProximityUUID() {
|
||||
return BLEUUID(m_beaconData.proximityUUID, 16, false);
|
||||
}
|
||||
|
||||
int8_t BLEBeacon::getSignalPower() {
|
||||
return m_beaconData.signalPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEBeacon::setData(std::string data) {
|
||||
if (data.length() != sizeof(m_beaconData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData));
|
||||
return;
|
||||
}
|
||||
memcpy(&m_beaconData, data.data(), sizeof(m_beaconData));
|
||||
} // setData
|
||||
|
||||
void BLEBeacon::setMajor(uint16_t major) {
|
||||
m_beaconData.major = ENDIAN_CHANGE_U16(major);
|
||||
} // setMajor
|
||||
|
||||
void BLEBeacon::setManufacturerId(uint16_t manufacturerId) {
|
||||
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
|
||||
} // setManufacturerId
|
||||
|
||||
void BLEBeacon::setMinor(uint16_t minor) {
|
||||
m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
|
||||
} // setMinior
|
||||
|
||||
void BLEBeacon::setProximityUUID(BLEUUID uuid) {
|
||||
uuid = uuid.to128();
|
||||
memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16);
|
||||
} // setProximityUUID
|
||||
|
||||
void BLEBeacon::setSignalPower(int8_t signalPower) {
|
||||
m_beaconData.signalPower = signalPower;
|
||||
} // setSignalPower
|
||||
|
||||
|
||||
#endif
|
43
libraries/BLE/src/BLEBeacon.h
Normal file
43
libraries/BLE/src/BLEBeacon.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* BLEBeacon2.h
|
||||
*
|
||||
* Created on: Jan 4, 2018
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEBEACON_H_
|
||||
#include "BLEUUID.h"
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://en.wikipedia.org/wiki/IBeacon
|
||||
*/
|
||||
class BLEBeacon {
|
||||
private:
|
||||
struct {
|
||||
uint16_t manufacturerId;
|
||||
uint8_t subType;
|
||||
uint8_t subTypeLength;
|
||||
uint8_t proximityUUID[16];
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
int8_t signalPower;
|
||||
} __attribute__((packed)) m_beaconData;
|
||||
public:
|
||||
BLEBeacon();
|
||||
std::string getData();
|
||||
uint16_t getMajor();
|
||||
uint16_t getMinor();
|
||||
uint16_t getManufacturerId();
|
||||
BLEUUID getProximityUUID();
|
||||
int8_t getSignalPower();
|
||||
void setData(std::string data);
|
||||
void setMajor(uint16_t major);
|
||||
void setMinor(uint16_t minor);
|
||||
void setManufacturerId(uint16_t manufacturerId);
|
||||
void setProximityUUID(BLEUUID uuid);
|
||||
void setSignalPower(int8_t signalPower);
|
||||
}; // BLEBeacon
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */
|
760
libraries/BLE/src/BLECharacteristic.cpp
Normal file
760
libraries/BLE/src/BLECharacteristic.cpp
Normal file
@ -0,0 +1,760 @@
|
||||
/*
|
||||
* BLECharacteristic.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLE2902.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLECharacteristic";
|
||||
#endif
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID (const char*) for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
*/
|
||||
BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a characteristic
|
||||
* @param [in] uuid - UUID for the characteristic.
|
||||
* @param [in] properties - Properties for the characteristic.
|
||||
*/
|
||||
BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
|
||||
m_bleUUID = uuid;
|
||||
m_handle = NULL_HANDLE;
|
||||
m_properties = (esp_gatt_char_prop_t)0;
|
||||
m_pCallbacks = nullptr;
|
||||
|
||||
setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0);
|
||||
setReadProperty((properties & PROPERTY_READ) != 0);
|
||||
setWriteProperty((properties & PROPERTY_WRITE) != 0);
|
||||
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0);
|
||||
setIndicateProperty((properties & PROPERTY_INDICATE) != 0);
|
||||
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0);
|
||||
} // BLECharacteristic
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
BLECharacteristic::~BLECharacteristic() {
|
||||
//free(m_value.attr_value); // Release the storage for the value.
|
||||
} // ~BLECharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Associate a descriptor with this characteristic.
|
||||
* @param [in] pDescriptor
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
|
||||
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
|
||||
ESP_LOGD(LOG_TAG, "<< addDescriptor()");
|
||||
} // addDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register a new characteristic with the ESP runtime.
|
||||
* @param [in] pService The service with which to associate this characteristic.
|
||||
*/
|
||||
void BLECharacteristic::executeCreate(BLEService* pService) {
|
||||
ESP_LOGD(LOG_TAG, ">> executeCreate()");
|
||||
|
||||
if (m_handle != NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "Characteristic already has a handle.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pService = pService; // Save the service to which this characteristic belongs.
|
||||
|
||||
ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s",
|
||||
getUUID().toString().c_str(),
|
||||
m_pService->toString().c_str());
|
||||
|
||||
esp_attr_control_t control;
|
||||
control.auto_rsp = ESP_GATT_RSP_BY_APP;
|
||||
|
||||
m_semaphoreCreateEvt.take("executeCreate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_add_char(
|
||||
m_pService->getHandle(),
|
||||
getUUID().getNative(),
|
||||
static_cast<esp_gatt_perm_t>(m_permissions),
|
||||
getProperties(),
|
||||
nullptr,
|
||||
&control); // Whether to auto respond or not.
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
m_semaphoreCreateEvt.wait("executeCreate");
|
||||
|
||||
BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
|
||||
while (pDescriptor != nullptr) {
|
||||
pDescriptor->executeCreate(this);
|
||||
pDescriptor = m_descriptorMap.getNext();
|
||||
} // End while
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< executeCreate");
|
||||
} // executeCreate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
||||
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
||||
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) {
|
||||
return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID));
|
||||
} // getDescriptorByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
|
||||
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
|
||||
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) {
|
||||
return m_descriptorMap.getByUUID(descriptorUUID);
|
||||
} // getDescriptorByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the handle of the characteristic.
|
||||
* @return The handle of the characteristic.
|
||||
*/
|
||||
uint16_t BLECharacteristic::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) {
|
||||
m_permissions = perm;
|
||||
}
|
||||
|
||||
esp_gatt_char_prop_t BLECharacteristic::getProperties() {
|
||||
return m_properties;
|
||||
} // getProperties
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service associated with this characteristic.
|
||||
*/
|
||||
BLEService* BLECharacteristic::getService() {
|
||||
return m_pService;
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the characteristic.
|
||||
* @return The UUID of the characteristic.
|
||||
*/
|
||||
BLEUUID BLECharacteristic::getUUID() {
|
||||
return m_bleUUID;
|
||||
} // getUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current value of the characteristic.
|
||||
* @return A pointer to storage containing the current characteristic value.
|
||||
*/
|
||||
std::string BLECharacteristic::getValue() {
|
||||
return m_value.getValue();
|
||||
} // getValue
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current raw data of the characteristic.
|
||||
* @return A pointer to storage containing the current characteristic data.
|
||||
*/
|
||||
uint8_t* BLECharacteristic::getData() {
|
||||
return m_value.getData();
|
||||
} // getData
|
||||
|
||||
|
||||
/**
|
||||
* Handle a GATT server event.
|
||||
*/
|
||||
void BLECharacteristic::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str());
|
||||
|
||||
switch(event) {
|
||||
// Events handled:
|
||||
//
|
||||
// ESP_GATTS_ADD_CHAR_EVT
|
||||
// ESP_GATTS_CONF_EVT
|
||||
// ESP_GATTS_CONNECT_EVT
|
||||
// ESP_GATTS_DISCONNECT_EVT
|
||||
// ESP_GATTS_EXEC_WRITE_EVT
|
||||
// ESP_GATTS_READ_EVT
|
||||
// ESP_GATTS_WRITE_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTS_EXEC_WRITE_EVT
|
||||
// When we receive this event it is an indication that a previous write long needs to be committed.
|
||||
//
|
||||
// exec_write:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL
|
||||
//
|
||||
case ESP_GATTS_EXEC_WRITE_EVT: {
|
||||
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
|
||||
m_value.commit();
|
||||
if (m_pCallbacks != nullptr) {
|
||||
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} else {
|
||||
m_value.cancel();
|
||||
}
|
||||
// ???
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if,
|
||||
param->write.conn_id,
|
||||
param->write.trans_id, ESP_GATT_OK, nullptr);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_EXEC_WRITE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
|
||||
// add_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
if (getHandle() == param->add_char.attr_handle) {
|
||||
// we have created characteristic, now we can create descriptors
|
||||
// BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
|
||||
// while (pDescriptor != nullptr) {
|
||||
// pDescriptor->executeCreate(this);
|
||||
// pDescriptor = m_descriptorMap.getNext();
|
||||
// } // End while
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
|
||||
//
|
||||
// write:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool need_rsp
|
||||
// - bool is_prep
|
||||
// - uint16_t len
|
||||
// - uint8_t *value
|
||||
//
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
// We check if this write request is for us by comparing the handles in the event. If it is for us
|
||||
// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need
|
||||
// to send a response. If we do, then we formulate a response and send it.
|
||||
if (param->write.handle == m_handle) {
|
||||
if (param->write.is_prep) {
|
||||
m_value.addPart(param->write.value, param->write.len);
|
||||
} else {
|
||||
setValue(param->write.value, param->write.len);
|
||||
}
|
||||
|
||||
ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s",
|
||||
getHandle(), getUUID().toString().c_str());
|
||||
|
||||
char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len);
|
||||
ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData);
|
||||
free(pHexData);
|
||||
|
||||
if (param->write.need_rsp) {
|
||||
esp_gatt_rsp_t rsp;
|
||||
|
||||
rsp.attr_value.len = param->write.len;
|
||||
rsp.attr_value.handle = m_handle;
|
||||
rsp.attr_value.offset = param->write.offset;
|
||||
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
memcpy(rsp.attr_value.value, param->write.value, param->write.len);
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if,
|
||||
param->write.conn_id,
|
||||
param->write.trans_id, ESP_GATT_OK, &rsp);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
} // Response needed
|
||||
|
||||
if (m_pCallbacks != nullptr && param->write.is_prep != true) {
|
||||
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} // Match on handles.
|
||||
break;
|
||||
} // ESP_GATTS_WRITE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
|
||||
//
|
||||
// read:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool is_long
|
||||
// - bool need_rsp
|
||||
//
|
||||
case ESP_GATTS_READ_EVT: {
|
||||
if (param->read.handle == m_handle) {
|
||||
|
||||
|
||||
|
||||
// Here's an interesting thing. The read request has the option of saying whether we need a response
|
||||
// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like
|
||||
// a very strange read.
|
||||
//
|
||||
// We have to handle the case where the data we wish to send back to the client is greater than the maximum
|
||||
// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes.
|
||||
// The apparent algorithm is as follows:
|
||||
//
|
||||
// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes.
|
||||
// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than
|
||||
// 22 bytes, then we "just" send it and thats the end of the story.
|
||||
// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request.
|
||||
// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request.
|
||||
// Because of follow on request processing, we need to maintain an offset of how much data we have already sent
|
||||
// so that when a follow on request arrives, we know where to start in the data to send the next sequence.
|
||||
// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response.
|
||||
// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length.
|
||||
//
|
||||
// The following code has deliberately not been factored to make it fewer statements because this would cloud the
|
||||
// the logic flow comprehension.
|
||||
//
|
||||
|
||||
// get mtu for peer device that we are sending read request to
|
||||
uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1;
|
||||
ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset);
|
||||
if (param->read.need_rsp) {
|
||||
ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)");
|
||||
esp_gatt_rsp_t rsp;
|
||||
|
||||
if (param->read.is_long) {
|
||||
std::string value = m_value.getValue();
|
||||
|
||||
if (value.length() - m_value.getReadOffset() < maxOffset) {
|
||||
// This is the last in the chain
|
||||
rsp.attr_value.len = value.length() - m_value.getReadOffset();
|
||||
rsp.attr_value.offset = m_value.getReadOffset();
|
||||
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
|
||||
m_value.setReadOffset(0);
|
||||
} else {
|
||||
// There will be more to come.
|
||||
rsp.attr_value.len = maxOffset;
|
||||
rsp.attr_value.offset = m_value.getReadOffset();
|
||||
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
|
||||
m_value.setReadOffset(rsp.attr_value.offset + maxOffset);
|
||||
}
|
||||
} else { // read.is_long == false
|
||||
|
||||
std::string value = m_value.getValue();
|
||||
|
||||
if (value.length() + 1 > maxOffset) {
|
||||
// Too big for a single shot entry.
|
||||
m_value.setReadOffset(maxOffset);
|
||||
rsp.attr_value.len = maxOffset;
|
||||
rsp.attr_value.offset = 0;
|
||||
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
|
||||
} else {
|
||||
// Will fit in a single packet with no callbacks required.
|
||||
rsp.attr_value.len = value.length();
|
||||
rsp.attr_value.offset = 0;
|
||||
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
|
||||
}
|
||||
|
||||
if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback
|
||||
m_pCallbacks->onRead(this); // Invoke the read callback.
|
||||
}
|
||||
}
|
||||
rsp.attr_value.handle = param->read.handle;
|
||||
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
|
||||
char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len);
|
||||
ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset);
|
||||
free(pHexData);
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_response(
|
||||
gatts_if, param->read.conn_id,
|
||||
param->read.trans_id,
|
||||
ESP_GATT_OK,
|
||||
&rsp);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
} // Response needed
|
||||
} // Handle matches this characteristic.
|
||||
break;
|
||||
} // ESP_GATTS_READ_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_CONF_EVT
|
||||
//
|
||||
// conf:
|
||||
// - esp_gatt_status_t status – The status code.
|
||||
// - uint16_t conn_id – The connection used.
|
||||
//
|
||||
case ESP_GATTS_CONF_EVT: {
|
||||
// ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle);
|
||||
if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet
|
||||
m_semaphoreConfEvt.give(param->conf.status);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTS_DISCONNECT_EVT: {
|
||||
m_semaphoreConfEvt.give();
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
} // default
|
||||
|
||||
} // switch event
|
||||
|
||||
// Give each of the descriptors associated with this characteristic the opportunity to handle the
|
||||
// event.
|
||||
|
||||
m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
|
||||
ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send an indication.
|
||||
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
|
||||
* will block waiting a positive confirmation from the client.
|
||||
* @return N/A
|
||||
*/
|
||||
void BLECharacteristic::indicate() {
|
||||
|
||||
ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length());
|
||||
notify(false);
|
||||
ESP_LOGD(LOG_TAG, "<< indicate");
|
||||
} // indicate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a notify.
|
||||
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
|
||||
* will not block; it is a fire and forget.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::notify(bool is_notification) {
|
||||
ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length());
|
||||
|
||||
assert(getService() != nullptr);
|
||||
assert(getService()->getServer() != nullptr);
|
||||
|
||||
GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
|
||||
|
||||
if (getService()->getServer()->getConnectedCount() == 0) {
|
||||
ESP_LOGD(LOG_TAG, "<< notify: No connected clients.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled
|
||||
// and, if not, prevent the notification.
|
||||
|
||||
BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
|
||||
if(is_notification) {
|
||||
if (p2902 != nullptr && !p2902->getNotifications()) {
|
||||
ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (p2902 != nullptr && !p2902->getIndications()) {
|
||||
ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &myPair : getService()->getServer()->getPeerDevices(false)) {
|
||||
uint16_t _mtu = (myPair.second.mtu);
|
||||
if (m_value.getValue().length() > _mtu - 3) {
|
||||
ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
|
||||
}
|
||||
|
||||
size_t length = m_value.getValue().length();
|
||||
if(!is_notification)
|
||||
m_semaphoreConfEvt.take("indicate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_send_indicate(
|
||||
getService()->getServer()->getGattsIf(),
|
||||
myPair.first,
|
||||
getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc));
|
||||
m_semaphoreConfEvt.give();
|
||||
return;
|
||||
}
|
||||
if(!is_notification)
|
||||
m_semaphoreConfEvt.wait("indicate");
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< notify");
|
||||
} // Notify
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the permission to broadcast.
|
||||
* A characteristics has properties associated with it which define what it is capable of doing.
|
||||
* One of these is the broadcast flag.
|
||||
* @param [in] value The flag value of the property.
|
||||
* @return N/A
|
||||
*/
|
||||
void BLECharacteristic::setBroadcastProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
}
|
||||
} // setBroadcastProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callback handlers for this characteristic.
|
||||
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
|
||||
*/
|
||||
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) {
|
||||
ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
|
||||
m_pCallbacks = pCallbacks;
|
||||
ESP_LOGD(LOG_TAG, "<< setCallbacks");
|
||||
} // setCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the BLE handle associated with this characteristic.
|
||||
* A user program will request that a characteristic be created against a service. When the characteristic has been
|
||||
* registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
|
||||
* server/service but it is told to the service, not the characteristic associated with the service. This internally
|
||||
* exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
|
||||
* to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
|
||||
* that will be propagated down to it which contain a handle value and now know that the event is destined for it.
|
||||
* @param [in] handle The handle associated with this characteristic.
|
||||
*/
|
||||
void BLECharacteristic::setHandle(uint16_t handle) {
|
||||
ESP_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
|
||||
m_handle = handle;
|
||||
ESP_LOGD(LOG_TAG, "<< setHandle");
|
||||
} // setHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Indicate property value.
|
||||
* @param [in] value Set to true if we are to allow indicate messages.
|
||||
*/
|
||||
void BLECharacteristic::setIndicateProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
}
|
||||
} // setIndicateProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Notify property value.
|
||||
* @param [in] value Set to true if we are to allow notification messages.
|
||||
*/
|
||||
void BLECharacteristic::setNotifyProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
}
|
||||
} // setNotifyProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Read property value.
|
||||
* @param [in] value Set to true if we are to allow reads.
|
||||
*/
|
||||
void BLECharacteristic::setReadProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
}
|
||||
} // setReadProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic.
|
||||
* @param [in] data The data to set for the characteristic.
|
||||
* @param [in] length The length of the data in bytes.
|
||||
*/
|
||||
void BLECharacteristic::setValue(uint8_t* data, size_t length) {
|
||||
char* pHex = BLEUtils::buildHexData(nullptr, data, length);
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
|
||||
free(pHex);
|
||||
if (length > ESP_GATT_MAX_ATTR_LEN) {
|
||||
ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
|
||||
return;
|
||||
}
|
||||
m_value.setValue(data, length);
|
||||
ESP_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the characteristic from string data.
|
||||
* We set the value of the characteristic from the bytes contained in the
|
||||
* string.
|
||||
* @param [in] Set the value of the characteristic.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristic::setValue(std::string value) {
|
||||
setValue((uint8_t*)(value.data()), value.length());
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(uint16_t& data16) {
|
||||
uint8_t temp[2];
|
||||
temp[0] = data16;
|
||||
temp[1] = data16 >> 8;
|
||||
setValue(temp, 2);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(uint32_t& data32) {
|
||||
uint8_t temp[4];
|
||||
temp[0] = data32;
|
||||
temp[1] = data32 >> 8;
|
||||
temp[2] = data32 >> 16;
|
||||
temp[3] = data32 >> 24;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(int& data32) {
|
||||
uint8_t temp[4];
|
||||
temp[0] = data32;
|
||||
temp[1] = data32 >> 8;
|
||||
temp[2] = data32 >> 16;
|
||||
temp[3] = data32 >> 24;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(float& data32) {
|
||||
uint8_t temp[4];
|
||||
*((float*)temp) = data32;
|
||||
setValue(temp, 4);
|
||||
} // setValue
|
||||
|
||||
void BLECharacteristic::setValue(double& data64) {
|
||||
uint8_t temp[8];
|
||||
*((double*)temp) = data64;
|
||||
setValue(temp, 8);
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Write No Response property value.
|
||||
* @param [in] value Set to true if we are to allow writes with no response.
|
||||
*/
|
||||
void BLECharacteristic::setWriteNoResponseProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
}
|
||||
} // setWriteNoResponseProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the Write property value.
|
||||
* @param [in] value Set to true if we are to allow writes.
|
||||
*/
|
||||
void BLECharacteristic::setWriteProperty(bool value) {
|
||||
//ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value);
|
||||
if (value) {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
} else {
|
||||
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
}
|
||||
} // setWriteProperty
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the characteristic.
|
||||
* @return A string representation of the characteristic.
|
||||
*/
|
||||
std::string BLECharacteristic::toString() {
|
||||
std::stringstream stringstream;
|
||||
stringstream << std::hex << std::setfill('0');
|
||||
stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
|
||||
stringstream << " " <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_READ) ? "Read " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) ? "Write " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) ? "WriteNoResponse " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "Broadcast " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) ? "Notify " : "") <<
|
||||
((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) ? "Indicate " : "");
|
||||
return stringstream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
*/
|
||||
void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default");
|
||||
ESP_LOGD("BLECharacteristicCallbacks", "<< onRead");
|
||||
} // onRead
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pCharacteristic The characteristic that is the source of the event.
|
||||
*/
|
||||
void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default");
|
||||
ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite");
|
||||
} // onWrite
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
137
libraries/BLE/src/BLECharacteristic.h
Normal file
137
libraries/BLE/src/BLECharacteristic.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* BLECharacteristic.h
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "BLEUUID.h"
|
||||
#include <esp_gatts_api.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include "BLEDescriptor.h"
|
||||
#include "BLEValue.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEService;
|
||||
class BLEDescriptor;
|
||||
class BLECharacteristicCallbacks;
|
||||
|
||||
/**
|
||||
* @brief A management structure for %BLE descriptors.
|
||||
*/
|
||||
class BLEDescriptorMap {
|
||||
public:
|
||||
void setByUUID(const char* uuid, BLEDescriptor* pDescriptor);
|
||||
void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor);
|
||||
void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor);
|
||||
BLEDescriptor* getByUUID(const char* uuid);
|
||||
BLEDescriptor* getByUUID(BLEUUID uuid);
|
||||
BLEDescriptor* getByHandle(uint16_t handle);
|
||||
std::string toString();
|
||||
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
|
||||
BLEDescriptor* getFirst();
|
||||
BLEDescriptor* getNext();
|
||||
private:
|
||||
std::map<BLEDescriptor*, std::string> m_uuidMap;
|
||||
std::map<uint16_t, BLEDescriptor*> m_handleMap;
|
||||
std::map<BLEDescriptor*, std::string>::iterator m_iterator;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The model of a %BLE Characteristic.
|
||||
*
|
||||
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and
|
||||
* can be read and written to by a %BLE client.
|
||||
*/
|
||||
class BLECharacteristic {
|
||||
public:
|
||||
BLECharacteristic(const char* uuid, uint32_t properties = 0);
|
||||
BLECharacteristic(BLEUUID uuid, uint32_t properties = 0);
|
||||
virtual ~BLECharacteristic();
|
||||
|
||||
void addDescriptor(BLEDescriptor* pDescriptor);
|
||||
BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID);
|
||||
BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID);
|
||||
BLEUUID getUUID();
|
||||
std::string getValue();
|
||||
uint8_t* getData();
|
||||
|
||||
void indicate();
|
||||
void notify(bool is_notification = true);
|
||||
void setBroadcastProperty(bool value);
|
||||
void setCallbacks(BLECharacteristicCallbacks* pCallbacks);
|
||||
void setIndicateProperty(bool value);
|
||||
void setNotifyProperty(bool value);
|
||||
void setReadProperty(bool value);
|
||||
void setValue(uint8_t* data, size_t size);
|
||||
void setValue(std::string value);
|
||||
void setValue(uint16_t& data16);
|
||||
void setValue(uint32_t& data32);
|
||||
void setValue(int& data32);
|
||||
void setValue(float& data32);
|
||||
void setValue(double& data64);
|
||||
void setWriteProperty(bool value);
|
||||
void setWriteNoResponseProperty(bool value);
|
||||
std::string toString();
|
||||
uint16_t getHandle();
|
||||
void setAccessPermissions(esp_gatt_perm_t perm);
|
||||
|
||||
static const uint32_t PROPERTY_READ = 1<<0;
|
||||
static const uint32_t PROPERTY_WRITE = 1<<1;
|
||||
static const uint32_t PROPERTY_NOTIFY = 1<<2;
|
||||
static const uint32_t PROPERTY_BROADCAST = 1<<3;
|
||||
static const uint32_t PROPERTY_INDICATE = 1<<4;
|
||||
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
|
||||
|
||||
private:
|
||||
|
||||
friend class BLEServer;
|
||||
friend class BLEService;
|
||||
friend class BLEDescriptor;
|
||||
friend class BLECharacteristicMap;
|
||||
|
||||
BLEUUID m_bleUUID;
|
||||
BLEDescriptorMap m_descriptorMap;
|
||||
uint16_t m_handle;
|
||||
esp_gatt_char_prop_t m_properties;
|
||||
BLECharacteristicCallbacks* m_pCallbacks;
|
||||
BLEService* m_pService;
|
||||
BLEValue m_value;
|
||||
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||
|
||||
void handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
void executeCreate(BLEService* pService);
|
||||
esp_gatt_char_prop_t getProperties();
|
||||
BLEService* getService();
|
||||
void setHandle(uint16_t handle);
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
|
||||
}; // BLECharacteristic
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
|
||||
*
|
||||
* When a server application creates a %BLE characteristic, we may wish to be informed when there is either
|
||||
* a read or write request to the characteristic's value. An application can register a
|
||||
* sub-classed instance of this class and will be notified when such an event happens.
|
||||
*/
|
||||
class BLECharacteristicCallbacks {
|
||||
public:
|
||||
virtual ~BLECharacteristicCallbacks();
|
||||
virtual void onRead(BLECharacteristic* pCharacteristic);
|
||||
virtual void onWrite(BLECharacteristic* pCharacteristic);
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */
|
133
libraries/BLE/src/BLECharacteristicMap.cpp
Normal file
133
libraries/BLE/src/BLECharacteristicMap.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* BLECharacteristicMap.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "BLEService.h"
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by handle.
|
||||
* @param [in] handle The handle to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) {
|
||||
return m_handleMap.at(handle);
|
||||
} // getByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by UUID.
|
||||
* @param [in] UUID The UUID to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) {
|
||||
return getByUUID(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the characteristic by UUID.
|
||||
* @param [in] UUID The UUID to look up the characteristic.
|
||||
* @return The characteristic.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) {
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (myPair.first->getUUID().equals(uuid)) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
//return m_uuidMap.at(uuid.toString());
|
||||
return nullptr;
|
||||
} // getByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the first characteristic in the map.
|
||||
* @return The first characteristic in the map.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getFirst() {
|
||||
m_iterator = m_uuidMap.begin();
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLECharacteristic* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getFirst
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the next characteristic in the map.
|
||||
* @return The next characteristic in the map.
|
||||
*/
|
||||
BLECharacteristic* BLECharacteristicMap::getNext() {
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLECharacteristic* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getNext
|
||||
|
||||
|
||||
/**
|
||||
* @brief Pass the GATT server event onwards to each of the characteristics found in the mapping
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
|
||||
// Invoke the handler for every Service we have.
|
||||
for (auto& myPair : m_uuidMap) {
|
||||
myPair.first->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the characteristic by handle.
|
||||
* @param [in] handle The handle of the characteristic.
|
||||
* @param [in] characteristic The characteristic to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) {
|
||||
m_handleMap.insert(std::pair<uint16_t, BLECharacteristic*>(handle, characteristic));
|
||||
} // setByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the characteristic by UUID.
|
||||
* @param [in] uuid The uuid of the characteristic.
|
||||
* @param [in] characteristic The characteristic to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) {
|
||||
m_uuidMap.insert(std::pair<BLECharacteristic*, std::string>(pCharacteristic, uuid.toString()));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the characteristic map.
|
||||
* @return A string representation of the characteristic map.
|
||||
*/
|
||||
std::string BLECharacteristicMap::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << std::hex << std::setfill('0');
|
||||
int count = 0;
|
||||
for (auto &myPair: m_uuidMap) {
|
||||
if (count > 0) {
|
||||
stringStream << "\n";
|
||||
}
|
||||
count++;
|
||||
stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString();
|
||||
}
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
536
libraries/BLE/src/BLEClient.cpp
Normal file
536
libraries/BLE/src/BLEClient.cpp
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* BLEDevice.cpp
|
||||
*
|
||||
* Created on: Mar 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_bt.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gattc_api.h>
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEService.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include "BLEDevice.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEClient";
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Design
|
||||
* ------
|
||||
* When we perform a searchService() requests, we are asking the BLE server to return each of the services
|
||||
* that it exposes. For each service, we received an ESP_GATTC_SEARCH_RES_EVT event which contains details
|
||||
* of the exposed service including its UUID.
|
||||
*
|
||||
* The objects we will invent for a BLEClient will be as follows:
|
||||
* * BLERemoteService - A model of a remote service.
|
||||
* * BLERemoteCharacteristic - A model of a remote characteristic
|
||||
* * BLERemoteDescriptor - A model of a remote descriptor.
|
||||
*
|
||||
* Since there is a hierarchical relationship here, we will have the idea that from a BLERemoteService will own
|
||||
* zero or more remote characteristics and a BLERemoteCharacteristic will own zero or more remote BLEDescriptors.
|
||||
*
|
||||
* We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics
|
||||
* and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
BLEClient::BLEClient() {
|
||||
m_pClientCallbacks = nullptr;
|
||||
m_conn_id = ESP_GATT_IF_NONE;
|
||||
m_gattc_if = ESP_GATT_IF_NONE;
|
||||
m_haveServices = false;
|
||||
m_isConnected = false; // Initially, we are flagged as not connected.
|
||||
} // BLEClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
BLEClient::~BLEClient() {
|
||||
// We may have allocated service references associated with this client. Before we are finished
|
||||
// with the client, we must release resources.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
delete myPair.second;
|
||||
}
|
||||
m_servicesMap.clear();
|
||||
} // ~BLEClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Clear any existing services.
|
||||
*
|
||||
*/
|
||||
void BLEClient::clearServices() {
|
||||
ESP_LOGD(LOG_TAG, ">> clearServices");
|
||||
// Delete all the services.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
delete myPair.second;
|
||||
}
|
||||
m_servicesMap.clear();
|
||||
m_haveServices = false;
|
||||
ESP_LOGD(LOG_TAG, "<< clearServices");
|
||||
} // clearServices
|
||||
|
||||
/**
|
||||
* Add overloaded function to ease connect to peer device with not public address
|
||||
*/
|
||||
bool BLEClient::connect(BLEAdvertisedDevice* device) {
|
||||
BLEAddress address = device->getAddress();
|
||||
esp_ble_addr_type_t type = device->getAddressType();
|
||||
return connect(address, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to the partner (BLE Server).
|
||||
* @param [in] address The address of the partner.
|
||||
* @return True on success.
|
||||
*/
|
||||
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
|
||||
ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
|
||||
|
||||
// We need the connection handle that we get from registering the application. We register the app
|
||||
// and then block on its completion. When the event has arrived, we will have the handle.
|
||||
m_appId = BLEDevice::m_appId++;
|
||||
BLEDevice::addPeerDevice(this, true, m_appId);
|
||||
m_semaphoreRegEvt.take("connect");
|
||||
|
||||
// clearServices(); // we dont need to delete services since every client is unique?
|
||||
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_semaphoreRegEvt.wait("connect");
|
||||
|
||||
m_peerAddress = address;
|
||||
|
||||
// Perform the open connection request against the target BLE Server.
|
||||
m_semaphoreOpenEvt.take("connect");
|
||||
errRc = ::esp_ble_gattc_open(
|
||||
m_gattc_if,
|
||||
*getPeerAddress().getNative(), // address
|
||||
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
|
||||
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
|
||||
);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
|
||||
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
|
||||
return rc == ESP_GATT_OK;
|
||||
} // connect
|
||||
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the peer.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEClient::disconnect() {
|
||||
ESP_LOGD(LOG_TAG, ">> disconnect()");
|
||||
esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< disconnect()");
|
||||
} // disconnect
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT Client events
|
||||
*/
|
||||
void BLEClient::gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
|
||||
|
||||
// Execute handler code based on the type of event received.
|
||||
switch(event) {
|
||||
|
||||
case ESP_GATTC_SRVC_CHG_EVT:
|
||||
ESP_LOGI(LOG_TAG, "SERVICE CHANGED");
|
||||
break;
|
||||
|
||||
case ESP_GATTC_CLOSE_EVT: {
|
||||
// esp_ble_gattc_app_unregister(m_appId);
|
||||
// BLEDevice::removePeerDevice(m_gattc_if, true);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// ESP_GATTC_DISCONNECT_EVT
|
||||
//
|
||||
// disconnect:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
// If we receive a disconnect event, set the class flag that indicates that we are
|
||||
// no longer connected.
|
||||
m_isConnected = false;
|
||||
if (m_pClientCallbacks != nullptr) {
|
||||
m_pClientCallbacks->onDisconnect(this);
|
||||
}
|
||||
BLEDevice::removePeerDevice(m_appId, true);
|
||||
esp_ble_gattc_app_unregister(m_gattc_if);
|
||||
m_semaphoreRssiCmplEvt.give();
|
||||
m_semaphoreSearchCmplEvt.give(1);
|
||||
break;
|
||||
} // ESP_GATTC_DISCONNECT_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTC_OPEN_EVT
|
||||
//
|
||||
// open:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
// - esp_bd_addr_t remote_bda
|
||||
//
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
m_conn_id = evtParam->open.conn_id;
|
||||
if (m_pClientCallbacks != nullptr) {
|
||||
m_pClientCallbacks->onConnect(this);
|
||||
}
|
||||
if (evtParam->open.status == ESP_GATT_OK) {
|
||||
m_isConnected = true; // Flag us as connected.
|
||||
}
|
||||
m_semaphoreOpenEvt.give(evtParam->open.status);
|
||||
break;
|
||||
} // ESP_GATTC_OPEN_EVT
|
||||
|
||||
|
||||
//
|
||||
// ESP_GATTC_REG_EVT
|
||||
//
|
||||
// reg:
|
||||
// esp_gatt_status_t status
|
||||
// uint16_t app_id
|
||||
//
|
||||
case ESP_GATTC_REG_EVT: {
|
||||
m_gattc_if = gattc_if;
|
||||
m_semaphoreRegEvt.give();
|
||||
break;
|
||||
} // ESP_GATTC_REG_EVT
|
||||
|
||||
case ESP_GATTC_CFG_MTU_EVT:
|
||||
if(evtParam->cfg_mtu.status != ESP_GATT_OK) {
|
||||
ESP_LOGE(LOG_TAG,"Config mtu failed");
|
||||
}
|
||||
m_mtu = evtParam->cfg_mtu.mtu;
|
||||
break;
|
||||
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
BLEDevice::updatePeerDevice(this, true, m_gattc_if);
|
||||
esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTC_CONNECT_EVT
|
||||
|
||||
//
|
||||
// ESP_GATTC_SEARCH_CMPL_EVT
|
||||
//
|
||||
// search_cmpl:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t conn_id
|
||||
//
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam;
|
||||
if (p_data->search_cmpl.status != ESP_GATT_OK){
|
||||
ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
|
||||
break;
|
||||
}
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
// commented out just for now to keep backward compatibility
|
||||
// if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) {
|
||||
// ESP_LOGI(LOG_TAG, "Get service information from remote device");
|
||||
// } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) {
|
||||
// ESP_LOGI(LOG_TAG, "Get service information from flash");
|
||||
// } else {
|
||||
// ESP_LOGI(LOG_TAG, "unknown service source");
|
||||
// }
|
||||
#endif
|
||||
m_semaphoreSearchCmplEvt.give(0);
|
||||
break;
|
||||
} // ESP_GATTC_SEARCH_CMPL_EVT
|
||||
|
||||
|
||||
//
|
||||
// ESP_GATTC_SEARCH_RES_EVT
|
||||
//
|
||||
// search_res:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t start_handle
|
||||
// - uint16_t end_handle
|
||||
// - esp_gatt_id_t srvc_id
|
||||
//
|
||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||
BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id);
|
||||
BLERemoteService* pRemoteService = new BLERemoteService(
|
||||
evtParam->search_res.srvc_id,
|
||||
this,
|
||||
evtParam->search_res.start_handle,
|
||||
evtParam->search_res.end_handle
|
||||
);
|
||||
m_servicesMap.insert(std::pair<std::string, BLERemoteService*>(uuid.toString(), pRemoteService));
|
||||
m_servicesMapByInstID.insert(std::pair<BLERemoteService *, uint16_t>(pRemoteService, evtParam->search_res.srvc_id.inst_id));
|
||||
break;
|
||||
} // ESP_GATTC_SEARCH_RES_EVT
|
||||
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // Switch
|
||||
|
||||
// Pass the request on to all services.
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
|
||||
}
|
||||
|
||||
} // gattClientEventHandler
|
||||
|
||||
|
||||
uint16_t BLEClient::getConnId() {
|
||||
return m_conn_id;
|
||||
} // getConnId
|
||||
|
||||
|
||||
|
||||
esp_gatt_if_t BLEClient::getGattcIf() {
|
||||
return m_gattc_if;
|
||||
} // getGattcIf
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the address of the peer.
|
||||
*
|
||||
* Returns the Bluetooth device address of the %BLE peer to which this client is connected.
|
||||
*/
|
||||
BLEAddress BLEClient::getPeerAddress() {
|
||||
return m_peerAddress;
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ask the BLE server for the RSSI value.
|
||||
* @return The RSSI value.
|
||||
*/
|
||||
int BLEClient::getRssi() {
|
||||
ESP_LOGD(LOG_TAG, ">> getRssi()");
|
||||
if (!isConnected()) {
|
||||
ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected");
|
||||
return 0;
|
||||
}
|
||||
// We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive
|
||||
// an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion.
|
||||
//
|
||||
m_semaphoreRssiCmplEvt.take("getRssi");
|
||||
esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative());
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc));
|
||||
return 0;
|
||||
}
|
||||
int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi");
|
||||
ESP_LOGD(LOG_TAG, "<< getRssi(): %d", rssiValue);
|
||||
return rssiValue;
|
||||
} // getRssi
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service BLE Remote Service instance corresponding to the uuid.
|
||||
* @param [in] uuid The UUID of the service being sought.
|
||||
* @return A reference to the Service or nullptr if don't know about it.
|
||||
*/
|
||||
BLERemoteService* BLEClient::getService(const char* uuid) {
|
||||
return getService(BLEUUID(uuid));
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service object corresponding to the uuid.
|
||||
* @param [in] uuid The UUID of the service being sought.
|
||||
* @return A reference to the Service or nullptr if don't know about it.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
|
||||
ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
|
||||
// Design
|
||||
// ------
|
||||
// We wish to retrieve the service given its UUID. It is possible that we have not yet asked the
|
||||
// device what services it has in which case we have nothing to match against. If we have not
|
||||
// asked the device about its services, then we do that now. Once we get the results we can then
|
||||
// examine the services map to see if it has the service we are looking for.
|
||||
if (!m_haveServices) {
|
||||
getServices();
|
||||
}
|
||||
std::string uuidStr = uuid.toString();
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
if (myPair.first == uuidStr) {
|
||||
ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str());
|
||||
return myPair.second;
|
||||
}
|
||||
} // End of each of the services.
|
||||
ESP_LOGD(LOG_TAG, "<< getService: not found");
|
||||
return nullptr;
|
||||
} // getService
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ask the remote %BLE server for its services.
|
||||
* A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
|
||||
* services and wait until we have received them all.
|
||||
* @return N/A
|
||||
*/
|
||||
std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
|
||||
/*
|
||||
* Design
|
||||
* ------
|
||||
* We invoke esp_ble_gattc_search_service. This will request a list of the service exposed by the
|
||||
* peer BLE partner to be returned as events. Each event will be an an instance of ESP_GATTC_SEARCH_RES_EVT
|
||||
* and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received.
|
||||
*/
|
||||
ESP_LOGD(LOG_TAG, ">> getServices");
|
||||
// TODO implement retrieving services from cache
|
||||
clearServices(); // Clear any services that may exist.
|
||||
|
||||
esp_err_t errRc = esp_ble_gattc_search_service(
|
||||
getGattcIf(),
|
||||
getConnId(),
|
||||
NULL // Filter UUID
|
||||
);
|
||||
|
||||
m_semaphoreSearchCmplEvt.take("getServices");
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return &m_servicesMap;
|
||||
}
|
||||
// If sucessfull, remember that we now have services.
|
||||
m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0);
|
||||
ESP_LOGD(LOG_TAG, "<< getServices");
|
||||
return &m_servicesMap;
|
||||
} // getServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of a specific characteristic associated with a specific service.
|
||||
* @param [in] serviceUUID The service that owns the characteristic.
|
||||
* @param [in] characteristicUUID The characteristic whose value we wish to read.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) {
|
||||
ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue();
|
||||
ESP_LOGD(LOG_TAG, "<<getValue");
|
||||
return ret;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle a received GAP event.
|
||||
*
|
||||
* @param [in] event
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEClient::handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param) {
|
||||
ESP_LOGD(LOG_TAG, "BLEClient ... handling GAP event!");
|
||||
switch (event) {
|
||||
//
|
||||
// ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
|
||||
//
|
||||
// read_rssi_cmpl
|
||||
// - esp_bt_status_t status
|
||||
// - int8_t rssi
|
||||
// - esp_bd_addr_t remote_addr
|
||||
//
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
|
||||
m_semaphoreRssiCmplEvt.give((uint32_t) param->read_rssi_cmpl.rssi);
|
||||
break;
|
||||
} // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} // handleGAPEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Are we connected to a partner?
|
||||
* @return True if we are connected and false if we are not connected.
|
||||
*/
|
||||
bool BLEClient::isConnected() {
|
||||
return m_isConnected;
|
||||
} // isConnected
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callbacks that will be invoked.
|
||||
*/
|
||||
void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
|
||||
m_pClientCallbacks = pClientCallbacks;
|
||||
} // setClientCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of a specific characteristic associated with a specific service.
|
||||
* @param [in] serviceUUID The service that owns the characteristic.
|
||||
* @param [in] characteristicUUID The characteristic whose value we wish to write.
|
||||
* @throws BLEUuidNotFound
|
||||
*/
|
||||
void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value);
|
||||
ESP_LOGD(LOG_TAG, "<< setValue");
|
||||
} // setValue
|
||||
|
||||
uint16_t BLEClient::getMTU() {
|
||||
return m_mtu;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of this client.
|
||||
* @return A string representation of this client.
|
||||
*/
|
||||
std::string BLEClient::toString() {
|
||||
std::ostringstream ss;
|
||||
ss << "peer address: " << m_peerAddress.toString();
|
||||
ss << "\nServices:\n";
|
||||
for (auto &myPair : m_servicesMap) {
|
||||
ss << myPair.second->toString() << "\n";
|
||||
// myPair.second is the value
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
103
libraries/BLE/src/BLEClient.h
Normal file
103
libraries/BLE/src/BLEClient.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* BLEDevice.h
|
||||
*
|
||||
* Created on: Mar 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef MAIN_BLEDEVICE_H_
|
||||
#define MAIN_BLEDEVICE_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "BLEExceptions.h"
|
||||
#include "BLERemoteService.h"
|
||||
#include "BLEService.h"
|
||||
#include "BLEAddress.h"
|
||||
#include "BLEAdvertisedDevice.h"
|
||||
|
||||
class BLERemoteService;
|
||||
class BLEClientCallbacks;
|
||||
class BLEAdvertisedDevice;
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE client.
|
||||
*/
|
||||
class BLEClient {
|
||||
public:
|
||||
BLEClient();
|
||||
~BLEClient();
|
||||
|
||||
bool connect(BLEAdvertisedDevice* device);
|
||||
bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server
|
||||
void disconnect(); // Disconnect from the remote BLE Server
|
||||
BLEAddress getPeerAddress(); // Get the address of the remote BLE Server
|
||||
int getRssi(); // Get the RSSI of the remote BLE Server
|
||||
std::map<std::string, BLERemoteService*>* getServices(); // Get a map of the services offered by the remote BLE Server
|
||||
BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server.
|
||||
BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server.
|
||||
std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service.
|
||||
|
||||
|
||||
void handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
|
||||
bool isConnected(); // Return true if we are connected.
|
||||
|
||||
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
|
||||
void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service.
|
||||
|
||||
std::string toString(); // Return a string representation of this client.
|
||||
uint16_t getConnId();
|
||||
esp_gatt_if_t getGattcIf();
|
||||
uint16_t getMTU();
|
||||
|
||||
uint16_t m_appId;
|
||||
private:
|
||||
friend class BLEDevice;
|
||||
friend class BLERemoteService;
|
||||
friend class BLERemoteCharacteristic;
|
||||
friend class BLERemoteDescriptor;
|
||||
|
||||
void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param);
|
||||
|
||||
BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server.
|
||||
uint16_t m_conn_id;
|
||||
// int m_deviceType;
|
||||
esp_gatt_if_t m_gattc_if;
|
||||
bool m_haveServices = false; // Have we previously obtain the set of services from the remote server.
|
||||
bool m_isConnected = false; // Are we currently connected.
|
||||
|
||||
BLEClientCallbacks* m_pClientCallbacks;
|
||||
FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
|
||||
std::map<std::string, BLERemoteService*> m_servicesMap;
|
||||
std::map<BLERemoteService*, uint16_t> m_servicesMapByInstID;
|
||||
void clearServices(); // Clear any existing services.
|
||||
uint16_t m_mtu = 23;
|
||||
}; // class BLEDevice
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks associated with a %BLE client.
|
||||
*/
|
||||
class BLEClientCallbacks {
|
||||
public:
|
||||
virtual ~BLEClientCallbacks() {};
|
||||
virtual void onConnect(BLEClient *pClient) = 0;
|
||||
virtual void onDisconnect(BLEClient *pClient) = 0;
|
||||
};
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDEVICE_H_ */
|
296
libraries/BLE/src/BLEDescriptor.cpp
Normal file
296
libraries/BLE/src/BLEDescriptor.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* BLEDescriptor.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_err.h>
|
||||
#include "BLEService.h"
|
||||
#include "BLEDescriptor.h"
|
||||
#include "GeneralUtils.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEDescriptor";
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#define NULL_HANDLE (0xffff)
|
||||
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor constructor.
|
||||
*/
|
||||
BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor constructor.
|
||||
*/
|
||||
BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) {
|
||||
m_bleUUID = uuid;
|
||||
m_value.attr_len = 0; // Initial length is 0.
|
||||
m_value.attr_max_len = max_len; // Maximum length of the data.
|
||||
m_handle = NULL_HANDLE; // Handle is initially unknown.
|
||||
m_pCharacteristic = nullptr; // No initial characteristic.
|
||||
m_pCallback = nullptr; // No initial callback.
|
||||
|
||||
m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value.
|
||||
} // BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor destructor.
|
||||
*/
|
||||
BLEDescriptor::~BLEDescriptor() {
|
||||
free(m_value.attr_value); // Release the storage we created in the constructor.
|
||||
} // ~BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Execute the creation of the descriptor with the BLE runtime in ESP.
|
||||
* @param [in] pCharacteristic The characteristic to which to register this descriptor.
|
||||
*/
|
||||
void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) {
|
||||
ESP_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str());
|
||||
|
||||
if (m_handle != NULL_HANDLE) {
|
||||
ESP_LOGE(LOG_TAG, "Descriptor already has a handle.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service.
|
||||
|
||||
esp_attr_control_t control;
|
||||
control.auto_rsp = ESP_GATT_AUTO_RSP;
|
||||
m_semaphoreCreateEvt.take("executeCreate");
|
||||
esp_err_t errRc = ::esp_ble_gatts_add_char_descr(
|
||||
pCharacteristic->getService()->getHandle(),
|
||||
getUUID().getNative(),
|
||||
(esp_gatt_perm_t)m_permissions,
|
||||
&m_value,
|
||||
&control);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreCreateEvt.wait("executeCreate");
|
||||
ESP_LOGD(LOG_TAG, "<< executeCreate");
|
||||
} // executeCreate
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the BLE handle for this descriptor.
|
||||
* @return The handle for this descriptor.
|
||||
*/
|
||||
uint16_t BLEDescriptor::getHandle() {
|
||||
return m_handle;
|
||||
} // getHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the length of the value of this descriptor.
|
||||
* @return The length (in bytes) of the value of this descriptor.
|
||||
*/
|
||||
size_t BLEDescriptor::getLength() {
|
||||
return m_value.attr_len;
|
||||
} // getLength
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the UUID of the descriptor.
|
||||
*/
|
||||
BLEUUID BLEDescriptor::getUUID() {
|
||||
return m_bleUUID;
|
||||
} // getUUID
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of this descriptor.
|
||||
* @return A pointer to the value of this descriptor.
|
||||
*/
|
||||
uint8_t* BLEDescriptor::getValue() {
|
||||
return m_value.attr_value;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT server events for the descripttor.
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEDescriptor::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
switch (event) {
|
||||
// ESP_GATTS_ADD_CHAR_DESCR_EVT
|
||||
//
|
||||
// add_char_descr:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
case ESP_GATTS_ADD_CHAR_DESCR_EVT: {
|
||||
if (m_pCharacteristic != nullptr &&
|
||||
m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) &&
|
||||
m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle &&
|
||||
m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) {
|
||||
setHandle(param->add_char_descr.attr_handle);
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_DESCR_EVT
|
||||
|
||||
// ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived.
|
||||
//
|
||||
// write:
|
||||
// - uint16_t conn_id
|
||||
// - uint16_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool need_rsp
|
||||
// - bool is_prep
|
||||
// - uint16_t len
|
||||
// - uint8_t *value
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
if (param->write.handle == m_handle) {
|
||||
setValue(param->write.value, param->write.len); // Set the value of the descriptor.
|
||||
|
||||
if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now.
|
||||
m_pCallback->onWrite(this); // Invoke the onWrite callback handler.
|
||||
}
|
||||
} // End of ... this is our handle.
|
||||
|
||||
break;
|
||||
} // ESP_GATTS_WRITE_EVT
|
||||
|
||||
// ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived.
|
||||
//
|
||||
// read:
|
||||
// - uint16_t conn_id
|
||||
// - uint32_t trans_id
|
||||
// - esp_bd_addr_t bda
|
||||
// - uint16_t handle
|
||||
// - uint16_t offset
|
||||
// - bool is_long
|
||||
// - bool need_rsp
|
||||
//
|
||||
case ESP_GATTS_READ_EVT: {
|
||||
if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it
|
||||
|
||||
if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now.
|
||||
m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler.
|
||||
}
|
||||
|
||||
} // End of this is our handle
|
||||
break;
|
||||
} // ESP_GATTS_READ_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch event
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the callback handlers for this descriptor.
|
||||
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
|
||||
*/
|
||||
void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) {
|
||||
ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback);
|
||||
m_pCallback = pCallback;
|
||||
ESP_LOGD(LOG_TAG, "<< setCallbacks");
|
||||
} // setCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the handle of this descriptor.
|
||||
* Set the handle of this descriptor to be the supplied value.
|
||||
* @param [in] handle The handle to be associated with this descriptor.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptor::setHandle(uint16_t handle) {
|
||||
ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
|
||||
m_handle = handle;
|
||||
ESP_LOGD(LOG_TAG, "<< setHandle()");
|
||||
} // setHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the descriptor.
|
||||
* @param [in] data The data to set for the descriptor.
|
||||
* @param [in] length The length of the data in bytes.
|
||||
*/
|
||||
void BLEDescriptor::setValue(uint8_t* data, size_t length) {
|
||||
if (length > ESP_GATT_MAX_ATTR_LEN) {
|
||||
ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
|
||||
return;
|
||||
}
|
||||
m_value.attr_len = length;
|
||||
memcpy(m_value.attr_value, data, length);
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of the descriptor.
|
||||
* @param [in] value The value of the descriptor in string form.
|
||||
*/
|
||||
void BLEDescriptor::setValue(std::string value) {
|
||||
setValue((uint8_t*) value.data(), value.length());
|
||||
} // setValue
|
||||
|
||||
void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) {
|
||||
m_permissions = perm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the descriptor.
|
||||
* @return A string representation of the descriptor.
|
||||
*/
|
||||
std::string BLEDescriptor::toString() {
|
||||
std::stringstream stringstream;
|
||||
stringstream << std::hex << std::setfill('0');
|
||||
stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
|
||||
return stringstream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {}
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a read request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default");
|
||||
ESP_LOGD("BLEDescriptorCallbacks", "<< onRead");
|
||||
} // onRead
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function to support a write request.
|
||||
* @param [in] pDescriptor The descriptor that is the source of the event.
|
||||
*/
|
||||
void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) {
|
||||
ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default");
|
||||
ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite");
|
||||
} // onWrite
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
77
libraries/BLE/src/BLEDescriptor.h
Normal file
77
libraries/BLE/src/BLEDescriptor.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* BLEDescriptor.h
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string>
|
||||
#include "BLEUUID.h"
|
||||
#include "BLECharacteristic.h"
|
||||
#include <esp_gatts_api.h>
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
class BLEService;
|
||||
class BLECharacteristic;
|
||||
class BLEDescriptorCallbacks;
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE descriptor.
|
||||
*/
|
||||
class BLEDescriptor {
|
||||
public:
|
||||
BLEDescriptor(const char* uuid, uint16_t max_len = 100);
|
||||
BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100);
|
||||
virtual ~BLEDescriptor();
|
||||
|
||||
uint16_t getHandle(); // Get the handle of the descriptor.
|
||||
size_t getLength(); // Get the length of the value of the descriptor.
|
||||
BLEUUID getUUID(); // Get the UUID of the descriptor.
|
||||
uint8_t* getValue(); // Get a pointer to the value of the descriptor.
|
||||
void handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor.
|
||||
void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor.
|
||||
void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data.
|
||||
void setValue(std::string value); // Set the value of the descriptor as a data buffer.
|
||||
|
||||
std::string toString(); // Convert the descriptor to a string representation.
|
||||
|
||||
private:
|
||||
friend class BLEDescriptorMap;
|
||||
friend class BLECharacteristic;
|
||||
BLEUUID m_bleUUID;
|
||||
uint16_t m_handle;
|
||||
BLEDescriptorCallbacks* m_pCallback;
|
||||
BLECharacteristic* m_pCharacteristic;
|
||||
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
esp_attr_value_t m_value;
|
||||
|
||||
void executeCreate(BLECharacteristic* pCharacteristic);
|
||||
void setHandle(uint16_t handle);
|
||||
}; // BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
|
||||
*
|
||||
* When a server application creates a %BLE descriptor, we may wish to be informed when there is either
|
||||
* a read or write request to the descriptors value. An application can register a
|
||||
* sub-classed instance of this class and will be notified when such an event happens.
|
||||
*/
|
||||
class BLEDescriptorCallbacks {
|
||||
public:
|
||||
virtual ~BLEDescriptorCallbacks();
|
||||
virtual void onRead(BLEDescriptor* pDescriptor);
|
||||
virtual void onWrite(BLEDescriptor* pDescriptor);
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */
|
147
libraries/BLE/src/BLEDescriptorMap.cpp
Normal file
147
libraries/BLE/src/BLEDescriptorMap.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* BLEDescriptorMap.cpp
|
||||
*
|
||||
* Created on: Jun 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "BLECharacteristic.h"
|
||||
#include "BLEDescriptor.h"
|
||||
#include <esp_gatts_api.h> // ESP32 BLE
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by UUID.
|
||||
* @param [in] UUID The UUID to look up the descriptor.
|
||||
* @return The descriptor. If not present, then nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) {
|
||||
return getByUUID(BLEUUID(uuid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by UUID.
|
||||
* @param [in] UUID The UUID to look up the descriptor.
|
||||
* @return The descriptor. If not present, then nullptr is returned.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) {
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (myPair.first->getUUID().equals(uuid)) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
//return m_uuidMap.at(uuid.toString());
|
||||
return nullptr;
|
||||
} // getByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the descriptor by handle.
|
||||
* @param [in] handle The handle to look up the descriptor.
|
||||
* @return The descriptor.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) {
|
||||
return m_handleMap.at(handle);
|
||||
} // getByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by UUID.
|
||||
* @param [in] uuid The uuid of the descriptor.
|
||||
* @param [in] characteristic The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){
|
||||
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by UUID.
|
||||
* @param [in] uuid The uuid of the descriptor.
|
||||
* @param [in] characteristic The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) {
|
||||
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid.toString()));
|
||||
} // setByUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the descriptor by handle.
|
||||
* @param [in] handle The handle of the descriptor.
|
||||
* @param [in] descriptor The descriptor to cache.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) {
|
||||
m_handleMap.insert(std::pair<uint16_t, BLEDescriptor*>(handle, pDescriptor));
|
||||
} // setByHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the descriptor map.
|
||||
* @return A string representation of the descriptor map.
|
||||
*/
|
||||
std::string BLEDescriptorMap::toString() {
|
||||
std::stringstream stringStream;
|
||||
stringStream << std::hex << std::setfill('0');
|
||||
int count = 0;
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
if (count > 0) {
|
||||
stringStream << "\n";
|
||||
}
|
||||
count++;
|
||||
stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString();
|
||||
}
|
||||
return stringStream.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @breif Pass the GATT server event onwards to each of the descriptors found in the mapping
|
||||
* @param [in] event
|
||||
* @param [in] gatts_if
|
||||
* @param [in] param
|
||||
*/
|
||||
void BLEDescriptorMap::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
// Invoke the handler for every descriptor we have.
|
||||
for (auto &myPair : m_uuidMap) {
|
||||
myPair.first->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
} // handleGATTServerEvent
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the first descriptor in the map.
|
||||
* @return The first descriptor in the map.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getFirst() {
|
||||
m_iterator = m_uuidMap.begin();
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEDescriptor* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getFirst
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the next descriptor in the map.
|
||||
* @return The next descriptor in the map.
|
||||
*/
|
||||
BLEDescriptor* BLEDescriptorMap::getNext() {
|
||||
if (m_iterator == m_uuidMap.end()) return nullptr;
|
||||
BLEDescriptor* pRet = m_iterator->first;
|
||||
m_iterator++;
|
||||
return pRet;
|
||||
} // getNext
|
||||
#endif /* CONFIG_BT_ENABLED */
|
650
libraries/BLE/src/BLEDevice.cpp
Normal file
650
libraries/BLE/src/BLEDevice.cpp
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* BLE.cpp
|
||||
*
|
||||
* Created on: Mar 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_err.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_bt.h> // ESP32 BLE
|
||||
#include <esp_bt_device.h> // ESP32 BLE
|
||||
#include <esp_bt_main.h> // ESP32 BLE
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <esp_gatts_api.h> // ESP32 BLE
|
||||
#include <esp_gattc_api.h> // ESP32 BLE
|
||||
#include <esp_gatt_common_api.h>// ESP32 BLE
|
||||
#include <esp_err.h> // ESP32 ESP-IDF
|
||||
#include <map> // Part of C++ Standard library
|
||||
#include <sstream> // Part of C++ Standard library
|
||||
#include <iomanip> // Part of C++ Standard library
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "esp32-hal-bt.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define LOG_TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* LOG_TAG = "BLEDevice";
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Singletons for the BLEDevice.
|
||||
*/
|
||||
BLEServer* BLEDevice::m_pServer = nullptr;
|
||||
BLEScan* BLEDevice::m_pScan = nullptr;
|
||||
BLEClient* BLEDevice::m_pClient = nullptr;
|
||||
bool initialized = false;
|
||||
esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0;
|
||||
BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr;
|
||||
uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful
|
||||
BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr;
|
||||
uint16_t BLEDevice::m_appId = 0;
|
||||
std::map<uint16_t, conn_status_t> BLEDevice::m_connectedClientsMap;
|
||||
gap_event_handler BLEDevice::m_customGapHandler = nullptr;
|
||||
gattc_event_handler BLEDevice::m_customGattcHandler = nullptr;
|
||||
gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of a client.
|
||||
* @return A new instance of the client.
|
||||
*/
|
||||
/* STATIC */ BLEClient* BLEDevice::createClient() {
|
||||
ESP_LOGD(LOG_TAG, ">> createClient");
|
||||
#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig
|
||||
ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined");
|
||||
abort();
|
||||
#endif // CONFIG_GATTC_ENABLE
|
||||
m_pClient = new BLEClient();
|
||||
ESP_LOGD(LOG_TAG, "<< createClient");
|
||||
return m_pClient;
|
||||
} // createClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of a server.
|
||||
* @return A new instance of the server.
|
||||
*/
|
||||
/* STATIC */ BLEServer* BLEDevice::createServer() {
|
||||
ESP_LOGD(LOG_TAG, ">> createServer");
|
||||
#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig
|
||||
ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined");
|
||||
abort();
|
||||
#endif // CONFIG_GATTS_ENABLE
|
||||
m_pServer = new BLEServer();
|
||||
m_pServer->createApp(m_appId++);
|
||||
ESP_LOGD(LOG_TAG, "<< createServer");
|
||||
return m_pServer;
|
||||
} // createServer
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT server events.
|
||||
*
|
||||
* @param [in] event The event that has been newly received.
|
||||
* @param [in] gatts_if The connection to the GATT interface.
|
||||
* @param [in] param Parameters for the event.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gattServerEventHandler(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param
|
||||
) {
|
||||
ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gatts_if,
|
||||
BLEUtils::gattServerEventTypeToString(event).c_str());
|
||||
|
||||
BLEUtils::dumpGattServerEvent(event, gatts_if, param);
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTS_CONNECT_EVT: {
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
|
||||
|
||||
if (BLEDevice::m_pServer != nullptr) {
|
||||
BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
|
||||
if(m_customGattsHandler != nullptr) {
|
||||
m_customGattsHandler(event, gatts_if, param);
|
||||
}
|
||||
|
||||
} // gattServerEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GATT client events.
|
||||
*
|
||||
* Handler for the GATT client events.
|
||||
*
|
||||
* @param [in] event
|
||||
* @param [in] gattc_if
|
||||
* @param [in] param
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param) {
|
||||
|
||||
ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s",
|
||||
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
|
||||
BLEUtils::dumpGattClientEvent(event, gattc_if, param);
|
||||
|
||||
switch(event) {
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityLevel){
|
||||
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
for(auto &myPair : BLEDevice::getPeerDevices(true)) {
|
||||
conn_status_t conn_status = (conn_status_t)myPair.second;
|
||||
if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){
|
||||
((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_customGattcHandler != nullptr) {
|
||||
m_customGattcHandler(event, gattc_if, param);
|
||||
}
|
||||
|
||||
|
||||
} // gattClientEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle GAP events.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::gapEventHandler(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param) {
|
||||
|
||||
BLEUtils::dumpGapEvent(event, param);
|
||||
|
||||
switch(event) {
|
||||
|
||||
case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT");
|
||||
break;
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey));
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: ");
|
||||
// esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda));
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
/*
|
||||
* TODO should we add white/black list comparison?
|
||||
*/
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
/* send the positive(true) security response to the peer device to accept the security request.
|
||||
If not accept the security request, should sent the security response with negative(false) accept value*/
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks!=nullptr){
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest());
|
||||
}
|
||||
else{
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
/*
|
||||
*
|
||||
*/
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
|
||||
//display the passkey number to the user to input it in the peer deivce within 30 seconds
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey);
|
||||
if(BLEDevice::m_securityCallbacks!=nullptr){
|
||||
BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_KEY_EVT:
|
||||
//shows the ble key type info share with peer device to the user.
|
||||
ESP_LOGD(LOG_TAG, "ESP_GAP_BLE_KEY_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT");
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
if(BLEDevice::m_securityCallbacks != nullptr){
|
||||
BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
|
||||
}
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
break;
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
|
||||
if (BLEDevice::m_pClient != nullptr) {
|
||||
BLEDevice::m_pClient->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if (BLEDevice::m_pScan != nullptr) {
|
||||
BLEDevice::getScan()->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if(m_bleAdvertising != nullptr) {
|
||||
BLEDevice::getAdvertising()->handleGAPEvent(event, param);
|
||||
}
|
||||
|
||||
if(m_customGapHandler != nullptr) {
|
||||
BLEDevice::m_customGapHandler(event, param);
|
||||
}
|
||||
|
||||
} // gapEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the BLE device address.
|
||||
* @return The BLE device address.
|
||||
*/
|
||||
/* STATIC*/ BLEAddress BLEDevice::getAddress() {
|
||||
const uint8_t* bdAddr = esp_bt_dev_get_address();
|
||||
esp_bd_addr_t addr;
|
||||
memcpy(addr, bdAddr, sizeof(addr));
|
||||
return BLEAddress(addr);
|
||||
} // getAddress
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Scan object that we use for scanning.
|
||||
* @return The scanning object reference. This is a singleton object. The caller should not
|
||||
* try and release/delete it.
|
||||
*/
|
||||
/* STATIC */ BLEScan* BLEDevice::getScan() {
|
||||
//ESP_LOGD(LOG_TAG, ">> getScan");
|
||||
if (m_pScan == nullptr) {
|
||||
m_pScan = new BLEScan();
|
||||
//ESP_LOGD(LOG_TAG, " - creating a new scan object");
|
||||
}
|
||||
//ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan);
|
||||
return m_pScan;
|
||||
} // getScan
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the value of a characteristic of a service on a remote device.
|
||||
* @param [in] bdAddress
|
||||
* @param [in] serviceUUID
|
||||
* @param [in] characteristicUUID
|
||||
*/
|
||||
/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) {
|
||||
ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
BLEClient* pClient = createClient();
|
||||
pClient->connect(bdAddress);
|
||||
std::string ret = pClient->getValue(serviceUUID, characteristicUUID);
|
||||
pClient->disconnect();
|
||||
ESP_LOGD(LOG_TAG, "<< getValue");
|
||||
return ret;
|
||||
} // getValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the %BLE environment.
|
||||
* @param deviceName The device name of the device.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::init(std::string deviceName) {
|
||||
if(!initialized){
|
||||
initialized = true; // Set the initialization flag to ensure we are only initialized once.
|
||||
|
||||
esp_err_t errRc = ESP_OK;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!btStart()) {
|
||||
errRc = ESP_FAIL;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
errRc = ::nvs_flash_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#endif
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
errRc = esp_bt_controller_init(&bt_cfg);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
|
||||
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
|
||||
errRc = esp_bluedroid_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) {
|
||||
errRc = esp_bluedroid_enable();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig
|
||||
errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_GATTC_ENABLE
|
||||
|
||||
#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig
|
||||
errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_GATTS_ENABLE
|
||||
|
||||
errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
|
||||
errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
};
|
||||
#endif // CONFIG_BLE_SMP_ENABLE
|
||||
}
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue.
|
||||
} // init
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the transmission power.
|
||||
* The power level can be one of:
|
||||
* * ESP_PWR_LVL_N14
|
||||
* * ESP_PWR_LVL_N11
|
||||
* * ESP_PWR_LVL_N8
|
||||
* * ESP_PWR_LVL_N5
|
||||
* * ESP_PWR_LVL_N2
|
||||
* * ESP_PWR_LVL_P1
|
||||
* * ESP_PWR_LVL_P4
|
||||
* * ESP_PWR_LVL_P7
|
||||
* @param [in] powerLevel.
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) {
|
||||
ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel);
|
||||
esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
};
|
||||
ESP_LOGD(LOG_TAG, "<< setPower");
|
||||
} // setPower
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the value of a characteristic of a service on a remote device.
|
||||
* @param [in] bdAddress
|
||||
* @param [in] serviceUUID
|
||||
* @param [in] characteristicUUID
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
|
||||
ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
BLEClient* pClient = createClient();
|
||||
pClient->connect(bdAddress);
|
||||
pClient->setValue(serviceUUID, characteristicUUID, value);
|
||||
pClient->disconnect();
|
||||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the nature of this device.
|
||||
* @return A string representation of the nature of this device.
|
||||
*/
|
||||
/* STATIC */ std::string BLEDevice::toString() {
|
||||
std::ostringstream oss;
|
||||
oss << "BD Address: " << getAddress().toString();
|
||||
return oss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
/**
|
||||
* @brief Add an entry to the BLE white list.
|
||||
* @param [in] address The address to add to the white list.
|
||||
*/
|
||||
void BLEDevice::whiteListAdd(BLEAddress address) {
|
||||
ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str());
|
||||
esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< whiteListAdd");
|
||||
} // whiteListAdd
|
||||
|
||||
|
||||
/**
|
||||
* @brief Remove an entry from the BLE white list.
|
||||
* @param [in] address The address to remove from the white list.
|
||||
*/
|
||||
void BLEDevice::whiteListRemove(BLEAddress address) {
|
||||
ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str());
|
||||
esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry.
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< whiteListRemove");
|
||||
} // whiteListRemove
|
||||
|
||||
/*
|
||||
* @brief Set encryption level that will be negotiated with peer device durng connection
|
||||
* @param [in] level Requested encryption level
|
||||
*/
|
||||
void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) {
|
||||
BLEDevice::m_securityLevel = level;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
|
||||
* @param [in] cllbacks Pointer to BLESecurityCallbacks class callback
|
||||
*/
|
||||
void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) {
|
||||
BLEDevice::m_securityCallbacks = callbacks;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer
|
||||
* @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517
|
||||
*/
|
||||
esp_err_t BLEDevice::setMTU(uint16_t mtu) {
|
||||
ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
|
||||
esp_err_t err = esp_ble_gatt_set_local_mtu(mtu);
|
||||
if (err == ESP_OK) {
|
||||
m_localMTU = mtu;
|
||||
} else {
|
||||
ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu);
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "<< setLocalMTU");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Get local MTU value set during mtu request or default value
|
||||
*/
|
||||
uint16_t BLEDevice::getMTU() {
|
||||
return m_localMTU;
|
||||
}
|
||||
|
||||
bool BLEDevice::getInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
BLEAdvertising* BLEDevice::getAdvertising() {
|
||||
if(m_bleAdvertising == nullptr) {
|
||||
m_bleAdvertising = new BLEAdvertising();
|
||||
ESP_LOGI(LOG_TAG, "create advertising");
|
||||
}
|
||||
ESP_LOGD(LOG_TAG, "get advertising");
|
||||
return m_bleAdvertising;
|
||||
}
|
||||
|
||||
void BLEDevice::startAdvertising() {
|
||||
ESP_LOGD(LOG_TAG, ">> startAdvertising");
|
||||
getAdvertising()->start();
|
||||
ESP_LOGD(LOG_TAG, "<< startAdvertising");
|
||||
} // startAdvertising
|
||||
|
||||
/* multi connect support */
|
||||
/* requires a little more work */
|
||||
std::map<uint16_t, conn_status_t> BLEDevice::getPeerDevices(bool _client) {
|
||||
return m_connectedClientsMap;
|
||||
}
|
||||
|
||||
BLEClient* BLEDevice::getClientByGattIf(uint16_t conn_id) {
|
||||
return (BLEClient*)m_connectedClientsMap.find(conn_id)->second.peer_device;
|
||||
}
|
||||
|
||||
void BLEDevice::updatePeerDevice(void* peer, bool _client, uint16_t conn_id) {
|
||||
ESP_LOGD(LOG_TAG, "update conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
|
||||
std::map<uint16_t, conn_status_t>::iterator it = m_connectedClientsMap.find(ESP_GATT_IF_NONE);
|
||||
if (it != m_connectedClientsMap.end()) {
|
||||
std::swap(m_connectedClientsMap[conn_id], it->second);
|
||||
m_connectedClientsMap.erase(it);
|
||||
}else{
|
||||
it = m_connectedClientsMap.find(conn_id);
|
||||
if (it != m_connectedClientsMap.end()) {
|
||||
conn_status_t _st = it->second;
|
||||
_st.peer_device = peer;
|
||||
std::swap(m_connectedClientsMap[conn_id], _st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
|
||||
ESP_LOGI(LOG_TAG, "add conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
|
||||
conn_status_t status = {
|
||||
.peer_device = peer,
|
||||
.connected = true,
|
||||
.mtu = 23
|
||||
};
|
||||
|
||||
m_connectedClientsMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
|
||||
}
|
||||
|
||||
void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) {
|
||||
ESP_LOGI(LOG_TAG, "remove: %d, GATT role %s", conn_id, _client?"client":"server");
|
||||
if(m_connectedClientsMap.find(conn_id) != m_connectedClientsMap.end())
|
||||
m_connectedClientsMap.erase(conn_id);
|
||||
}
|
||||
|
||||
/* multi connect support */
|
||||
|
||||
/**
|
||||
* @brief de-Initialize the %BLE environment.
|
||||
* @param release_memory release the internal BT stack memory
|
||||
*/
|
||||
/* STATIC */ void BLEDevice::deinit(bool release_memory) {
|
||||
if (!initialized) return;
|
||||
|
||||
esp_bluedroid_disable();
|
||||
esp_bluedroid_deinit();
|
||||
esp_bt_controller_disable();
|
||||
esp_bt_controller_deinit();
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
if (release_memory) {
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it)
|
||||
} else {
|
||||
initialized = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGapHandler(gap_event_handler handler) {
|
||||
m_customGapHandler = handler;
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) {
|
||||
m_customGattcHandler = handler;
|
||||
}
|
||||
|
||||
void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) {
|
||||
m_customGattsHandler = handler;
|
||||
}
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
99
libraries/BLE/src/BLEDevice.h
Normal file
99
libraries/BLE/src/BLEDevice.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* BLEDevice.h
|
||||
*
|
||||
* Created on: Mar 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef MAIN_BLEDevice_H_
|
||||
#define MAIN_BLEDevice_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_gap_ble_api.h> // ESP32 BLE
|
||||
#include <esp_gattc_api.h> // ESP32 BLE
|
||||
#include <map> // Part of C++ STL
|
||||
#include <string>
|
||||
#include <esp_bt.h>
|
||||
|
||||
#include "BLEServer.h"
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEAddress.h"
|
||||
|
||||
/**
|
||||
* @brief BLE functions.
|
||||
*/
|
||||
typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
|
||||
typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param);
|
||||
typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
class BLEDevice {
|
||||
public:
|
||||
|
||||
static BLEClient* createClient(); // Create a new BLE client.
|
||||
static BLEServer* createServer(); // Cretae a new BLE server.
|
||||
static BLEAddress getAddress(); // Retrieve our own local BD address.
|
||||
static BLEScan* getScan(); // Get the scan object
|
||||
static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server.
|
||||
static void init(std::string deviceName); // Initialize the local BLE environment.
|
||||
static void setPower(esp_power_level_t powerLevel); // Set our power level.
|
||||
static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server.
|
||||
static std::string toString(); // Return a string representation of our device.
|
||||
static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list.
|
||||
static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list.
|
||||
static void setEncryptionLevel(esp_ble_sec_act_t level);
|
||||
static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks);
|
||||
static esp_err_t setMTU(uint16_t mtu);
|
||||
static uint16_t getMTU();
|
||||
static bool getInitialized(); // Returns the state of the device, is it initialized or not?
|
||||
/* move advertising to BLEDevice for saving ram and flash in beacons */
|
||||
static BLEAdvertising* getAdvertising();
|
||||
static void startAdvertising();
|
||||
static uint16_t m_appId;
|
||||
/* multi connect */
|
||||
static std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
|
||||
static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
|
||||
static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id);
|
||||
static void removePeerDevice(uint16_t conn_id, bool client);
|
||||
static BLEClient* getClientByGattIf(uint16_t conn_id);
|
||||
static void setCustomGapHandler(gap_event_handler handler);
|
||||
static void setCustomGattcHandler(gattc_event_handler handler);
|
||||
static void setCustomGattsHandler(gatts_event_handler handler);
|
||||
static void deinit(bool release_memory = false);
|
||||
static uint16_t m_localMTU;
|
||||
static esp_ble_sec_act_t m_securityLevel;
|
||||
|
||||
private:
|
||||
static BLEServer* m_pServer;
|
||||
static BLEScan* m_pScan;
|
||||
static BLEClient* m_pClient;
|
||||
static BLESecurityCallbacks* m_securityCallbacks;
|
||||
static BLEAdvertising* m_bleAdvertising;
|
||||
static esp_gatt_if_t getGattcIF();
|
||||
static std::map<uint16_t, conn_status_t> m_connectedClientsMap;
|
||||
|
||||
static void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* param);
|
||||
|
||||
static void gattServerEventHandler(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
|
||||
static void gapEventHandler(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
|
||||
public:
|
||||
/* custom gap and gatt handlers for flexibility */
|
||||
static gap_event_handler m_customGapHandler;
|
||||
static gattc_event_handler m_customGattcHandler;
|
||||
static gatts_event_handler m_customGattsHandler;
|
||||
|
||||
}; // class BLE
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDevice_H_ */
|
150
libraries/BLE/src/BLEEddystoneTLM.cpp
Normal file
150
libraries/BLE/src/BLEEddystoneTLM.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* BLEEddystoneTLM.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <esp_log.h>
|
||||
#include "BLEEddystoneTLM.h"
|
||||
|
||||
static const char LOG_TAG[] = "BLEEddystoneTLM";
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
|
||||
#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24))
|
||||
|
||||
BLEEddystoneTLM::BLEEddystoneTLM() {
|
||||
beaconUUID = 0xFEAA;
|
||||
m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE;
|
||||
m_eddystoneData.version = 0;
|
||||
m_eddystoneData.volt = 3300; // 3300mV = 3.3V
|
||||
m_eddystoneData.temp = (uint16_t) ((float) 23.00);
|
||||
m_eddystoneData.advCount = 0;
|
||||
m_eddystoneData.tmil = 0;
|
||||
} // BLEEddystoneTLM
|
||||
|
||||
std::string BLEEddystoneTLM::getData() {
|
||||
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
|
||||
} // getData
|
||||
|
||||
BLEUUID BLEEddystoneTLM::getUUID() {
|
||||
return BLEUUID(beaconUUID);
|
||||
} // getUUID
|
||||
|
||||
uint8_t BLEEddystoneTLM::getVersion() {
|
||||
return m_eddystoneData.version;
|
||||
} // getVersion
|
||||
|
||||
uint16_t BLEEddystoneTLM::getVolt() {
|
||||
return m_eddystoneData.volt;
|
||||
} // getVolt
|
||||
|
||||
float BLEEddystoneTLM::getTemp() {
|
||||
return (float)m_eddystoneData.temp;
|
||||
} // getTemp
|
||||
|
||||
uint32_t BLEEddystoneTLM::getCount() {
|
||||
return m_eddystoneData.advCount;
|
||||
} // getCount
|
||||
|
||||
uint32_t BLEEddystoneTLM::getTime() {
|
||||
return m_eddystoneData.tmil;
|
||||
} // getTime
|
||||
|
||||
std::string BLEEddystoneTLM::toString() {
|
||||
std::stringstream ss;
|
||||
std::string out = "";
|
||||
uint32_t rawsec;
|
||||
ss << "Version ";
|
||||
ss << std::dec << m_eddystoneData.version;
|
||||
ss << "\n";
|
||||
|
||||
ss << "Battery Voltage ";
|
||||
ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt);
|
||||
ss << " mV\n";
|
||||
|
||||
ss << "Temperature ";
|
||||
ss << (float) m_eddystoneData.temp;
|
||||
ss << " °C\n";
|
||||
|
||||
ss << "Adv. Count ";
|
||||
ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
|
||||
|
||||
ss << "\n";
|
||||
|
||||
ss << "Time ";
|
||||
|
||||
rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
|
||||
std::stringstream buffstream;
|
||||
buffstream << "0000";
|
||||
buffstream << std::dec << rawsec / 864000;
|
||||
std::string buff = buffstream.str();
|
||||
|
||||
ss << buff.substr(buff.length() - 4, buff.length());
|
||||
ss << ".";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 36000) % 24;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length()-2, buff.length());
|
||||
ss << ":";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 600) % 60;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length() - 2, buff.length());
|
||||
ss << ":";
|
||||
|
||||
buffstream.str("");
|
||||
buffstream.clear();
|
||||
buffstream << "00";
|
||||
buffstream << std::dec << (rawsec / 10) % 60;
|
||||
buff = buffstream.str();
|
||||
ss << buff.substr(buff.length() - 2, buff.length());
|
||||
ss << "\n";
|
||||
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEEddystoneTLM::setData(std::string data) {
|
||||
if (data.length() != sizeof(m_eddystoneData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData));
|
||||
return;
|
||||
}
|
||||
memcpy(&m_eddystoneData, data.data(), data.length());
|
||||
} // setData
|
||||
|
||||
void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) {
|
||||
beaconUUID = l_uuid.getNative()->uuid.uuid16;
|
||||
} // setUUID
|
||||
|
||||
void BLEEddystoneTLM::setVersion(uint8_t version) {
|
||||
m_eddystoneData.version = version;
|
||||
} // setVersion
|
||||
|
||||
void BLEEddystoneTLM::setVolt(uint16_t volt) {
|
||||
m_eddystoneData.volt = volt;
|
||||
} // setVolt
|
||||
|
||||
void BLEEddystoneTLM::setTemp(float temp) {
|
||||
m_eddystoneData.temp = (uint16_t)temp;
|
||||
} // setTemp
|
||||
|
||||
void BLEEddystoneTLM::setCount(uint32_t advCount) {
|
||||
m_eddystoneData.advCount = advCount;
|
||||
} // setCount
|
||||
|
||||
void BLEEddystoneTLM::setTime(uint32_t tmil) {
|
||||
m_eddystoneData.tmil = tmil;
|
||||
} // setTime
|
||||
|
||||
#endif
|
51
libraries/BLE/src/BLEEddystoneTLM.h
Normal file
51
libraries/BLE/src/BLEEddystoneTLM.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* BLEEddystoneTLM.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
|
||||
#ifndef _BLEEddystoneTLM_H_
|
||||
#define _BLEEddystoneTLM_H_
|
||||
#include "BLEUUID.h"
|
||||
|
||||
#define EDDYSTONE_TLM_FRAME_TYPE 0x20
|
||||
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://github.com/google/eddystone
|
||||
*/
|
||||
class BLEEddystoneTLM {
|
||||
public:
|
||||
BLEEddystoneTLM();
|
||||
std::string getData();
|
||||
BLEUUID getUUID();
|
||||
uint8_t getVersion();
|
||||
uint16_t getVolt();
|
||||
float getTemp();
|
||||
uint32_t getCount();
|
||||
uint32_t getTime();
|
||||
std::string toString();
|
||||
void setData(std::string data);
|
||||
void setUUID(BLEUUID l_uuid);
|
||||
void setVersion(uint8_t version);
|
||||
void setVolt(uint16_t volt);
|
||||
void setTemp(float temp);
|
||||
void setCount(uint32_t advCount);
|
||||
void setTime(uint32_t tmil);
|
||||
|
||||
private:
|
||||
uint16_t beaconUUID;
|
||||
struct {
|
||||
uint8_t frameType;
|
||||
uint8_t version;
|
||||
uint16_t volt;
|
||||
uint16_t temp;
|
||||
uint32_t advCount;
|
||||
uint32_t tmil;
|
||||
} __attribute__((packed)) m_eddystoneData;
|
||||
|
||||
}; // BLEEddystoneTLM
|
||||
|
||||
#endif /* _BLEEddystoneTLM_H_ */
|
148
libraries/BLE/src/BLEEddystoneURL.cpp
Normal file
148
libraries/BLE/src/BLEEddystoneURL.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* BLEEddystoneURL.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include "BLEEddystoneURL.h"
|
||||
|
||||
static const char LOG_TAG[] = "BLEEddystoneURL";
|
||||
|
||||
BLEEddystoneURL::BLEEddystoneURL() {
|
||||
beaconUUID = 0xFEAA;
|
||||
lengthURL = 0;
|
||||
m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE;
|
||||
m_eddystoneData.advertisedTxPower = 0;
|
||||
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
|
||||
} // BLEEddystoneURL
|
||||
|
||||
std::string BLEEddystoneURL::getData() {
|
||||
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
|
||||
} // getData
|
||||
|
||||
BLEUUID BLEEddystoneURL::getUUID() {
|
||||
return BLEUUID(beaconUUID);
|
||||
} // getUUID
|
||||
|
||||
int8_t BLEEddystoneURL::getPower() {
|
||||
return m_eddystoneData.advertisedTxPower;
|
||||
} // getPower
|
||||
|
||||
std::string BLEEddystoneURL::getURL() {
|
||||
return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url));
|
||||
} // getURL
|
||||
|
||||
std::string BLEEddystoneURL::getDecodedURL() {
|
||||
std::string decodedURL = "";
|
||||
|
||||
switch (m_eddystoneData.url[0]) {
|
||||
case 0x00:
|
||||
decodedURL += "http://www.";
|
||||
break;
|
||||
case 0x01:
|
||||
decodedURL += "https://www.";
|
||||
break;
|
||||
case 0x02:
|
||||
decodedURL += "http://";
|
||||
break;
|
||||
case 0x03:
|
||||
decodedURL += "https://";
|
||||
break;
|
||||
default:
|
||||
decodedURL += m_eddystoneData.url[0];
|
||||
}
|
||||
|
||||
for (int i = 1; i < lengthURL; i++) {
|
||||
if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) {
|
||||
decodedURL += m_eddystoneData.url[i];
|
||||
} else {
|
||||
switch (m_eddystoneData.url[i]) {
|
||||
case 0x00:
|
||||
decodedURL += ".com/";
|
||||
break;
|
||||
case 0x01:
|
||||
decodedURL += ".org/";
|
||||
break;
|
||||
case 0x02:
|
||||
decodedURL += ".edu/";
|
||||
break;
|
||||
case 0x03:
|
||||
decodedURL += ".net/";
|
||||
break;
|
||||
case 0x04:
|
||||
decodedURL += ".info/";
|
||||
break;
|
||||
case 0x05:
|
||||
decodedURL += ".biz/";
|
||||
break;
|
||||
case 0x06:
|
||||
decodedURL += ".gov/";
|
||||
break;
|
||||
case 0x07:
|
||||
decodedURL += ".com";
|
||||
break;
|
||||
case 0x08:
|
||||
decodedURL += ".org";
|
||||
break;
|
||||
case 0x09:
|
||||
decodedURL += ".edu";
|
||||
break;
|
||||
case 0x0A:
|
||||
decodedURL += ".net";
|
||||
break;
|
||||
case 0x0B:
|
||||
decodedURL += ".info";
|
||||
break;
|
||||
case 0x0C:
|
||||
decodedURL += ".biz";
|
||||
break;
|
||||
case 0x0D:
|
||||
decodedURL += ".gov";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return decodedURL;
|
||||
} // getDecodedURL
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the raw data for the beacon record.
|
||||
*/
|
||||
void BLEEddystoneURL::setData(std::string data) {
|
||||
if (data.length() > sizeof(m_eddystoneData)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData));
|
||||
return;
|
||||
}
|
||||
memset(&m_eddystoneData, 0, sizeof(m_eddystoneData));
|
||||
memcpy(&m_eddystoneData, data.data(), data.length());
|
||||
lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url));
|
||||
} // setData
|
||||
|
||||
void BLEEddystoneURL::setUUID(BLEUUID l_uuid) {
|
||||
beaconUUID = l_uuid.getNative()->uuid.uuid16;
|
||||
} // setUUID
|
||||
|
||||
void BLEEddystoneURL::setPower(int8_t advertisedTxPower) {
|
||||
m_eddystoneData.advertisedTxPower = advertisedTxPower;
|
||||
} // setPower
|
||||
|
||||
void BLEEddystoneURL::setURL(std::string url) {
|
||||
if (url.length() > sizeof(m_eddystoneData.url)) {
|
||||
ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url));
|
||||
return;
|
||||
}
|
||||
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
|
||||
memcpy(m_eddystoneData.url, url.data(), url.length());
|
||||
lengthURL = url.length();
|
||||
} // setURL
|
||||
|
||||
|
||||
#endif
|
43
libraries/BLE/src/BLEEddystoneURL.h
Normal file
43
libraries/BLE/src/BLEEddystoneURL.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* BLEEddystoneURL.cpp
|
||||
*
|
||||
* Created on: Mar 12, 2018
|
||||
* Author: pcbreflux
|
||||
*/
|
||||
|
||||
#ifndef _BLEEddystoneURL_H_
|
||||
#define _BLEEddystoneURL_H_
|
||||
#include "BLEUUID.h"
|
||||
|
||||
#define EDDYSTONE_URL_FRAME_TYPE 0x10
|
||||
|
||||
/**
|
||||
* @brief Representation of a beacon.
|
||||
* See:
|
||||
* * https://github.com/google/eddystone
|
||||
*/
|
||||
class BLEEddystoneURL {
|
||||
public:
|
||||
BLEEddystoneURL();
|
||||
std::string getData();
|
||||
BLEUUID getUUID();
|
||||
int8_t getPower();
|
||||
std::string getURL();
|
||||
std::string getDecodedURL();
|
||||
void setData(std::string data);
|
||||
void setUUID(BLEUUID l_uuid);
|
||||
void setPower(int8_t advertisedTxPower);
|
||||
void setURL(std::string url);
|
||||
|
||||
private:
|
||||
uint16_t beaconUUID;
|
||||
uint8_t lengthURL;
|
||||
struct {
|
||||
uint8_t frameType;
|
||||
int8_t advertisedTxPower;
|
||||
uint8_t url[16];
|
||||
} __attribute__((packed)) m_eddystoneData;
|
||||
|
||||
}; // BLEEddystoneURL
|
||||
|
||||
#endif /* _BLEEddystoneURL_H_ */
|
9
libraries/BLE/src/BLEExceptions.cpp
Normal file
9
libraries/BLE/src/BLEExceptions.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* BLExceptions.cpp
|
||||
*
|
||||
* Created on: Nov 27, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "BLEExceptions.h"
|
||||
|
31
libraries/BLE/src/BLEExceptions.h
Normal file
31
libraries/BLE/src/BLEExceptions.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* BLExceptions.h
|
||||
*
|
||||
* Created on: Nov 27, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_CXX_EXCEPTIONS != 1
|
||||
#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
|
||||
|
||||
class BLEDisconnectedException : public std::exception {
|
||||
const char* what() const throw () {
|
||||
return "BLE Disconnected";
|
||||
}
|
||||
};
|
||||
|
||||
class BLEUuidNotFoundException : public std::exception {
|
||||
const char* what() const throw () {
|
||||
return "No such UUID";
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user