diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..a1b16285
Binary files /dev/null and b/.DS_Store differ
diff --git a/.cproject b/.cproject
index 379a8d68..54db1eb5 100644
--- a/.cproject
+++ b/.cproject
@@ -26,15 +26,46 @@
+
+
+
diff --git a/.gitignore b/.gitignore
index 46b14a0a..15d2ae94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,10 @@
.project
.cproject
.settings/
+
+# IDEs
+.vs/
+.idea/
+
+# CMake
+cmake-build-debug/
diff --git a/Documentation/BLE C++ Guide.pdf b/Documentation/BLE C++ Guide.pdf
old mode 100644
new mode 100755
index e68e0398..cc4ca4bb
Binary files a/Documentation/BLE C++ Guide.pdf and b/Documentation/BLE C++ Guide.pdf differ
diff --git a/Documentation/ideas/FastFlash/README.md b/Documentation/ideas/FastFlash/README.md
new file mode 100644
index 00000000..d599b4aa
--- /dev/null
+++ b/Documentation/ideas/FastFlash/README.md
@@ -0,0 +1,8 @@
+# FastFlash
+As we build ESP32 applications, we typically perform a compile, flash, test loop cycle. We compile an app, we flash the ESP32 with that app and then we test whether it works. We perform these actions over and over again. When we look at the time taken in each step, we see there is compile time on our PC and then the time taken to flash the PC. This story talks about the time taken to flash the PC.
+
+The ESP32 is typically configured to flash at 115200 kbps. This is 115200 bits per second. If we think that a typical ESP32 application is 800KBytes then this requires a transmission of:
+
+800000 * 9 = 7.2 million bits = 62.5 seconds
+
+we can increase our baud rate up to 921600 = 7.8 seconds
\ No newline at end of file
diff --git a/README.md b/README.md
index c785b3c6..fbea27ae 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,2 @@
# ESP32 Snippets
-This repository hosts a set of ESP32 snippets that are in different
-stages of completeness. They are made available in the hope that there
-may be something of value to you and under the notion that something
-is better than nothing. The samples are being categorized.
-
-* vfs - Virtual File System
-* wifi - WiFi access
-* curl - Examples of curl usage
+This repository is no longer being actively maintained. It was previously archived to make it read-only but, by request, has been un-archived so that others may continue to post comments and issues. However, please understand that issues raised are unlikely to be resolved against this repository.
diff --git a/VisualStudioCode/README.md b/VisualStudioCode/README.md
new file mode 100644
index 00000000..ff5ab2f2
--- /dev/null
+++ b/VisualStudioCode/README.md
@@ -0,0 +1,4 @@
+These are file for Microsoft Visual Studio Code and can be copied into your `.vscode` project folder. For more information on this area, see:
+
+* [VSCode JTAG Debugging of ESP32 - Part 2](https://gojimmypi.blogspot.com/2017/05/vscode-remote-jtag-debugging-of-esp32.html)
+* [Deous/VSC-Guide-for-esp32](https://github.com/Deous/VSC-Guide-for-esp32)
\ No newline at end of file
diff --git a/VisualStudioCode/c_cpp_properties.json b/VisualStudioCode/c_cpp_properties.json
new file mode 100644
index 00000000..9ef83b14
--- /dev/null
+++ b/VisualStudioCode/c_cpp_properties.json
@@ -0,0 +1,266 @@
+{
+ "configurations": [
+ {
+ "name": "ESP32-Linux",
+ "includePath": [
+ "${workspaceRoot}",
+ "${workspaceRoot}/components",
+ "${workspaceRoot}/build",
+ "${workspaceRoot}/build/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/utils/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/smp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/sdp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/rfcomm/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/l2cap/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/gatt/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/gap/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avrc/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avdt/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avct/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/a2dp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/osi/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/hci/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/gki/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/encoder/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/decoder/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/device/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btcore/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/smp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/hid/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/dis/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/battery/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/a2dp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/blufi/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/bta/sys/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/bta/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/api/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/include",
+ "${env:IDF_PATH}/components/aws_iot/include",
+ "${env:IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include",
+ "${env:IDF_PATH}/components/app_trace/include",
+ "${env:IDF_PATH}/components/app_update/include",
+ "${env:IDF_PATH}/components/xtensa-debug-module/include",
+ "${env:IDF_PATH}/components/bootloader_support/include",
+ "${env:IDF_PATH}/components/bootloader_support/include_priv",
+ "${env:IDF_PATH}/components/bt/include",
+ "${env:IDF_PATH}/components/coap/port/include",
+ "${env:IDF_PATH}/components/coap/port/include/coap",
+ "${env:IDF_PATH}/components/coap/libcoap/include",
+ "${env:IDF_PATH}/components/coap/libcoap/include/coap",
+ "${env:IDF_PATH}/components/cxx/include",
+ "${env:IDF_PATH}/components/driver/include",
+ "${env:IDF_PATH}/components/driver/include/driver",
+ "${env:IDF_PATH}/components/esp32/include",
+ "${env:IDF_PATH}/components/ethernet/include",
+ "${env:IDF_PATH}/components/expat/include/expat",
+ "${env:IDF_PATH}/components/expat/port/include",
+ "${env:IDF_PATH}/components/fatfs/src",
+ "${env:IDF_PATH}/components/freertos/include",
+ "${env:IDF_PATH}/components/heap/include",
+ "${env:IDF_PATH}/components/jsmn/include",
+ "${env:IDF_PATH}/components/json/include",
+ "${env:IDF_PATH}/components/json/port/include",
+ "${env:IDF_PATH}/components/json/cJSON",
+ "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include",
+ "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include/sodium",
+ "${env:IDF_PATH}/components/log/include",
+ "${env:IDF_PATH}/components/lwip/include/lwip",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix",
+ "${env:IDF_PATH}/components/lwip/apps/ping",
+ "${env:IDF_PATH}/components/lwip/include/lwip/apps",
+ "${env:IDF_PATH}/components/lwip/include/lwip/apps/sntp",
+ "${env:IDF_PATH}/components/lwip/include/lwip/lwip",
+ "${env:IDF_PATH}/components/lwip/include/lwip/lwip/priv",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp/polarssl",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/arch",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/arpa",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/netif",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/netinet",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix/sys",
+ "${env:IDF_PATH}/components/mbedtls/port/include",
+ "${env:IDF_PATH}/components/mbedtls/mbedtls/include",
+ "${env:IDF_PATH}/components/mbedtls/port/include/mbedtls",
+ "${env:IDF_PATH}/components/mdns/include",
+ "${env:IDF_PATH}/components/micro-ecc/micro-ecc",
+ "${env:IDF_PATH}/components/newlib/include",
+ "${env:IDF_PATH}/components/newlib/include/sys",
+ "${env:IDF_PATH}/components/newlib/platform_include",
+ "${env:IDF_PATH}/components/nghttp/include",
+ "${env:IDF_PATH}/components/nghttp/port/include",
+ "${env:IDF_PATH}/components/nvs_flash/include",
+ "${env:IDF_PATH}/components/openssl/include",
+ "${env:IDF_PATH}/components/openssl/include/internal",
+ "${env:IDF_PATH}/components/openssl/include/platform",
+ "${env:IDF_PATH}/components/openssl/include/openssl",
+ "${env:IDF_PATH}/components/pthread/include",
+ "${env:IDF_PATH}/components/sdmmc/include",
+ "${env:IDF_PATH}/components/spi_flash/include",
+ "${env:IDF_PATH}/components/tcpip_adapter/include",
+ "${env:IDF_PATH}/components/soc/esp32/include",
+ "${env:IDF_PATH}/components/soc/include",
+ "${env:IDF_PATH}/components/soc/esp32/include/soc",
+ "${env:IDF_PATH}/components/spi_flash",
+ "${env:IDF_PATH}/components/spiffs/include",
+ "${env:IDF_PATH}/components/tcpip_adapter/include",
+ "${env:IDF_PATH}/components/heap/include",
+ "${env:IDF_PATH}/components/ulp/include",
+ "${env:IDF_PATH}/components/ulp/include/esp32",
+ "${env:IDF_PATH}/components/vfs/include",
+ "${env:IDF_PATH}/components/vfs/include/sys",
+ "${env:IDF_PATH}/components/wear_levelling/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/port/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/crypto",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/eap_peer",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/tls",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/utils",
+ "${env:IDF_PATH}/components/xtensa-debug-module/include",
+ "C:/Program Files/Espressif/ESP-IDF Tools/toolchain/lib/gcc/xtensa-esp32-elf/5.2.0/include"
+ ],
+ "intelliSenseMode": "clang-x64",
+ "browse": {
+ "path": [
+ "${workspaceRoot}",
+ "${workspaceRoot}/components",
+ "${workspaceRoot}/build",
+ "${workspaceRoot}/build/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/utils/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/smp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/sdp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/rfcomm/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/l2cap/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/gatt/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/gap/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avrc/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avdt/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/avct/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/a2dp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/stack/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/osi/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/hci/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/gki/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/encoder/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/external/sbc/decoder/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/device/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btcore/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/smp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/hid/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/dis/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/battery/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/a2dp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/std/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/blufi/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/profile/esp/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/btc/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/bta/sys/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/bta/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/api/include",
+ "${env:IDF_PATH}/components/bt/bluedroid/include",
+ "${env:IDF_PATH}/components/aws_iot/include",
+ "${env:IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include",
+ "${env:IDF_PATH}/components/app_trace/include",
+ "${env:IDF_PATH}/components/app_update/include",
+ "${env:IDF_PATH}/components/xtensa-debug-module/include",
+ "${env:IDF_PATH}/components/bootloader_support/include",
+ "${env:IDF_PATH}/components/bootloader_support/include_priv",
+ "${env:IDF_PATH}/components/bt/include",
+ "${env:IDF_PATH}/components/coap/port/include",
+ "${env:IDF_PATH}/components/coap/port/include/coap",
+ "${env:IDF_PATH}/components/coap/libcoap/include",
+ "${env:IDF_PATH}/components/coap/libcoap/include/coap",
+ "${env:IDF_PATH}/components/cxx/include",
+ "${env:IDF_PATH}/components/driver/include",
+ "${env:IDF_PATH}/components/driver/include/driver",
+ "${env:IDF_PATH}/components/esp32/include",
+ "${env:IDF_PATH}/components/ethernet/include",
+ "${env:IDF_PATH}/components/expat/include/expat",
+ "${env:IDF_PATH}/components/expat/port/include",
+ "${env:IDF_PATH}/components/fatfs/src",
+ "${env:IDF_PATH}/components/freertos/include",
+ "${env:IDF_PATH}/components/heap/include",
+ "${env:IDF_PATH}/components/jsmn/include",
+ "${env:IDF_PATH}/components/json/include",
+ "${env:IDF_PATH}/components/json/port/include",
+ "${env:IDF_PATH}/components/json/cJSON",
+ "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include",
+ "${env:IDF_PATH}/components/libsodium/libsodium/src/libsodium/include/sodium",
+ "${env:IDF_PATH}/components/log/include",
+ "${env:IDF_PATH}/components/lwip/include/lwip",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix",
+ "${env:IDF_PATH}/components/lwip/apps/ping",
+ "${env:IDF_PATH}/components/lwip/include/lwip/apps",
+ "${env:IDF_PATH}/components/lwip/include/lwip/apps/sntp",
+ "${env:IDF_PATH}/components/lwip/include/lwip/lwip",
+ "${env:IDF_PATH}/components/lwip/include/lwip/lwip/priv",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp",
+ "${env:IDF_PATH}/components/lwip/include/lwip/netif/ppp/polarssl",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/arch",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/arpa",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/netif",
+ "${env:IDF_PATH}/components/lwip/include/lwip/port/netinet",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix",
+ "${env:IDF_PATH}/components/lwip/include/lwip/posix/sys",
+ "${env:IDF_PATH}/components/mbedtls/port/include",
+ "${env:IDF_PATH}/components/mbedtls/include",
+ "${env:IDF_PATH}/components/mbedtls/port/include/mbedtls",
+ "${env:IDF_PATH}/components/mdns/include",
+ "${env:IDF_PATH}/components/micro-ecc/micro-ecc",
+ "${env:IDF_PATH}/components/newlib/include",
+ "${env:IDF_PATH}/components/newlib/include/sys",
+ "${env:IDF_PATH}/components/newlib/platform_include",
+ "${env:IDF_PATH}/components/nghttp/include",
+ "${env:IDF_PATH}/components/nghttp/port/include",
+ "${env:IDF_PATH}/components/nvs_flash/include",
+ "${env:IDF_PATH}/components/openssl/include",
+ "${env:IDF_PATH}/components/openssl/include/internal",
+ "${env:IDF_PATH}/components/openssl/include/platform",
+ "${env:IDF_PATH}/components/openssl/include/openssl",
+ "${env:IDF_PATH}/components/pthread/include",
+ "${env:IDF_PATH}/components/sdmmc/include",
+ "${env:IDF_PATH}/components/spi_flash/include",
+ "${env:IDF_PATH}/components/tcpip_adapter/include",
+ "${env:IDF_PATH}/components/soc/esp32/include",
+ "${env:IDF_PATH}/components/soc/include",
+ "${env:IDF_PATH}/components/soc/esp32/include/soc",
+ "${env:IDF_PATH}/components/spi_flash",
+ "${env:IDF_PATH}/components/spiffs/include",
+ "${env:IDF_PATH}/components/tcpip_adapter/include",
+ "${env:IDF_PATH}/components/heap/include",
+ "${env:IDF_PATH}/components/ulp/include",
+ "${env:IDF_PATH}/components/ulp/include/esp32",
+ "${env:IDF_PATH}/components/vfs/include",
+ "${env:IDF_PATH}/components/vfs/include/sys",
+ "${env:IDF_PATH}/components/wear_levelling/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/port/include",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/crypto",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/eap_peer",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/tls",
+ "${env:IDF_PATH}/components/wpa_supplicant/include/wpa2/utils",
+ "${env:IDF_PATH}/components/xtensa-debug-module/include",
+ "C:/Program Files/Espressif/ESP-IDF Tools/toolchain/lib/gcc/xtensa-esp32-elf/5.2.0/include"
+
+ ],
+ "limitSymbolsToIncludedHeaders": true,
+ "databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db"
+ },
+ "cStandard": "c11",
+ "cppStandard": "c++17"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/VisualStudioCode/launch.json b/VisualStudioCode/launch.json
new file mode 100644
index 00000000..257de4a0
--- /dev/null
+++ b/VisualStudioCode/launch.json
@@ -0,0 +1,43 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "name": "ESP32 OpenOCD launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "./build/app-template.elf",
+ "args": [],
+ "stopAtEntry": true,
+ "cwd": "${workspaceFolder}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "miDebuggerPath": "/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ },
+ {
+ "text": "target remote localhost:3333"
+ },
+ {
+ "text": "monitor reset halt"
+ },
+ {
+ "text": "flushregs"
+ },
+ {
+ "text": "thb app_main"
+ }
+ ],
+ "logging": {
+ "trace": true,
+ "traceResponse": true,
+ "engineLogging": true
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/VisualStudioCode/settings.json b/VisualStudioCode/settings.json
new file mode 100644
index 00000000..7a73a41b
--- /dev/null
+++ b/VisualStudioCode/settings.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/VisualStudioCode/tasks.json b/VisualStudioCode/tasks.json
new file mode 100644
index 00000000..d989b0f2
--- /dev/null
+++ b/VisualStudioCode/tasks.json
@@ -0,0 +1,41 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "menuconfig",
+ "type": "shell",
+ "command": "make menuconfig",
+ "problemMatcher": []
+ },
+ {
+ "label": "make",
+ "type": "shell",
+ "command": "make -j5",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "problemMatcher": []
+ },
+ {
+ "label": "flash",
+ "type": "shell",
+ "command": "make flash monitor",
+ "problemMatcher": []
+ },
+ {
+ "label": "monitor",
+ "type": "shell",
+ "command": "make monitor",
+ "problemMatcher": []
+ },
+ {
+ "label": "clean",
+ "type": "shell",
+ "command": "make clean",
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloud/GCP/JWT/base64url.cpp b/cloud/GCP/JWT/base64url.cpp
new file mode 100644
index 00000000..94221181
--- /dev/null
+++ b/cloud/GCP/JWT/base64url.cpp
@@ -0,0 +1,136 @@
+// https://raw.githubusercontent.com/zhicheng/base64/master/base64.c
+/* This is a public domain base64 implementation written by WEI Zhicheng. */
+
+#include
+
+#include "base64url.h"
+
+/* BASE 64 encode table */
+static const char base64en[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '-', '_',
+};
+
+#define BASE64_PAD '='
+
+
+#define BASE64DE_FIRST '+'
+#define BASE64DE_LAST 'z'
+/* ASCII order for BASE 64 decode, -1 in unused character */
+static const signed char base64de[] = {
+ /* '+', ',', '-', '.', '/', '0', '1', '2', */
+ 62, -1, -1, -1, 63, 52, 53, 54,
+
+ /* '3', '4', '5', '6', '7', '8', '9', ':', */
+ 55, 56, 57, 58, 59, 60, 61, -1,
+
+ /* ';', '<', '=', '>', '?', '@', 'A', 'B', */
+ -1, -1, -1, -1, -1, -1, 0, 1,
+
+ /* 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', */
+ 2, 3, 4, 5, 6, 7, 8, 9,
+
+ /* 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', */
+ 10, 11, 12, 13, 14, 15, 16, 17,
+
+ /* 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', */
+ 18, 19, 20, 21, 22, 23, 24, 25,
+
+ /* '[', '\', ']', '^', '_', '`', 'a', 'b', */
+ -1, -1, -1, -1, -1, -1, 26, 27,
+
+ /* 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', */
+ 28, 29, 30, 31, 32, 33, 34, 35,
+
+ /* 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', */
+ 36, 37, 38, 39, 40, 41, 42, 43,
+
+ /* 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', */
+ 44, 45, 46, 47, 48, 49, 50, 51,
+};
+
+int base64url_encode(const unsigned char *in, unsigned int inlen, char *out)
+{
+ unsigned int i, j;
+
+ for (i = j = 0; i < inlen; i++) {
+ int s = i % 3; /* from 6/gcd(6, 8) */
+
+ switch (s) {
+ case 0:
+ out[j++] = base64en[(in[i] >> 2) & 0x3F];
+ continue;
+ case 1:
+ out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)];
+ continue;
+ case 2:
+ out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)];
+ out[j++] = base64en[in[i] & 0x3F];
+ }
+ }
+
+ /* move back */
+ i -= 1;
+
+ /* check the last and add padding */
+
+ if ((i % 3) == 0) {
+ out[j++] = base64en[(in[i] & 0x3) << 4];
+ //out[j++] = BASE64_PAD;
+ //out[j++] = BASE64_PAD;
+ } else if ((i % 3) == 1) {
+ out[j++] = base64en[(in[i] & 0xF) << 2];
+ //out[j++] = BASE64_PAD;
+ }
+
+ out[j++] = 0;
+
+ return BASE64_OK;
+}
+
+int base64url_decode(const char *in, unsigned int inlen, unsigned char *out)
+{
+ unsigned int i, j;
+
+ for (i = j = 0; i < inlen; i++) {
+ int c;
+ int s = i % 4; /* from 8/gcd(6, 8) */
+
+ if (in[i] == '=')
+ return BASE64_OK;
+
+ if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST ||
+ (c = base64de[in[i] - BASE64DE_FIRST]) == -1)
+ return BASE64_INVALID;
+
+ switch (s) {
+ case 0:
+ out[j] = ((unsigned int)c << 2) & 0xFF;
+ continue;
+ case 1:
+ out[j++] += ((unsigned int)c >> 4) & 0x3;
+
+ /* if not last char with padding */
+ if (i < (inlen - 3) || in[inlen - 2] != '=')
+ out[j] = ((unsigned int)c & 0xF) << 4;
+ continue;
+ case 2:
+ out[j++] += ((unsigned int)c >> 2) & 0xF;
+
+ /* if not last char with padding */
+ if (i < (inlen - 2) || in[inlen - 1] != '=')
+ out[j] = ((unsigned int)c & 0x3) << 6;
+ continue;
+ case 3:
+ out[j++] += (unsigned char)c;
+ }
+ }
+
+ return BASE64_OK;
+}
diff --git a/cloud/GCP/JWT/base64url.h b/cloud/GCP/JWT/base64url.h
new file mode 100644
index 00000000..7a8c80ca
--- /dev/null
+++ b/cloud/GCP/JWT/base64url.h
@@ -0,0 +1,15 @@
+// https://raw.githubusercontent.com/zhicheng/base64/master/base64.h
+#ifndef __BASE64URL_H__
+#define __BASE64URL_H__
+
+enum {BASE64_OK = 0, BASE64_INVALID};
+
+#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4)
+#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3)
+
+int base64url_encode(const unsigned char *in, unsigned int inlen, char *out);
+
+int base64url_decode(const char *in, unsigned int inlen, unsigned char *out);
+
+
+#endif /* __BASE64URL_H__ */
diff --git a/cloud/GCP/JWT/main.cpp b/cloud/GCP/JWT/main.cpp
new file mode 100644
index 00000000..fcf8ec8b
--- /dev/null
+++ b/cloud/GCP/JWT/main.cpp
@@ -0,0 +1,189 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "passwords.h"
+#include "base64url.h"
+
+// This is an "xxd" file of the PEM of the private key.
+#include "device1_private_pem.h"
+
+
+extern "C" {
+ void app_main();
+}
+
+/**
+ * Return a string representation of an mbedtls error code
+ */
+static char* mbedtlsError(int errnum) {
+ static char buffer[200];
+ mbedtls_strerror(errnum, buffer, sizeof(buffer));
+ return buffer;
+} // mbedtlsError
+
+
+/**
+ * Create a JWT token for GCP.
+ * For full details, perform a Google search on JWT. However, in summary, we build two strings. One that represents the
+ * header and one that represents the payload. Both are JSON and are as described in the GCP and JWT documentation. Next
+ * we base64url encode both strings. Note that is distinct from normal/simple base64 encoding. Once we have a string for
+ * the base64url encoding of both header and payload, we concatenate both strings together separated by a ".". This resulting
+ * string is then signed using RSASSA which basically produces an SHA256 message digest that is then signed. The resulting
+ * binary is then itself converted into base64url and concatenated with the previously built base64url combined header and
+ * payload and that is our resulting JWT token.
+ * @param projectId The GCP project.
+ * @param privateKey The PEM or DER of the private key.
+ * @param privateKeySize The size in bytes of the private key.
+ * @returns A JWT token for transmission to GCP.
+ */
+char* createGCPJWT(const char* projectId, uint8_t* privateKey, size_t privateKeySize) {
+ char base64Header[100];
+ const char header[] = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
+ base64url_encode(
+ (unsigned char *)header, // Data to encode.
+ strlen(header), // Length of data to encode.
+ base64Header); // Base64 encoded data.
+
+ time_t now;
+ time(&now);
+ uint32_t iat = now; // Set the time now.
+ uint32_t exp = iat + 60*60; // Set the expiry time.
+
+ char payload[100];
+ sprintf(payload, "{\"iat\":%d,\"exp\":%d,\"aud\":\"%s\"}", iat, exp, projectId);
+
+ char base64Payload[100];
+ base64url_encode(
+ (unsigned char *)payload, // Data to encode.
+ strlen(payload), // Length of data to encode.
+ base64Payload); // Base64 encoded data.
+
+ uint8_t headerAndPayload[800];
+ sprintf((char*)headerAndPayload, "%s.%s", base64Header, base64Payload);
+
+ // At this point we have created the header and payload parts, converted both to base64 and concatenated them
+ // together as a single string. Now we need to sign them using RSASSA
+
+ mbedtls_pk_context pk_context;
+ mbedtls_pk_init(&pk_context);
+ int rc = mbedtls_pk_parse_key(&pk_context, privateKey, privateKeySize, NULL, 0);
+ if (rc != 0) {
+ printf("Failed to mbedtls_pk_parse_key: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
+ return nullptr;
+ }
+
+ uint8_t oBuf[5000];
+
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+
+ const char* pers="MyEntropy";
+
+ mbedtls_ctr_drbg_seed(
+ &ctr_drbg,
+ mbedtls_entropy_func,
+ &entropy,
+ (const unsigned char*)pers,
+ strlen(pers));
+
+
+ uint8_t digest[32];
+ rc = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), headerAndPayload, strlen((char*)headerAndPayload), digest);
+ if (rc != 0) {
+ printf("Failed to mbedtls_md: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
+ return nullptr;
+ }
+
+ size_t retSize;
+ rc = mbedtls_pk_sign(&pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), oBuf, &retSize, mbedtls_ctr_drbg_random, &ctr_drbg);
+ if (rc != 0) {
+ printf("Failed to mbedtls_pk_sign: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
+ return nullptr;
+ }
+
+
+ char base64Signature[600];
+ base64url_encode((unsigned char *)oBuf, retSize, base64Signature);
+
+ char* retData = (char*)malloc(strlen((char*)headerAndPayload) + 1 + strlen((char*)base64Signature) + 1);
+
+ sprintf(retData, "%s.%s", headerAndPayload, base64Signature);
+
+ mbedtls_pk_free(&pk_context);
+ return retData;
+}
+
+void run(void *) {
+ printf("Task starting!\n");
+ const char* projectId = "test-214415";
+ sntp_setoperatingmode(SNTP_OPMODE_POLL);
+ sntp_setservername(0, "time-a-g.nist.gov");
+ sntp_init();
+ // https://www.epochconverter.com/
+ time_t now = 0;
+ time(&now);
+ while(now < 5000) {
+ vTaskDelay(1000 * portTICK_RATE_MS);
+ time(&now);
+ }
+
+ char* jwt = createGCPJWT(projectId, device1_private_pem, device1_private_pem_len);
+ if (jwt != nullptr) {
+ printf("JWT: %s\n", jwt);
+ free(jwt);
+ }
+ vTaskDelete(nullptr);
+}
+
+esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+ if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) {
+ printf("Our IP address is " IPSTR "\n",
+ IP2STR(&event->event_info.got_ip.ip_info.ip));
+ printf("We have now connected to a station and can do things...\n");
+ xTaskCreate(run, "run", 16000, nullptr, 0, nullptr);
+
+ }
+
+ if (event->event_id == SYSTEM_EVENT_STA_START) {
+ ESP_ERROR_CHECK(esp_wifi_connect());
+ }
+ return ESP_OK;
+}
+
+void app_main(void)
+{
+ printf("Starting\n");
+ nvs_flash_init();
+ tcpip_adapter_init();
+ ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+ ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
+ wifi_config_t sta_config;
+ memset(&sta_config, 0, sizeof(sta_config));
+ strcpy((char*)sta_config.sta.ssid, SSID);
+ strcpy((char*)sta_config.sta.password, SSID_PASSWORD);
+ ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config));
+ ESP_ERROR_CHECK(esp_wifi_start());
+}
+
diff --git a/console/main.cpp b/console/main.cpp
new file mode 100644
index 00000000..f5715f5d
--- /dev/null
+++ b/console/main.cpp
@@ -0,0 +1,14 @@
+extern "C" {
+ void app_main(void);
+}
+
+extern void test_linenoise();
+extern void test_argtable();
+extern void test_console();
+
+void app_main(void)
+{
+ //test_linenoise();
+ //test_argtable();
+ test_console();
+}
diff --git a/console/test_argtable.cpp b/console/test_argtable.cpp
new file mode 100644
index 00000000..160b728c
--- /dev/null
+++ b/console/test_argtable.cpp
@@ -0,0 +1,69 @@
+/**
+ * Test of argtable
+ *
+ * @author Neil Kolban
+ * @date 2018-05-26
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "sdkconfig.h"
+
+void test_argtable(void)
+{
+ // Boiler plate setup for using linenoise
+ setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stdout, NULL, _IONBF, 0);
+ esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+ esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+ uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0);
+ esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+
+
+ linenoiseClearScreen();
+ linenoiseSetMultiLine(0);
+
+ struct arg_lit* help = arg_lit0("h", "help", "Generate some help!");
+ struct arg_lit* test = arg_lit1("t", "test", "Generate some help!");
+ struct arg_end* end = arg_end(20);
+
+ void *argtable[] = {
+ help,
+ test,
+ end
+ };
+
+ char *argv[10];
+
+ while(1) {
+ char* line = linenoise("Enter command > ");
+ if (line != NULL) {
+ printf("Got: %s\n", line);
+ size_t argc = esp_console_split_argv(line, argv, 10);
+ printf("Parsed to %d argc count\n", argc);
+ for (int i=0; i0) {
+ printf("Number of errors: %d\n", numErrors);
+ arg_print_errors(stdout, end, "myprog");
+ arg_print_syntaxv(stdout, argtable, "Here:");
+ }
+ else {
+ if (help->count > 0) {
+ printf("Found help!\n");
+ }
+ }
+ linenoiseHistoryAdd(line);
+ linenoiseFree(line);
+ } // Line is not null
+ printf("--------------\n");
+ } // End while loop
+}
diff --git a/console/test_console.cpp b/console/test_console.cpp
new file mode 100644
index 00000000..20c240ad
--- /dev/null
+++ b/console/test_console.cpp
@@ -0,0 +1,95 @@
+/**
+ * Test of console
+ *
+ * @author Neil Kolban
+ * @date 2018-05-26
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "sdkconfig.h"
+
+
+struct arg_lit* show_arg_show = arg_lit0("s", "show", "generate show");
+struct arg_end* show_arg_end = arg_end(10);
+void *show_argtable[] = {
+ show_arg_show,
+ show_arg_end
+};
+
+int runShow(int argc, char *argv[]) {
+ printf("Found show!\n");
+ return 0;
+}
+
+struct arg_str* greet_arg_name = arg_str1("n", "name", "", "generate help");
+struct arg_end* greet_arg_end = arg_end(10);
+void *greet_argtable[] = {
+ greet_arg_name,
+ greet_arg_end
+};
+
+int runGreet(int argc, char *argv[]) {
+ printf("Found Greet!\n");
+ int numErrors = arg_parse(argc, argv, greet_argtable);
+ if (numErrors > 0) {
+ arg_print_errors(stdout, greet_arg_end, "greet");
+ } else {
+ printf("Hello %s\n", greet_arg_name->sval[0]);
+ }
+ return 0;
+}
+
+void test_console(void)
+{
+ // Boiler plate setup for using linenoise
+ setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stdout, NULL, _IONBF, 0);
+ esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+ esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+ uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0);
+ esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+
+ esp_console_config_t consoleConfig;
+ consoleConfig.max_cmdline_args = 5;
+ consoleConfig.max_cmdline_length = 100;
+
+ esp_console_init(&consoleConfig);
+ esp_console_register_help_command();
+
+ esp_console_cmd_t consoleCmd;
+ consoleCmd.command = "show";
+ consoleCmd.func = runShow;
+ consoleCmd.help = "Show something";
+ consoleCmd.argtable = show_argtable;
+ esp_console_cmd_register(&consoleCmd);
+
+ consoleCmd.command = "greet";
+ consoleCmd.func = runGreet;
+ consoleCmd.help = "Greet someone";
+ consoleCmd.argtable = greet_argtable;
+ esp_console_cmd_register(&consoleCmd);
+
+
+ linenoiseClearScreen();
+ linenoiseSetMultiLine(0);
+
+
+ while(1) {
+ char* line = linenoise("Enter command > ");
+ if (line != NULL) {
+ printf("Got: %s\n", line);
+ int ret;
+ esp_console_run(line, &ret);
+ linenoiseHistoryAdd(line);
+ linenoiseFree(line);
+ } // Line is not null
+ printf("--------------\n");
+ } // End while loop
+}
diff --git a/console/test_linenoise.cpp b/console/test_linenoise.cpp
new file mode 100644
index 00000000..b2d36cf4
--- /dev/null
+++ b/console/test_linenoise.cpp
@@ -0,0 +1,40 @@
+/**
+ * Test of linenoise
+ *
+ * @author Neil Kolban
+ * @date 2018-05-26
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "sdkconfig.h"
+
+void test_linenoise(void)
+{
+ // Boiler plate setup for using linenoise
+ setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stdout, NULL, _IONBF, 0);
+ esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+ esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+ uart_driver_install((uart_port_t)CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0);
+ esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+
+
+ linenoiseClearScreen();
+ linenoiseSetMultiLine(0);
+
+ while(1) {
+ char* line = linenoise("Enter command > ");
+ if (line != NULL) {
+ printf("Got: %s\n", line);
+ linenoiseHistoryAdd(line);
+ linenoiseFree(line);
+ } // Line is not null
+ printf("--------------\n");
+ } // End while loop
+}
diff --git a/cpp_utils/AWS.cpp b/cpp_utils/AWS.cpp
index 561162c3..7687be21 100644
--- a/cpp_utils/AWS.cpp
+++ b/cpp_utils/AWS.cpp
@@ -19,6 +19,129 @@ AWS::AWS() {
AWS::~AWS() {
}
+/**
+ * Convert an AWS IoT error code to a string representation.
+ * @param err The error code to be mapped.
+ * @return A string representation of the error code.
+ */
+/* static */ std::string AWS::errorToString(IoT_Error_t err) {
+ switch(err) {
+ case NETWORK_PHYSICAL_LAYER_CONNECTED :
+ return "NETWORK_PHYSICAL_LAYER_CONNECTED";
+ case NETWORK_MANUALLY_DISCONNECTED :
+ return "NETWORK_MANUALLY_DISCONNECTED";
+ case NETWORK_ATTEMPTING_RECONNECT:
+ return "NETWORK_ATTEMPTING_RECONNECT";
+ case NETWORK_RECONNECTED:
+ return "NETWORK_RECONNECTED";
+ case MQTT_NOTHING_TO_READ :
+ return "MQTT_NOTHING_TO_READ";
+ case MQTT_CONNACK_CONNECTION_ACCEPTED:
+ return "MQTT_CONNACK_CONNECTION_ACCEPTED";
+ case SUCCESS :
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ case NULL_VALUE_ERROR :
+ return "NULL_VALUE_ERROR";
+ case TCP_CONNECTION_ERROR :
+ return "TCP_CONNECTION_ERROR";
+ case SSL_CONNECTION_ERROR:
+ return "SSL_CONNECTION_ERROR";
+ case TCP_SETUP_ERROR :
+ return "TCP_SETUP_ERROR";
+ case NETWORK_SSL_CONNECT_TIMEOUT_ERROR :
+ return "NETWORK_SSL_CONNECT_TIMEOUT_ERROR";
+ case NETWORK_SSL_WRITE_ERROR :
+ return "NETWORK_SSL_WRITE_ERROR";
+ case NETWORK_SSL_INIT_ERROR :
+ return "NETWORK_SSL_INIT_ERROR";
+ case NETWORK_SSL_CERT_ERROR :
+ return "NETWORK_SSL_CERT_ERROR";
+ case NETWORK_SSL_WRITE_TIMEOUT_ERROR :
+ return "NETWORK_SSL_WRITE_TIMEOUT_ERROR";
+ case NETWORK_SSL_READ_TIMEOUT_ERROR :
+ return "NETWORK_SSL_READ_TIMEOUT_ERROR";
+ case NETWORK_SSL_READ_ERROR :
+ return "NETWORK_SSL_READ_ERROR";
+ case NETWORK_DISCONNECTED_ERROR :
+ return "NETWORK_DISCONNECTED_ERROR";
+ case NETWORK_RECONNECT_TIMED_OUT_ERROR:
+ return "NETWORK_RECONNECT_TIMED_OUT_ERROR";
+ case NETWORK_ALREADY_CONNECTED_ERROR :
+ return "NETWORK_ALREADY_CONNECTED_ERROR";
+ case NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED :
+ return "NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED";
+ case NETWORK_SSL_UNKNOWN_ERROR :
+ return "NETWORK_SSL_UNKNOWN_ERROR";
+ case NETWORK_PHYSICAL_LAYER_DISCONNECTED :
+ return "NETWORK_PHYSICAL_LAYER_DISCONNECTED";
+ case NETWORK_X509_ROOT_CRT_PARSE_ERROR :
+ return "NETWORK_X509_ROOT_CRT_PARSE_ERROR";
+ case NETWORK_X509_DEVICE_CRT_PARSE_ERROR :
+ return "NETWORK_X509_DEVICE_CRT_PARSE_ERROR";
+ case NETWORK_PK_PRIVATE_KEY_PARSE_ERROR :
+ return "NETWORK_PK_PRIVATE_KEY_PARSE_ERROR";
+ case NETWORK_ERR_NET_SOCKET_FAILED :
+ return "NETWORK_ERR_NET_SOCKET_FAILED";
+ case NETWORK_ERR_NET_UNKNOWN_HOST :
+ return "NETWORK_ERR_NET_UNKNOWN_HOST";
+ case NETWORK_ERR_NET_CONNECT_FAILED :
+ return "NETWORK_ERR_NET_CONNECT_FAILED";
+ case NETWORK_SSL_NOTHING_TO_READ :
+ return "NETWORK_SSL_NOTHING_TO_READ";
+ case MQTT_CONNECTION_ERROR :
+ return "MQTT_CONNECTION_ERROR";
+ case MQTT_CONNECT_TIMEOUT_ERROR :
+ return "MQTT_CONNECT_TIMEOUT_ERROR";
+ case MQTT_REQUEST_TIMEOUT_ERROR:
+ return "MQTT_REQUEST_TIMEOUT_ERROR";
+ case MQTT_UNEXPECTED_CLIENT_STATE_ERROR :
+ return "MQTT_UNEXPECTED_CLIENT_STATE_ERROR";
+ case MQTT_CLIENT_NOT_IDLE_ERROR :
+ return "MQTT_CLIENT_NOT_IDLE_ERROR";
+ case MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR :
+ return "MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR";
+ case MQTT_RX_BUFFER_TOO_SHORT_ERROR :
+ return "MQTT_RX_BUFFER_TOO_SHORT_ERROR";
+ case MQTT_TX_BUFFER_TOO_SHORT_ERROR :
+ return "MQTT_TX_BUFFER_TOO_SHORT_ERROR";
+ case MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR :
+ return "MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR";
+ case MQTT_DECODE_REMAINING_LENGTH_ERROR :
+ return "MQTT_DECODE_REMAINING_LENGTH_ERROR";
+ case MQTT_CONNACK_UNKNOWN_ERROR :
+ return "MQTT_CONNACK_UNKNOWN_ERROR";
+ case MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR :
+ return "MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR";
+ case MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR:
+ return "MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR";
+ case MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR :
+ return "MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR";
+ case MQTT_CONNACK_BAD_USERDATA_ERROR:
+ return "MQTT_CONNACK_BAD_USERDATA_ERROR";
+ case MQTT_CONNACK_NOT_AUTHORIZED_ERROR :
+ return "MQTT_CONNACK_NOT_AUTHORIZED_ERROR";
+ case JSON_PARSE_ERROR :
+ return "JSON_PARSE_ERROR";
+ case SHADOW_WAIT_FOR_PUBLISH :
+ return "SHADOW_WAIT_FOR_PUBLISH";
+ case SHADOW_JSON_BUFFER_TRUNCATED :
+ return "SHADOW_JSON_BUFFER_TRUNCATED";
+ case SHADOW_JSON_ERROR :
+ return "SHADOW_JSON_ERROR";
+ case MUTEX_INIT_ERROR :
+ return "MUTEX_INIT_ERROR";
+ case MUTEX_LOCK_ERROR:
+ return "MUTEX_LOCK_ERROR";
+ case MUTEX_UNLOCK_ERROR :
+ return "MUTEX_UNLOCK_ERROR";
+ case MUTEX_DESTROY_ERROR :
+ return "MUTEX_DESTROY_ERROR";
+ default:
+ return "Unknown error!";
+ }
+} // AWS#errorToString
/**
* @brief Connect to the AWS IoT service.
diff --git a/cpp_utils/AWS.h b/cpp_utils/AWS.h
index 5d2c891c..abbd0613 100644
--- a/cpp_utils/AWS.h
+++ b/cpp_utils/AWS.h
@@ -24,6 +24,7 @@ class AWS {
void connect(std::string clientId);
void disconnect();
+ static std::string errorToString(IoT_Error_t err); // Convert an AWS IoT error code to a string representation.
void init(std::string host=CONFIG_AWS_IOT_MQTT_HOST, uint16_t port=CONFIG_AWS_IOT_MQTT_PORT);
void publish(std::string topic, std::string payload, QoS qos = QOS0);
void subscribe(std::string topic);
diff --git a/cpp_utils/Apa102.cpp b/cpp_utils/Apa102.cpp
index 274ec83f..c35c9150 100644
--- a/cpp_utils/Apa102.cpp
+++ b/cpp_utils/Apa102.cpp
@@ -37,7 +37,7 @@ void Apa102::show() {
double brigthnessScale = getBrightness() / 100.0;
// Loop over all the pixels in the pixels array to set the colors.
- for (int i=0; i
#include
#include "BLEAdvertisedDevice.h"
#include "BLEUtils.h"
-#ifdef ARDUINO_ARCH_ESP32
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
-#endif
+#define LOG_TAG ""
+#else
+#include "esp_log.h"
static const char* LOG_TAG="BLEAdvertisedDevice";
+#endif
BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_adFlag = 0;
@@ -29,6 +31,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
+ m_serviceData = "";
m_txPower = 0;
m_pScan = nullptr;
@@ -36,6 +39,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
+ m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
@@ -65,7 +69,7 @@ BLEAddress BLEAdvertisedDevice::getAddress() {
*/
uint16_t BLEAdvertisedDevice::getAppearance() {
return m_appearance;
-}
+} // getAppearance
/**
@@ -74,7 +78,7 @@ uint16_t BLEAdvertisedDevice::getAppearance() {
*/
std::string BLEAdvertisedDevice::getManufacturerData() {
return m_manufacturerData;
-}
+} // getManufacturerData
/**
@@ -104,6 +108,24 @@ BLEScan* BLEAdvertisedDevice::getScan() {
} // 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.
@@ -117,9 +139,8 @@ BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is
* @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;
+ for (int i = 0; i < m_serviceUUIDs.size(); i++) {
+ if (m_serviceUUIDs[i].equals(uuid)) return true;
}
return false;
}
@@ -132,6 +153,8 @@ 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.
@@ -168,6 +191,15 @@ bool BLEAdvertisedDevice::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.
@@ -198,15 +230,17 @@ bool BLEAdvertisedDevice::haveTXPower() {
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
-void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
+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
+ 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.
@@ -219,15 +253,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
free(pHex);
-
-
switch(ad_type) {
- case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
+ case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
setName(std::string(reinterpret_cast(payload), length));
break;
} // ESP_BLE_AD_TYPE_NAME_CMPL
- case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
+ case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
setTXPower(*payload);
break;
} // ESP_BLE_AD_TYPE_TX_PWR
@@ -237,23 +269,23 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
break;
} // ESP_BLE_AD_TYPE_APPEARANCE
- case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
+ 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
+ case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
for (int var = 0; var < length/2; ++var) {
- setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*2)));
+ setServiceUUID(BLEUUID(*reinterpret_cast(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
+ case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
for (int var = 0; var < length/4; ++var) {
- setServiceUUID(BLEUUID(*reinterpret_cast(payload+var*4)));
+ setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 4)));
}
break;
} // ESP_BLE_AD_TYPE_32SRV_PART
@@ -274,6 +306,45 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
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(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(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(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;
@@ -283,9 +354,9 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
} // Length <> 0
- if (sizeConsumed >=31 || length == 0) {
+ if (sizeConsumed >= total_len)
finished = true;
- }
+
} // !finished
} // parseAdvertisement
@@ -326,7 +397,7 @@ void BLEAdvertisedDevice::setAppearance(uint16_t appearance) {
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());
+ 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
@@ -383,6 +454,26 @@ void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
} // 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.
@@ -418,5 +509,24 @@ std::string BLEAdvertisedDevice::toString() {
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;
+}
+
+void BLEAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice dev) {}
+void BLEAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice* dev) {}
+
#endif /* CONFIG_BT_ENABLED */
diff --git a/cpp_utils/BLEAdvertisedDevice.h b/cpp_utils/BLEAdvertisedDevice.h
index aea6da08..cd206d4a 100644
--- a/cpp_utils/BLEAdvertisedDevice.h
+++ b/cpp_utils/BLEAdvertisedDevice.h
@@ -35,14 +35,22 @@ class BLEAdvertisedDevice {
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();
@@ -51,7 +59,7 @@ class BLEAdvertisedDevice {
private:
friend class BLEScan;
- void parseAdvertisement(uint8_t* payload);
+ void parseAdvertisement(uint8_t* payload, size_t total_len=62);
void setAddress(BLEAddress address);
void setAdFlag(uint8_t adFlag);
void setAdvertizementResult(uint8_t* payload);
@@ -60,6 +68,8 @@ class BLEAdvertisedDevice {
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);
@@ -68,6 +78,7 @@ class BLEAdvertisedDevice {
bool m_haveManufacturerData;
bool m_haveName;
bool m_haveRSSI;
+ bool m_haveServiceData;
bool m_haveServiceUUID;
bool m_haveTXPower;
@@ -82,6 +93,11 @@ class BLEAdvertisedDevice {
int m_rssi;
std::vector m_serviceUUIDs;
int8_t m_txPower;
+ std::string m_serviceData;
+ BLEUUID m_serviceDataUUID;
+ uint8_t* m_payload = nullptr;
+ size_t m_payloadLength = 0;
+ esp_ble_addr_type_t m_addressType;
};
/**
@@ -100,7 +116,8 @@ class BLEAdvertisedDeviceCallbacks {
* 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;
+ virtual void onResult(BLEAdvertisedDevice advertisedDevice);
+ virtual void onResult(BLEAdvertisedDevice* advertisedDevice);
};
#endif /* CONFIG_BT_ENABLED */
diff --git a/cpp_utils/BLEAdvertising.cpp b/cpp_utils/BLEAdvertising.cpp
index 7406819b..230d77cb 100644
--- a/cpp_utils/BLEAdvertising.cpp
+++ b/cpp_utils/BLEAdvertising.cpp
@@ -19,16 +19,18 @@
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEAdvertising.h"
-#include
#include
#include "BLEUtils.h"
#include "GeneralUtils.h"
-#ifdef ARDUINO_ARCH_ESP32
+#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
-static const char* LOG_TAG = "BLEAdvertising";
/**
@@ -56,6 +58,7 @@ BLEAdvertising::BLEAdvertising() {
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
@@ -91,6 +94,55 @@ 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.
@@ -133,21 +185,20 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData
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];
+ 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; iuuid.uuid128, 16);
- p+=16;
+ p += 16;
}
} else {
m_advData.service_uuid_len = 0;
@@ -156,9 +207,11 @@ void BLEAdvertising::start() {
esp_err_t errRc;
- if (m_customAdvData == false) {
- // Set the configuration for advertising.
+ 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));
@@ -166,8 +219,10 @@ void BLEAdvertising::start() {
}
}
- if (m_customScanResponseData == false) {
+ 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));
@@ -229,8 +284,8 @@ void BLEAdvertisementData::addData(std::string data) {
void BLEAdvertisementData::setAppearance(uint16_t appearance) {
char cdata[2];
cdata[0] = 3;
- cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE;
- addData(std::string(cdata, 2) + std::string((char *)&appearance,2));
+ cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19
+ addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
} // setAppearance
@@ -240,28 +295,28 @@ void BLEAdvertisementData::setAppearance(uint16_t appearance) {
*/
void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
char cdata[2];
- switch(uuid.bitSize()) {
+ switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
- cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL;
- addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2));
+ 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;
- addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4));
+ 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;
- addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16));
+ cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07
+ addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16));
break;
}
@@ -285,7 +340,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
void BLEAdvertisementData::setFlags(uint8_t flag) {
char cdata[3];
cdata[0] = 2;
- cdata[1] = ESP_BLE_AD_TYPE_FLAG;
+ cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01
cdata[2] = flag;
addData(std::string(cdata, 3));
} // setFlag
@@ -300,8 +355,8 @@ 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;
- addData(std::string(cdata, 2) + data);
+ cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff
+ addData(std::string(cdata, 2) + data);
ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData");
} // setManufacturerData
@@ -314,8 +369,8 @@ 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;
- addData(std::string(cdata, 2) + name);
+ cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09
+ addData(std::string(cdata, 2) + name);
ESP_LOGD("BLEAdvertisementData", "<< setName");
} // setName
@@ -326,28 +381,28 @@ void BLEAdvertisementData::setName(std::string name) {
*/
void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
char cdata[2];
- switch(uuid.bitSize()) {
+ switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
- cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART;
- addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2));
+ 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;
- addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4));
+ 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;
- addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16));
+ cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06
+ addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16));
break;
}
@@ -357,6 +412,44 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
} // 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.
@@ -365,13 +458,12 @@ 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;
- addData(std::string(cdata, 2) + name);
+ 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.
@@ -380,6 +472,34 @@ 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 */
diff --git a/cpp_utils/BLEAdvertising.h b/cpp_utils/BLEAdvertising.h
index 433de603..3128b50f 100644
--- a/cpp_utils/BLEAdvertising.h
+++ b/cpp_utils/BLEAdvertising.h
@@ -12,7 +12,7 @@
#include
#include "BLEUUID.h"
#include
-
+#include "FreeRTOS.h"
/**
* @brief Advertisement data set by the programmer to be published by the %BLE server.
@@ -28,14 +28,14 @@ class BLEAdvertisementData {
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.
-
- void addData(std::string data); // Add data to the payload.
- std::string getPayload(); // Retrieve the current advert payload.
}; // BLEAdvertisementData
@@ -52,15 +52,27 @@ class BLEAdvertising {
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 m_serviceUUIDs;
- bool m_customAdvData; // Are we using custom advertising data?
- bool m_customScanResponseData; // Are we using custom scan response data?
+ 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_ */
+#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */
\ No newline at end of file
diff --git a/cpp_utils/BLEBeacon.cpp b/cpp_utils/BLEBeacon.cpp
new file mode 100644
index 00000000..68f8d8ed
--- /dev/null
+++ b/cpp_utils/BLEBeacon.cpp
@@ -0,0 +1,89 @@
+/*
+ * BLEBeacon.cpp
+ *
+ * Created on: Jan 4, 2018
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include
+#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
diff --git a/cpp_utils/BLEBeacon.h b/cpp_utils/BLEBeacon.h
new file mode 100644
index 00000000..277bd670
--- /dev/null
+++ b/cpp_utils/BLEBeacon.h
@@ -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_ */
diff --git a/cpp_utils/BLECharacteristic.cpp b/cpp_utils/BLECharacteristic.cpp
index 4c0935a7..2868ee26 100644
--- a/cpp_utils/BLECharacteristic.cpp
+++ b/cpp_utils/BLECharacteristic.cpp
@@ -11,18 +11,20 @@
#include
#include
#include "sdkconfig.h"
-#include
#include
#include "BLECharacteristic.h"
#include "BLEService.h"
+#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLE2902.h"
#include "GeneralUtils.h"
-#ifdef ARDUINO_ARCH_ESP32
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
-#endif
-
+#define LOG_TAG ""
+#else
+#include "esp_log.h"
static const char* LOG_TAG = "BLECharacteristic";
+#endif
#define NULL_HANDLE (0xffff)
@@ -46,12 +48,12 @@ BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
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);
+ 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
/**
@@ -86,7 +88,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) {
return;
}
- m_pService = pService; // Save the service for to which this characteristic belongs.
+ 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(),
@@ -96,20 +98,11 @@ void BLECharacteristic::executeCreate(BLEService* pService) {
control.auto_rsp = ESP_GATT_RSP_BY_APP;
m_semaphoreCreateEvt.take("executeCreate");
-
- /*
- esp_attr_value_t value;
- value.attr_len = m_value.getLength();
- value.attr_max_len = ESP_GATT_MAX_ATTR_LEN;
- value.attr_value = m_value.getData();
- */
-
esp_err_t errRc = ::esp_ble_gatts_add_char(
m_pService->getHandle(),
getUUID().getNative(),
- static_cast(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
+ static_cast(m_permissions),
getProperties(),
- //&value,
nullptr,
&control); // Whether to auto respond or not.
@@ -117,15 +110,9 @@ void BLECharacteristic::executeCreate(BLEService* pService) {
ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
-
m_semaphoreCreateEvt.wait("executeCreate");
- // Now that we have registered the characteristic, we must also register all the descriptors associated with this
- // characteristic. We iterate through each of those and invoke the registration call to register them with the
- // ESP environment.
-
BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
-
while (pDescriptor != nullptr) {
pDescriptor->executeCreate(this);
pDescriptor = m_descriptorMap.getNext();
@@ -163,6 +150,9 @@ 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;
@@ -194,18 +184,36 @@ 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_WRITE_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.
//
@@ -213,24 +221,27 @@ void BLECharacteristic::handleGATTServerEvent(
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
- // - uint8_t exec_write_flag
+ // - 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.
+ if(m_writeEvt){
+ m_writeEvt = false;
+ 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));
}
- } 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
@@ -243,9 +254,13 @@ void BLECharacteristic::handleGATTServerEvent(
// - uint16_t service_handle
// - esp_bt_uuid_t char_uuid
case ESP_GATTS_ADD_CHAR_EVT: {
- if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) &&
- getHandle() == param->add_char.attr_handle &&
- getService()->getHandle()==param->add_char.service_handle) {
+ 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;
@@ -272,8 +287,12 @@ void BLECharacteristic::handleGATTServerEvent(
if (param->write.handle == m_handle) {
if (param->write.is_prep) {
m_value.addPart(param->write.value, param->write.len);
+ m_writeEvt = true;
} else {
setValue(param->write.value, param->write.len);
+ if (m_pCallbacks != nullptr && param->write.is_prep != true) {
+ m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
+ }
}
ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s",
@@ -301,9 +320,6 @@ void BLECharacteristic::handleGATTServerEvent(
}
} // 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
@@ -323,17 +339,16 @@ void BLECharacteristic::handleGATTServerEvent(
case ESP_GATTS_READ_EVT: {
if (param->read.handle == m_handle) {
- if (m_pCallbacks != nullptr) {
- m_pCallbacks->onRead(this); // Invoke the read callback.
- }
+
// 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 uints of 22 bytes.
-// The apparent algorithm is as follows.
+// 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.
@@ -347,12 +362,18 @@ void BLECharacteristic::handleGATTServerEvent(
// 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;
- std::string value = m_value.getValue();
+
if (param->read.is_long) {
- if (value.length() - m_value.getReadOffset() < 22) {
+ 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();
@@ -360,16 +381,22 @@ void BLECharacteristic::handleGATTServerEvent(
m_value.setReadOffset(0);
} else {
// There will be more to come.
- rsp.attr_value.len = 22;
+ 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 + 22);
+ m_value.setReadOffset(rsp.attr_value.offset + maxOffset);
}
- } else {
- if (value.length() > 21) {
+ } else { // read.is_long == false
+
+ 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.
+ }
+ std::string value = m_value.getValue();
+
+ if (value.length() + 1 > maxOffset) {
// Too big for a single shot entry.
- m_value.setReadOffset(22);
- rsp.attr_value.len = 22;
+ 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 {
@@ -378,6 +405,10 @@ void BLECharacteristic::handleGATTServerEvent(
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;
@@ -399,6 +430,7 @@ void BLECharacteristic::handleGATTServerEvent(
break;
} // ESP_GATTS_READ_EVT
+
// ESP_GATTS_CONF_EVT
//
// conf:
@@ -406,6 +438,17 @@ void BLECharacteristic::handleGATTServerEvent(
// - 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;
}
@@ -420,7 +463,7 @@ void BLECharacteristic::handleGATTServerEvent(
// event.
m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
-
+ ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
} // handleGATTServerEvent
@@ -433,48 +476,7 @@ void BLECharacteristic::handleGATTServerEvent(
void BLECharacteristic::indicate() {
ESP_LOGD(LOG_TAG, ">> indicate: 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, "<< indicate: No connected clients.");
- return;
- }
-
- // Test to see if we have a 0x2902 descriptor. If we do, then check to see if indications are enabled
- // and, if not, prevent the indication.
-
- BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
- if (p2902 != nullptr && !p2902->getIndications()) {
- ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring");
- return;
- }
-
- if (m_value.getValue().length() > 20) {
- ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
- }
-
- size_t length = m_value.getValue().length();
- if (length > 20) {
- length = 20;
- }
-
- m_semaphoreConfEvt.take("indicate");
-
- esp_err_t errRc = ::esp_ble_gatts_send_indicate(
- getService()->getServer()->getGattsIf(),
- getService()->getServer()->getConnId(),
- getHandle(), length, (uint8_t*)m_value.getValue().data(), true); // The need_confirm = true makes this an indication.
-
- if (errRc != ESP_OK) {
- ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
- return;
- }
-
- m_semaphoreConfEvt.wait("indicate");
+ notify(false);
ESP_LOGD(LOG_TAG, "<< indicate");
} // indicate
@@ -485,14 +487,12 @@ void BLECharacteristic::indicate() {
* will not block; it is a fire and forget.
* @return N/A.
*/
-void BLECharacteristic::notify() {
+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) {
@@ -503,30 +503,44 @@ void BLECharacteristic::notify() {
// 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 (p2902 != nullptr && !p2902->getNotifications()) {
- ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring");
+ BLE2902* p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
+ if(p2902 == nullptr){
+ ESP_LOGE(LOG_TAG, "Characteristic without 0x2902 descriptor");
return;
}
-
- if (m_value.getValue().length() > 20) {
- ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
+ if(is_notification) {
+ if (p2902 != nullptr && !p2902->getNotifications()) {
+ ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring");
+ return;
+ }
}
-
- size_t length = m_value.getValue().length();
- if (length > 20) {
- length = 20;
+ 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);
+ }
- esp_err_t errRc = ::esp_ble_gatts_send_indicate(
- getService()->getServer()->getGattsIf(),
- getService()->getServer()->getConnId(),
- getHandle(), length, (uint8_t*)m_value.getValue().data(), false); // The need_confirm = false makes this a notify.
- if (errRc != ESP_OK) {
- ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
- return;
+ 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
@@ -624,7 +638,7 @@ void BLECharacteristic::setReadProperty(bool value) {
* @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);
+ 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) {
@@ -647,6 +661,43 @@ 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.
@@ -685,22 +736,24 @@ std::string BLECharacteristic::toString() {
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 ":"");
+ ((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) {
+void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) {
ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default");
ESP_LOGD("BLECharacteristicCallbacks", "<< onRead");
} // onRead
@@ -710,7 +763,7 @@ void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) {
* @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) {
+void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default");
ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite");
} // onWrite
diff --git a/cpp_utils/BLECharacteristic.h b/cpp_utils/BLECharacteristic.h
index 09301834..82a8d7d0 100644
--- a/cpp_utils/BLECharacteristic.h
+++ b/cpp_utils/BLECharacteristic.h
@@ -13,6 +13,7 @@
#include