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 #include "BLEUUID.h" #include +#include #include "BLEDescriptor.h" #include "BLEValue.h" #include "FreeRTOS.h" @@ -26,30 +27,27 @@ class BLECharacteristicCallbacks; */ class BLEDescriptorMap { public: - void setByUUID(const char* uuid, BLEDescriptor *pDescriptor); - void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); - void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); + 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); + 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 m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; + std::map m_uuidMap; + std::map m_handleMap; + std::map::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 + * 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 { @@ -61,13 +59,12 @@ class BLECharacteristic { void addDescriptor(BLEDescriptor* pDescriptor); BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); - //size_t getLength(); BLEUUID getUUID(); - + std::string getValue(); + uint8_t* getData(); void indicate(); - void notify(); - std::string getValue(); + void notify(bool is_notification = true); void setBroadcastProperty(bool value); void setCallbacks(BLECharacteristicCallbacks* pCallbacks); void setIndicateProperty(bool value); @@ -75,11 +72,16 @@ class BLECharacteristic { 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; @@ -89,6 +91,7 @@ class BLECharacteristic { static const uint32_t PROPERTY_WRITE_NR = 1<<5; private: + friend class BLEServer; friend class BLEService; friend class BLEDescriptor; @@ -101,6 +104,8 @@ class BLECharacteristic { BLECharacteristicCallbacks* m_pCallbacks; BLEService* m_pService; BLEValue m_value; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + bool m_writeEvt = false; void handleGATTServerEvent( esp_gatts_cb_event_t event, @@ -120,7 +125,7 @@ class 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 + * 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 { diff --git a/cpp_utils/BLECharacteristicMap.cpp b/cpp_utils/BLECharacteristicMap.cpp index 86a97441..d73aae99 100644 --- a/cpp_utils/BLECharacteristicMap.cpp +++ b/cpp_utils/BLECharacteristicMap.cpp @@ -56,9 +56,7 @@ BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { */ BLECharacteristic* BLECharacteristicMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; @@ -70,9 +68,7 @@ BLECharacteristic* BLECharacteristicMap::getFirst() { * @return The next characteristic in the map. */ BLECharacteristic* BLECharacteristicMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } + if (m_iterator == m_uuidMap.end()) return nullptr; BLECharacteristic* pRet = m_iterator->first; m_iterator++; return pRet; @@ -85,12 +81,9 @@ BLECharacteristic* BLECharacteristicMap::getNext() { * @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) { +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) { + for (auto& myPair : m_uuidMap) { myPair.first->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent @@ -102,9 +95,8 @@ void BLECharacteristicMap::handleGATTServerEvent( * @param [in] characteristic The characteristic to cache. * @return N/A. */ -void BLECharacteristicMap::setByHandle(uint16_t handle, - BLECharacteristic *characteristic) { - m_handleMap.insert(std::pair(handle, characteristic)); +void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) { + m_handleMap.insert(std::pair(handle, characteristic)); } // setByHandle @@ -114,10 +106,8 @@ void BLECharacteristicMap::setByHandle(uint16_t handle, * @param [in] characteristic The characteristic to cache. * @return N/A. */ -void BLECharacteristicMap::setByUUID( - BLECharacteristic *pCharacteristic, - BLEUUID uuid) { - m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); +void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) { + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); } // setByUUID @@ -128,7 +118,7 @@ void BLECharacteristicMap::setByUUID( std::string BLECharacteristicMap::toString() { std::stringstream stringStream; stringStream << std::hex << std::setfill('0'); - int count=0; + int count = 0; for (auto &myPair: m_uuidMap) { if (count > 0) { stringStream << "\n"; diff --git a/cpp_utils/BLEClient.cpp b/cpp_utils/BLEClient.cpp index 189d5d74..d6e75988 100644 --- a/cpp_utils/BLEClient.cpp +++ b/cpp_utils/BLEClient.cpp @@ -6,8 +6,7 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include -#include +#include #include #include #include @@ -18,10 +17,16 @@ #include #include #include -#ifdef ARDUINO_ARCH_ESP32 +#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 * ------ @@ -42,14 +47,28 @@ * * */ -static const char* LOG_TAG = "BLEClient"; BLEClient::BLEClient() { m_pClientCallbacks = nullptr; - m_conn_id = 0; - m_gattc_if = 0; + 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. + + + m_appId = BLEDevice::m_appId++; + m_appId = m_appId%100; + BLEDevice::addPeerDevice(this, true, m_appId); + m_semaphoreRegEvt.take("connect"); + + 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; + } + + m_semaphoreRegEvt.wait("connect"); + } // BLEClient @@ -59,10 +78,12 @@ BLEClient::BLEClient() { 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(); + clearServices(); + esp_ble_gattc_app_unregister(m_gattc_if); + BLEDevice::removePeerDevice(m_appId, true); + if(m_deleteCallbacks) + delete m_pClientCallbacks; + } // ~BLEClient @@ -77,40 +98,38 @@ void BLEClient::clearServices() { delete myPair.second; } m_servicesMap.clear(); + m_servicesMapByInstID.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) { +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_semaphoreRegEvt.take("connect"); - - clearServices(); // Delete any services that may exist. - - esp_err_t errRc = ::esp_ble_gattc_app_register(0); - 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"); - + clearServices(); m_peerAddress = address; // Perform the open connection request against the target BLE Server. m_semaphoreOpenEvt.take("connect"); - errRc = ::esp_ble_gattc_open( - getGattcIf(), + esp_err_t errRc = ::esp_ble_gattc_open( + m_gattc_if, *getPeerAddress().getNative(), // address - 1 // direct connection + 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)); @@ -129,13 +148,12 @@ bool BLEClient::connect(BLEAddress address) { */ void BLEClient::disconnect() { ESP_LOGD(LOG_TAG, ">> disconnect()"); + // ESP_LOGW(__func__, "gattIf: %d, connId: %d", getGattcIf(), getConnId()); 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_ble_gattc_app_unregister(getGattcIf()); - m_peerAddress = BLEAddress("00:00:00:00:00:00"); ESP_LOGD(LOG_TAG, "<< disconnect()"); } // disconnect @@ -148,9 +166,22 @@ void BLEClient::gattClientEventHandler( 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: + if(m_gattc_if != gattc_if) + break; + + ESP_LOGI(LOG_TAG, "SERVICE CHANGED"); + break; + + case ESP_GATTC_CLOSE_EVT: + break; + // // ESP_GATTC_DISCONNECT_EVT // @@ -159,16 +190,22 @@ void BLEClient::gattClientEventHandler( // - 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. - if (m_pClientCallbacks != nullptr) { - m_pClientCallbacks->onDisconnect(this); - } - m_isConnected = false; + ESP_LOGE(__func__, "disconnect event, reason: %d, connId: %d, my connId: %d, my IF: %d, gattc_if: %d", (int)evtParam->disconnect.reason, evtParam->disconnect.conn_id, getConnId(), getGattcIf(), gattc_if); + if(m_gattc_if != gattc_if) break; + m_semaphoreOpenEvt.give(evtParam->disconnect.reason); + if(!m_isConnected) + break; + // If we receive a disconnect event, set the class flag that indicates that we are + // no longer connected. + esp_ble_gattc_close(m_gattc_if, m_conn_id); + m_isConnected = false; + if (m_pClientCallbacks != nullptr) { + m_pClientCallbacks->onDisconnect(this); + } + break; } // ESP_GATTC_DISCONNECT_EVT - // // ESP_GATTC_OPEN_EVT // @@ -176,15 +213,17 @@ void BLEClient::gattClientEventHandler( // - esp_gatt_status_t status // - uint16_t conn_id // - esp_bd_addr_t remote_bda - // - uint16_t mtu // case ESP_GATTC_OPEN_EVT: { + if(m_gattc_if != gattc_if) + break; 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_mtu = evtParam->open.mtu; } m_semaphoreOpenEvt.give(evtParam->open.status); break; @@ -199,11 +238,38 @@ void BLEClient::gattClientEventHandler( // uint16_t app_id // case ESP_GATTC_REG_EVT: { - m_gattc_if = gattc_if; - m_semaphoreRegEvt.give(); + if(m_appId == evtParam->reg.app_id){ + ESP_LOGI(__func__, "register app id: %d, %d, gattc_if: %d", m_appId, evtParam->reg.app_id, gattc_if); + 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"); + } + else + m_mtu = evtParam->cfg_mtu.mtu; + break; + + case ESP_GATTC_CONNECT_EVT: { + if(m_gattc_if != gattc_if) + break; + m_conn_id = evtParam->connect.conn_id; + 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 @@ -213,7 +279,22 @@ void BLEClient::gattClientEventHandler( // - uint16_t conn_id // case ESP_GATTC_SEARCH_CMPL_EVT: { - m_semaphoreSearchCmplEvt.give(); + if(m_gattc_if != gattc_if) + break; + if (evtParam->search_cmpl.status != ESP_GATT_OK){ + ESP_LOGE(LOG_TAG, "search service failed, error status = %x", evtParam->search_cmpl.status); + } +#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(evtParam->search_cmpl.status); break; } // ESP_GATTC_SEARCH_CMPL_EVT @@ -228,6 +309,9 @@ void BLEClient::gattClientEventHandler( // - esp_gatt_id_t srvc_id // case ESP_GATTC_SEARCH_RES_EVT: { + if(m_gattc_if != gattc_if) + break; + BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id); BLERemoteService* pRemoteService = new BLERemoteService( evtParam->search_res.srvc_id, @@ -235,7 +319,8 @@ void BLEClient::gattClientEventHandler( evtParam->search_res.start_handle, evtParam->search_res.end_handle ); - m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); + m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); + m_servicesMapByInstID.insert(std::pair(pRemoteService, evtParam->search_res.srvc_id.inst_id)); break; } // ESP_GATTC_SEARCH_RES_EVT @@ -334,7 +419,7 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) { } } // End of each of the services. ESP_LOGD(LOG_TAG, "<< getService: not found"); - throw new BLEUuidNotFoundException; + return nullptr; } // getService @@ -353,21 +438,22 @@ std::map* BLEClient::getServices() { * 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(), - nullptr // Filter UUID + 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; } - m_semaphoreSearchCmplEvt.wait("getServices"); - m_haveServices = true; // Remember that we now have services. + // If sucessfull, remember that we now have services. + m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0); ESP_LOGD(LOG_TAG, "<< getServices"); return &m_servicesMap; } // getServices @@ -397,7 +483,7 @@ 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) { + switch (event) { // // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT // @@ -407,7 +493,7 @@ void BLEClient::handleGAPEvent( // - esp_bd_addr_t remote_addr // case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { - m_semaphoreRssiCmplEvt.give((uint32_t)param->read_rssi_cmpl.rssi); + m_semaphoreRssiCmplEvt.give((uint32_t) param->read_rssi_cmpl.rssi); break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT @@ -431,8 +517,9 @@ bool BLEClient::isConnected() { /** * @brief Set the callbacks that will be invoked. */ -void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) { +void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { m_pClientCallbacks = pClientCallbacks; + m_deleteCallbacks = deleteCallbacks; } // setClientCallbacks @@ -448,6 +535,9 @@ void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::s ESP_LOGD(LOG_TAG, "<< setValue"); } // setValue +uint16_t BLEClient::getMTU() { + return m_mtu; +} /** * @brief Return a string representation of this client. diff --git a/cpp_utils/BLEClient.h b/cpp_utils/BLEClient.h index a60ed102..4b3c7eea 100644 --- a/cpp_utils/BLEClient.h +++ b/cpp_utils/BLEClient.h @@ -19,9 +19,11 @@ #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. @@ -31,7 +33,8 @@ class BLEClient { BLEClient(); ~BLEClient(); - bool connect(BLEAddress address); // Connect to the remote BLE Server + 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 @@ -47,40 +50,39 @@ class BLEClient { bool isConnected(); // Return true if we are connected. - void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); + void setClientCallbacks(BLEClientCallbacks *pClientCallbacks, bool deleteCallbacks = true); 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); + void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); - uint16_t getConnId(); - esp_gatt_if_t getGattcIf(); 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; // Have we previously obtain the set of services from the remote server. - bool m_isConnected; // Are we currently connected. + bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. + bool m_isConnected = false; // Are we currently connected. + bool m_deleteCallbacks = true; - BLEClientCallbacks* m_pClientCallbacks; + BLEClientCallbacks* m_pClientCallbacks = nullptr; 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 m_servicesMap; + std::map m_servicesMapByInstID; void clearServices(); // Clear any existing services. - + uint16_t m_mtu = 23; }; // class BLEDevice diff --git a/cpp_utils/BLEDescriptor.cpp b/cpp_utils/BLEDescriptor.cpp index 054ef420..ba5753de 100644 --- a/cpp_utils/BLEDescriptor.cpp +++ b/cpp_utils/BLEDescriptor.cpp @@ -11,16 +11,19 @@ #include #include #include "sdkconfig.h" -#include #include #include "BLEService.h" #include "BLEDescriptor.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 = "BLEDescriptor"; #endif -static const char* LOG_TAG = "BLEDescriptor"; + #define NULL_HANDLE (0xffff) @@ -29,21 +32,21 @@ static const char* LOG_TAG = "BLEDescriptor"; /** * @brief BLEDescriptor constructor. */ -BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) { +BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) { } /** * @brief BLEDescriptor constructor. */ -BLEDescriptor::BLEDescriptor(BLEUUID uuid) { +BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) { m_bleUUID = uuid; - m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value. - m_value.attr_len = 0; - m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; - m_handle = NULL_HANDLE; - m_pCharacteristic = nullptr; // No initial characteristic. - m_pCallback = nullptr; // No initial callback. + 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 @@ -51,7 +54,7 @@ BLEDescriptor::BLEDescriptor(BLEUUID uuid) { * @brief BLEDescriptor destructor. */ BLEDescriptor::~BLEDescriptor() { - free(m_value.attr_value); + free(m_value.attr_value); // Release the storage we created in the constructor. } // ~BLEDescriptor @@ -70,12 +73,12 @@ void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. esp_attr_control_t control; - control.auto_rsp = ESP_GATT_RSP_BY_APP; + 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)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE), + (esp_gatt_perm_t)m_permissions, &m_value, &control); if (errRc != ESP_OK) { @@ -133,8 +136,8 @@ uint8_t* BLEDescriptor::getValue() { 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_ble_gatts_cb_param_t* param) { + switch (event) { // ESP_GATTS_ADD_CHAR_DESCR_EVT // // add_char_descr: @@ -143,19 +146,8 @@ void BLEDescriptor::handleGATTServerEvent( // - uint16_t service_handle // - esp_bt_uuid_t char_uuid case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - /* - ESP_LOGD(LOG_TAG, "DEBUG: m_pCharacteristic: %x", (uint32_t)m_pCharacteristic); - ESP_LOGD(LOG_TAG, "DEBUG: m_bleUUID: %s, add_char_descr.char_uuid: %s, equals: %d", - m_bleUUID.toString().c_str(), - BLEUUID(param->add_char_descr.char_uuid).toString().c_str(), - m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid))); - ESP_LOGD(LOG_TAG, "DEBUG: service->getHandle: %x, add_char_descr.service_handle: %x", - m_pCharacteristic->getService()->getHandle(), param->add_char_descr.service_handle); - ESP_LOGD(LOG_TAG, "DEBUG: service->lastCharacteristic: %x", - (uint32_t)m_pCharacteristic->getService()->getLastCreatedCharacteristic()); - */ if (m_pCharacteristic != nullptr && - m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid)) && + 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); @@ -180,23 +172,6 @@ void BLEDescriptor::handleGATTServerEvent( if (param->write.handle == m_handle) { setValue(param->write.value, param->write.len); // Set the value of the descriptor. - esp_gatt_rsp_t rsp; // Build a response. - rsp.attr_value.len = getLength(); - rsp.attr_value.handle = m_handle; - rsp.attr_value.offset = 0; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.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) { // Check the return code from the send of the response. - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - 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. } @@ -223,34 +198,13 @@ void BLEDescriptor::handleGATTServerEvent( m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. } - if (param->read.need_rsp) { // Do we need a response - ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); - esp_gatt_rsp_t rsp; - rsp.attr_value.len = getLength(); - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.offset = 0; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len); - - 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) { // Check the return code from the send of the response. - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } // End of need a response. } // End of this is our handle break; } // ESP_GATTS_READ_EVT - default: { + default: break; - } - }// switch event + } // switch event } // handleGATTServerEvent @@ -259,7 +213,7 @@ void BLEDescriptor::handleGATTServerEvent( * @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); + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback); m_pCallback = pCallback; ESP_LOGD(LOG_TAG, "<< setCallbacks"); } // setCallbacks @@ -298,9 +252,12 @@ void BLEDescriptor::setValue(uint8_t* data, size_t length) { * @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((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. diff --git a/cpp_utils/BLEDescriptor.h b/cpp_utils/BLEDescriptor.h index 4eda4c91..03cc5791 100644 --- a/cpp_utils/BLEDescriptor.h +++ b/cpp_utils/BLEDescriptor.h @@ -24,35 +24,41 @@ class BLEDescriptorCallbacks; */ class BLEDescriptor { public: - BLEDescriptor(const char* uuid); - BLEDescriptor(BLEUUID uuid); + BLEDescriptor(const char* uuid, uint16_t max_len = 100); + BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); virtual ~BLEDescriptor(); - uint16_t getHandle(); - size_t getLength(); - BLEUUID getUUID(); - uint8_t* getValue(); + 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 setCallbacks(BLEDescriptorCallbacks* pCallbacks); - void setValue(uint8_t* data, size_t size); - void setValue(std::string value); - std::string toString(); + + 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; - esp_attr_value_t m_value; - uint16_t m_handle; - BLECharacteristic* m_pCharacteristic; + 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); - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); -}; +}; // BLEDescriptor + /** * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. diff --git a/cpp_utils/BLEDescriptorMap.cpp b/cpp_utils/BLEDescriptorMap.cpp index 4e372e1d..6b845833 100644 --- a/cpp_utils/BLEDescriptorMap.cpp +++ b/cpp_utils/BLEDescriptorMap.cpp @@ -21,7 +21,7 @@ * @return The descriptor. If not present, then nullptr is returned. */ BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } @@ -32,8 +32,8 @@ BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { */ BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) { for (auto &myPair : m_uuidMap) { - if (myPair.second->getUUID().equals(uuid)) { - return myPair.second; + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; } } //return m_uuidMap.at(uuid.toString()); @@ -57,8 +57,8 @@ BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) { * @param [in] characteristic The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor *pDescriptor){ - m_uuidMap.insert(std::pair(uuid, pDescriptor)); +void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ + m_uuidMap.insert(std::pair(pDescriptor, uuid)); } // setByUUID @@ -69,8 +69,8 @@ void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor *pDescriptor){ * @param [in] characteristic The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor) { - m_uuidMap.insert(std::pair(uuid.toString(), pDescriptor)); +void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) { + m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); } // setByUUID @@ -80,9 +80,8 @@ void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor) { * @param [in] descriptor The descriptor to cache. * @return N/A. */ -void BLEDescriptorMap::setByHandle(uint16_t handle, - BLEDescriptor *pDescriptor) { - m_handleMap.insert(std::pair(handle, pDescriptor)); +void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) { + m_handleMap.insert(std::pair(handle, pDescriptor)); } // setByHandle @@ -93,13 +92,13 @@ void BLEDescriptorMap::setByHandle(uint16_t handle, std::string BLEDescriptorMap::toString() { std::stringstream stringStream; stringStream << std::hex << std::setfill('0'); - int count=0; - for (auto &myPair: m_uuidMap) { + int count = 0; + for (auto &myPair : m_uuidMap) { if (count > 0) { stringStream << "\n"; } count++; - stringStream << "handle: 0x" << std::setw(2) << myPair.second->getHandle() << ", uuid: " + myPair.second->getUUID().toString(); + stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); } return stringStream.str(); } // toString @@ -114,10 +113,10 @@ std::string BLEDescriptorMap::toString() { void BLEDescriptorMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { + esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every descriptor we have. for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); + myPair.first->handleGATTServerEvent(event, gatts_if, param); } } // handleGATTServerEvent @@ -128,10 +127,8 @@ void BLEDescriptorMap::handleGATTServerEvent( */ BLEDescriptor* BLEDescriptorMap::getFirst() { m_iterator = m_uuidMap.begin(); - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEDescriptor *pRet = m_iterator->second; + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->first; m_iterator++; return pRet; } // getFirst @@ -142,10 +139,8 @@ BLEDescriptor* BLEDescriptorMap::getFirst() { * @return The next descriptor in the map. */ BLEDescriptor* BLEDescriptorMap::getNext() { - if (m_iterator == m_uuidMap.end()) { - return nullptr; - } - BLEDescriptor *pRet = m_iterator->second; + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->first; m_iterator++; return pRet; } // getNext diff --git a/cpp_utils/BLEDevice.cpp b/cpp_utils/BLEDevice.cpp index 26b4203e..78bd13d3 100644 --- a/cpp_utils/BLEDevice.cpp +++ b/cpp_utils/BLEDevice.cpp @@ -11,14 +11,14 @@ #include #include #include -#include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 ESP-IDF -#include // ESP32 ESP-IDF #include // Part of C++ Standard library #include // Part of C++ Standard library #include // Part of C++ Standard library @@ -27,28 +27,47 @@ #include "BLEClient.h" #include "BLEUtils.h" #include "GeneralUtils.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" -#endif - +#define LOG_TAG "" +#else +#include "esp_log.h" static const char* LOG_TAG = "BLEDevice"; +#endif +#if defined(ARDUINO_ARCH_ESP32) +#include "esp32-hal-bt.h" +#endif /** * Singletons for the BLEDevice. */ BLEServer* BLEDevice::m_pServer = nullptr; BLEScan* BLEDevice::m_pScan = nullptr; -BLEClient* BLEDevice::m_pClient = nullptr; -bool initialized = false; // Have we been initialized? - +// 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 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() { - m_pClient = new BLEClient(); + 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 + BLEClient* m_pClient = new BLEClient(); + ESP_LOGD(LOG_TAG, "<< createClient"); return m_pClient; } // createClient @@ -59,8 +78,12 @@ bool initialized = false; // Have we been initialized? */ /* STATIC */ BLEServer* BLEDevice::createServer() { ESP_LOGD(LOG_TAG, ">> createServer"); - m_pServer = new BLEServer(); - m_pServer->createApp(0); +#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 + BLEDevice::m_pServer = new BLEServer(); + m_pServer->createApp(m_appId++); ESP_LOGD(LOG_TAG, "<< createServer"); return m_pServer; } // createServer @@ -84,9 +107,30 @@ bool initialized = false; // Have we been initialized? 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 @@ -107,19 +151,32 @@ bool initialized = false; // Have we been initialized? 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) { - default: { + 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 we have a client registered, call it. - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param); + if(m_customGattcHandler != nullptr) { + m_customGattcHandler(event, gattc_if, param); } + } // gattClientEventHandler @@ -133,30 +190,94 @@ bool initialized = false; // Have we been initialized? BLEUtils::dumpGapEvent(event, param); switch(event) { - case ESP_GAP_BLE_SEC_REQ_EVT: { - esp_err_t errRc = ::esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_security_rsp: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + + 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_pServer != nullptr) { - BLEDevice::m_pServer->handleGAPEvent(event, param); + if (BLEDevice::m_pScan != nullptr) { + BLEDevice::getScan()->handleGAPEvent(event, param); } - if (BLEDevice::m_pClient != nullptr) { - BLEDevice::m_pClient->handleGAPEvent(event, param); + if(m_bleAdvertising != nullptr) { + BLEDevice::getAdvertising()->handleGAPEvent(event, param); } - if (BLEDevice::m_pScan != nullptr) { - BLEDevice::getScan()->handleGAPEvent(event, param); + if(m_customGapHandler != nullptr) { + BLEDevice::m_customGapHandler(event, param); } + } // gapEventHandler @@ -196,7 +317,7 @@ bool initialized = false; // Have we been initialized? */ /* 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(); + BLEClient* pClient = createClient(); pClient->connect(bdAddress); std::string ret = pClient->getValue(serviceUUID, characteristicUUID); pClient->disconnect(); @@ -211,25 +332,33 @@ bool initialized = false; // Have we been initialized? */ /* STATIC */ void BLEDevice::init(std::string deviceName) { if(!initialized){ - initialized = true; // Set the initialization flag to ensure we are only initialized once. + initialized = true; // Set the initialization flag to ensure we are only initialized once. - esp_err_t errRc = ::nvs_flash_init(); + 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; } - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - errRc = esp_bt_controller_init(&bt_cfg); +#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 - // esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); - //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; @@ -240,18 +369,24 @@ bool initialized = false; // Have we been initialized? ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } +#endif #endif - errRc = esp_bluedroid_init(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + 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; + } } - errRc = esp_bluedroid_enable(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: 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); @@ -260,17 +395,21 @@ bool initialized = false; // Have we been initialized? 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) { @@ -278,14 +417,16 @@ bool initialized = false; // Have we been initialized? 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. + vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. } // init @@ -320,7 +461,7 @@ bool initialized = false; // Have we been initialized? */ /* 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(); + BLEClient* pClient = createClient(); pClient->connect(bdAddress); pClient->setValue(serviceUUID, characteristicUUID, value); pClient->disconnect(); @@ -337,4 +478,167 @@ bool initialized = false; // Have we been initialized? 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 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::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(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 diff --git a/cpp_utils/BLEDevice.h b/cpp_utils/BLEDevice.h index 5399ff7c..b01f09be 100644 --- a/cpp_utils/BLEDevice.h +++ b/cpp_utils/BLEDevice.h @@ -13,7 +13,7 @@ #include // ESP32 BLE #include // Part of C++ STL #include -#include +#include #include "BLEServer.h" #include "BLEClient.h" @@ -22,8 +22,12 @@ #include "BLEAddress.h" /** - * @brief %BLE functions. + * @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: @@ -36,13 +40,38 @@ class BLEDevice { 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 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 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 m_connectedClientsMap; static void gattClientEventHandler( esp_gattc_cb_event_t event, @@ -58,6 +87,12 @@ class BLEDevice { 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 diff --git a/cpp_utils/BLEEddystoneTLM.cpp b/cpp_utils/BLEEddystoneTLM.cpp new file mode 100755 index 00000000..a92bcdb2 --- /dev/null +++ b/cpp_utils/BLEEddystoneTLM.cpp @@ -0,0 +1,150 @@ +/* + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#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 diff --git a/cpp_utils/BLEEddystoneTLM.h b/cpp_utils/BLEEddystoneTLM.h new file mode 100755 index 00000000..a93e224f --- /dev/null +++ b/cpp_utils/BLEEddystoneTLM.h @@ -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_ */ diff --git a/cpp_utils/BLEEddystoneURL.cpp b/cpp_utils/BLEEddystoneURL.cpp new file mode 100755 index 00000000..af3b674c --- /dev/null +++ b/cpp_utils/BLEEddystoneURL.cpp @@ -0,0 +1,148 @@ +/* + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#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 diff --git a/cpp_utils/BLEEddystoneURL.h b/cpp_utils/BLEEddystoneURL.h new file mode 100755 index 00000000..0b538c07 --- /dev/null +++ b/cpp_utils/BLEEddystoneURL.h @@ -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_ */ diff --git a/cpp_utils/BLEExceptions.h b/cpp_utils/BLEExceptions.h index 369fcaf6..ea9db855 100644 --- a/cpp_utils/BLEExceptions.h +++ b/cpp_utils/BLEExceptions.h @@ -17,13 +17,13 @@ class BLEDisconnectedException : public std::exception { - const char *what() const throw () { + const char* what() const throw () { return "BLE Disconnected"; } }; class BLEUuidNotFoundException : public std::exception { - const char *what() const throw () { + const char* what() const throw () { return "No such UUID"; } }; diff --git a/cpp_utils/BLEHIDDevice.cpp b/cpp_utils/BLEHIDDevice.cpp new file mode 100644 index 00000000..46770b74 --- /dev/null +++ b/cpp_utils/BLEHIDDevice.cpp @@ -0,0 +1,244 @@ +/* + * BLEHIDDevice.cpp + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEHIDDevice.h" +#include "BLE2904.h" + + +BLEHIDDevice::BLEHIDDevice(BLEServer* server) { + /* + * Here we create mandatory services described in bluetooth specification + */ + m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a)); + m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40); + m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); + + /* + * Mandatory characteristic for device info service + */ + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ); + + /* + * Mandatory characteristics for HID service + */ + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); + + /* + * Mandatory battery level characteristic with notification and presence descriptor + */ + BLE2904* batteryLevelDescriptor = new BLE2904(); + batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8); + batteryLevelDescriptor->setNamespace(1); + batteryLevelDescriptor->setUnit(0x27ad); + + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); + m_batteryLevelCharacteristic->addDescriptor(new BLE2902()); + + /* + * This value is setup here because its default value in most usage cases, its very rare to use boot mode + * and we want to simplify library using as much as possible + */ + const uint8_t pMode[] = { 0x01 }; + protocolMode()->setValue((uint8_t*) pMode, 1); +} + +BLEHIDDevice::~BLEHIDDevice() { +} + +/* + * @brief + */ +void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { + m_reportMapCharacteristic->setValue(map, size); +} + +/* + * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service + */ +void BLEHIDDevice::startServices() { + m_deviceInfoService->start(); + m_hidService->start(); + m_batteryService->start(); +} + +/* + * @brief Create manufacturer characteristic (this characteristic is optional) + */ +BLECharacteristic* BLEHIDDevice::manufacturer() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ); + return m_manufacturerCharacteristic; +} + +/* + * @brief Set manufacturer name + * @param [in] name manufacturer name + */ +void BLEHIDDevice::manufacturer(std::string name) { + m_manufacturerCharacteristic->setValue(name); +} + +/* + * @brief + */ +void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { + uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; + m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); +} + +/* + * @brief + */ +void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { + uint8_t info[] = { 0x11, 0x1, country, flags }; + m_hidInfoCharacteristic->setValue(info, sizeof(info)); +} + +/* + * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic + * @return pointer to new input report characteristic + */ +BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { + BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + BLE2902* p2902 = new BLE2902(); + inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x01 }; + inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + inputReportCharacteristic->addDescriptor(p2902); + inputReportCharacteristic->addDescriptor(inputReportDescriptor); + + return inputReportCharacteristic; +} + +/* + * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic + * @return Pointer to new output report characteristic + */ +BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { + BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x02 }; + outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + outputReportCharacteristic->addDescriptor(outputReportDescriptor); + + return outputReportCharacteristic; +} + +/* + * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic + * @return Pointer to new feature report characteristic + */ +BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { + BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); + BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + + featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x03 }; + featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); + featureReportCharacteristic->addDescriptor(featureReportDescriptor); + + return featureReportCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootInput() { + BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY); + bootInputCharacteristic->addDescriptor(new BLE2902()); + + return bootInputCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootOutput() { + return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::hidControl() { + return m_hidControlCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::protocolMode() { + return m_protocolModeCharacteristic; +} + +void BLEHIDDevice::setBatteryLevel(uint8_t level) { + m_batteryLevelCharacteristic->setValue(&level, 1); + m_batteryLevelCharacteristic->notify(); +} +/* + * @brief Returns battery level characteristic + * @ return battery level characteristic + *//* +BLECharacteristic* BLEHIDDevice::batteryLevel() { + return m_batteryLevelCharacteristic; +} + + + +BLECharacteristic* BLEHIDDevice::reportMap() { + return m_reportMapCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::pnp() { + return m_pnpCharacteristic; +} + + +BLECharacteristic* BLEHIDDevice::hidInfo() { + return m_hidInfoCharacteristic; +} +*/ +/* + * @brief + */ +BLEService* BLEHIDDevice::deviceInfo() { + return m_deviceInfoService; +} + +/* + * @brief + */ +BLEService* BLEHIDDevice::hidService() { + return m_hidService; +} + +/* + * @brief + */ +BLEService* BLEHIDDevice::batteryService() { + return m_batteryService; +} + +#endif // CONFIG_BT_ENABLED + diff --git a/cpp_utils/BLEHIDDevice.h b/cpp_utils/BLEHIDDevice.h new file mode 100644 index 00000000..33e6b46c --- /dev/null +++ b/cpp_utils/BLEHIDDevice.h @@ -0,0 +1,75 @@ +/* + * BLEHIDDevice.h + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ + +#ifndef _BLEHIDDEVICE_H_ +#define _BLEHIDDEVICE_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLECharacteristic.h" +#include "BLEService.h" +#include "BLEDescriptor.h" +#include "BLE2902.h" +#include "HIDTypes.h" + +#define GENERIC_HID 0x03C0 +#define HID_KEYBOARD 0x03C1 +#define HID_MOUSE 0x03C2 +#define HID_JOYSTICK 0x03C3 +#define HID_GAMEPAD 0x03C4 +#define HID_TABLET 0x03C5 +#define HID_CARD_READER 0x03C6 +#define HID_DIGITAL_PEN 0x03C7 +#define HID_BARCODE 0x03C8 + +class BLEHIDDevice { +public: + BLEHIDDevice(BLEServer*); + virtual ~BLEHIDDevice(); + + void reportMap(uint8_t* map, uint16_t); + void startServices(); + + BLEService* deviceInfo(); + BLEService* hidService(); + BLEService* batteryService(); + + BLECharacteristic* manufacturer(); + void manufacturer(std::string name); + //BLECharacteristic* pnp(); + void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); + //BLECharacteristic* hidInfo(); + void hidInfo(uint8_t country, uint8_t flags); + //BLECharacteristic* batteryLevel(); + void setBatteryLevel(uint8_t level); + + + //BLECharacteristic* reportMap(); + BLECharacteristic* hidControl(); + BLECharacteristic* inputReport(uint8_t reportID); + BLECharacteristic* outputReport(uint8_t reportID); + BLECharacteristic* featureReport(uint8_t reportID); + BLECharacteristic* protocolMode(); + BLECharacteristic* bootInput(); + BLECharacteristic* bootOutput(); + +private: + BLEService* m_deviceInfoService; //0x180a + BLEService* m_hidService; //0x1812 + BLEService* m_batteryService = 0; //0x180f + + BLECharacteristic* m_manufacturerCharacteristic; //0x2a29 + BLECharacteristic* m_pnpCharacteristic; //0x2a50 + BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a + BLECharacteristic* m_reportMapCharacteristic; //0x2a4b + BLECharacteristic* m_hidControlCharacteristic; //0x2a4c + BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e + BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 +}; +#endif // CONFIG_BT_ENABLED +#endif /* _BLEHIDDEVICE_H_ */ diff --git a/cpp_utils/BLERemoteCharacteristic.cpp b/cpp_utils/BLERemoteCharacteristic.cpp index d9c64c91..ef16da7b 100644 --- a/cpp_utils/BLERemoteCharacteristic.cpp +++ b/cpp_utils/BLERemoteCharacteristic.cpp @@ -11,7 +11,6 @@ #if defined(CONFIG_BT_ENABLED) #include -#include #include #include @@ -19,12 +18,15 @@ #include "BLEUtils.h" #include "GeneralUtils.h" #include "BLERemoteDescriptor.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 = "BLERemoteCharacteristic"; // The logging tag for this class. #endif -static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. /** * @brief Constructor. @@ -55,6 +57,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic( */ BLERemoteCharacteristic::~BLERemoteCharacteristic() { removeDescriptors(); // Release resources for any descriptor information we may have allocated. + if(m_rawData != nullptr) free(m_rawData); } // ~BLERemoteCharacteristic @@ -146,12 +149,8 @@ static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { * @param [in] evtParam Payload data for the event. * @returns N/A */ -void BLERemoteCharacteristic::gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* evtParam) { +void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { switch(event) { - // // ESP_GATTC_NOTIFY_EVT // // notify @@ -165,25 +164,15 @@ void BLERemoteCharacteristic::gattClientEventHandler( // We have received a notification event which means that the server wishes us to know about a notification // piece of data. What we must now do is find the characteristic with the associated handle and then // invoke its notification callback (if it has one). - // case ESP_GATTC_NOTIFY_EVT: { - if (evtParam->notify.handle != getHandle()) { - break; - } + if (evtParam->notify.handle != getHandle()) break; if (m_notifyCallback != nullptr) { ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str()); - m_notifyCallback( - this, - evtParam->notify.value, - evtParam->notify.value_len, - evtParam->notify.is_notify - ); + m_notifyCallback(this, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify); } // End we have a callback function ... break; } // ESP_GATTC_NOTIFY_EVT - - // // ESP_GATTC_READ_CHAR_EVT // This event indicates that the server has responded to the read request. // @@ -193,17 +182,17 @@ void BLERemoteCharacteristic::gattClientEventHandler( // - uint16_t handle // - uint8_t* value // - uint16_t value_len - // case ESP_GATTC_READ_CHAR_EVT: { // If this event is not for us, then nothing further to do. - if (evtParam->read.handle != getHandle()) { - break; - } + if (evtParam->read.handle != getHandle()) break; // At this point, we have determined that the event is for us, so now we save the value // and unlock the semaphore to ensure that the requestor of the data can continue. if (evtParam->read.status == ESP_GATT_OK) { - m_value = std::string((char*)evtParam->read.value, evtParam->read.value_len); + m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len); + if(m_rawData != nullptr) free(m_rawData); + m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); + memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); } else { m_value = ""; } @@ -212,56 +201,41 @@ void BLERemoteCharacteristic::gattClientEventHandler( break; } // ESP_GATTC_READ_CHAR_EVT - - // // ESP_GATTC_REG_FOR_NOTIFY_EVT // // reg_for_notify: // - esp_gatt_status_t status // - uint16_t handle - // case ESP_GATTC_REG_FOR_NOTIFY_EVT: { // If the request is not for this BLERemoteCharacteristic then move on to the next. - if (evtParam->reg_for_notify.handle != getHandle()) { - break; - } + if (evtParam->reg_for_notify.handle != getHandle()) break; // We have processed the notify registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT - - // // ESP_GATTC_UNREG_FOR_NOTIFY_EVT // // unreg_for_notify: // - esp_gatt_status_t status // - uint16_t handle - // case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { - if (evtParam->unreg_for_notify.handle != getHandle()) { - break; - } + if (evtParam->unreg_for_notify.handle != getHandle()) break; // We have processed the notify un-registration and can unlock the semaphore. m_semaphoreRegForNotifyEvt.give(); break; } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT: - - // // ESP_GATTC_WRITE_CHAR_EVT // // write: // - esp_gatt_status_t status // - uint16_t conn_id // - uint16_t handle - // case ESP_GATTC_WRITE_CHAR_EVT: { // Determine if this event is for us and, if not, pass onwards. - if (evtParam->write.handle != getHandle()) { - break; - } + if (evtParam->write.handle != getHandle()) break; // There is nothing further we need to do here. This is merely an indication // that the write has completed and we can unlock the caller. @@ -270,9 +244,8 @@ void BLERemoteCharacteristic::gattClientEventHandler( } // ESP_GATTC_WRITE_CHAR_EVT - default: { + default: break; - } } // End switch }; // gattClientEventHandler @@ -289,7 +262,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { // For each descriptor we find, create a BLERemoteDescriptor instance. uint16_t offset = 0; esp_gattc_descr_elem_t result; - while(1) { + while(true) { uint16_t count = 1; esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( getRemoteService()->getClient()->getGattcIf(), @@ -300,7 +273,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { offset ); - if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { // We have reached the end of the entries. break; } @@ -309,14 +282,12 @@ void BLERemoteCharacteristic::retrieveDescriptors() { break; } - if (count == 0) { - break; - } - ESP_LOGE(LOG_TAG, ""); + if (count == 0) break; + ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); // We now have a new characteristic ... let us add that to our set of known characteristics - BLERemoteDescriptor *pNewRemoteDescriptor = new BLERemoteDescriptor( + BLERemoteDescriptor* pNewRemoteDescriptor = new BLERemoteDescriptor( result.handle, BLEUUID(result.uuid), this @@ -334,7 +305,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { /** * @brief Retrieve the map of descriptors keyed by UUID. */ -std::map* BLERemoteCharacteristic::getDescriptors() { +std::map* BLERemoteCharacteristic::getDescriptors() { return &m_descriptorMap; } // getDescriptors @@ -391,7 +362,7 @@ BLEUUID BLERemoteCharacteristic::getUUID() { * @brief Read an unsigned 16 bit value * @return The unsigned 16 bit value. */ -uint16_t BLERemoteCharacteristic::readUInt16(void) { +uint16_t BLERemoteCharacteristic::readUInt16() { std::string value = readValue(); if (value.length() >= 2) { return *(uint16_t*)(value.data()); @@ -404,7 +375,7 @@ uint16_t BLERemoteCharacteristic::readUInt16(void) { * @brief Read an unsigned 32 bit value. * @return the unsigned 32 bit value. */ -uint32_t BLERemoteCharacteristic::readUInt32(void) { +uint32_t BLERemoteCharacteristic::readUInt32() { std::string value = readValue(); if (value.length() >= 4) { return *(uint32_t*)(value.data()); @@ -417,7 +388,7 @@ uint32_t BLERemoteCharacteristic::readUInt32(void) { * @brief Read a byte value * @return The value as a byte */ -uint8_t BLERemoteCharacteristic::readUInt8(void) { +uint8_t BLERemoteCharacteristic::readUInt8() { std::string value = readValue(); if (value.length() >= 1) { return (uint8_t)value[0]; @@ -470,12 +441,7 @@ std::string BLERemoteCharacteristic::readValue() { * unregistering a notification. * @return N/A. */ -void BLERemoteCharacteristic::registerForNotify( - void (*notifyCallback)( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify)) { +void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications) { ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); m_notifyCallback = notifyCallback; // Save the notification callback. @@ -492,6 +458,12 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + + uint8_t val[] = {0x01, 0x00}; + if(!notifications) val[0] = 0x02; + BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902)); + if(desc != nullptr) + desc->writeValue(val, 2); } // End Register else { // If we weren't passed a callback function, then this is an unregistration. esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( @@ -503,6 +475,11 @@ void BLERemoteCharacteristic::registerForNotify( if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + + uint8_t val[] = {0x00, 0x00}; + BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902); + if(desc != nullptr) + desc->writeValue(val, 2); } // End Unregister m_semaphoreRegForNotifyEvt.wait("registerForNotify"); @@ -545,62 +522,72 @@ std::string BLERemoteCharacteristic::toString() { * @brief Write the new value for the characteristic. * @param [in] newValue The new value to write. * @param [in] response Do we expect a response? - * @return N/A. + * @return false if not connected or cant perform write for some reason. + */ +bool BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { + return writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic. + * + * This is a convenience function. Many BLE characteristics are a single byte of data. + * @param [in] newValue The new byte value to write. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or cant perform write for some reason. */ -void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { - ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", newValue.length()); +bool BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { + return writeValue(&newValue, 1, response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or cant perform write for some reason. + */ +bool BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { + // writeValue(std::string((char*)data, length), response); + ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); // Check to see that we are connected. if (!getRemoteService()->getClient()->isConnected()) { ESP_LOGE(LOG_TAG, "Disconnected"); - throw BLEDisconnectedException(); + return false; } m_semaphoreWriteCharEvt.take("writeValue"); - // Invoke the ESP-IDF API to perform the write. esp_err_t errRc = ::esp_ble_gattc_write_char( m_pRemoteService->getClient()->getGattcIf(), m_pRemoteService->getClient()->getConnId(), getHandle(), - newValue.length(), - (uint8_t*)newValue.data(), + length, + data, response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE ); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; + return false; } m_semaphoreWriteCharEvt.wait("writeValue"); ESP_LOGD(LOG_TAG, "<< writeValue"); + return true; } // writeValue - -/** - * @brief Write the new value for the characteristic. - * - * This is a convenience function. Many BLE characteristics are a single byte of data. - * @param [in] newValue The new byte value to write. - * @param [in] response Whether we require a response from the write. - * @return N/A. - */ -void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { - writeValue(std::string(reinterpret_cast(&newValue), 1), response); -} // writeValue - - /** - * @brief Write the new value for the characteristic from a data buffer. - * @param [in] data A pointer to a data buffer. - * @param [in] length The length of the data in the data buffer. - * @param [in] response Whether we require a response from the write. + * @brief Read raw data from remote characteristic as hex bytes + * @return return pointer data read */ -void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { - writeValue(std::string((char *)data, length), response); -} // writeValue +uint8_t* BLERemoteCharacteristic::readRawData() { + return m_rawData; +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLERemoteCharacteristic.h b/cpp_utils/BLERemoteCharacteristic.h index 6f23f497..910b11a4 100644 --- a/cpp_utils/BLERemoteCharacteristic.h +++ b/cpp_utils/BLERemoteCharacteristic.h @@ -21,6 +21,7 @@ class BLERemoteService; class BLERemoteDescriptor; +typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); /** * @brief A model of a remote %BLE characteristic. @@ -37,18 +38,20 @@ class BLERemoteCharacteristic { bool canWrite(); bool canWriteNoResponse(); BLERemoteDescriptor* getDescriptor(BLEUUID uuid); - std::map* getDescriptors(); + std::map* getDescriptors(); uint16_t getHandle(); BLEUUID getUUID(); - std::string readValue(void); - uint8_t readUInt8(void); - uint16_t readUInt16(void); - uint32_t readUInt32(void); - void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)); - void writeValue(uint8_t* data, size_t length, bool response = false); - void writeValue(std::string newValue, bool response = false); - void writeValue(uint8_t newValue, bool response = false); - std::string toString(void); + std::string readValue(); + uint8_t readUInt8(); + uint16_t readUInt16(); + uint32_t readUInt32(); + void registerForNotify(notify_callback _callback, bool notifications = true); + bool writeValue(uint8_t* data, size_t length, bool response = false); + bool writeValue(std::string newValue, bool response = false); + bool writeValue(uint8_t newValue, bool response = false); + std::string toString(); + uint8_t* readRawData(); + BLERemoteService* getRemoteService(); private: BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); @@ -57,13 +60,8 @@ class BLERemoteCharacteristic { friend class BLERemoteDescriptor; // Private member functions - void gattClientEventHandler( - esp_gattc_cb_event_t event, - esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t* evtParam); - + void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); - BLERemoteService* getRemoteService(); void removeDescriptors(); void retrieveDescriptors(); @@ -76,7 +74,8 @@ class BLERemoteCharacteristic { FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; - void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + uint8_t *m_rawData = nullptr; + notify_callback m_notifyCallback = nullptr; // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. std::map m_descriptorMap; diff --git a/cpp_utils/BLERemoteDescriptor.cpp b/cpp_utils/BLERemoteDescriptor.cpp index 4d14ba80..96a8a577 100644 --- a/cpp_utils/BLERemoteDescriptor.cpp +++ b/cpp_utils/BLERemoteDescriptor.cpp @@ -9,12 +9,15 @@ #include #include "BLERemoteDescriptor.h" #include "GeneralUtils.h" -#include -#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 = "BLERemoteDescriptor"; #endif -static const char* LOG_TAG = "BLERemoteDescriptor"; + BLERemoteDescriptor::BLERemoteDescriptor( @@ -55,7 +58,7 @@ BLEUUID BLERemoteDescriptor::getUUID() { } // getUUID -std::string BLERemoteDescriptor::readValue(void) { +std::string BLERemoteDescriptor::readValue() { ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); // Check to see that we are connected. @@ -87,28 +90,28 @@ std::string BLERemoteDescriptor::readValue(void) { } // readValue -uint8_t BLERemoteDescriptor::readUInt8(void) { +uint8_t BLERemoteDescriptor::readUInt8() { std::string value = readValue(); if (value.length() >= 1) { - return (uint8_t)value[0]; + return (uint8_t) value[0]; } return 0; } // readUInt8 -uint16_t BLERemoteDescriptor::readUInt16(void) { +uint16_t BLERemoteDescriptor::readUInt16() { std::string value = readValue(); if (value.length() >= 2) { - return *(uint16_t*)(value.data()); + return *(uint16_t*) value.data(); } return 0; } // readUInt16 -uint32_t BLERemoteDescriptor::readUInt32(void) { +uint32_t BLERemoteDescriptor::readUInt32() { std::string value = readValue(); if (value.length() >= 4) { - return *(uint32_t*)(value.data()); + return *(uint32_t*) value.data(); } return 0; } // readUInt32 @@ -118,7 +121,7 @@ uint32_t BLERemoteDescriptor::readUInt32(void) { * @brief Return a string representation of this BLE Remote Descriptor. * @retun A string representation of this BLE Remote Descriptor. */ -std::string BLERemoteDescriptor::toString(void) { +std::string BLERemoteDescriptor::toString() { std::stringstream ss; ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); return ss.str(); @@ -131,10 +134,7 @@ std::string BLERemoteDescriptor::toString(void) { * @param [in] length The length of the data to send. * @param [in] response True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - uint8_t* data, - size_t length, - bool response) { +void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) { ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); // Check to see that we are connected. if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { @@ -148,7 +148,7 @@ void BLERemoteDescriptor::writeValue( getHandle(), length, // Data length data, // Data - ESP_GATT_WRITE_TYPE_NO_RSP, + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE ); if (errRc != ESP_OK) { @@ -163,10 +163,8 @@ void BLERemoteDescriptor::writeValue( * @param [in] newValue The data to send to the remote descriptor. * @param [in] response True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - std::string newValue, - bool response) { - writeValue(newValue.data(), newValue.length()); +void BLERemoteDescriptor::writeValue(std::string newValue, bool response) { + writeValue((uint8_t*) newValue.data(), newValue.length(), response); } // writeValue @@ -175,9 +173,7 @@ void BLERemoteDescriptor::writeValue( * @param [in] The single byte to write. * @param [in] True if we expect a response. */ -void BLERemoteDescriptor::writeValue( - uint8_t newValue, - bool response) { +void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { writeValue(&newValue, 1, response); } // writeValue diff --git a/cpp_utils/BLERemoteService.cpp b/cpp_utils/BLERemoteService.cpp index 78ff9914..91089eab 100644 --- a/cpp_utils/BLERemoteService.cpp +++ b/cpp_utils/BLERemoteService.cpp @@ -11,13 +11,16 @@ #include "BLERemoteService.h" #include "BLEUtils.h" #include "GeneralUtils.h" -#include #include -#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 = "BLERemoteService"; #endif -static const char* LOG_TAG = "BLERemoteService"; + BLERemoteService::BLERemoteService( esp_gatt_id_t srvcId, @@ -60,61 +63,14 @@ static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { void BLERemoteService::gattClientEventHandler( esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *evtParam) { - switch(event) { - // - // ESP_GATTC_GET_CHAR_EVT - // - // get_char: - // - esp_gatt_status_t status - // - uin1t6_t conn_id - // - esp_gatt_srvc_id_t srvc_id - // - esp_gatt_id_t char_id - // - esp_gatt_char_prop_t char_prop - // - /* - case ESP_GATTC_GET_CHAR_EVT: { - // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be - // the same. - if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) { - break; - } - - // If the status is NOT OK then we have a problem and continue. - if (evtParam->get_char.status != ESP_GATT_OK) { - m_semaphoreGetCharEvt.give(); - break; - } - - // This is an indication that we now have the characteristic details for a characteristic owned - // by this service so remember it. - m_characteristicMap.insert(std::pair( - BLEUUID(evtParam->get_char.char_id.uuid).toString(), - new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) )); - - - // Now that we have received a characteristic, lets ask for the next one. - esp_err_t errRc = ::esp_ble_gattc_get_characteristic( - m_pClient->getGattcIf(), - m_pClient->getConnId(), - &m_srvcId, - &evtParam->get_char.char_id); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - break; - } - - //m_semaphoreGetCharEvt.give(); + esp_ble_gattc_cb_param_t* evtParam) { + switch (event) { + default: break; - } // ESP_GATTC_GET_CHAR_EVT -*/ - default: { - break; - } } // switch // Send the event to each of the characteristics owned by this service. - for (auto &myPair : m_characteristicMap) { + for (auto &myPair : m_characteristicMapByHandle) { myPair.second->gattClientEventHandler(event, gattc_if, evtParam); } } // gattClientEventHandler @@ -130,7 +86,6 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { return getCharacteristic(BLEUUID(uuid)); } // getCharacteristic - /** * @brief Get the characteristic object for the UUID. * @param [in] uuid Characteristic uuid. @@ -153,7 +108,8 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { return myPair.second; } } - throw new BLEUuidNotFoundException(); + // throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another + return nullptr; } // getCharacteristic @@ -163,15 +119,14 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { * @return N/A */ void BLERemoteService::retrieveCharacteristics() { - - ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); + ESP_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); removeCharacteristics(); // Forget any previous characteristics. uint16_t offset = 0; esp_gattc_char_elem_t result; - while(1) { - uint16_t count = 1; + while (true) { + uint16_t count = 1; // this value is used as in parameter that allows to search max 10 chars with the same uuid esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( getClient()->getGattcIf(), getClient()->getConnId(), @@ -182,7 +137,7 @@ void BLERemoteService::retrieveCharacteristics() { offset ); - if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { // We have reached the end of the entries. break; } @@ -206,20 +161,20 @@ void BLERemoteService::retrieveCharacteristics() { ); m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); - + m_characteristicMapByHandle.insert(std::pair(result.char_handle, pNewRemoteCharacteristic)); offset++; // Increment our count of number of descriptors found. } // Loop forever (until we break inside the loop). m_haveCharacteristics = true; // Remember that we have received the characteristics. - ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); -} // getCharacteristics + ESP_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); +} // retrieveCharacteristics /** * @brief Retrieve a map of all the characteristics of this service. * @return A map of all the characteristics of this service. */ -std::map * BLERemoteService::getCharacteristics() { +std::map* BLERemoteService::getCharacteristics() { ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); // If is possible that we have not read the characteristics associated with the service so do that // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking @@ -231,6 +186,29 @@ std::map * BLERemoteService::getCharacte return &m_characteristicMap; } // getCharacteristics +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* BLERemoteService::getCharacteristicsByHandle() { + ESP_LOGD(LOG_TAG, ">> getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str()); + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + ESP_LOGD(LOG_TAG, "<< getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str()); + return &m_characteristicMapByHandle; +} // getCharacteristicsByHandle + +/** + * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID + */ +void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { +#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" + *pCharacteristicMap = m_characteristicMapByHandle; +} // Get the characteristics map. /** * @brief Get the client associated with this service. @@ -287,11 +265,11 @@ std::string BLERemoteService::getValue(BLEUUID characteristicUuid) { * @return N/A. */ void BLERemoteService::removeCharacteristics() { - for (auto &myPair : m_characteristicMap) { + m_characteristicMap.clear(); // Clear the map + for (auto &myPair : m_characteristicMapByHandle) { delete myPair.second; - //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear } - m_characteristicMap.clear(); // Clear the map + m_characteristicMapByHandle.clear(); // Clear the map } // removeCharacteristics diff --git a/cpp_utils/BLERemoteService.h b/cpp_utils/BLERemoteService.h index 222c6e45..2ab86738 100644 --- a/cpp_utils/BLERemoteService.h +++ b/cpp_utils/BLERemoteService.h @@ -26,7 +26,6 @@ class BLERemoteCharacteristic; */ class BLERemoteService { public: - virtual ~BLERemoteService(); // Public methods @@ -34,7 +33,8 @@ class BLERemoteService { BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. std::map* getCharacteristics(); - void getCharacteristics(std::map* pCharacteristicMap); // Get the characteristics map. + std::map* getCharacteristicsByHandle(); // Get the characteristics map. + void getCharacteristics(std::map* pCharacteristicMap); BLEClient* getClient(void); // Get a reference to the client associated with this service. uint16_t getHandle(); // Get the handle of this service. @@ -67,10 +67,10 @@ class BLERemoteService { // Properties // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. - std::map m_characteristicMap; + std::map m_characteristicMap; // We maintain a map of characteristics owned by this service keyed by a handle. - std::map m_characteristicMapByHandle; + std::map m_characteristicMapByHandle; bool m_haveCharacteristics; // Have we previously obtained the characteristics. BLEClient* m_pClient; diff --git a/cpp_utils/BLEScan.cpp b/cpp_utils/BLEScan.cpp index 7c5b7bd9..a96e6df6 100644 --- a/cpp_utils/BLEScan.cpp +++ b/cpp_utils/BLEScan.cpp @@ -8,7 +8,6 @@ #if defined(CONFIG_BT_ENABLED) -#include #include #include @@ -17,11 +16,15 @@ #include "BLEScan.h" #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 = "BLEScan"; #endif -static const char* LOG_TAG = "BLEScan"; + /** @@ -33,6 +36,7 @@ BLEScan::BLEScan() { m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; m_pAdvertisedDeviceCallbacks = nullptr; m_stopped = true; + m_wantDuplicates = false; setInterval(100); setWindow(100); } // BLEScan @@ -49,20 +53,19 @@ void BLEScan::handleGAPEvent( switch(event) { - // ESP_GAP_BLE_SCAN_RESULT_EVT - // --------------------------- - // scan_rst: - // esp_gap_search_evt_t search_evt - // esp_bd_addr_t bda - // esp_bt_dev_type_t dev_type - // esp_ble_addr_type_t ble_addr_type - // esp_ble_evt_type_t ble_evt_type - // int rssi - // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] - // int flag - // int num_resps - // uint8_t adv_data_len - // uint8_t scan_rsp_len + // --------------------------- + // scan_rst: + // esp_gap_search_evt_t search_evt + // esp_bd_addr_t bda + // esp_bt_dev_type_t dev_type + // esp_ble_addr_type_t ble_addr_type + // esp_ble_evt_type_t ble_evt_type + // int rssi + // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] + // int flag + // int num_resps + // uint8_t adv_data_len + // uint8_t scan_rsp_len case ESP_GAP_BLE_SCAN_RESULT_EVT: { switch(param->scan_rst.search_evt) { @@ -72,8 +75,12 @@ void BLEScan::handleGAPEvent( // Event that indicates that the duration allowed for the search has completed or that we have been // asked to stop. case ESP_GAP_SEARCH_INQ_CMPL_EVT: { + ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT"); m_stopped = true; m_semaphoreScanEnd.give(); + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } break; } // ESP_GAP_SEARCH_INQ_CMPL_EVT @@ -91,31 +98,38 @@ void BLEScan::handleGAPEvent( BLEAddress advertisedAddress(param->scan_rst.bda); bool found = false; - for (int i=0; iscan_rst.rssi); - advertisedDevice.setAdFlag(param->scan_rst.flag); - advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv); - advertisedDevice.setScan(this); + // ESP_LOG_BUFFER_HEXDUMP(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG); + // ESP_LOGW(LOG_TAG, "bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type); + BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice(); + advertisedDevice->setAddress(advertisedAddress); + advertisedDevice->setRSSI(param->scan_rst.rssi); + advertisedDevice->setAdFlag(param->scan_rst.flag); + advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len); + advertisedDevice->setScan(this); + advertisedDevice->setAddressType(param->scan_rst.ble_addr_type); + + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); + } if (m_pAdvertisedDeviceCallbacks) { m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } - - m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice); + if(found) + delete advertisedDevice; break; } // ESP_GAP_SEARCH_INQ_RES_EVT @@ -154,8 +168,10 @@ void BLEScan::setActiveScan(bool active) { /** * @brief Set the call backs to be invoked. * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. */ -void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks) { +void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { + m_wantDuplicates = wantDuplicates; m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; } // setAdvertisedDeviceCallbacks @@ -181,21 +197,31 @@ void BLEScan::setWindow(uint16_t windowMSecs) { /** * @brief Start scanning. * @param [in] duration The duration in seconds for which to scan. - * @return N/A. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @param [in] are we continue scan (true) or we want to clear stored devices (false) + * @return True if scan started or false if there was an error. */ -BLEScanResults BLEScan::start(uint32_t duration) { +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) { ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); m_semaphoreScanEnd.take(std::string("start")); - - m_scanResults.m_vectorAdvertisedDevices.clear(); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. + + // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals + // then we should not clear map or we will connect the same device few times + if(!is_continue) { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); + } esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); m_semaphoreScanEnd.give(); - return m_scanResults; + return false; } errRc = ::esp_ble_gap_start_scanning(duration); @@ -203,14 +229,25 @@ BLEScanResults BLEScan::start(uint32_t duration) { if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); m_semaphoreScanEnd.give(); - return m_scanResults; + return false; } m_stopped = false; - m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. - ESP_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) { + if(start(duration, nullptr, is_continue)) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } return m_scanResults; } // start @@ -225,17 +262,24 @@ void BLEScan::stop() { esp_err_t errRc = ::esp_ble_gap_stop_scanning(); m_stopped = true; + m_semaphoreScanEnd.give(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); return; } - m_semaphoreScanEnd.give(); - ESP_LOGD(LOG_TAG, "<< stop()"); } // stop +// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address +void BLEScan::erase(BLEAddress address) { + ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); + BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second; + m_scanResults.m_vectorAdvertisedDevices.erase(address.toString()); + delete advertisedDevice; +} + /** * @brief Dump the scan results to the log. @@ -264,8 +308,25 @@ int BLEScanResults::getCount() { * @return The device at the specified index. */ BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) { - return m_vectorAdvertisedDevices.at(i); + uint32_t x = 0; + BLEAdvertisedDevice dev = *m_vectorAdvertisedDevices.begin()->second; + for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) { + dev = *it->second; + if (x==i) break; + x++; + } + return dev; +} + +BLEScanResults BLEScan::getResults() { + return m_scanResults; } +void BLEScan::clearResults() { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); +} #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEScan.h b/cpp_utils/BLEScan.h index edf79a9a..2f71a727 100644 --- a/cpp_utils/BLEScan.h +++ b/cpp_utils/BLEScan.h @@ -11,7 +11,8 @@ #if defined(CONFIG_BT_ENABLED) #include -#include +// #include +#include #include "BLEAdvertisedDevice.h" #include "BLEClient.h" #include "FreeRTOS.h" @@ -37,7 +38,7 @@ class BLEScanResults { private: friend BLEScan; - std::vector m_vectorAdvertisedDevices; + std::map m_vectorAdvertisedDevices; }; /** @@ -48,11 +49,17 @@ class BLEScanResults { class BLEScan { public: void setActiveScan(bool active); - void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks); + void setAdvertisedDeviceCallbacks( + BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates = false); void setInterval(uint16_t intervalMSecs); void setWindow(uint16_t windowMSecs); - BLEScanResults start(uint32_t duration); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false); + BLEScanResults start(uint32_t duration, bool is_continue = false); void stop(); + void erase(BLEAddress address); + BLEScanResults getResults(); + void clearResults(); private: BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. @@ -64,10 +71,12 @@ class BLEScan { esp_ble_scan_params_t m_scan_params; - BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks; - bool m_stopped; + BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; + bool m_stopped = true; FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); BLEScanResults m_scanResults; + bool m_wantDuplicates; + void (*m_scanCompleteCB)(BLEScanResults scanResults); }; // BLEScan #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLESecurity.cpp b/cpp_utils/BLESecurity.cpp new file mode 100644 index 00000000..f3b2cd3c --- /dev/null +++ b/cpp_utils/BLESecurity.cpp @@ -0,0 +1,115 @@ +/* + * BLESecurity.cpp + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#include "BLESecurity.h" +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +BLESecurity::BLESecurity() { +} + +BLESecurity::~BLESecurity() { +} +/* + * @brief Set requested authentication mode + */ +void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { + m_authReq = auth_req; + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode +} + +/** + * @brief Set our device IO capability to let end user perform authorization + * either by displaying or entering generated 6-digits pin code + */ +void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { + m_iocap = iocap; + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); +} // setCapability + + +/** + * @brief Init encryption key by server + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setInitEncryptionKey(uint8_t init_key) { + m_initKey = init_key; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); +} // setInitEncryptionKey + + +/** + * @brief Init encryption key by client + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { + m_respKey = resp_key; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); +} // setRespEncryptionKey + + +/** + * + * + */ +void BLESecurity::setKeySize(uint8_t key_size) { + m_keySize = key_size; + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); +} //setKeySize + +/** + * Setup for static PIN connection, call it first and then call setAuthenticationMode eventually to change it + */ +void BLESecurity::setStaticPIN(uint32_t pin){ + uint32_t passkey = pin; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + setCapability(ESP_IO_CAP_OUT); + setKeySize(); + setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +} + +/** + * @brief Debug function to display what keys are exchanged by peers + */ +char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { + char* key_str = nullptr; + switch (key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*) "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*) "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*) "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*) "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*) "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*) "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = (char*) "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = (char*) "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = (char*) "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = (char*) "INVALID BLE KEY TYPE"; + break; + } + return key_str; +} // esp_key_type_to_str +#endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLESecurity.h b/cpp_utils/BLESecurity.h new file mode 100644 index 00000000..dc6d6d71 --- /dev/null +++ b/cpp_utils/BLESecurity.h @@ -0,0 +1,73 @@ +/* + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +class BLESecurity { +public: + BLESecurity(); + virtual ~BLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + void setStaticPIN(uint32_t pin); + static char* esp_key_type_to_str(esp_ble_key_type_t key_type); + +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; + +}; // BLESecurity + + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class BLESecurityCallbacks { +public: + virtual ~BLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + + virtual bool onSecurityRequest() = 0 ; + /** + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/cpp_utils/BLEServer.cpp b/cpp_utils/BLEServer.cpp old mode 100644 new mode 100755 index 448c8031..83497331 --- a/cpp_utils/BLEServer.cpp +++ b/cpp_utils/BLEServer.cpp @@ -7,24 +7,26 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include -#include +#include #include -#include #include +#include "GeneralUtils.h" #include "BLEDevice.h" #include "BLEServer.h" #include "BLEService.h" #include "BLEUtils.h" #include #include -#include #include -#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 = "BLEServer"; #endif -static const char* LOG_TAG = "BLEServer"; + /** @@ -34,19 +36,17 @@ static const char* LOG_TAG = "BLEServer"; * the BLEDevice class. */ BLEServer::BLEServer() { - m_appId = -1; - m_gatts_if = -1; + m_appId = ESP_GATT_IF_NONE; + m_gatts_if = ESP_GATT_IF_NONE; m_connectedCount = 0; - m_connId = -1; + m_connId = ESP_GATT_IF_NONE; m_pServerCallbacks = nullptr; - - //createApp(0); } // BLEServer void BLEServer::createApp(uint16_t appId) { m_appId = appId; - registerApp(); + registerApp(appId); } // createApp @@ -70,21 +70,21 @@ BLEService* BLEServer::createService(const char* uuid) { * of a new service. Every service must have a unique UUID. * @param [in] uuid The UUID of the new service. * @param [in] numHandles The maximum number of handles associated with this service. + * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. * @return A reference to the new service object. */ -BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); m_semaphoreCreateEvt.take("createService"); // Check that a service with the supplied UUID does not already exist. if (m_serviceMap.getByUUID(uuid) != nullptr) { - ESP_LOGE(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); - m_semaphoreCreateEvt.give(); - return nullptr; } BLEService* pService = new BLEService(uuid, numHandles); + pService->m_instId = inst_id; m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. @@ -95,13 +95,31 @@ BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) { } // createService +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(const char* uuid) { + return m_serviceMap.getByUUID(uuid); +} + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { + return m_serviceMap.getByUUID(uuid); +} + /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. * * @return An advertising object. */ BLEAdvertising* BLEServer::getAdvertising() { - return &m_bleAdvertising; + return BLEDevice::getAdvertising(); } uint16_t BLEServer::getConnId() { @@ -122,41 +140,6 @@ uint16_t BLEServer::getGattsIf() { return m_gatts_if; } -/** - * @brief Handle a received GAP event. - * - * @param [in] event - * @param [in] param - */ -void BLEServer::handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { - ESP_LOGD(LOG_TAG, "BLEServer ... handling GAP event!"); - switch(event) { - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - /* - esp_ble_adv_params_t adv_params; - adv_params.adv_int_min = 0x20; - adv_params.adv_int_max = 0x40; - adv_params.adv_type = ADV_TYPE_IND; - adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - adv_params.channel_map = ADV_CHNL_ALL; - adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; - ESP_LOGD(tag, "Starting advertising"); - esp_err_t errRc = ::esp_ble_gap_start_advertising(&adv_params); - if (errRc != ESP_OK) { - ESP_LOGE(tag, "esp_ble_gap_start_advertising: rc=%d %s", errRc, espToString(errRc)); - return; - } - */ - break; - } - default: - break; - } -} // handleGAPEvent - - /** * @brief Handle a GATT Server Event. @@ -166,17 +149,10 @@ void BLEServer::handleGAPEvent( * @param [in] param * */ -void BLEServer::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { - +void BLEServer::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()); - // Invoke the handler for every Service we have. - m_serviceMap.handleGATTServerEvent(event, gatts_if, param); - switch(event) { // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. // add_char: @@ -189,19 +165,23 @@ void BLEServer::handleGATTServerEvent( break; } // ESP_GATTS_ADD_CHAR_EVT + case ESP_GATTS_MTU_EVT: + updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); + break; // ESP_GATTS_CONNECT_EVT // connect: // - uint16_t conn_id // - esp_bd_addr_t remote_bda - // - bool is_connected // case ESP_GATTS_CONNECT_EVT: { - m_connId = param->connect.conn_id; // Save the connection id. + m_connId = param->connect.conn_id; + addPeerDevice((void*)this, false, m_connId); if (m_pServerCallbacks != nullptr) { m_pServerCallbacks->onConnect(this); + m_pServerCallbacks->onConnect(this, param); } - m_connectedCount++; // Increment the number of connected devices count. + m_connectedCount++; // Increment the number of connected devices count. break; } // ESP_GATTS_CONNECT_EVT @@ -215,7 +195,7 @@ void BLEServer::handleGATTServerEvent( // * esp_gatt_srvc_id_t service_id // case ESP_GATTS_CREATE_EVT: { - BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid); + BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid m_serviceMap.setByHandle(param->create.service_handle, pService); m_semaphoreCreateEvt.give(); break; @@ -225,9 +205,9 @@ void BLEServer::handleGATTServerEvent( // ESP_GATTS_DISCONNECT_EVT // // disconnect - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda - // - bool is_connected + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason // // If we receive a disconnect event then invoke the callback for disconnects (if one is present). // we also want to start advertising again. @@ -236,7 +216,8 @@ void BLEServer::handleGATTServerEvent( if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. m_pServerCallbacks->onDisconnect(this); } - startAdvertising(); + startAdvertising(); //- do this with some delay from the loop() + removePeerDevice(param->disconnect.conn_id, false); break; } // ESP_GATTS_DISCONNECT_EVT @@ -286,10 +267,17 @@ void BLEServer::handleGATTServerEvent( break; } - default: { + case ESP_GATTS_OPEN_EVT: + m_semaphoreOpenEvt.give(param->open.status); + break; + + default: break; - } } + + // Invoke the handler for every Service we have. + m_serviceMap.handleGATTServerEvent(event, gatts_if, param); + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); } // handleGATTServerEvent @@ -299,7 +287,7 @@ void BLEServer::handleGATTServerEvent( * * @return N/A */ -void BLEServer::registerApp() { +void BLEServer::registerApp(uint16_t m_appId) { ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. ::esp_ble_gatts_app_register(m_appId); @@ -321,6 +309,14 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { m_pServerCallbacks = pCallbacks; } // setCallbacks +/* + * Remove service + */ +void BLEServer::removeService(BLEService* service) { + service->stop(); + service->executeDelete(); + m_serviceMap.removeService(service); +} /** * @brief Start advertising. @@ -330,10 +326,35 @@ void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { */ void BLEServer::startAdvertising() { ESP_LOGD(LOG_TAG, ">> startAdvertising"); - m_bleAdvertising.start(); + BLEDevice::startAdvertising(); ESP_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising +/** + * Allow to connect GATT server to peer device + * Probably can be used in ANCS for iPhone + */ +bool BLEServer::connect(BLEAddress address) { + esp_bd_addr_t addr; + memcpy(&addr, address.getNative(), 6); + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + esp_err_t errRc = ::esp_ble_gatts_open( + getGattsIf(), + addr, // address + 1 // direct 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 + + void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); @@ -341,10 +362,69 @@ void BLEServerCallbacks::onConnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect +void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + + void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect +/* multi connect support */ +/* TODO do some more tweaks */ +void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { + // set mtu in conn_status_t + const std::map::iterator it = m_connectedServersMap.find(conn_id); + if (it != m_connectedServersMap.end()) { + it->second.mtu = mtu; + std::swap(m_connectedServersMap[conn_id], it->second); + } +} + +std::map BLEServer::getPeerDevices(bool _client) { + return m_connectedServersMap; +} + + +uint16_t BLEServer::getPeerMTU(uint16_t conn_id) { + return m_connectedServersMap.find(conn_id)->second.mtu; +} + +void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedServersMap.insert(std::pair(conn_id, status)); +} + +void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { + m_connectedServersMap.erase(conn_id); +} +/* multi connect support */ + +/** + * Update connection parameters can be called only after connection has been established + */ +void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { + esp_ble_conn_update_params_t conn_params; + memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); + conn_params.latency = latency; + conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms + conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms + conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms + esp_ble_gap_update_conn_params(&conn_params); +} + +void BLEServer::disconnect(uint16_t connId){ + esp_ble_gatts_close(m_gatts_if, connId); +} + #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEServer.h b/cpp_utils/BLEServer.h old mode 100644 new mode 100755 index 524e88f3..d2f8038d --- a/cpp_utils/BLEServer.h +++ b/cpp_utils/BLEServer.h @@ -13,14 +13,23 @@ #include #include +// #include "BLEDevice.h" #include "BLEUUID.h" #include "BLEAdvertising.h" #include "BLECharacteristic.h" #include "BLEService.h" +#include "BLESecurity.h" #include "FreeRTOS.h" +#include "BLEAddress.h" class BLEServerCallbacks; +/* TODO possibly refactor this struct */ +typedef struct { + void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here + bool connected; // do we need it? + uint16_t mtu; // every peer device negotiate own mtu +} conn_status_t; /** @@ -30,19 +39,21 @@ class BLEServiceMap { public: BLEService* getByHandle(uint16_t handle); BLEService* getByUUID(const char* uuid); - BLEService* getByUUID(BLEUUID uuid); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); + BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setByHandle(uint16_t handle, BLEService* service); void setByUUID(const char* uuid, BLEService* service); void setByUUID(BLEUUID uuid, BLEService* service); std::string toString(); + BLEService* getFirst(); + BLEService* getNext(); + void removeService(BLEService *service); + int getRegisteredServiceCount(); private: - std::map m_uuidMap; std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; }; @@ -53,10 +64,26 @@ class BLEServer { public: uint32_t getConnectedCount(); BLEService* createService(const char* uuid); - BLEService* createService(BLEUUID uuid, uint32_t numHandles=15); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); BLEAdvertising* getAdvertising(); void setCallbacks(BLEServerCallbacks* pCallbacks); void startAdvertising(); + void removeService(BLEService* service); + BLEService* getServiceByUUID(const char* uuid); + BLEService* getServiceByUUID(BLEUUID uuid); + bool connect(BLEAddress address); + void disconnect(uint16_t connId); + uint16_t m_appId; + void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + + /* multi connection support */ + std::map getPeerDevices(bool client); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); + BLEServer* getServerByConnId(uint16_t conn_id); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); private: @@ -65,22 +92,22 @@ class BLEServer { friend class BLECharacteristic; friend class BLEDevice; esp_ble_adv_data_t m_adv_data; - uint16_t m_appId; - BLEAdvertising m_bleAdvertising; - uint16_t m_connId; - uint32_t m_connectedCount; - uint16_t m_gatts_if; - FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); - FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + // BLEAdvertising m_bleAdvertising; + uint16_t m_connId; + uint32_t m_connectedCount; + uint16_t m_gatts_if; + std::map m_connectedServersMap; + + FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); BLEServiceMap m_serviceMap; - BLEServerCallbacks* m_pServerCallbacks; + BLEServerCallbacks* m_pServerCallbacks = nullptr; void createApp(uint16_t appId); - uint16_t getConnId(); uint16_t getGattsIf(); - void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void registerApp(); + void registerApp(uint16_t); }; // BLEServer @@ -98,7 +125,7 @@ class BLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the client connection. */ virtual void onConnect(BLEServer* pServer); - + virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); /** * @brief Handle an existing client disconnection. * @@ -110,6 +137,5 @@ class BLEServerCallbacks { }; // BLEServerCallbacks - #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ diff --git a/cpp_utils/BLEService.cpp b/cpp_utils/BLEService.cpp index 32bcc570..3034cf18 100644 --- a/cpp_utils/BLEService.cpp +++ b/cpp_utils/BLEService.cpp @@ -11,7 +11,6 @@ #if defined(CONFIG_BT_ENABLED) #include #include -#include #include #include @@ -22,20 +21,23 @@ #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 = "BLEService"; // Tag for logging. #endif #define NULL_HANDLE (0xffff) -static const char* LOG_TAG = "BLEService"; // Tag for logging. /** * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { +BLEService::BLEService(const char* uuid, uint16_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { } @@ -44,7 +46,7 @@ BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUU * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { +BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) { m_uuid = uuid; m_handle = NULL_HANDLE; m_pServer = nullptr; @@ -61,24 +63,16 @@ BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) { * @return N/A. */ -void BLEService::executeCreate(BLEServer *pServer) { - //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); - //getUUID(); // Needed for a weird bug fix - //char x[1000]; - //memcpy(x, &m_uuid, sizeof(m_uuid)); - //char x[10]; - //memcpy(x, &deleteMe, 10); +void BLEService::executeCreate(BLEServer* pServer) { + ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); m_pServer = pServer; m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT esp_gatt_srvc_id_t srvc_id; - srvc_id.id.inst_id = 0; + srvc_id.is_primary = true; + srvc_id.id.inst_id = m_instId; srvc_id.id.uuid = *m_uuid.getNative(); - esp_err_t errRc = ::esp_ble_gatts_create_service( - getServer()->getGattsIf(), - &srvc_id, - m_numHandles // The maximum number of handles associated with the service. - ); + esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -90,6 +84,28 @@ void BLEService::executeCreate(BLEServer *pServer) { } // executeCreate +/** + * @brief Delete the service. + * Delete the service. + * @return N/A. + */ + +void BLEService::executeDelete() { + ESP_LOGD(LOG_TAG, ">> executeDelete()"); + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT + + esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle()); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreDeleteEvt.wait("executeDelete"); + ESP_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + /** * @brief Dump details of this BLE GATT service. * @return N/A. @@ -128,10 +144,9 @@ void BLEService::start() { return; } - BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); - while(pCharacteristic != nullptr) { + while (pCharacteristic != nullptr) { m_lastCreatedCharacteristic = pCharacteristic; pCharacteristic->executeCreate(this); @@ -152,6 +167,32 @@ void BLEService::start() { } // start +/** + * @brief Stop the service. + */ +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start + + /** * @brief Set the handle associated with this service. * @param [in] handle The handle associated with the service. @@ -181,10 +222,10 @@ uint16_t BLEService::getHandle() { * @param [in] pCharacteristic A pointer to the characteristic to be added. */ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { -// We maintain a mapping of characteristics owned by this service. These are managed by the -// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic -// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). -// + // We maintain a mapping of characteristics owned by this service. These are managed by the + // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic + // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), @@ -192,7 +233,7 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // Check that we don't add the same characteristic twice. if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { - ESP_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID"); + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); //return; } @@ -222,7 +263,7 @@ BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t p * @return The new BLE characteristic. */ BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { - BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties); + BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties); addCharacteristic(pCharacteristic); return pCharacteristic; } // createCharacteristic @@ -231,13 +272,8 @@ BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t prope /** * @brief Handle a GATTS server event. */ -void BLEService::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - - - switch(event) { +void BLEService::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_EVT - Indicate that a characteristic was added to the service. // add_char: // - esp_gatt_status_t status @@ -258,7 +294,6 @@ void BLEService::handleGATTServerEvent( } pCharacteristic->setHandle(param->add_char.attr_handle); m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); - //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str()); break; } // Reached the correct service. break; @@ -277,6 +312,19 @@ void BLEService::handleGATTServerEvent( break; } // ESP_GATTS_START_EVT + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } + break; + } // ESP_GATTS_STOP_EVT + // ESP_GATTS_CREATE_EVT // Called when a new service is registered as having been created. @@ -291,16 +339,30 @@ void BLEService::handleGATTServerEvent( // * - bool is_primary // case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid))) { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) { setHandle(param->create.service_handle); m_semaphoreCreateEvt.give(); } break; } // ESP_GATTS_CREATE_EVT - default: { + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // + case ESP_GATTS_DELETE_EVT: { + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } + break; + } // ESP_GATTS_DELETE_EVT + + default: break; - } // Default } // Switch // Invoke the GATTS handler in each of the associated characteristics. diff --git a/cpp_utils/BLEService.h b/cpp_utils/BLEService.h index fdba54fd..b42d57f2 100644 --- a/cpp_utils/BLEService.h +++ b/cpp_utils/BLEService.h @@ -33,11 +33,7 @@ class BLECharacteristicMap { BLECharacteristic* getFirst(); BLECharacteristic* getNext(); std::string toString(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); - + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); private: std::map m_uuidMap; @@ -52,23 +48,25 @@ class BLECharacteristicMap { */ class BLEService { public: - BLEService(const char* uuid, uint32_t numHandles=10); - BLEService(BLEUUID uuid, uint32_t numHandles=10); - void addCharacteristic(BLECharacteristic* pCharacteristic); BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); void dump(); void executeCreate(BLEServer* pServer); + void executeDelete(); BLECharacteristic* getCharacteristic(const char* uuid); BLECharacteristic* getCharacteristic(BLEUUID uuid); BLEUUID getUUID(); BLEServer* getServer(); void start(); + void stop(); std::string toString(); uint16_t getHandle(); + uint8_t m_instId = 0; private: + BLEService(const char* uuid, uint16_t numHandles); + BLEService(BLEUUID uuid, uint16_t numHandles); friend class BLEServer; friend class BLEServiceMap; friend class BLEDescriptor; @@ -77,20 +75,19 @@ class BLEService { BLECharacteristicMap m_characteristicMap; uint16_t m_handle; - BLECharacteristic* m_lastCreatedCharacteristic; - BLEServer* m_pServer; + BLECharacteristic* m_lastCreatedCharacteristic = nullptr; + BLEServer* m_pServer = nullptr; BLEUUID m_uuid; FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); - uint32_t m_numHandles; + uint16_t m_numHandles; BLECharacteristic* getLastCreatedCharacteristic(); - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setHandle(uint16_t handle); //void setService(esp_gatt_srvc_id_t srvc_id); }; // BLEService diff --git a/cpp_utils/BLEServiceMap.cpp b/cpp_utils/BLEServiceMap.cpp old mode 100644 new mode 100755 index 8fdbd5ac..cf4f75f4 --- a/cpp_utils/BLEServiceMap.cpp +++ b/cpp_utils/BLEServiceMap.cpp @@ -17,18 +17,18 @@ * @return The characteristic. */ BLEService* BLEServiceMap::getByUUID(const char* uuid) { - return getByUUID(BLEUUID(uuid)); + return getByUUID(BLEUUID(uuid)); } - + /** * @brief Return the service by UUID. * @param [in] UUID The UUID to look up the service. * @return The characteristic. */ -BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) { +BLEService* BLEServiceMap::getByUUID(BLEUUID uuid, uint8_t inst_id) { for (auto &myPair : m_uuidMap) { - if (myPair.second->getUUID().equals(uuid)) { - return myPair.second; + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; } } //return m_uuidMap.at(uuid.toString()); @@ -52,9 +52,8 @@ BLEService* BLEServiceMap::getByHandle(uint16_t handle) { * @param [in] characteristic The service to cache. * @return N/A. */ -void BLEServiceMap::setByUUID(BLEUUID uuid, - BLEService *service) { - m_uuidMap.insert(std::pair(uuid.toString(), service)); +void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); } // setByUUID @@ -64,9 +63,8 @@ void BLEServiceMap::setByUUID(BLEUUID uuid, * @param [in] service The service to cache. * @return N/A. */ -void BLEServiceMap::setByHandle(uint16_t handle, - BLEService* service) { - m_handleMap.insert(std::pair(handle, service)); +void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { + m_handleMap.insert(std::pair(handle, service)); } // setByHandle @@ -86,10 +84,51 @@ std::string BLEServiceMap::toString() { void BLEServiceMap::handleGATTServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { + esp_ble_gatts_cb_param_t* param) { // Invoke the handler for every Service we have. for (auto &myPair : m_uuidMap) { - myPair.second->handleGATTServerEvent(event, gatts_if, param); + myPair.first->handleGATTServerEvent(event, gatts_if, param); } } + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +BLEService* BLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +BLEService* BLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void BLEServiceMap::removeService(BLEService* service) { + m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +/** + * @brief Returns the amount of registered services + * @return amount of registered services + */ +int BLEServiceMap::getRegisteredServiceCount(){ + return m_handleMap.size(); +} + #endif /* CONFIG_BT_ENABLED */ diff --git a/cpp_utils/BLEUUID.cpp b/cpp_utils/BLEUUID.cpp index 9ca7cdd7..4ddf8fc2 100644 --- a/cpp_utils/BLEUUID.cpp +++ b/cpp_utils/BLEUUID.cpp @@ -6,7 +6,6 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include #include #include #include @@ -14,12 +13,16 @@ #include #include #include "BLEUUID.h" -static const char* LOG_TAG = "BLEUUID"; -#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 = "BLEUUID"; #endif + /** * @brief Copy memory from source to target but in reverse order. * @@ -41,7 +44,7 @@ static const char* LOG_TAG = "BLEUUID"; */ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { assert(size > 0); - target+=(size-1); // Point target to the last byte of the target data + target += (size - 1); // Point target to the last byte of the target data while (size > 0) { *target = *source; target--; @@ -70,45 +73,51 @@ static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { */ BLEUUID::BLEUUID(std::string value) { m_valueSet = true; - if (value.length() == 2) { + if (value.length() == 4) { m_uuid.len = ESP_UUID_LEN_16; - m_uuid.uuid.uuid16 = value[0] | (value[1] << 8); + m_uuid.uuid.uuid16 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid16 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4; + i+=2; + } } - else if (value.length() == 4) { + else if (value.length() == 8) { m_uuid.len = ESP_UUID_LEN_32; - m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + m_uuid.uuid.uuid32 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; + i+=2; + } } - else if (value.length() == 16) { + else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) m_uuid.len = ESP_UUID_LEN_128; memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); } else if (value.length() == 36) { -// If the length of the string is 36 bytes then we will assume it is a long hex string in -// UUID format. + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. m_uuid.len = ESP_UUID_LEN_128; - int vals[16]; - sscanf(value.c_str(), "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x", - &vals[15], - &vals[14], - &vals[13], - &vals[12], - &vals[11], - &vals[10], - &vals[9], - &vals[8], - &vals[7], - &vals[6], - &vals[5], - &vals[4], - &vals[3], - &vals[2], - &vals[1], - &vals[0] - ); - - int i; - for (i=0; i<16; i++) { - m_uuid.uuid.uuid128[i] = vals[i]; + int n = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid128[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F); + i+=2; } } else { @@ -136,7 +145,7 @@ BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { } else { memcpy(m_uuid.uuid.uuid128, pData, 16); } - m_valueSet = true; + m_valueSet = true; } // BLEUUID @@ -149,7 +158,6 @@ BLEUUID::BLEUUID(uint16_t uuid) { m_uuid.len = ESP_UUID_LEN_16; m_uuid.uuid.uuid16 = uuid; m_valueSet = true; - } // BLEUUID @@ -194,24 +202,18 @@ BLEUUID::BLEUUID() { * @brief Get the number of bits in this uuid. * @return The number of bits in the UUID. One of 16, 32 or 128. */ -int BLEUUID::bitSize() { - if (m_valueSet == false) { - return 0; - } - switch(m_uuid.len) { - case ESP_UUID_LEN_16: { +uint8_t BLEUUID::bitSize() { + if (!m_valueSet) return 0; + switch (m_uuid.len) { + case ESP_UUID_LEN_16: return 16; - } - case ESP_UUID_LEN_32: { + case ESP_UUID_LEN_32: return 32; - } - case ESP_UUID_LEN_128: { + case ESP_UUID_LEN_128: return 128; - } - default: { + default: ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); return 0; - } } // End of switch } // bitSize @@ -224,9 +226,7 @@ int BLEUUID::bitSize() { */ bool BLEUUID::equals(BLEUUID uuid) { //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str()); - if (m_valueSet == false || uuid.m_valueSet == false) { - return false; - } + if (!m_valueSet || !uuid.m_valueSet) return false; if (uuid.m_uuid.len != m_uuid.len) { return uuid.toString() == toString(); @@ -253,14 +253,14 @@ bool BLEUUID::equals(BLEUUID uuid) { * NNNNNNNN * */ -BLEUUID BLEUUID::fromString(std::string _uuid){ +BLEUUID BLEUUID::fromString(std::string _uuid) { uint8_t start = 0; if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. start = 2; } uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. - if( len == 4) { + if(len == 4) { uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); return BLEUUID(x); } else if (len == 8) { @@ -299,7 +299,7 @@ BLEUUID BLEUUID::to128() { //ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str()); // If we either don't have a value or are already a 128 bit UUID, nothing further to do. - if (m_valueSet == false || m_uuid.len == ESP_UUID_LEN_128) { + if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) { return *this; } @@ -356,9 +356,7 @@ BLEUUID BLEUUID::to128() { * @return A string representation of the UUID. */ std::string BLEUUID::toString() { - if (m_valueSet == false) { // If we have no value, nothing to format. - return ""; - } + if (!m_valueSet) return ""; // If we have no value, nothing to format. // If the UUIDs are 16 or 32 bit, pad correctly. std::stringstream ss; @@ -386,24 +384,23 @@ std::string BLEUUID::toString() { // // UUID string format: // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP - // ss << std::hex << std::setfill('0') << - std::setw(2) << (int)m_uuid.uuid.uuid128[15] << - std::setw(2) << (int)m_uuid.uuid.uuid128[14] << - std::setw(2) << (int)m_uuid.uuid.uuid128[13] << - std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[11] << - std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[9] << - std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[7] << - std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" << - std::setw(2) << (int)m_uuid.uuid.uuid128[5] << - std::setw(2) << (int)m_uuid.uuid.uuid128[4] << - std::setw(2) << (int)m_uuid.uuid.uuid128[3] << - std::setw(2) << (int)m_uuid.uuid.uuid128[2] << - std::setw(2) << (int)m_uuid.uuid.uuid128[1] << - std::setw(2) << (int)m_uuid.uuid.uuid128[0]; + std::setw(2) << (int) m_uuid.uuid.uuid128[15] << + std::setw(2) << (int) m_uuid.uuid.uuid128[14] << + std::setw(2) << (int) m_uuid.uuid.uuid128[13] << + std::setw(2) << (int) m_uuid.uuid.uuid128[12] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[11] << + std::setw(2) << (int) m_uuid.uuid.uuid128[10] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[9] << + std::setw(2) << (int) m_uuid.uuid.uuid128[8] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[7] << + std::setw(2) << (int) m_uuid.uuid.uuid128[6] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[5] << + std::setw(2) << (int) m_uuid.uuid.uuid128[4] << + std::setw(2) << (int) m_uuid.uuid.uuid128[3] << + std::setw(2) << (int) m_uuid.uuid.uuid128[2] << + std::setw(2) << (int) m_uuid.uuid.uuid128[1] << + std::setw(2) << (int) m_uuid.uuid.uuid128[0]; return ss.str(); } // toString diff --git a/cpp_utils/BLEUUID.h b/cpp_utils/BLEUUID.h index 5fb7795b..700739be 100644 --- a/cpp_utils/BLEUUID.h +++ b/cpp_utils/BLEUUID.h @@ -24,7 +24,7 @@ class BLEUUID { BLEUUID(uint8_t* pData, size_t size, bool msbFirst); BLEUUID(esp_gatt_id_t gattId); BLEUUID(); - int bitSize(); // Get the number of bits in this uuid. + uint8_t bitSize(); // Get the number of bits in this uuid. bool equals(BLEUUID uuid); esp_bt_uuid_t* getNative(); BLEUUID to128(); @@ -32,8 +32,8 @@ class BLEUUID { static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string private: - esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. - bool m_valueSet; // Is there a value set for this instance. + esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet = false; // Is there a value set for this instance. }; // BLEUUID #endif /* CONFIG_BT_ENABLED */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/cpp_utils/BLEUtils.cpp b/cpp_utils/BLEUtils.cpp index d8d318f2..a9b735df 100644 --- a/cpp_utils/BLEUtils.cpp +++ b/cpp_utils/BLEUtils.cpp @@ -14,7 +14,7 @@ #include #include -#include // ESP32 BLE +#include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE #include // ESP32 BLE @@ -31,8 +31,8 @@ static const char* LOG_TAG = "BLEUtils"; // Tag for logging. /* -static std::map g_addressMap; -static std::map g_connIdMap; +static std::map g_addressMap; +static std::map g_connIdMap; */ typedef struct { @@ -41,6 +41,7 @@ typedef struct { } member_t; static const member_t members_ids[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 {0xFE08, "Microsoft"}, {0xFE09, "Pillsy, Inc."}, {0xFE0A, "ruwido austria gmbh"}, @@ -290,6 +291,7 @@ static const member_t members_ids[] = { {0xFEFE, "GN ReSound A/S"}, {0xFEFF, "GN Netcom"}, {0xFFFF, "Reserved"}, /*for testing purposes only*/ +#endif {0, "" } }; @@ -299,6 +301,7 @@ typedef struct { } gattdescriptor_t; static const gattdescriptor_t g_descriptor_ids[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 {0x2905,"Characteristic Aggregate Format"}, {0x2900,"Characteristic Extended Properties"}, {0x2904,"Characteristic Presentation Format"}, @@ -314,6 +317,7 @@ static const gattdescriptor_t g_descriptor_ids[] = { {0x290E,"Time Trigger Setting"}, {0x2906,"Valid Range"}, {0x290A,"Value Trigger Setting"}, +#endif { 0, "" } }; @@ -323,6 +327,7 @@ typedef struct { } characteristicMap_t; static const characteristicMap_t g_characteristicsMappings[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 {0x2A7E,"Aerobic Heart Rate Lower Limit"}, {0x2A84,"Aerobic Heart Rate Upper Limit"}, {0x2A7F,"Aerobic Threshold"}, @@ -539,6 +544,7 @@ static const characteristicMap_t g_characteristicsMappings[] = { {0x2A9D,"Weight Measurement"}, {0x2A9E,"Weight Scale Feature"}, {0x2A79,"Wind Chill"}, +#endif {0, ""} }; @@ -556,6 +562,7 @@ typedef struct { * Definition of the service ids to names that we know about. */ static const gattService_t g_gattServices[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 {"Alert Notification Service", "org.bluetooth.service.alert_notification", 0x1811}, {"Automation IO", "org.bluetooth.service.automation_io", 0x1815 }, {"Battery Service","org.bluetooth.service.battery_service", 0x180F}, @@ -591,6 +598,7 @@ static const gattService_t g_gattServices[] = { {"Tx Power", "org.bluetooth.service.tx_power", 0x1804}, {"User Data", "org.bluetooth.service.user_data", 0x181C}, {"Weight Scale", "org.bluetooth.service.weight_scale", 0x181D}, +#endif {"", "", 0 } }; @@ -628,7 +636,7 @@ static std::string gattIdToString(esp_gatt_id_t gattId) { * @brief Convert an esp_ble_addr_type_t to a string representation. */ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { - switch(type) { + switch (type) { case BLE_ADDR_TYPE_PUBLIC: return "BLE_ADDR_TYPE_PUBLIC"; case BLE_ADDR_TYPE_RANDOM: @@ -650,19 +658,19 @@ const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { */ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { std::stringstream ss; - if (adFlags & (1<<0)) { + if (adFlags & (1 << 0)) { ss << "[LE Limited Discoverable Mode] "; } - if (adFlags & (1<<1)) { + if (adFlags & (1 << 1)) { ss << "[LE General Discoverable Mode] "; } - if (adFlags & (1<<2)) { + if (adFlags & (1 << 2)) { ss << "[BR/EDR Not Supported] "; } - if (adFlags & (1<<3)) { + if (adFlags & (1 << 3)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Controller)] "; } - if (adFlags & (1<<4)) { + if (adFlags & (1 << 4)) { ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Host)] "; } return ss.str(); @@ -678,84 +686,59 @@ std::string BLEUtils::adFlagsToString(uint8_t adFlags) { * @return A string representation of the type. */ const char* BLEUtils::advTypeToString(uint8_t advType) { - switch(advType) { - case ESP_BLE_AD_TYPE_FLAG: // 0x01 + switch (advType) { + case ESP_BLE_AD_TYPE_FLAG: // 0x01 return "ESP_BLE_AD_TYPE_FLAG"; - - case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 + case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 return "ESP_BLE_AD_TYPE_16SRV_PART"; - - case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 + case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 return "ESP_BLE_AD_TYPE_16SRV_CMPL"; - - case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 + case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 return "ESP_BLE_AD_TYPE_32SRV_PART"; - - case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 + case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 return "ESP_BLE_AD_TYPE_32SRV_CMPL"; - - case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 + case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 return "ESP_BLE_AD_TYPE_128SRV_PART"; - - case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 + case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 return "ESP_BLE_AD_TYPE_128SRV_CMPL"; - - case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 + case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 return "ESP_BLE_AD_TYPE_NAME_SHORT"; - - case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 + case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 return "ESP_BLE_AD_TYPE_NAME_CMPL"; - - case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a + case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a return "ESP_BLE_AD_TYPE_TX_PWR"; - - case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b + case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b return "ESP_BLE_AD_TYPE_DEV_CLASS"; - - case ESP_BLE_AD_TYPE_SM_TK: // 0x10 + case ESP_BLE_AD_TYPE_SM_TK: // 0x10 return "ESP_BLE_AD_TYPE_SM_TK"; - - case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 + case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 return "ESP_BLE_AD_TYPE_SM_OOB_FLAG"; - - case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 + case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 return "ESP_BLE_AD_TYPE_INT_RANGE"; - - case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14 + case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14 return "ESP_BLE_AD_TYPE_SOL_SRV_UUID"; - - case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15 + case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15 return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID"; - - case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 + case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 return "ESP_BLE_AD_TYPE_SERVICE_DATA"; - - case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 + case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 return "ESP_BLE_AD_TYPE_PUBLIC_TARGET"; - - case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 + case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 return "ESP_BLE_AD_TYPE_RANDOM_TARGET"; - - case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 + case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 return "ESP_BLE_AD_TYPE_APPEARANCE"; - - case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a + case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a return "ESP_BLE_AD_TYPE_ADV_INT"; - case ESP_BLE_AD_TYPE_32SOL_SRV_UUID: return "ESP_BLE_AD_TYPE_32SOL_SRV_UUID"; - - case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 + case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 return "ESP_BLE_AD_TYPE_32SERVICE_DATA"; - - case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 + case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; - case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; - default: - ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType); + ESP_LOGV(LOG_TAG, " adv data type: 0x%x", advType); return ""; } // End switch } // advTypeToString @@ -768,8 +751,7 @@ esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) { return retGattId; } -esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, - bool is_primary) { +esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary) { esp_gatt_srvc_id_t retSrvcId; retSrvcId.id = gattId; retSrvcId.is_primary = is_primary; @@ -784,29 +766,26 @@ esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, * @param [in] length The length of the data to convert. * @return A pointer to the formatted buffer. */ -char* BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { -// Guard against too much data. - if (length > 100) { - length = 100; - } +char* BLEUtils::buildHexData(uint8_t* target, uint8_t* source, uint8_t length) { + // Guard against too much data. + if (length > 100) length = 100; if (target == nullptr) { - target = (uint8_t *)malloc(length * 2 + 1); + target = (uint8_t*) malloc(length * 2 + 1); if (target == nullptr) { ESP_LOGE(LOG_TAG, "buildHexData: malloc failed"); return nullptr; } } - char *startOfData = (char *)target; + char* startOfData = (char*) target; - int i; - for (i=0; i 4 // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT // adv_data_cmpl // - esp_bt_status_t - // case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT // // adv_data_raw_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); break; } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_ADV_START_COMPLETE_EVT // // adv_start_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); break; } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT - - // // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT // // adv_stop_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); break; } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT - - // // ESP_GAP_BLE_AUTH_CMPL_EVT // // auth_cmpl @@ -1148,9 +1081,8 @@ void BLEUtils::dumpGapEvent( // - uint8_t fail_reason // - esp_bd_addr_type_t addr_type // - esp_bt_dev_type_t dev_type - // case ESP_GAP_BLE_AUTH_CMPL_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", + ESP_LOGV(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", BLEAddress(param->ble_security.auth_cmpl.bd_addr).toString().c_str(), param->ble_security.auth_cmpl.key_present, param->ble_security.auth_cmpl.key_type, @@ -1161,56 +1093,41 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_AUTH_CMPL_EVT - - // // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT // // clear_bond_dev_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); break; } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT - - // // ESP_GAP_BLE_LOCAL_IR_EVT - // case ESP_GAP_BLE_LOCAL_IR_EVT: { break; } // ESP_GAP_BLE_LOCAL_IR_EVT - - // // ESP_GAP_BLE_LOCAL_ER_EVT - // case ESP_GAP_BLE_LOCAL_ER_EVT: { break; } // ESP_GAP_BLE_LOCAL_ER_EVT - - // // ESP_GAP_BLE_NC_REQ_EVT - // case ESP_GAP_BLE_NC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s, passkey: %d]", + ESP_LOGV(LOG_TAG, "[bd_addr: %s, passkey: %d]", BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(), param->ble_security.key_notif.passkey); break; } // ESP_GAP_BLE_NC_REQ_EVT - - // // 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: { - ESP_LOGD(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", + ESP_LOGV(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", param->read_rssi_cmpl.status, param->read_rssi_cmpl.rssi, BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str() @@ -1218,20 +1135,15 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT // // scan_param_cmpl. // - esp_bt_status_t status - // case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); break; } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_RESULT_EVT // // scan_rst: @@ -1246,11 +1158,10 @@ void BLEUtils::dumpGapEvent( // - num_resps // - adv_data_len // - scan_rsp_len - // case ESP_GAP_BLE_SCAN_RESULT_EVT: { - switch(param->scan_rst.search_evt) { + switch (param->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: { - ESP_LOGD(LOG_TAG, "search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d (%s), num_resps: %d, adv_data_len: %d, scan_rsp_len: %d", + ESP_LOGV(LOG_TAG, "search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d (%s), num_resps: %d, adv_data_len: %d, scan_rsp_len: %d", searchEventTypeToString(param->scan_rst.search_evt), BLEAddress(param->scan_rst.bda).toString().c_str(), devTypeToString(param->scan_rst.dev_type), @@ -1267,59 +1178,46 @@ void BLEUtils::dumpGapEvent( } // ESP_GAP_SEARCH_INQ_RES_EVT default: { - ESP_LOGD(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); + ESP_LOGV(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); break; } } break; } // ESP_GAP_BLE_SCAN_RESULT_EVT - - // // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT // // scan_rsp_data_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); break; } // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT - // case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); break; } // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT // // scan_start_cmpl // - esp_bt_status_t status case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); break; } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT - - // // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT // // scan_stop_cmpl // - esp_bt_status_t status - // case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); break; } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT - - // // ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT // // update_conn_params @@ -1330,9 +1228,8 @@ void BLEUtils::dumpGapEvent( // - uint16_t latency // - uint16_t conn_int // - uint16_t timeout - // case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { - ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", + ESP_LOGV(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", param->update_conn_params.status, BLEAddress(param->update_conn_params.bda).toString().c_str(), param->update_conn_params.min_int, @@ -1344,18 +1241,14 @@ void BLEUtils::dumpGapEvent( break; } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT - - // // ESP_GAP_BLE_SEC_REQ_EVT - // case ESP_GAP_BLE_SEC_REQ_EVT: { - ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + ESP_LOGV(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); break; } // ESP_GAP_BLE_SEC_REQ_EVT - - +#endif default: { - ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); + ESP_LOGV(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); break; } // default } // switch @@ -1373,10 +1266,10 @@ void BLEUtils::dumpGattClientEvent( esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { - //esp_ble_gattc_cb_param_t *evtParam = (esp_ble_gattc_cb_param_t *)param; - ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); - switch(event) { - // + //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; + ESP_LOGV(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); + switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 // ESP_GATTC_CLOSE_EVT // // close: @@ -1384,16 +1277,14 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda // - esp_gatt_conn_reason_t reason - // case ESP_GATTC_CLOSE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->close.status).c_str(), BLEUtils::gattCloseReasonToString(evtParam->close.reason).c_str(), evtParam->close.conn_id); break; } - // // ESP_GATTC_CONNECT_EVT // // connect: @@ -1401,14 +1292,13 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str() ); break; } - // // ESP_GATTC_DISCONNECT_EVT // // disconnect: @@ -1416,7 +1306,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t conn_id // - esp_bd_addr_t remote_bda case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[reason: %s, conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[reason: %s, conn_id: %d, remote_bda: %s]", BLEUtils::gattCloseReasonToString(evtParam->disconnect.reason).c_str(), evtParam->disconnect.conn_id, BLEAddress(evtParam->disconnect.remote_bda).toString().c_str() @@ -1424,7 +1314,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_DISCONNECT_EVT - // // ESP_GATTC_GET_CHAR_EVT // // get_char: @@ -1433,7 +1322,6 @@ void BLEUtils::dumpGattClientEvent( // - esp_gatt_srvc_id_t srvc_id // - esp_gatt_id_t char_id // - esp_gatt_char_prop_t char_prop - // /* case ESP_GATTC_GET_CHAR_EVT: { @@ -1444,7 +1332,7 @@ void BLEUtils::dumpGattClientEvent( if (evtParam->get_char.char_id.uuid.len == ESP_UUID_LEN_16) { description = BLEUtils::gattCharacteristicUUIDToString(evtParam->get_char.char_id.uuid.uuid.uuid16); } - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]", BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), evtParam->get_char.conn_id, BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str(), @@ -1453,7 +1341,7 @@ void BLEUtils::dumpGattClientEvent( BLEUtils::characteristicPropertiesToString(evtParam->get_char.char_prop).c_str() ); } else { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s]", BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), evtParam->get_char.conn_id, BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str() @@ -1463,7 +1351,6 @@ void BLEUtils::dumpGattClientEvent( } // ESP_GATTC_GET_CHAR_EVT */ - // // ESP_GATTC_NOTIFY_EVT // // notify @@ -1475,7 +1362,7 @@ void BLEUtils::dumpGattClientEvent( // bool is_notify // case ESP_GATTC_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", evtParam->notify.conn_id, BLEAddress(evtParam->notify.remote_bda).toString().c_str(), evtParam->notify.handle, @@ -1486,7 +1373,6 @@ void BLEUtils::dumpGattClientEvent( break; } - // // ESP_GATTC_OPEN_EVT // // open: @@ -1496,7 +1382,7 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t mtu // case ESP_GATTC_OPEN_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]", BLEUtils::gattStatusToString(evtParam->open.status).c_str(), evtParam->open.conn_id, BLEAddress(evtParam->open.remote_bda).toString().c_str(), @@ -1504,8 +1390,6 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_OPEN_EVT - - // // ESP_GATTC_READ_CHAR_EVT // // Callback to indicate that requested data that we wanted to read is now available. @@ -1518,7 +1402,7 @@ void BLEUtils::dumpGattClientEvent( // uint16_t value_type // uint16_t value_len case ESP_GATTC_READ_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", BLEUtils::gattStatusToString(evtParam->read.status).c_str(), evtParam->read.conn_id, evtParam->read.handle, @@ -1528,38 +1412,33 @@ void BLEUtils::dumpGattClientEvent( if (evtParam->read.status == ESP_GATT_OK) { GeneralUtils::hexDump(evtParam->read.value, evtParam->read.value_len); /* - char *pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); - ESP_LOGD(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); + char* pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); + ESP_LOGV(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); free(pHexData); */ } break; } // ESP_GATTC_READ_CHAR_EVT - - // // ESP_GATTC_REG_EVT // // reg: // - esp_gatt_status_t status // - uint16_t app_id - // case ESP_GATTC_REG_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, app_id: 0x%x]", + ESP_LOGV(LOG_TAG, "[status: %s, app_id: 0x%x]", BLEUtils::gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; } // ESP_GATTC_REG_EVT - - // // ESP_GATTC_REG_FOR_NOTIFY_EVT // // reg_for_notify: // - esp_gatt_status_t status // - uint16_t handle case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, handle: %d 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, handle: %d 0x%.2x]", BLEUtils::gattStatusToString(evtParam->reg_for_notify.status).c_str(), evtParam->reg_for_notify.handle, evtParam->reg_for_notify.handle @@ -1567,23 +1446,18 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_REG_FOR_NOTIFY_EVT - - // // ESP_GATTC_SEARCH_CMPL_EVT // // search_cmpl: // - esp_gatt_status_t status // - uint16_t conn_id - // case ESP_GATTC_SEARCH_CMPL_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d]", BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(), evtParam->search_cmpl.conn_id); break; } // ESP_GATTC_SEARCH_CMPL_EVT - - // // ESP_GATTC_SEARCH_RES_EVT // // search_res: @@ -1591,9 +1465,8 @@ void BLEUtils::dumpGattClientEvent( // - uint16_t start_handle // - uint16_t end_handle // - esp_gatt_id_t srvc_id - // case ESP_GATTC_SEARCH_RES_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", + ESP_LOGV(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", evtParam->search_res.conn_id, evtParam->search_res.start_handle, evtParam->search_res.start_handle, @@ -1603,25 +1476,24 @@ void BLEUtils::dumpGattClientEvent( break; } // ESP_GATTC_SEARCH_RES_EVT - - // // ESP_GATTC_WRITE_CHAR_EVT // // write: // - esp_gatt_status_t status // - uint16_t conn_id // - uint16_t handle - // + // - uint16_t offset case ESP_GATTC_WRITE_CHAR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]", BLEUtils::gattStatusToString(evtParam->write.status).c_str(), evtParam->write.conn_id, evtParam->write.handle, - evtParam->write.handle + evtParam->write.handle, + evtParam->write.offset ); break; } // ESP_GATTC_WRITE_CHAR_EVT - +#endif default: break; } @@ -1642,23 +1514,24 @@ void BLEUtils::dumpGattServerEvent( esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* evtParam) { - ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - switch(event) { + ESP_LOGV(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + ESP_LOGV(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char_descr.status).c_str(), evtParam->add_char_descr.attr_handle, evtParam->add_char_descr.attr_handle, evtParam->add_char_descr.service_handle, evtParam->add_char_descr.service_handle, - BLEUUID(evtParam->add_char_descr.char_uuid).toString().c_str()); + BLEUUID(evtParam->add_char_descr.descr_uuid).toString().c_str()); break; } // ESP_GATTS_ADD_CHAR_DESCR_EVT case ESP_GATTS_ADD_CHAR_EVT: { if (evtParam->add_char.status == ESP_GATT_OK) { - ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + ESP_LOGV(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", gattStatusToString(evtParam->add_char.status).c_str(), evtParam->add_char.attr_handle, evtParam->add_char.attr_handle, @@ -1683,9 +1556,8 @@ void BLEUtils::dumpGattServerEvent( // 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, "[status: %s, conn_id: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: 0x%.2x]", gattStatusToString(evtParam->conf.status).c_str(), evtParam->conf.conn_id); break; @@ -1693,21 +1565,21 @@ void BLEUtils::dumpGattServerEvent( case ESP_GATTS_CONGEST_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, congested: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, congested: %d]", evtParam->congest.conn_id, evtParam->congest.congested); break; } // ESP_GATTS_CONGEST_EVT case ESP_GATTS_CONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; } // ESP_GATTS_CONNECT_EVT case ESP_GATTS_CREATE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", gattStatusToString(evtParam->create.status).c_str(), evtParam->create.service_handle, evtParam->create.service_handle, @@ -1716,7 +1588,7 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_CREATE_EVT case ESP_GATTS_DISCONNECT_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", evtParam->connect.conn_id, BLEAddress(evtParam->connect.remote_bda).toString().c_str()); break; @@ -1729,26 +1601,25 @@ void BLEUtils::dumpGattServerEvent( // - uint32_t trans_id // - esp_bd_addr_t bda // - uint8_t exec_write_flag - // case ESP_GATTS_EXEC_WRITE_EVT: { char* pWriteFlagText; - switch(evtParam->exec_write.exec_write_flag) { + switch (evtParam->exec_write.exec_write_flag) { case ESP_GATT_PREP_WRITE_EXEC: { - pWriteFlagText = (char*)"WRITE"; + pWriteFlagText = (char*) "WRITE"; break; } case ESP_GATT_PREP_WRITE_CANCEL: { - pWriteFlagText = (char*)"CANCEL"; + pWriteFlagText = (char*) "CANCEL"; break; } default: - pWriteFlagText = (char*)""; + pWriteFlagText = (char*) ""; break; } - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]", evtParam->exec_write.conn_id, evtParam->exec_write.trans_id, BLEAddress(evtParam->exec_write.bda).toString().c_str(), @@ -1759,14 +1630,14 @@ void BLEUtils::dumpGattServerEvent( case ESP_GATTS_MTU_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, mtu: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, mtu: %d]", evtParam->mtu.conn_id, evtParam->mtu.mtu); break; } // ESP_GATTS_MTU_EVT case ESP_GATTS_READ_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]", evtParam->read.conn_id, evtParam->read.trans_id, BLEAddress(evtParam->read.bda).toString().c_str(), @@ -1777,14 +1648,14 @@ void BLEUtils::dumpGattServerEvent( } // ESP_GATTS_READ_EVT case ESP_GATTS_RESPONSE_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, handle: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, handle: 0x%.2x]", gattStatusToString(evtParam->rsp.status).c_str(), evtParam->rsp.handle); break; } // ESP_GATTS_RESPONSE_EVT case ESP_GATTS_REG_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, app_id: %d]", + ESP_LOGV(LOG_TAG, "[status: %s, app_id: %d]", gattStatusToString(evtParam->reg.status).c_str(), evtParam->reg.app_id); break; @@ -1796,9 +1667,8 @@ void BLEUtils::dumpGattServerEvent( // start: // - esp_gatt_status_t status // - uint16_t service_handle - // case ESP_GATTS_START_EVT: { - ESP_LOGD(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", gattStatusToString(evtParam->start.status).c_str(), evtParam->start.service_handle); break; @@ -1817,9 +1687,8 @@ void BLEUtils::dumpGattServerEvent( // - bool is_prep – Is this a write prepare? If set, then this is to be considered part of the received value and not the whole value. A subsequent ESP_GATTS_EXEC_WRITE will mark the total. // - uint16_t len – The length of the incoming value part. // - uint8_t* value – The data for this value part. - // case ESP_GATTS_WRITE_EVT: { - ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]", + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]", evtParam->write.conn_id, evtParam->write.trans_id, BLEAddress(evtParam->write.bda).toString().c_str(), @@ -1829,13 +1698,13 @@ void BLEUtils::dumpGattServerEvent( evtParam->write.is_prep, evtParam->write.len); char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); - ESP_LOGD(LOG_TAG, "[Data: %s]", pHex); + ESP_LOGV(LOG_TAG, "[Data: %s]", pHex); free(pHex); break; } // ESP_GATTS_WRITE_EVT - +#endif default: - ESP_LOGD(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); + ESP_LOGV(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); break; } } // dumpGattServerEvent @@ -1847,7 +1716,7 @@ void BLEUtils::dumpGattServerEvent( * @return The event type as a string. */ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { - switch(eventType) { + switch (eventType) { case ESP_BLE_EVT_CONN_ADV: return "ESP_BLE_EVT_CONN_ADV"; case ESP_BLE_EVT_CONN_DIR_ADV: @@ -1859,7 +1728,7 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { case ESP_BLE_EVT_SCAN_RSP: return "ESP_BLE_EVT_SCAN_RSP"; default: - ESP_LOGD(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); + ESP_LOGV(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); return "*** Unknown ***"; } } // eventTypeToString @@ -1872,98 +1741,70 @@ const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { * @return A string representation of the event type. */ const char* BLEUtils::gapEventToString(uint32_t eventType) { - switch(eventType) { + switch (eventType) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: return "ESP_GAP_BLE_ADV_START_COMPLETE_EVT"; - - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /*!< When stop adv complete, the event comes */ + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /* !< When stop adv complete, the event comes */ return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; - - case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */ + case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */ return "ESP_GAP_BLE_AUTH_CMPL_EVT"; - case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT"; - case ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT"; - - case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */ + case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */ return "ESP_GAP_BLE_KEY_EVT"; - - case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ return "ESP_GAP_BLE_LOCAL_IR_EVT"; - - case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ return "ESP_GAP_BLE_LOCAL_ER_EVT"; - - case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */ + case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */ return "ESP_GAP_BLE_NC_REQ_EVT"; - - case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ return "ESP_GAP_BLE_OOB_REQ_EVT"; - - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"; - - case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ return "ESP_GAP_BLE_PASSKEY_REQ_EVT"; - case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; - case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_RESULT_EVT: return "ESP_GAP_BLE_SCAN_RESULT_EVT"; - case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT"; - case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: return "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT"; - - case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ + case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ return "ESP_GAP_BLE_SEC_REQ_EVT"; - case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; - case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; - case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: return "ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT"; - case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT"; - - default: - ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + ESP_LOGV(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } } // gapEventToString std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { - const characteristicMap_t *p = g_characteristicsMappings; + const characteristicMap_t* p = g_characteristicsMappings; while (strlen(p->name) > 0) { if (p->assignedNumber == characteristicUUID) { return std::string(p->name); @@ -1980,7 +1821,7 @@ std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID * @return The string representation of a descriptor UUID. */ std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { - gattdescriptor_t* p = (gattdescriptor_t *)g_descriptor_ids; + gattdescriptor_t* p = (gattdescriptor_t*) g_descriptor_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == descriptorUUID) { return std::string(p->name); @@ -2016,7 +1857,7 @@ std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { std::string BLEUtils::gattServiceToString(uint32_t serviceId) { - gattService_t* p = (gattService_t *)g_gattServices; + gattService_t* p = (gattService_t*) g_gattServices; while (strlen(p->name) > 0) { if (p->assignedNumber == serviceId) { return std::string(p->name); @@ -2034,7 +1875,7 @@ std::string BLEUtils::gattServiceToString(uint32_t serviceId) { * @return A string representation of the status. */ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { - switch(status) { + switch (status) { case ESP_GATT_OK: return "ESP_GATT_OK"; case ESP_GATT_INVALID_HANDLE: @@ -2129,11 +1970,11 @@ std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { std::string BLEUtils::getMember(uint32_t memberId) { - member_t* p = (member_t *)members_ids; + member_t* p = (member_t*) members_ids; while (strlen(p->name) > 0) { if (p->assignedNumber == memberId) { - return std::string(p->name); + return std::string(p->name); } p++; } @@ -2146,7 +1987,7 @@ std::string BLEUtils::getMember(uint32_t memberId) { * @return The search event type as a string. */ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { - switch(searchEvt) { + switch (searchEvt) { case ESP_GAP_SEARCH_INQ_RES_EVT: return "ESP_GAP_SEARCH_INQ_RES_EVT"; case ESP_GAP_SEARCH_INQ_CMPL_EVT: @@ -2162,7 +2003,7 @@ const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { case ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT: return "ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT"; default: - ESP_LOGD(LOG_TAG, "Unknown event type: 0x%x", searchEvt); + ESP_LOGV(LOG_TAG, "Unknown event type: 0x%x", searchEvt); return "Unknown event type"; } } // searchEventTypeToString diff --git a/cpp_utils/BLEUtils.h b/cpp_utils/BLEUtils.h index b2baee53..7981691c 100644 --- a/cpp_utils/BLEUtils.h +++ b/cpp_utils/BLEUtils.h @@ -23,12 +23,12 @@ class BLEUtils { static const char* addressTypeToString(esp_ble_addr_type_t type); static std::string adFlagsToString(uint8_t adFlags); static const char* advTypeToString(uint8_t advType); - static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0); - static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true); static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); static std::string buildPrintData(uint8_t* source, size_t length); static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); static const char* devTypeToString(esp_bt_dev_type_t type); + static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id = 0); + static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true); static void dumpGapEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); @@ -47,7 +47,7 @@ class BLEUtils { static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); - static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement); + static std::string gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement); static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); diff --git a/cpp_utils/BLEValue.cpp b/cpp_utils/BLEValue.cpp index 49818e27..ec1e61f5 100644 --- a/cpp_utils/BLEValue.cpp +++ b/cpp_utils/BLEValue.cpp @@ -6,15 +6,17 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) - -#include - #include "BLEValue.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="BLEValue"; #endif -static const char* LOG_TAG="BLEValue"; + BLEValue::BLEValue() { m_accumulation = ""; @@ -42,7 +44,7 @@ void BLEValue::addPart(std::string part) { */ void BLEValue::addPart(uint8_t* pData, size_t length) { ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length); - m_accumulation += std::string((char *)pData, length); + m_accumulation += std::string((char*) pData, length); } // addPart @@ -65,9 +67,7 @@ void BLEValue::cancel() { void BLEValue::commit() { ESP_LOGD(LOG_TAG, ">> commit"); // If there is nothing to commit, do nothing. - if (m_accumulation.length() == 0) { - return; - } + if (m_accumulation.length() == 0) return; setValue(m_accumulation); m_accumulation = ""; m_readOffset = 0; @@ -79,7 +79,7 @@ void BLEValue::commit() { * @return A pointer to the data. */ uint8_t* BLEValue::getData() { - return (uint8_t*)m_value.data(); + return (uint8_t*) m_value.data(); } @@ -132,11 +132,8 @@ void BLEValue::setValue(std::string value) { * @param [in] The length of the new current value. */ void BLEValue::setValue(uint8_t* pData, size_t length) { - m_value = std::string((char*)pData, length); + m_value = std::string((char*) pData, length); } // setValue - - - #endif // CONFIG_BT_ENABLED diff --git a/cpp_utils/BLEValue.h b/cpp_utils/BLEValue.h index 92a7f9a4..5df904c1 100644 --- a/cpp_utils/BLEValue.h +++ b/cpp_utils/BLEValue.h @@ -17,13 +17,13 @@ class BLEValue { public: BLEValue(); - void addPart(std::string part); - void addPart(uint8_t* pData, size_t length); - void cancel(); - void commit(); - uint8_t* getData(); - size_t getLength(); - uint16_t getReadOffset(); + void addPart(std::string part); + void addPart(uint8_t* pData, size_t length); + void cancel(); + void commit(); + uint8_t* getData(); + size_t getLength(); + uint16_t getReadOffset(); std::string getValue(); void setReadOffset(uint16_t readOffset); void setValue(std::string value); @@ -33,6 +33,7 @@ class BLEValue { std::string m_accumulation; uint16_t m_readOffset; std::string m_value; + }; #endif // CONFIG_BT_ENABLED #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ diff --git a/cpp_utils/BLEXML/Characteristics/code/run.sh b/cpp_utils/BLEXML/Characteristics/code/run.sh index 77f193fe..f0c7930a 100755 --- a/cpp_utils/BLEXML/Characteristics/code/run.sh +++ b/cpp_utils/BLEXML/Characteristics/code/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash BASE_URL="https://www.bluetooth.com/api/gatt/XmlFile?xmlFileName=" RESULT=characteristics.json COUNT=0 @@ -17,4 +17,4 @@ do COUNT=$(expr ${COUNT} + 1) done echo -e "\n]\n" >> ${RESULT} -echo "done" \ No newline at end of file +echo "done" diff --git a/cpp_utils/BLEXML/Services/code/run.sh b/cpp_utils/BLEXML/Services/code/run.sh index bfa084d0..6716a53a 100755 --- a/cpp_utils/BLEXML/Services/code/run.sh +++ b/cpp_utils/BLEXML/Services/code/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash BASE_URL="https://www.bluetooth.com/api/gatt/XmlFile?xmlFileName=" RESULT=services.json COUNT=0 @@ -17,4 +17,4 @@ do COUNT=$(expr ${COUNT} + 1) done echo -e "\n]\n" >> ${RESULT} -echo "done" \ No newline at end of file +echo "done" diff --git a/cpp_utils/CMakeLists.txt b/cpp_utils/CMakeLists.txt new file mode 100644 index 00000000..cb9b7267 --- /dev/null +++ b/cpp_utils/CMakeLists.txt @@ -0,0 +1,20 @@ +# Edit following two lines to set component requirements (see docs) +set(COMPONENT_REQUIRES + "console" + "fatfs" + "json" + "mdns" + "nvs_flash" +) +set(COMPONENT_PRIV_REQUIRES ) + +file(GLOB COMPONENT_SRCS + LIST_DIRECTORIES false + "*.h" + "*.cpp" + "*.c" + "*.S" +) +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/cpp_utils/CPPNVS.cpp b/cpp_utils/CPPNVS.cpp index d0a2a0bb..18914ab5 100644 --- a/cpp_utils/CPPNVS.cpp +++ b/cpp_utils/CPPNVS.cpp @@ -70,14 +70,22 @@ void NVS::erase(std::string key) { * @param [in] key The key to read from the namespace. * @param [out] result The string read from the %NVS storage. */ -void NVS::get(std::string key, std::string* result, bool isBlob) { +int NVS::get(std::string key, std::string* result, bool isBlob) { size_t length; if (isBlob) { - ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); + esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), NULL, &length); + if (rc != ESP_OK) { + ESP_LOGI(LOG_TAG, "Error getting key: %i", rc); + return rc; + } } else { - ::nvs_get_str(m_handle, key.c_str(), NULL, &length); + esp_err_t rc = ::nvs_get_str(m_handle, key.c_str(), NULL, &length); + if (rc != ESP_OK) { + ESP_LOGI(LOG_TAG, "Error getting key: %i", rc); + return rc; + } } - char *data = (char *)malloc(length); + char* data = (char*) malloc(length); if (isBlob) { ::nvs_get_blob(m_handle, key.c_str(), data, &length); } else { @@ -85,21 +93,23 @@ void NVS::get(std::string key, std::string* result, bool isBlob) { } *result = std::string(data); free(data); + return ESP_OK; } // get -void NVS::get(std::string key, uint32_t& value) { - ::nvs_get_u32(m_handle, key.c_str(), &value); +int NVS::get(std::string key, uint32_t& value) { + return ::nvs_get_u32(m_handle, key.c_str(), &value); } // get - uint32_t -void NVS::get(std::string key, uint8_t* result, size_t& length) { +int NVS::get(std::string key, uint8_t* result, size_t& length) { ESP_LOGD(LOG_TAG, ">> get: key: %s, blob: inputSize: %d", key.c_str(), length); esp_err_t rc = ::nvs_get_blob(m_handle, key.c_str(), result, &length); if (rc != ESP_OK) { ESP_LOGD(LOG_TAG, "nvs_get_blob: %d", rc); } ESP_LOGD(LOG_TAG, "<< get: outputSize: %d", length); + return rc; } // get - blob diff --git a/cpp_utils/CPPNVS.h b/cpp_utils/CPPNVS.h index 755f551b..9155891b 100644 --- a/cpp_utils/CPPNVS.h +++ b/cpp_utils/CPPNVS.h @@ -21,15 +21,17 @@ class NVS { void erase(); void erase(std::string key); - void get(std::string key, std::string* result, bool isBlob=false); - void get(std::string key, uint8_t* result, size_t &length); - void get(std::string key, uint32_t& value); - void set(std::string key, std::string data, bool isBlob=false); + int get(std::string key, std::string* result, bool isBlob = false); + int get(std::string key, uint8_t* result, size_t& length); + int get(std::string key, uint32_t& value); + void set(std::string key, std::string data, bool isBlob = false); void set(std::string key, uint32_t value); void set(std::string key, uint8_t* data, size_t length); + private: std::string m_name; nvs_handle m_handle; + }; #endif /* COMPONENTS_CPP_UTILS_CPPNVS_H_ */ diff --git a/cpp_utils/Console.cpp b/cpp_utils/Console.cpp new file mode 100644 index 00000000..c0f5655f --- /dev/null +++ b/cpp_utils/Console.cpp @@ -0,0 +1,211 @@ +/* + * Console.cpp + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +/** + * Example: + * Argtable argtable; + * argtable.addString("myparam", "l", "list", "Get Listings"); + * argtable.parse(argc, argv); + * ArgTableEntry_String* pStr = (ArgTableEntry_String*)argTable.get("myparam"); + * pStr->getValue(); + */ +#include "Console.h" + +/** + * Argtable instance constructor. + */ +ArgTable::ArgTable() { + m_argtable = nullptr; + m_argEnd = nullptr; +} // ArgTable#ArgTable + + +/** + * Argtable instance destructor. + */ +ArgTable::~ArgTable() { + freeArgtable(); // Release any resources associated with the argtable. +} // ArgTable#~ArgTable + + +/** + * Build the ArgTable that will be used for parsing. + */ +/* private */ void ArgTable::build() { + /* + * The m_argTableEntries is a std::list that contains the argtable entries in the form of a std::pair. We + * allocate storage for the ArgTable and then populate it. The last entry of the argtable must be an end marker. + */ + int size = m_argTableEntries.size(); + m_argtable = new void*[size + 1]; + int i = 0; + for (auto it = m_argTableEntries.begin(); it != m_argTableEntries.end(); ++it) { + m_argtable[i] = it->second->getEntry(); + i++; + } + m_argEnd = arg_end(10); + m_argtable[i] = m_argEnd; +} // ArgTable#build + + +ArgTableEntry_Date ArgTable::addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Date* pDate = new ArgTableEntry_Date(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDate)); + return *pDate; +} // ArgTable#addDate + + +ArgTableEntry_Double ArgTable::addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Double* pDouble = new ArgTableEntry_Double(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDouble)); + return *pDouble; +} // ArgTable#addDouble + + +ArgTableEntry_File ArgTable::addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_File* pFile = new ArgTableEntry_File(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pFile)); + return *pFile; +} // ArgTable#addFile + + +ArgTableEntry_Int ArgTable::addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Int* pInt = new ArgTableEntry_Int(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pInt)); + return *pInt; +} // ArgTable#addInt + + +ArgTableEntry_Lit ArgTable::addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Lit* pLit = new ArgTableEntry_Lit(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pLit)); + return *pLit; +} // ArgTable#addLit + + +ArgTableEntry_String ArgTable::addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + ArgTableEntry_String* pStr = new ArgTableEntry_String(shortopts, longopts, glossary, min, max); + m_argTableEntries.push_back(std::make_pair(name, pStr)); + return *pStr; +} // ArgTable#addString + + +/** + * Parse the input and output parameters against this argtable. + * @param argc A count of the number of parameters. + * @param argv An array of string parameters. + * @return The number of errors detected. + */ +int ArgTable::parse(int argc, char* argv[]) { + if (m_argtable == nullptr) { // If we don't have an argtable, build it. + build(); + } + int nErrors = arg_parse(argc, argv, m_argtable); + return nErrors; +} // ArgTable#parse + + +/** + * Print any errors associated with the parsing. + */ +void ArgTable::printErrors(FILE* fp, std::string progName) { + if (m_argEnd != nullptr) { + arg_print_errors(fp, m_argEnd, progName.c_str()); + } +} // ArgTable#printErrors + + +/** + * Release the argtable data. + */ +/* private */void ArgTable::freeArgtable() { + if (m_argtable != nullptr) { + arg_free(m_argtable); + m_argtable = nullptr; + m_argEnd = nullptr; + } +} // ArgTable#freeArgtable + + +ArgTableEntry_Date::ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary) { + m_argDate = arg_daten(shortopts.c_str(), longopts.c_str(), "", "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DATE; +} // ArgTableEntry_Date#ArgTableEntry_Date + + +int ArgTableEntry_Date::getCount() { + return m_argDate->count; +} // ArgTableEntry_Date#getCount + + +ArgTableEntry_Double::ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary) { + m_argDbl = arg_dbln(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DBL; +} + + +int ArgTableEntry_Double::getCount() { + return m_argDbl->count; +} + + +ArgTableEntry_File::ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary) { + m_argFile = arg_filen(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::FILE; +} + + +int ArgTableEntry_File::getCount() { + return m_argFile->count; +} + + +ArgTableEntry_Int::ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary) { + m_argInt = arg_intn(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::INT; +} + + +int ArgTableEntry_Int::getCount() { + return m_argInt->count; +} + + +ArgTableEntry_Lit::ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary) { + m_argLit = arg_litn(shortopts.c_str(), longopts.c_str(), 0, 1, glossary.c_str()); + m_type = ArgType_t::LIT; +} + + +int ArgTableEntry_Lit::getCount() { + return m_argLit->count; +} + + +int ArgTableEntry_Regex::getCount() { + return m_argRex->count; +} + + +ArgTableEntry_String::ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + m_argStr = arg_strn(shortopts.c_str(), longopts.c_str(), "", min, max, glossary.c_str()); + m_type = ArgType_t::STR; +} + + +int ArgTableEntry_String::getCount() { + return m_argStr->count; +} + + +Console::Console() { +} + + +Console::~Console() { +} + diff --git a/cpp_utils/Console.h b/cpp_utils/Console.h new file mode 100644 index 00000000..a94f63a1 --- /dev/null +++ b/cpp_utils/Console.h @@ -0,0 +1,149 @@ +/* + * Console.h + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_CONSOLE_H_ +#define COMPONENTS_CPP_UTILS_CONSOLE_H_ + +#include +#include +#include +#include + +class Console { +public: + Console(); + virtual ~Console(); +}; + + +enum class ArgType_t { LIT, INT, DBL, STR, REX, FILE, DATE }; + + +class ArgTableEntry_Generic { +protected: + ArgType_t m_type; +public: + virtual int getCount(); + bool hasValue() { + return getCount() > 0; + } + virtual void* getEntry() = 0; +}; + + +class ArgTableEntry_Lit : public ArgTableEntry_Generic { +private: + struct arg_lit* m_argLit; +public: + int getCount(); + ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary); + void* getEntry() { + return m_argLit; + } +}; + +class ArgTableEntry_Int : public ArgTableEntry_Generic { +private: + struct arg_int* m_argInt; +public: + ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + int getValue(int index = 0); + void* getEntry() { + return m_argInt; + } +}; + + +class ArgTableEntry_Double : public ArgTableEntry_Generic { +private: + struct arg_dbl* m_argDbl; +public: + ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + double getValue(int index = 0); + void* getEntry() { + return m_argDbl; + } +}; + + +class ArgTableEntry_String : public ArgTableEntry_Generic { +private: + struct arg_str* m_argStr; +public: + ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int getCount(); + std::string getValue(int index = 0); + void* getEntry() { + return m_argStr; + } +}; + + +class ArgTableEntry_Regex : public ArgTableEntry_Generic { +private: + struct arg_rex* m_argRex; +public: + int getCount(); + std::string getValue(int index = 0); + void* getEntry() { + return m_argRex; + } +}; + + +class ArgTableEntry_File : public ArgTableEntry_Generic { +private: + struct arg_file* m_argFile; +public: + ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + std::string getFilename(int index = 0); + std::string getBasename(int index = 0); + std::string getExtension(int index = 0); + void* getEntry() { + return m_argFile; + } +}; + + +class ArgTableEntry_Date : public ArgTableEntry_Generic { +private: + struct arg_date* m_argDate; +public: + ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + struct tm* getValue(int index = 0); + void* getEntry() { + return m_argDate; + } +}; + + +class ArgTable { +private: + void** m_argtable; + struct arg_end* m_argEnd; + std::list> m_argTableEntries; + void build(); + void freeArgtable(); + +public: + ArgTable(); + ~ArgTable(); + ArgTableEntry_Date addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Double addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_File addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Int addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Lit addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_String addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int parse(int argc, char* argv[]); + void printErrors(FILE* fp, std::string progName=""); +}; + +#endif /* COMPONENTS_CPP_UTILS_CONSOLE_H_ */ diff --git a/cpp_utils/DesignNotes/BLECPP.md b/cpp_utils/DesignNotes/BLECPP.md index 9ce9715a..64e7a04c 100644 --- a/cpp_utils/DesignNotes/BLECPP.md +++ b/cpp_utils/DesignNotes/BLECPP.md @@ -40,7 +40,10 @@ The following advertising types are supported: |0x07|Complete list of 128 bit service UUIDs |0x08|Shortened local name |0x09|Complete local name +|0x16|Service data (16 bit) |0x19|Appearance +|0x20|Service data (32 bit) +|0x21|Service data (128 bit) |0xFF|Manufacturer data @@ -61,5 +64,6 @@ See also: |0x03, 0x05, 0x07|`setCompleteServices(BLEUUID)` |0x08|`setShortName(std::string)` |0x09|`setName(std::string)` +|0x16, 0x20, 0x21|`setServiceData(BLEUUID, std::string)` |0x19|`setAppearance(uint16_t)` |0xFF|`setManufacturerData(std::string)` \ No newline at end of file diff --git a/cpp_utils/FTPCallbacks.cpp b/cpp_utils/FTPCallbacks.cpp new file mode 100644 index 00000000..69e462c9 --- /dev/null +++ b/cpp_utils/FTPCallbacks.cpp @@ -0,0 +1,152 @@ +#include "FTPServer.h" +#include +#include +#include +#include + +static const char* LOG_TAG = "FTPCallbacks"; +/** + * Called at the start of a STOR request. The file name is the name of the file the client would like to + * save. + */ +void FTPFileCallbacks::onStoreStart(std::string fileName) { + ESP_LOGD(LOG_TAG, ">> FTPFileCallbacks::onStoreStart: fileName=%s", fileName.c_str()); + m_storeFile.open(fileName, std::ios::binary); // Open the file for writing. + if (m_storeFile.fail()) { + throw FTPServer::FileException(); + } + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreStart"); +} // FTPFileCallbacks#onStoreStart + + +/** + * Called when the client presents a new chunk of data to be saved. + */ +size_t FTPFileCallbacks::onStoreData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onStoreData: size=%d", size); + m_storeFile.write((char*) data, size); // Store data received. + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreData: size=%d", size); + return size; +} // FTPFileCallbacks#onStoreData + + +/** + * Called at the end of a STOR request. This indicates that the client has completed its transmission of the + * file. + */ +void FTPFileCallbacks::onStoreEnd() { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onStoreEnd"); + m_storeFile.close(); // Close the open file. + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onStoreEnd"); +} // FTPFileCallbacks#onStoreEnd + + +/** + * Called when the client requests retrieval of a file. + */ +void FTPFileCallbacks::onRetrieveStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveStart: fileName=%s", fileName.c_str()); + m_byteCount = 0; + m_retrieveFile.open(fileName, std::ios::binary); + if (m_retrieveFile.fail()) { + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveStart: ***FileException***"); + throw FTPServer::FileException(); + } + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveStart"); +} // FTPFileCallbacks#onRetrieveStart + + +/** + * Called when the client is ready to receive the next piece of the file. To indicate that there + * is no more data to send, return a size of 0. + * @param data The data buffer that we can fill to return data back to the client. + * @param size The maximum size of the data buffer that we can populate. + * @return The size of data being returned. Return 0 to indicate that there is no more data to return. + */ +size_t FTPFileCallbacks::onRetrieveData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveData"); + m_retrieveFile.read((char*) data, size); + size_t readSize = m_retrieveFile.gcount(); + m_byteCount += readSize; + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveData: sizeRead=%d", readSize); + return m_retrieveFile.gcount(); // Return the number of bytes read. +} // FTPFileCallbacks#onRetrieveData + + +/** + * Called when the retrieval has been completed. + */ +void FTPFileCallbacks::onRetrieveEnd() { + ESP_LOGD(LOG_TAG,">> FTPFileCallbacks::onRetrieveEnd"); + m_retrieveFile.close(); + ESP_LOGD(LOG_TAG,"<< FTPFileCallbacks::onRetrieveEnd: bytesTransmitted=%d", m_byteCount); +} // FTPFileCallbacks#onRetrieveEnd + + +/** + * Return a list of files in the file system. + * @return a list of files in the file system. + */ +std::string FTPFileCallbacks::onDir() { + DIR* dir = opendir(FTPServer::getCurrentDirectory().c_str()); + std::stringstream ss; + while (true) { + struct dirent* pDirentry = readdir(dir); + if (pDirentry == nullptr) break; + ss << pDirentry->d_name << "\r\n"; + } + closedir(dir); + return ss.str(); +} // FTPFileCallbacks#onDir + + +/// ---- END OF FTPFileCallbacks + + +void FTPCallbacks::onStoreStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreStart: fileName=%s", fileName.c_str()); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreStart"); +} // FTPCallbacks#onStoreStart + + +size_t FTPCallbacks::onStoreData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreData: size=%d", size); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreData"); + return 0; +} // FTPCallbacks#onStoreData + + +void FTPCallbacks::onStoreEnd() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onStoreEnd"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onStoreEnd"); +} // FTPCallbacks#onStoreEnd + + +void FTPCallbacks::onRetrieveStart(std::string fileName) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveStart"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveStart"); +} // FTPCallbacks#onRetrieveStart + + +size_t FTPCallbacks::onRetrieveData(uint8_t* data, size_t size) { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveData"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveData: 0"); + return 0; +} // FTPCallbacks#onRetrieveData + + +void FTPCallbacks::onRetrieveEnd() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onRetrieveEnd"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onRetrieveEnd"); +} // FTPCallbacks#onRetrieveEnd + + +std::string FTPCallbacks::onDir() { + ESP_LOGD(LOG_TAG,">> FTPCallbacks::onDir"); + ESP_LOGD(LOG_TAG,"<< FTPCallbacks::onDir"); + return ""; +} // FTPCallbacks#onDir + +FTPCallbacks::~FTPCallbacks() { + +} // FTPCallbacks#~FTPCallbacks diff --git a/cpp_utils/FTPServer.cpp b/cpp_utils/FTPServer.cpp new file mode 100644 index 00000000..63cf6a9c --- /dev/null +++ b/cpp_utils/FTPServer.cpp @@ -0,0 +1,805 @@ +/* + * FTPServer.cpp + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#include "FTPServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* LOG_TAG = "FTPServer"; + +// trim from start (in place) +static void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} // ltrim + + +// trim from end (in place) +static void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} // rtrim + + +// trim from both ends (in place) +static void trim(std::string &s) { + ltrim(s); + rtrim(s); +} // trim + + +FTPServer::FTPServer() { + ESP_LOGD(LOG_TAG,">> FTPServer()"); + + m_dataSocket = -1; + m_clientSocket = -1; + m_dataPort = -1; + m_dataIp = -1; + m_passiveSocket = -1; + m_serverSocket = -1; + + m_callbacks = nullptr; + m_isPassive = false; + m_isImage = true; + m_chunkSize = 4096; + m_port = 21; // The default Server-PI port + m_loginRequired = false; + m_isAuthenticated = false; + m_userid = ""; + m_password = ""; + + ESP_LOGD(LOG_TAG,"<< FTPServer()"); +} // FTPServer#FTPServer + + +FTPServer::~FTPServer() { + // Nothing to do here by default. +} // FTPServer#~FTPServer + + +/** + * Close the connection to the FTP client. + */ +void FTPServer::closeConnection() { + ESP_LOGD(LOG_TAG,">> closeConnection"); + close(m_clientSocket); + ESP_LOGD(LOG_TAG,"<< closeConnection"); +} // FTPServer#closeConnection + + +/** + * Close a previously opened data connection. + */ +void FTPServer::closeData() { + ESP_LOGD(LOG_TAG,">> closeData"); + close(m_dataSocket); + m_dataSocket = -1; + ESP_LOGD(LOG_TAG,"<< closeData"); +} // FTPServer#closeData + + +/** + * Close the passive listening socket that was opened by listenPassive. + */ +void FTPServer::closePassive() { + ESP_LOGD(LOG_TAG,">> closePassive"); + close(m_passiveSocket); + m_passiveSocket = -1; + ESP_LOGD(LOG_TAG, "<< closePassive"); +} // FTPServer#closePassive + + +/** + * Retrieve the current directory. + */ +/* STATIC */ std::string FTPServer::getCurrentDirectory() { + char maxDirectory[256]; + std::string currentDirectory = getcwd(maxDirectory, sizeof(maxDirectory)); + return currentDirectory; +} // FTPServer#getCurrentDirectory + + +/** + * Create a listening socket for the new passive connection. + * @return a String for the passive parameters. + */ +std::string FTPServer::listenPassive() { + ESP_LOGD(LOG_TAG, ">> listenPassive"); + + m_passiveSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_passiveSocket == -1) { + ESP_LOGD(LOG_TAG, "socket: %s", strerror(errno)); + } + + struct sockaddr_in clientAddrInfo; + unsigned int addrInfoSize = sizeof(clientAddrInfo); + getsockname(m_clientSocket, (struct sockaddr*) &clientAddrInfo, &addrInfoSize); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(0); + int rc = bind(m_passiveSocket, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "bind: %s", strerror(errno)); + } + + rc = listen(m_passiveSocket, 5); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "listen: %s", strerror(errno)); + } + + unsigned int addrLen = sizeof(serverAddress); + rc = getsockname(m_passiveSocket, (struct sockaddr*)&serverAddress, &addrLen); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "getsockname: %s", strerror(errno)); + } + + std::stringstream ss; + ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 16) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 24) & 0xff) << + "," << ((serverAddress.sin_port >> 0) & 0xff) << + "," << ((serverAddress.sin_port >> 8) & 0xff); + std::string retStr = ss.str(); + + ESP_LOGD(LOG_TAG, "<< listenPassive: %s", retStr.c_str()); + return retStr; +} // FTPServer#listenPassive + + +/** + * Handle the AUTH command. + */ +void FTPServer::onAuth(std::istringstream& ss) { + std::string param; + ss >> param; + ESP_LOGD(LOG_TAG, ">> onAuth: %s", param.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + ESP_LOGD(LOG_TAG, "<< onAuth"); +} // FTPServer#onAuth + + +/** + * Change the current working directory. + * @param ss A string stream where the first parameter is the directory to change to. + */ +void FTPServer::onCwd(std::istringstream& ss) { + std::string path; + ss >> path; + ESP_LOGD(LOG_TAG, ">> onCwd: path=%s", path.c_str()); + chdir(path.c_str()); + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); + ESP_LOGD(LOG_TAG, "<< onCwd"); +} // FTPServer#onCwd + + +/** + * Process the client transmitted LIST request. + */ +void FTPServer::onList(std::istringstream& ss) { + std::string directory; + ss >> directory; + ESP_LOGD(LOG_TAG, ">> onList: directory=%s", directory.c_str()); + + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + if (m_callbacks != nullptr) { + std::string dirString = m_callbacks->onDir(); + sendData((uint8_t*) dirString.data(), dirString.length()); + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + ESP_LOGD(LOG_TAG, "<< onList"); +} // FTPServer#onList + + +void FTPServer::onMkd(std::istringstream& ss) { + std::string path; + ss >> path; + ESP_LOGD(LOG_TAG, ">> onMkd: path=%s", path.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onMkd"); +} // FTPServer#onMkd + + +/** + * Process a NOOP operation. + */ +void FTPServer::onNoop(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onNoop"); + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + ESP_LOGD(LOG_TAG, "<< onNoop"); +} // FTPServer#onNoop + + +/** + * Process PORT request. The information provided is encoded in the parameter as + * h1,h2,h3,h4,p1,p2 where h1,h2,h3,h4 is the IP address we should connect to + * and p1,p2 is the port number. The data is MSB + * + * Our logic does not form any connection but remembers the ip address and port number + * to be used for a subsequence data connection. + * + * Possible responses: + * 200 + * 500, 501, 421, 530 + */ +void FTPServer::onPort(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPort"); + char c; + uint16_t h1, h2, h3, h4, p1, p2; + ss >> h1 >> c >> h2 >> c >> h3 >> c >> h4 >> c >> p1 >> c >> p2; + m_dataPort = p1 * 256 + p2; + ESP_LOGD(LOG_TAG, "%d.%d.%d.%d %d", h1, h2, h3, h4, m_dataPort); + m_dataIp = h1 << 24 | h2 << 16 | h3 << 8 | h4; + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + m_isPassive = false; + + ESP_LOGD(LOG_TAG, "<< onPort"); +} // FTPServer#onPort + + +/** + * Process the PASS command. + * Possible responses: + * 230 + * 202 + * 530 + * 500, 501, 503, 421 + * 332 + */ +void FTPServer::onPass(std::istringstream& ss) { + std::string password; + ss >> password; + ESP_LOGD(LOG_TAG, ">> onPass: password=%s", password.c_str()); + + // If the immediate last command wasn't USER then don't try and process PASS. + if (m_lastCommand != "USER") { + sendResponse(RESPONSE_503_BAD_SEQUENCE); + ESP_LOGD(LOG_TAG, "<< onPass"); + return; + } + + // Compare the supplied userid and passwords. + if (m_userid == m_suppliedUserid && password == m_password) { + sendResponse(RESPONSE_230_USER_LOGGED_IN); + m_isAuthenticated = true; + } else { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + closeConnection(); + m_isAuthenticated = false; + } + ESP_LOGD(LOG_TAG, "<< onPass"); +} // FTPServer#onPass + + +/** + * Process the PASV command. + * Possible responses: + * 227 + * 500, 501, 502, 421, 530 + */ +void FTPServer::onPasv(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPasv"); + std::string ipInfo = listenPassive(); + std::ostringstream responseTextSS; + responseTextSS << "Entering Passive Mode (" << ipInfo << ")."; + std::string responseText; + responseText = responseTextSS.str(); + sendResponse(RESPONSE_227_ENTERING_PASSIVE_MODE, responseText.c_str()); + m_isPassive = true; + + ESP_LOGD(LOG_TAG, "<< onPasv"); +} // FTPServer#onPasv + + +/** + * Process the PWD command to determine our current working directory. + * Possible responses: + * 257 + * 500, 501, 502, 421, 550 + */ +void FTPServer::onPWD(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onPWD"); + sendResponse(257, "\"" + getCurrentDirectory() + "\""); + ESP_LOGD(LOG_TAG, "<< onPWD: %s", getCurrentDirectory().c_str()); +} // FTPServer#onPWD + + +/** + * Possible responses: + * 221 + * 500 + */ +void FTPServer::onQuit(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onQuit"); + sendResponse(FTPServer::RESPONSE_221_CLOSING_CONTROL_CONNECTION); // Service closing control connection. + closeConnection(); // Close the connection to the client. + ESP_LOGD(LOG_TAG, "<< onQuit"); +} // FTPServer#onQuit + + +/** + * Process a RETR command. The client sends this command to retrieve the content of a file. + * The name of the file is the first parameter in the input stream. + * + * Possible responses: + * 125, 150 + * (110) + * 226, 250 + * 425, 426, 451 + * 450, 550 + * 500, 501, 421, 530 + * @param ss The parameter stream. + */ +void FTPServer::onRetr(std::istringstream& ss) { + // We open a data connection back to the client. We then invoke the callback to indicate that we have + // started a retrieve operation. We call the retrieve callback to request the next chunk of data and + // transmit this down the data connection. We repeat this until there is no more data to send at which + // point we close the data connection and we are done. + ESP_LOGD(LOG_TAG, ">> onRetr"); + std::string fileName; + + ss >> fileName; + uint8_t data[m_chunkSize]; + + if (m_callbacks != nullptr) { + try { + m_callbacks->onRetrieveStart(fileName); + } catch (FTPServer::FileException& e) { + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + ESP_LOGD(LOG_TAG, "<< onRetr: Returned 550 to client."); + return; + } + } + + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + openData(); + if (m_callbacks != nullptr) { + int readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + while (readSize > 0) { + sendData(data, readSize); + readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + } + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + ESP_LOGD(LOG_TAG, "<< onRetr"); +} // FTPServer#onRetr + + +void FTPServer::onRmd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onRmd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onRmd"); +} // FTPServer#onRmd + + +/** + * Called to process a STOR request. This means that the client wishes to store a file + * on the server. The name of the file is found in the parameter. + */ +void FTPServer::onStor(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onStor"); + std::string fileName; + ss >> fileName; + + receiveFile(fileName); + ESP_LOGD(LOG_TAG, "<< onStor"); +} // FTPServer#onStor + + +void FTPServer::onSyst(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onSyst"); + sendResponse(215, "UNIX Type: L8"); + ESP_LOGD(LOG_TAG, "<< onSyst"); +} // FTPServer#onSyst + + +/** + * Process a TYPE request. The parameter that follows is the type of transfer we wish + * to process. Types include: + * I and A. + * + * Possible responses: + * 200 + * 500, 501, 504, 421, 530 + */ +void FTPServer::onType(std::istringstream& ss) { + ESP_LOGD(LOG_TAG, ">> onType"); + std::string type; + ss >> type; + m_isImage = (type.compare("I") == 0); + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + ESP_LOGD(LOG_TAG, "<< onType: isImage=%d", m_isImage); +} // FTPServer#onType + + +/** + * Process a USER request. The parameter that follows is the identity of the user. + * + * Possible responses: + * 230 + * 530 + * 500, 501, 421 + * 331, 332 + * + */ +void FTPServer::onUser(std::istringstream& ss) { + // When we receive a user command, we next want to know if we should ask for a password. If the m_loginRequired + // flag is set then we do indeed want a password and will send the response that we wish one. + std::string userName; + ss >> userName; + ESP_LOGD(LOG_TAG, ">> onUser: userName=%s", userName.c_str()); + sendResponse(m_loginRequired ? FTPServer::RESPONSE_331_PASSWORD_REQUIRED : FTPServer::RESPONSE_200_COMMAND_OK); + m_suppliedUserid = userName; // Save the username that was supplied. + ESP_LOGD(LOG_TAG, "<< onUser"); +} // FTPServer#onUser + + +void FTPServer::onXmkd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onXmkd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onXmkd"); +} // FTPServer#onXmkd + + +void FTPServer::onXrmd(std::istringstream &ss) { + ESP_LOGD(LOG_TAG, ">> onXrmd"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + ESP_LOGD(LOG_TAG, "<< onXrmd"); +} // FTPServer#onXrmd + + +/** + * Open a data connection with the client. + * We will use closeData() to close the connection. + * @return True if the data connection succeeded. + */ +bool FTPServer::openData() { + if (m_isPassive) { + // Handle a passive connection ... here we receive a connection from the client from the passive socket. + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_dataSocket = accept(m_passiveSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + if (m_dataSocket == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::openData: accept(): %s", strerror(errno)); + closePassive(); + return false; + } + closePassive(); + } else { + // Handle an active connection ... here we connect to the client. + m_dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(m_dataIp); + serverAddress.sin_port = htons(m_dataPort); + + int rc = connect(m_dataSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::openData: connect(): %s", strerror(errno)); + return false; + } + } + return true; +} // FTPServer#openData + + +/** + * Process commands received from the client. + */ +void FTPServer::processCommand() { + sendResponse(FTPServer::RESPONSE_220_SERVICE_READY); // Service ready. + ESP_LOGD(LOG_TAG, ">> FTPServer::processCommand"); + m_lastCommand = ""; + while (true) { + std::string line = ""; + char currentChar; + char lastChar = '\0'; + int rc = recv(m_clientSocket, ¤tChar, 1, 0); + while (rc != -1 && rc != 0) { + line += currentChar; + if (lastChar == '\r' && currentChar == '\n') break; +// printf("%c\n", currentChar); + lastChar = currentChar; + rc = recv(m_clientSocket, ¤tChar, 1, 0); + } // End while we are waiting for a line. + + if (rc == 0 || rc == -1) break; // If we didn't get a line or an error, then we have finished processing commands. + + std::string command; + std::istringstream ss(line); + getline(ss, command, ' '); + trim(command); + + // We now have a command to process. + + ESP_LOGD(LOG_TAG, "Command: \"%s\"", command.c_str()); + if (command.compare("USER") == 0) { + onUser(ss); + } + else if (command.compare("PASS") == 0) { + onPass(ss); + } + else if (m_loginRequired && !m_isAuthenticated) { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + } + else if (command.compare("PASV") == 0) { + onPasv(ss); + } + else if (command.compare("SYST") == 0) { + onSyst(ss); + } + else if (command.compare("PORT") == 0) { + onPort(ss); + } + else if (command.compare("LIST") == 0) { + onList(ss); + } + else if (command.compare("TYPE") == 0) { + onType(ss); + } + else if (command.compare("RETR") == 0) { + onRetr(ss); + } + else if (command.compare("QUIT") == 0) { + onQuit(ss); + } + else if (command.compare("AUTH") == 0) { + onAuth(ss); + } + else if (command.compare("STOR") == 0) { + onStor(ss); + } + else if (command.compare("PWD") == 0) { + onPWD(ss); + } + else if (command.compare("MKD") == 0) { + onMkd(ss); + } + else if (command.compare("XMKD") == 0) { + onXmkd(ss); + } + else if (command.compare("RMD") == 0) { + onRmd(ss); + } + else if (command.compare("XRMD") == 0) { + onXrmd(ss); + } + else if (command.compare("CWD") == 0) { + onCwd(ss); + } + else { + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + } + m_lastCommand = command; + } // End loop processing commands. + + close(m_clientSocket); // We won't be processing any further commands from this client. + ESP_LOGD(LOG_TAG, "<< FTPServer::processCommand"); +} // FTPServer::processCommand + + +/** + * Receive a file from the FTP client (STOR). The name of the file to be created is passed as a + * parameter. + */ +void FTPServer::receiveFile(std::string fileName) { + ESP_LOGD(LOG_TAG, ">> receiveFile: %s", fileName.c_str()); + if (m_callbacks != nullptr) { + try { + m_callbacks->onStoreStart(fileName); + } catch(FTPServer::FileException& e) { + ESP_LOGD(LOG_TAG, "Caught a file exception!"); + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + return; + } + } + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + uint8_t buf[m_chunkSize]; + uint32_t totalSizeRead = 0; + while (true) { + int rc = recv(m_dataSocket, &buf, m_chunkSize, 0); + if (rc <= 0) break; + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveData(buf, rc); + } + totalSizeRead += rc; + } + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + closeData(); + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + ESP_LOGD(LOG_TAG, "<< receiveFile: totalSizeRead=%d", totalSizeRead); +} // FTPServer#receiveFile + + +/** + * Send data to the client over the data connection previously opened with a call to openData(). + * @param pData A pointer to the data to send. + * @param size The number of bytes to send. + */ +void FTPServer::sendData(uint8_t* pData, uint32_t size) { + ESP_LOGD(LOG_TAG, ">> FTPServer::sendData: size=%d", size); + int rc = send(m_dataSocket, pData, size, 0); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "FTPServer::sendData: send(): %s", strerror(errno)); + } + ESP_LOGD(LOG_TAG, "<< FTPServer::sendData"); +} // FTPServer#sendData + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. + */ +void FTPServer::sendResponse(int code, std::string text) { + ESP_LOGD(LOG_TAG, ">> sendResponse: (%d) %s", code, text.c_str()); + std::ostringstream ss; + ss << code << " " << text << "\r\n"; + int rc = send(m_clientSocket, ss.str().data(), ss.str().length(), 0); + if (rc == -1) { + ESP_LOGE(LOG_TAG,"send: %s", strerror(errno)); + } + ESP_LOGD(LOG_TAG, "<< sendResponse"); +} // FTPServer#sendResponse + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. In this function, a standard piece of text is used based on + * the code. + */ +void FTPServer::sendResponse(int code) { + std::string text = "unknown"; + + switch(code) { // Map the code to a text string. + case RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION: + text = "File status okay; about to open data connection."; + break; + case RESPONSE_200_COMMAND_OK: + text = "Command okay."; + break; + case RESPONSE_220_SERVICE_READY: + text = "Service ready."; + break; + case RESPONSE_221_CLOSING_CONTROL_CONNECTION: + text = "Service closing control connection."; + break; + case RESPONSE_226_CLOSING_DATA_CONNECTION: + text = "Closing data connection."; + break; + case RESPONSE_230_USER_LOGGED_IN: + text = "User logged in, proceed."; + break; + case RESPONSE_331_PASSWORD_REQUIRED: + text = "Password required."; + break; + case RESPONSE_500_COMMAND_UNRECOGNIZED: + text = "Syntax error, command unrecognized."; + break; + case RESPONSE_502_COMMAND_NOT_IMPLEMENTED: + text = "Command not implemented."; + break; + case RESPONSE_503_BAD_SEQUENCE: + text = "Bad sequence of commands."; + break; + case RESPONSE_530_NOT_LOGGED_IN: + text = "Not logged in."; + break; + case RESPONSE_550_ACTION_NOT_TAKEN: + text = "Requested action not taken."; + break; + default: + break; + } + sendResponse(code, text); // Send the code AND the text to the FTP client. +} // FTPServer#sendResponse + + +/** + * Set the callbacks that are to be invoked to perform work. + * @param pCallbacks An instance of an FTPCallbacks based class. + */ +void FTPServer::setCallbacks(FTPCallbacks* pCallbacks) { + m_callbacks = pCallbacks; +} // FTPServer#setCallbacks + + +void FTPServer::setCredentials(std::string userid, std::string password) { + ESP_LOGD(LOG_TAG, ">> setCredentials: userid=%s", userid.c_str()); + m_loginRequired = true; + m_userid = userid; + m_password = password; + ESP_LOGD(LOG_TAG, "<< setCredentials"); +} // FTPServer#setCredentials + + +/** + * Set the TCP port we should listen on for FTP client requests. + */ +void FTPServer::setPort(uint16_t port) { + m_port = port; +} // FTPServer#setPort + + +/** + * Start being an FTP Server. + */ +void FTPServer::start() { + m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_serverSocket == -1) { + ESP_LOGD(LOG_TAG, "socket: %s", strerror(errno)); + } + + int enable = 1; + setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_port); + int rc = bind(m_serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "bind: %s", strerror(errno)); + } + rc = listen(m_serverSocket, 5); + if (rc == -1) { + ESP_LOGD(LOG_TAG, "listen: %s", strerror(errno)); + } + while (true) { + waitForFTPClient(); + processCommand(); + } +} // FTPServer#start + + +/** + * Wait for a new client to connect. + */ +int FTPServer::waitForFTPClient() { + ESP_LOGD(LOG_TAG, ">> FTPServer::waitForFTPClient"); + + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_clientSocket = accept(m_serverSocket, (struct sockaddr*) &clientAddress, &clientAddressLength); + + char ipAddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddr, sizeof(ipAddr)); + ESP_LOGD(LOG_TAG, "Received connection from %s [%d]", ipAddr, clientAddress.sin_port); + + struct sockaddr_in socketAddressInfo; + unsigned int socketAddressInfoSize = sizeof(socketAddressInfo); + getsockname(m_clientSocket, (struct sockaddr*) &socketAddressInfo, &socketAddressInfoSize); + + inet_ntop(AF_INET, &socketAddressInfo.sin_addr, ipAddr, sizeof(ipAddr)); + ESP_LOGD(LOG_TAG, "Connected at %s [%d]", ipAddr, socketAddressInfo.sin_port); + ESP_LOGD(LOG_TAG, "<< FTPServer::waitForFTPClient: fd=%d\n", m_clientSocket); + + return m_clientSocket; +} // FTPServer::waitForFTPClient + diff --git a/cpp_utils/FTPServer.h b/cpp_utils/FTPServer.h new file mode 100644 index 00000000..5736201d --- /dev/null +++ b/cpp_utils/FTPServer.h @@ -0,0 +1,138 @@ +/* + * FTPServer.h + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#ifndef NETWORKING_FTPSERVER_FTPSERVER_H_ +#define NETWORKING_FTPSERVER_FTPSERVER_H_ +#include +#include +#include +#include +#include + + +class FTPCallbacks { +public: + virtual void onStoreStart(std::string fileName); + virtual size_t onStoreData(uint8_t* data, size_t size); + virtual void onStoreEnd(); + virtual void onRetrieveStart(std::string fileName); + virtual size_t onRetrieveData(uint8_t *data, size_t size); + virtual void onRetrieveEnd(); + virtual std::string onDir(); + virtual ~FTPCallbacks(); + +}; + +/** + * An implementation of FTPCallbacks that uses Posix File I/O to perform file access. + */ +class FTPFileCallbacks : public FTPCallbacks { +public: + void onStoreStart(std::string fileName) override; // Called for a STOR request. + size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. + void onStoreEnd() override; // Called at the end of a STOR request. + void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. + size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. + void onRetrieveEnd() override; // Called when we have retrieved all the data. + std::string onDir() override; // Called to retrieve all the directory entries. + +private: + std::ofstream m_storeFile; // File used to store data from the client. + std::ifstream m_retrieveFile; // File used to retrieve data for the client. + uint32_t m_byteCount; // Count of bytes sent over wire. + +}; + + +class FTPServer { +public: + FTPServer(); + virtual ~FTPServer(); + void setCredentials(std::string userid, std::string password); + void start(); + void setPort(uint16_t port); + void setCallbacks(FTPCallbacks* pFTPCallbacks); + static std::string getCurrentDirectory(); + class FileException: public std::exception { + }; + + // Response codes. + static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; + static const int RESPONSE_200_COMMAND_OK = 200; + static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; + static const int RESPONSE_212_DIRECTORY_STATUS = 212; + static const int RESPONSE_213_FILE_STATUS = 213; + static const int RESPONSE_214_HELP_MESSAGE = 214; + static const int RESPONSE_220_SERVICE_READY = 220; + static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; + static const int RESPONSE_230_USER_LOGGED_IN = 230; + static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; + static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; + static const int RESPONSE_331_PASSWORD_REQUIRED = 331; + static const int RESPONSE_332_NEED_ACCOUNT = 332; + static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; + static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; + static const int RESPONSE_503_BAD_SEQUENCE = 503; + static const int RESPONSE_530_NOT_LOGGED_IN = 530; + static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; + static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; + +private: + int m_serverSocket; // The socket the FTP server is listening on. + int m_clientSocket; // The current client socket. + int m_dataSocket; // The data socket. + int m_passiveSocket; // The socket on which the server is listening for passive FTP connections. + uint16_t m_port; // The port the FTP server will use. + uint16_t m_dataPort; // The port for data connections. + uint32_t m_dataIp; // The ip address for data connections. + bool m_isPassive; // Are we in passive mode? If not, then we are in active mode. + bool m_isImage; // Are we in image mode? + size_t m_chunkSize; // The maximum chunk size. + std::string m_userid; // The required userid. + std::string m_password; // The required password. + std::string m_suppliedUserid; // The userid supplied from the USER command. + bool m_loginRequired; // Do we required a login? + bool m_isAuthenticated; // Have we authenticated? + std::string m_lastCommand; // The last command that was processed. + + FTPCallbacks* m_callbacks; // The callbacks for processing. + + void closeConnection(); + void closeData(); + void closePassive(); + void onAuth(std::istringstream& ss); + void onCwd(std::istringstream& ss); + void onList(std::istringstream& ss); + void onMkd(std::istringstream& ss); + void onNoop(std::istringstream& ss); + void onPass(std::istringstream& ss); + void onPasv(std::istringstream& ss); + void onPort(std::istringstream& ss); + void onPWD(std::istringstream& ss); + void onQuit(std::istringstream& ss); + void onRetr(std::istringstream& ss); + void onRmd(std::istringstream& ss); + void onStor(std::istringstream& ss); + void onSyst(std::istringstream& ss); + void onType(std::istringstream& ss); + void onUser(std::istringstream& ss); + void onXmkd(std::istringstream& ss); + void onXrmd(std::istringstream& ss); + + bool openData(); + + void receiveFile(std::string fileName); + void sendResponse(int code); + void sendResponse(int code, std::string text); + void sendData(uint8_t* pData, uint32_t size); + std::string listenPassive(); + int waitForFTPClient(); + void processCommand(); + +}; + +#endif /* NETWORKING_FTPSERVER_FTPSERVER_H_ */ diff --git a/cpp_utils/File.cpp b/cpp_utils/File.cpp index 670936aa..3ac4672f 100644 --- a/cpp_utils/File.cpp +++ b/cpp_utils/File.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "GeneralUtils.h" static const char* LOG_TAG = "File"; /** @@ -33,15 +33,13 @@ File::File(std::string path, uint8_t type) { std::string File::getContent(bool base64Encode) { uint32_t size = length(); ESP_LOGD(LOG_TAG, "File:: getContent(), path=%s, length=%d", m_path.c_str(), size); - if (size == 0) { - return ""; - } - uint8_t *pData = (uint8_t *)malloc(size); + if (size == 0) return ""; + uint8_t* pData = (uint8_t*) malloc(size); if (pData == nullptr) { ESP_LOGE(LOG_TAG, "getContent: Failed to allocate memory"); return ""; } - FILE *file = fopen(m_path.c_str(), "r"); + FILE* file = fopen(m_path.c_str(), "r"); fread(pData, size, 1, file); fclose(file); std::string ret((char *)pData, size); @@ -65,19 +63,17 @@ std::string File::getContent(uint32_t offset, uint32_t readSize) { uint32_t fileSize = length(); ESP_LOGD(LOG_TAG, "File:: getContent(), name=%s, fileSize=%d, offset=%d, readSize=%d", m_path.c_str(), fileSize, offset, readSize); - if (fileSize == 0 || offset > fileSize) { - return ""; - } - uint8_t *pData = (uint8_t *)malloc(readSize); + if (fileSize == 0 || offset > fileSize) return ""; + uint8_t* pData = (uint8_t*) malloc(readSize); if (pData == nullptr) { ESP_LOGE(LOG_TAG, "getContent: Failed to allocate memory"); return ""; } - FILE *file = fopen(m_path.c_str(), "r"); + FILE* file = fopen(m_path.c_str(), "r"); fseek(file, offset, SEEK_SET); size_t bytesRead = fread(pData, 1, readSize, file); fclose(file); - std::string ret((char *)pData, bytesRead); + std::string ret((char*) pData, bytesRead); free(pData); return ret; } // getContent @@ -92,10 +88,8 @@ std::string File::getPath() { */ std::string File::getName() { size_t pos = m_path.find_last_of('/'); - if (pos == std::string::npos) { - return m_path; - } - return m_path.substr(pos+1); + if (pos == std::string::npos) return m_path; + return m_path.substr(pos + 1); } // getName @@ -116,10 +110,8 @@ uint8_t File::getType() { uint32_t File::length() { struct stat buf; int rc = stat(m_path.c_str(), &buf); - if (rc != 0 || S_ISDIR(buf.st_mode)) { - return 0; - } - return buf.st_size; + if (rc != 0 || S_ISDIR(buf.st_mode)) return 0; + return (uint32_t) buf.st_size; } // length @@ -130,8 +122,6 @@ uint32_t File::length() { bool File::isDirectory() { struct stat buf; int rc = stat(m_path.c_str(), &buf); - if (rc != 0) { - return false; - } + if (rc != 0) return false; return S_ISDIR(buf.st_mode); } // isDirectory diff --git a/cpp_utils/File.h b/cpp_utils/File.h index 19ef7c34..c1c18014 100644 --- a/cpp_utils/File.h +++ b/cpp_utils/File.h @@ -17,7 +17,7 @@ class File { public: File(std::string name, uint8_t type = DT_UNKNOWN); - std::string getContent(bool base64Encode=false); + std::string getContent(bool base64Encode = false); std::string getContent(uint32_t offset, uint32_t size); std::string getName(); std::string getPath(); diff --git a/cpp_utils/FileSystem.cpp b/cpp_utils/FileSystem.cpp index 564c68b4..1920235c 100644 --- a/cpp_utils/FileSystem.cpp +++ b/cpp_utils/FileSystem.cpp @@ -26,25 +26,28 @@ static const char* LOG_TAG = "FileSystem"; * @return N/A. */ void FileSystem::dumpDirectory(std::string path) { - DIR *pDir = ::opendir(path.c_str()); + DIR* pDir = ::opendir(path.c_str()); if (pDir == nullptr) { ESP_LOGD(LOG_TAG, "Unable to open directory: %s [errno=%d]", path.c_str(), errno); return; } - struct dirent *pDirent; + struct dirent* pDirent; ESP_LOGD(LOG_TAG, "Directory dump of %s", path.c_str()); - while((pDirent = readdir(pDir)) != nullptr) { + while ((pDirent = readdir(pDir)) != nullptr) { std::string type; - switch(pDirent->d_type) { - case DT_UNKNOWN: - type = "Unknown"; - break; - case DT_REG: - type = "Regular"; - break; - case DT_DIR: - type = "Directory"; - break; + switch (pDirent->d_type) { + case DT_UNKNOWN: + type = "Unknown"; + break; + case DT_REG: + type = "Regular"; + break; + case DT_DIR: + type = "Directory"; + break; + default: + type = "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Entry: d_ino: %d, d_name: %s, d_type: %s", pDirent->d_ino, pDirent->d_name, type.c_str()); } @@ -59,14 +62,14 @@ void FileSystem::dumpDirectory(std::string path) { */ std::vector FileSystem::getDirectoryContents(std::string path) { std::vector ret; - DIR *pDir = ::opendir(path.c_str()); + DIR* pDir = ::opendir(path.c_str()); if (pDir == nullptr) { ESP_LOGE(LOG_TAG, "getDirectoryContents:: Unable to open directory: %s [errno=%d]", path.c_str(), errno); return ret; } - struct dirent *pDirent; + struct dirent* pDirent; ESP_LOGD(LOG_TAG, "Directory dump of %s", path.c_str()); - while((pDirent = readdir(pDir)) != nullptr) { + while ((pDirent = readdir(pDir)) != nullptr) { File file(path +"/" + std::string(pDirent->d_name), pDirent->d_type); ret.push_back(file); } @@ -82,9 +85,7 @@ std::vector FileSystem::getDirectoryContents(std::string path) { bool FileSystem::isDirectory(std::string path) { struct stat statBuf; int rc = stat(path.c_str(), &statBuf); - if (rc != 0) { - return false; - } + if (rc != 0) return false; return S_ISDIR(statBuf.st_mode); } // isDirectory @@ -129,11 +130,11 @@ std::vector FileSystem::pathSplit(std::string path) { std::istringstream stream(path); std::vector ret; std::string pathPart; - while(std::getline(stream, pathPart, '/')) { + while (std::getline(stream, pathPart, '/')) { ret.push_back(pathPart); } // Debug - for (int i=0; i #include #include "FreeRTOS.h" -#include #include "sdkconfig.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 = "FreeRTOS"; +#endif /** * Sleep for the specified number of milliseconds. * @param[in] ms The period in milliseconds for which to sleep. */ void FreeRTOS::sleep(uint32_t ms) { - ::vTaskDelay(ms/portTICK_PERIOD_MS); + ::vTaskDelay(ms / portTICK_PERIOD_MS); } // sleep @@ -32,7 +36,7 @@ void FreeRTOS::sleep(uint32_t ms) { * @param[in] param An optional parameter to be passed to the started task. * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. */ -void FreeRTOS::startTask(void task(void*), std::string taskName, void *param, int stackSize) { +void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) { ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); } // startTask @@ -51,23 +55,9 @@ void FreeRTOS::deleteTask(TaskHandle_t pTask) { * @return The time in milliseconds since the %FreeRTOS scheduler started. */ uint32_t FreeRTOS::getTimeSinceStart() { - return (uint32_t)(xTaskGetTickCount()*portTICK_PERIOD_MS); + return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS); } // getTimeSinceStart -/* - * public: - Semaphore(std::string = ""); - ~Semaphore(); - void give(); - void take(std::string owner=""); - void take(uint32_t timeoutMs, std::string owner=""); - private: - SemaphoreHandle_t m_semaphore; - std::string m_name; - std::string m_owner; - }; - * - */ /** * @brief Wait for a semaphore to be released by trying to take it and @@ -77,7 +67,8 @@ uint32_t FreeRTOS::getTimeSinceStart() { */ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); - + + m_owner = owner; if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); @@ -85,8 +76,6 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { xSemaphoreTake(m_semaphore, portMAX_DELAY); } - m_owner = owner; - if (m_usePthreads) { pthread_mutex_unlock(&m_pthread_mutex); } else { @@ -94,7 +83,6 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { } ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); - m_owner = std::string(""); return m_value; } // wait @@ -104,7 +92,8 @@ FreeRTOS::Semaphore::Semaphore(std::string name) { if (m_usePthreads) { pthread_mutex_init(&m_pthread_mutex, nullptr); } else { - m_semaphore = xSemaphoreCreateMutex(); + m_semaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(m_semaphore); } m_name = name; @@ -133,9 +122,9 @@ void FreeRTOS::Semaphore::give() { } else { xSemaphoreGive(m_semaphore); } -#ifdef ARDUINO_ARCH_ESP32 - FreeRTOS::sleep(10); -#endif +// #ifdef ARDUINO_ARCH_ESP32 +// FreeRTOS::sleep(10); +// #endif m_owner = std::string(""); } // Semaphore::give @@ -168,17 +157,24 @@ void FreeRTOS::Semaphore::giveFromISR() { /** * @brief Take a semaphore. * Take a semaphore and wait indefinitely. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. */ -void FreeRTOS::Semaphore::take(std::string owner) -{ +bool FreeRTOS::Semaphore::take(std::string owner) { ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); } else { - xSemaphoreTake(m_semaphore, portMAX_DELAY); + rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; } m_owner = owner; - ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + if (rc) { + ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; } // Semaphore::take @@ -186,21 +182,32 @@ void FreeRTOS::Semaphore::take(std::string owner) * @brief Take a semaphore. * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. * @param [in] timeoutMs Timeout in milliseconds. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. */ -void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { - +bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); - + bool rc = false; if (m_usePthreads) { - assert(false); + assert(false); // We apparently don't have a timed wait for pthreads. } else { - xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS); + rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; } m_owner = owner; - ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + if (rc) { + ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; } // Semaphore::take + +/** + * @brief Create a string representation of the semaphore. + * @return A string representation of the semaphore. + */ std::string FreeRTOS::Semaphore::toString() { std::stringstream stringStream; stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; @@ -208,6 +215,10 @@ std::string FreeRTOS::Semaphore::toString() { } // toString +/** + * @brief Set the name of the semaphore. + * @param [in] name The name of the semaphore. + */ void FreeRTOS::Semaphore::setName(std::string name) { m_name = name; } // setName @@ -255,8 +266,8 @@ void Ringbuffer::returnItem(void* item) { * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. * @return */ -uint32_t Ringbuffer::send(void* data, size_t length, TickType_t wait) { - return ::xRingbufferSend(m_handle, data, length, wait); +bool Ringbuffer::send(void* data, size_t length, TickType_t wait) { + return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE; } // send diff --git a/cpp_utils/FreeRTOS.h b/cpp_utils/FreeRTOS.h index 43a3b8f4..b861c875 100644 --- a/cpp_utils/FreeRTOS.h +++ b/cpp_utils/FreeRTOS.h @@ -23,7 +23,7 @@ class FreeRTOS { public: static void sleep(uint32_t ms); - static void startTask(void task(void *), std::string taskName, void *param=nullptr, int stackSize = 2048); + static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048); static void deleteTask(TaskHandle_t pTask = nullptr); static uint32_t getTimeSinceStart(); @@ -36,10 +36,10 @@ class FreeRTOS { void give(uint32_t value); void giveFromISR(); void setName(std::string name); - void take(std::string owner=""); - void take(uint32_t timeoutMs, std::string owner=""); + bool take(std::string owner = ""); + bool take(uint32_t timeoutMs, std::string owner = ""); std::string toString(); - uint32_t wait(std::string owner=""); + uint32_t wait(std::string owner = ""); private: SemaphoreHandle_t m_semaphore; @@ -48,6 +48,7 @@ class FreeRTOS { std::string m_owner; uint32_t m_value; bool m_usePthreads; + }; }; @@ -62,7 +63,7 @@ class Ringbuffer { void* receive(size_t* size, TickType_t wait = portMAX_DELAY); void returnItem(void* item); - uint32_t send(void* data, size_t length, TickType_t wait = portMAX_DELAY); + bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY); private: RingbufHandle_t m_handle; }; diff --git a/cpp_utils/FreeRTOSTimer.cpp b/cpp_utils/FreeRTOSTimer.cpp index 4baec3e2..4b096000 100644 --- a/cpp_utils/FreeRTOSTimer.cpp +++ b/cpp_utils/FreeRTOSTimer.cpp @@ -11,10 +11,10 @@ #include "FreeRTOSTimer.h" -static std::map timersMap; +static std::map timersMap; void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { - FreeRTOSTimer *timer = timersMap.at(xTimer); + FreeRTOSTimer* timer = timersMap.at(xTimer); timer->callback(timer); } @@ -33,7 +33,7 @@ void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { * * @code{.cpp} * void callback(FreeRTOSTimer *pTimer) { - * // Callback code here ... + * // Callback code here ... * } * @endcode * @@ -43,12 +43,7 @@ void FreeRTOSTimer::internalCallback(TimerHandle_t xTimer) { * @param [in] data Data to be passed to the callback. * @param [in] callback Callback function to be fired when the timer expires. */ -FreeRTOSTimer::FreeRTOSTimer( - char *name, - TickType_t period, - UBaseType_t reload, - void *data, - void (*callback)(FreeRTOSTimer *pTimer)) { +FreeRTOSTimer::FreeRTOSTimer(char* name, TickType_t period, UBaseType_t reload, void* data, void (*callback)(FreeRTOSTimer* pTimer)) { /* * The callback function to actually be called is saved as member data in the object and * a static callback function is called. This will be passed the FreeRTOS timer handle @@ -124,7 +119,7 @@ void FreeRTOSTimer::changePeriod(TickType_t newPeriod, TickType_t blockTime) { * * @return The name of the timer. */ -const char *FreeRTOSTimer::getName() { +const char* FreeRTOSTimer::getName() { return ::pcTimerGetTimerName(timerHandle); } // getName @@ -134,6 +129,6 @@ const char *FreeRTOSTimer::getName() { * * @return The user supplied data associated with the timer. */ -void *FreeRTOSTimer::getData() { +void* FreeRTOSTimer::getData() { return ::pvTimerGetTimerID(timerHandle); } // getData diff --git a/cpp_utils/FreeRTOSTimer.h b/cpp_utils/FreeRTOSTimer.h index 8556ea3f..5c71a0e7 100644 --- a/cpp_utils/FreeRTOSTimer.h +++ b/cpp_utils/FreeRTOSTimer.h @@ -15,21 +15,22 @@ */ class FreeRTOSTimer { public: - FreeRTOSTimer(char *name, TickType_t period, UBaseType_t reload, void *data, void (*callback)(FreeRTOSTimer *pTimer)); + FreeRTOSTimer(char* name, TickType_t period, UBaseType_t reload, void* data, void (*callback)(FreeRTOSTimer* pTimer)); virtual ~FreeRTOSTimer(); - void changePeriod(TickType_t newPeriod, TickType_t blockTime=portMAX_DELAY); - void *getData(); - const char *getName(); + void changePeriod(TickType_t newPeriod, TickType_t blockTime = portMAX_DELAY); + void* getData(); + const char* getName(); TickType_t getPeriod(); - void reset(TickType_t blockTime=portMAX_DELAY); - void start(TickType_t blockTime=portMAX_DELAY); - void stop(TickType_t blockTime=portMAX_DELAY); + void reset(TickType_t blockTime = portMAX_DELAY); + void start(TickType_t blockTime = portMAX_DELAY); + void stop(TickType_t blockTime = portMAX_DELAY); private: TimerHandle_t timerHandle; TickType_t period; - void (*callback)(FreeRTOSTimer *pTimer); + void (*callback)(FreeRTOSTimer* pTimer); static void internalCallback(TimerHandle_t xTimer); + }; #endif /* COMPONENTS_CPP_UTILS_FREERTOSTIMER_H_ */ diff --git a/cpp_utils/GPIO.cpp b/cpp_utils/GPIO.cpp index 4188d307..63941306 100644 --- a/cpp_utils/GPIO.cpp +++ b/cpp_utils/GPIO.cpp @@ -9,6 +9,7 @@ #include #include "sdkconfig.h" #include +#include #include "GeneralUtils.h" static const char* LOG_TAG = "GPIO"; @@ -21,15 +22,11 @@ static bool g_isrServiceInstalled = false; * @param [in] handler The function to be invoked when the interrupt is detected. * @param [in] pArgs Optional arguments to pass to the handler. */ -void ESP32CPP::GPIO::addISRHandler( - gpio_num_t pin, - gpio_isr_t handler, - void* pArgs) { - +void ESP32CPP::GPIO::addISRHandler(gpio_num_t pin, gpio_isr_t handler, void* pArgs) { ESP_LOGD(LOG_TAG, ">> addISRHandler: pin=%d", pin); // If we have not yet installed the ISR service handler, install it now. - if (g_isrServiceInstalled == false) { + if (!g_isrServiceInstalled) { ESP_LOGD(LOG_TAG, "Installing the global ISR service"); esp_err_t errRc = ::gpio_install_isr_service(0); if (errRc != ESP_OK) { @@ -69,10 +66,7 @@ void ESP32CPP::GPIO::high(gpio_num_t pin) { * @return The value of true if the pin is valid and false otherwise. */ bool ESP32CPP::GPIO::inRange(gpio_num_t pin) { - if (pin>=0 && pin<=39) { - return true; - } - return false; + return (pin >= 0 && pin <= 39); } // inRange @@ -123,7 +117,7 @@ void ESP32CPP::GPIO::low(gpio_num_t pin) { * @return True if the pin is high, false if the pin is low. */ bool ESP32CPP::GPIO::read(gpio_num_t pin) { - return ::gpio_get_level(pin); + return ::gpio_get_level(pin) == 1; } // read @@ -154,14 +148,11 @@ void ESP32CPP::GPIO::setInput(gpio_num_t pin) { * @param [in] intrType The type of interrupt. * @return N/A. */ -void ESP32CPP::GPIO::setInterruptType( - gpio_num_t pin, - gpio_int_type_t intrType) { +void ESP32CPP::GPIO::setInterruptType(gpio_num_t pin, gpio_int_type_t intrType) { esp_err_t rc = ::gpio_set_intr_type(pin, intrType); if (rc != ESP_OK) { ESP_LOGE(LOG_TAG, "setInterruptType: %d", rc); } - } // setInterruptType @@ -188,7 +179,7 @@ void ESP32CPP::GPIO::setOutput(gpio_num_t pin) { */ void ESP32CPP::GPIO::write(gpio_num_t pin, bool value) { //ESP_LOGD(LOG_TAG, ">> write: pin: %d, value: %d", pin, value); - esp_err_t errRc = ::gpio_set_level(pin, value); + esp_err_t errRc = ::gpio_set_level(pin, value ? 1 : 0); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "<< gpio_set_level: pin=%d, rc=%d %s", pin, errRc, GeneralUtils::errorToString(errRc)); } @@ -203,9 +194,9 @@ void ESP32CPP::GPIO::write(gpio_num_t pin, bool value) { */ void ESP32CPP::GPIO::writeByte(gpio_num_t pins[], uint8_t value, int bits) { ESP_LOGD(LOG_TAG, ">> writeByte: value: %.2x, bits: %d", value, bits); - for (int i=0; i -namespace ESP32CPP -{ +namespace ESP32CPP { /** * @brief Interface to %GPIO functions. * diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index fd0c740c..019c81bd 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -6,35 +6,43 @@ */ #include "GeneralUtils.h" -#include #include #include #include #include #include #include -#include +#include "FreeRTOS.h" #include #include #include +#include +#include +#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 = "GeneralUtils"; +#endif + static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; static int base64EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; } // base64EncodedLength -static int base64EncodedLength(const std::string &in) { +static int base64EncodedLength(const std::string& in) { return base64EncodedLength(in.length()); } // base64EncodedLength -static void a3_to_a4(unsigned char * a4, unsigned char * a3) { +static void a3_to_a4(unsigned char* a4, unsigned char* a3) { a4[0] = (a3[0] & 0xfc) >> 2; a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); @@ -42,7 +50,7 @@ static void a3_to_a4(unsigned char * a4, unsigned char * a3) { } // a3_to_a4 -static void a4_to_a3(unsigned char * a3, unsigned char * a4) { +static void a4_to_a3(unsigned char* a3, unsigned char* a4) { a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; @@ -54,7 +62,7 @@ static void a4_to_a3(unsigned char * a3, unsigned char * a4) { * @param [in] in * @param [out] out */ -bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { +bool GeneralUtils::base64Encode(const std::string& in, std::string* out) { int i = 0, j = 0; size_t enc_len = 0; unsigned char a3[3]; @@ -98,6 +106,23 @@ bool GeneralUtils::base64Encode(const std::string &in, std::string *out) { } // base64Encode +/** + * @brief Dump general info to the log. + * Data includes: + * * Amount of free RAM + */ +void GeneralUtils::dumpInfo() { + size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + ESP_LOGV(LOG_TAG, "--- dumpInfo ---"); + ESP_LOGV(LOG_TAG, "Free heap: %d", freeHeap); + ESP_LOGV(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); + ESP_LOGV(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); + ESP_LOGV(LOG_TAG, "---"); +} // dumpInfo + + /** * @brief Does the string end with a specific character? * @param [in] str The string to examine. @@ -108,16 +133,16 @@ bool GeneralUtils::endsWith(std::string str, char c) { if (str.empty()) { return false; } - if (str.at(str.length()-1) == c) { + if (str.at(str.length() - 1) == c) { return true; } return false; } // endsWidth -static int DecodedLength(const std::string &in) { +static int DecodedLength(const std::string& in) { int numEq = 0; - int n = in.size(); + int n = (int) in.size(); for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; @@ -141,7 +166,7 @@ static unsigned char b64_lookup(unsigned char c) { * @param [in] in The string to be decoded. * @param [out] out The resulting data. */ -bool GeneralUtils::base64Decode(const std::string &in, std::string *out) { +bool GeneralUtils::base64Decode(const std::string& in, std::string* out) { int i = 0, j = 0; size_t dec_len = 0; unsigned char a3[3]; @@ -212,7 +237,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { if (index % 16 == 0) { strcpy(hexBuf, hex.str().c_str()); strcpy(asciiBuf, ascii.str().c_str()); - ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); hex.str(""); ascii.str(""); } @@ -224,8 +249,8 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { } strcpy(hexBuf, hex.str().c_str()); strcpy(asciiBuf, ascii.str().c_str()); - ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf); - //ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); + //ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); } FreeRTOS::sleep(1000); } @@ -247,7 +272,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { } index++; if (index % 16 == 0) { - ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); hex.str(""); ascii.str(""); } @@ -257,7 +282,7 @@ void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { hex << " "; index++; } - ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); } FreeRTOS::sleep(1000); } @@ -277,11 +302,12 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { char tempBuf[80]; uint32_t lineNumber = 0; - ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ----------------"); + ESP_LOGV(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + ESP_LOGV(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); strcpy(ascii, ""); strcpy(hex, ""); - uint32_t index=0; - while(index < length) { + uint32_t index = 0; + while (index < length) { sprintf(tempBuf, "%.2x ", pData[index]); strcat(hex, tempBuf); if (isprint(pData[index])) { @@ -292,18 +318,18 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { strcat(ascii, tempBuf); index++; if (index % 16 == 0) { - ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); strcpy(ascii, ""); strcpy(hex, ""); lineNumber++; } } if (index %16 != 0) { - while(index % 16 != 0) { + while (index % 16 != 0) { strcat(hex, " "); index++; } - ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii); + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); } } // hexDump @@ -315,36 +341,55 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { */ std::string GeneralUtils::ipToString(uint8_t *ip) { std::stringstream s; - s << (int)ip[0] << '.' << (int)ip[1] << '.' << (int)ip[2] << '.' << (int)ip[3]; + s << (int) ip[0] << '.' << (int) ip[1] << '.' << (int) ip[2] << '.' << (int) ip[3]; return s.str(); } // ipToString +/** + * @brief Split a string into parts based on a delimiter. + * @param [in] source The source string to split. + * @param [in] delimiter The delimiter characters. + * @return A vector of strings that are the split of the input. + */ +std::vector GeneralUtils::split(std::string source, char delimiter) { + // See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g + std::vector strings; + std::istringstream iss(source); + std::string s; + while (std::getline(iss, s, delimiter)) { + strings.push_back(trim(s)); + } + return strings; +} // split + + /** * @brief Convert an ESP error code to a string. * @param [in] errCode The errCode to be converted. * @return A string representation of the error code. */ const char* GeneralUtils::errorToString(esp_err_t errCode) { - switch(errCode) { + switch (errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 case ESP_OK: - return "OK"; + return "ESP_OK"; case ESP_FAIL: - return "Fail"; + return "ESP_FAIL"; case ESP_ERR_NO_MEM: - return "No memory"; + return "ESP_ERR_NO_MEM"; case ESP_ERR_INVALID_ARG: - return "Invalid argument"; + return "ESP_ERR_INVALID_ARG"; case ESP_ERR_INVALID_SIZE: - return "Invalid state"; + return "ESP_ERR_INVALID_SIZE"; case ESP_ERR_INVALID_STATE: - return "Invalid state"; + return "ESP_ERR_INVALID_STATE"; case ESP_ERR_NOT_FOUND: - return "Not found"; + return "ESP_ERR_NOT_FOUND"; case ESP_ERR_NOT_SUPPORTED: - return "Not supported"; + return "ESP_ERR_NOT_SUPPORTED"; case ESP_ERR_TIMEOUT: - return "Timeout"; + return "ESP_ERR_TIMEOUT"; case ESP_ERR_NVS_NOT_INITIALIZED: return "ESP_ERR_NVS_NOT_INITIALIZED"; case ESP_ERR_NVS_NOT_FOUND: @@ -393,9 +438,107 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) { return "ESP_ERR_WIFI_TIMEOUT"; case ESP_ERR_WIFI_WAKE_FAIL: return "ESP_ERR_WIFI_WAKE_FAIL"; +#endif + default: + return "Unknown ESP_ERR error"; } - return "Unknown ESP_ERR error"; } // errorToString +/** + * @brief Convert a wifi_err_reason_t code to a string. + * @param [in] errCode The errCode to be converted. + * @return A string representation of the error code. + * + * @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h. + */ +const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { + if (errCode == ESP_OK) return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)"; + if (errCode == UINT8_MAX) return "Not Connected (default value)"; + + switch ((wifi_err_reason_t) errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case WIFI_REASON_UNSPECIFIED: + return "WIFI_REASON_UNSPECIFIED"; + case WIFI_REASON_AUTH_EXPIRE: + return "WIFI_REASON_AUTH_EXPIRE"; + case WIFI_REASON_AUTH_LEAVE: + return "WIFI_REASON_AUTH_LEAVE"; + case WIFI_REASON_ASSOC_EXPIRE: + return "WIFI_REASON_ASSOC_EXPIRE"; + case WIFI_REASON_ASSOC_TOOMANY: + return "WIFI_REASON_ASSOC_TOOMANY"; + case WIFI_REASON_NOT_AUTHED: + return "WIFI_REASON_NOT_AUTHED"; + case WIFI_REASON_NOT_ASSOCED: + return "WIFI_REASON_NOT_ASSOCED"; + case WIFI_REASON_ASSOC_LEAVE: + return "WIFI_REASON_ASSOC_LEAVE"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "WIFI_REASON_ASSOC_NOT_AUTHED"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "WIFI_REASON_DISASSOC_PWRCAP_BAD"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "WIFI_REASON_DISASSOC_SUPCHAN_BAD"; + case WIFI_REASON_IE_INVALID: + return "WIFI_REASON_IE_INVALID"; + case WIFI_REASON_MIC_FAILURE: + return "WIFI_REASON_MIC_FAILURE"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "WIFI_REASON_IE_IN_4WAY_DIFFERS"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "WIFI_REASON_GROUP_CIPHER_INVALID"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "WIFI_REASON_PAIRWISE_CIPHER_INVALID"; + case WIFI_REASON_AKMP_INVALID: + return "WIFI_REASON_AKMP_INVALID"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "WIFI_REASON_UNSUPP_RSN_IE_VERSION"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "WIFI_REASON_INVALID_RSN_IE_CAP"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "WIFI_REASON_802_1X_AUTH_FAILED"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "WIFI_REASON_CIPHER_SUITE_REJECTED"; + case WIFI_REASON_BEACON_TIMEOUT: + return "WIFI_REASON_BEACON_TIMEOUT"; + case WIFI_REASON_NO_AP_FOUND: + return "WIFI_REASON_NO_AP_FOUND"; + case WIFI_REASON_AUTH_FAIL: + return "WIFI_REASON_AUTH_FAIL"; + case WIFI_REASON_ASSOC_FAIL: + return "WIFI_REASON_ASSOC_FAIL"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_HANDSHAKE_TIMEOUT"; +#endif + default: + return "Unknown ESP_ERR error"; + } +} // wifiErrorToString +/** + * @brief Convert a string to lower case. + * @param [in] value The string to convert to lower case. + * @return A lower case representation of the string. + */ +std::string GeneralUtils::toLower(std::string& value) { + // Question: Could this be improved with a signature of: + // std::string& GeneralUtils::toLower(std::string& value) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + return value; +} // toLower + + +/** + * @brief Remove white space from a string. + */ +std::string GeneralUtils::trim(const std::string& str) { + size_t first = str.find_first_not_of(' '); + if (std::string::npos == first) return str; + size_t last = str.find_last_not_of(' '); + return str.substr(first, (last - first + 1)); +} // trim diff --git a/cpp_utils/GeneralUtils.h b/cpp_utils/GeneralUtils.h index 54aae5e3..8eecbd4d 100644 --- a/cpp_utils/GeneralUtils.h +++ b/cpp_utils/GeneralUtils.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include /** * @brief General utilities. @@ -18,10 +20,15 @@ class GeneralUtils { public: static bool base64Decode(const std::string& in, std::string* out); static bool base64Encode(const std::string& in, std::string* out); + static void dumpInfo(); static bool endsWith(std::string str, char c); static const char* errorToString(esp_err_t errCode); + static const char* wifiErrorToString(uint8_t value); static void hexDump(const uint8_t* pData, uint32_t length); static std::string ipToString(uint8_t* ip); + static std::vector split(std::string source, char delimiter); + static std::string toLower(std::string& value); + static std::string trim(const std::string& str); }; diff --git a/cpp_utils/HIDKeyboardTypes.h b/cpp_utils/HIDKeyboardTypes.h new file mode 100644 index 00000000..4e221d57 --- /dev/null +++ b/cpp_utils/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/cpp_utils/HIDTypes.h b/cpp_utils/HIDTypes.h new file mode 100644 index 00000000..64850ef8 --- /dev/null +++ b/cpp_utils/HIDTypes.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#ifdef ARDUINO_ARCH_ESP32 +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#else +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#endif +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/cpp_utils/HttpParser.cpp b/cpp_utils/HttpParser.cpp index 9594316a..a6bbbc34 100644 --- a/cpp_utils/HttpParser.cpp +++ b/cpp_utils/HttpParser.cpp @@ -10,6 +10,7 @@ #include #include "HttpParser.h" #include "HttpRequest.h" +#include "GeneralUtils.h" #include @@ -35,32 +36,17 @@ static const char* LOG_TAG = "HttpParser"; static std::string lineTerminator = "\r\n"; -/** - * @brief Remove white space from a string. - */ -static std::string trim(const std::string& str) -{ - size_t first = str.find_first_not_of(' '); - if (std::string::npos == first) - { - return str; - } - size_t last = str.find_last_not_of(' '); - return str.substr(first, (last - first + 1)); -} // trim - - /** * @brief Parse an incoming line of text until we reach a delimiter. * @param [in/out] it The current iterator in the text input. * @param [in] str The string we are parsing. * @param [in] token The token delimiter. */ -static std::string toStringToken(std::string::iterator &it, std::string &str, std::string &token) { +static std::string toStringToken(std::string::iterator& it, std::string& str, std::string& token) { std::string ret; std::string part; auto itToken = token.begin(); - for(; it != str.end(); ++it) { + for (; it != str.end(); ++it) { if ((*it) == (*itToken)) { part += (*itToken); ++itToken; @@ -88,7 +74,7 @@ static std::string toStringToken(std::string::iterator &it, std::string &str, st * @param [in] token The token terminating the parse. * @return The parsed string token. */ -static std::string toCharToken(std::string::iterator &it, std::string &str, char token) { +static std::string toCharToken(std::string::iterator& it, std::string& str, char token) { std::string ret; for(; it != str.end(); ++it) { if ((*it) == token) { @@ -111,21 +97,20 @@ static std::string toCharToken(std::string::iterator &it, std::string &str, char * @param [in] line The line of text to parse. * @return A pair of the form name/value. */ -std::pair parseHeader(std::string &line) { - auto it = line.begin(); - auto name = toCharToken(it, line, ':'); - auto value = trim(toStringToken(it, line, lineTerminator)); +std::pair parseHeader(std::string& line) { + auto it = line.begin(); + std::string name = toCharToken(it, line, ':'); // Parse the line until we find a ':' + // We normalize the header name to be lower case. + GeneralUtils::toLower(name); + auto value = GeneralUtils::trim(toStringToken(it, line, lineTerminator)); return std::pair(name, value); } // parseHeader HttpParser::HttpParser() { - // TODO Auto-generated constructor stub - } HttpParser::~HttpParser() { - // TODO Auto-generated destructor stub } @@ -148,17 +133,23 @@ std::string HttpParser::getBody() { } +/** + * @brief Retrieve the value of the named header. + * @param [in] name The name of the header to retrieve. + * @return The value of the named header or null if not present. + */ std::string HttpParser::getHeader(const std::string& name) { - if (!hasHeader(name)) { - return ""; - } - return m_headers.at(name); -} + // We normalize the header name to be lower case. + std::string localName = name; + GeneralUtils::toLower(localName); + if (!hasHeader(localName)) return ""; + return m_headers.at(localName); +} // getHeader std::map HttpParser::getHeaders() { return m_headers; -} +} // getHeaders std::string HttpParser::getMethod() { @@ -175,6 +166,13 @@ std::string HttpParser::getVersion() { return m_version; } // getVersion +std::string HttpParser::getStatus() { + return m_status; +} // getStatus + +std::string HttpParser::getReason() { + return m_reason; +} // getReason /** * @brief Determine if we have a header of the given name. @@ -182,7 +180,9 @@ std::string HttpParser::getVersion() { * @return True if the header is present and false otherwise. */ bool HttpParser::hasHeader(const std::string& name) { - return m_headers.find(name) != m_headers.end(); + // We normalize the header name to be lower case. + std::string localName = name; + return m_headers.find(GeneralUtils::toLower(localName)) != m_headers.end(); } // hasHeader @@ -191,16 +191,18 @@ bool HttpParser::hasHeader(const std::string& name) { * @param [in] s The socket from which to retrieve data. */ void HttpParser::parse(Socket s) { + ESP_LOGD(LOG_TAG, ">> parse: socket: %s", s.toString().c_str()); std::string line; line = s.readToDelim(lineTerminator); parseRequestLine(line); line = s.readToDelim(lineTerminator); - while(!line.empty()) { + while (!line.empty()) { m_headers.insert(parseHeader(line)); line = s.readToDelim(lineTerminator); } // Only PUT and POST requests have a body if (getMethod() != "POST" && getMethod() != "PUT") { + ESP_LOGD(LOG_TAG, "<< parse"); return; } @@ -212,13 +214,15 @@ void HttpParser::parse(Socket s) { int length = std::atoi(val.c_str()); uint8_t data[length]; s.receive(data, length, true); - m_body = std::string((char *)data, length); + m_body = std::string((char*) data, length); } else { uint8_t data[512]; int rc = s.receive(data, sizeof(data)); - m_body = std::string((char *)data, rc); + if (rc > 0) { + m_body = std::string((char*) data, rc); + } } - ESP_LOGD(LOG_TAG, "Size of body: %d", m_body.length()); + ESP_LOGD(LOG_TAG, "<< parse: Size of body: %d", m_body.length()); } // parse @@ -247,10 +251,9 @@ void HttpParser::parse(std::string message) { * @brief Parse A request line. * @param [in] line The request line to parse. */ -// A request Line is built from: -// -// -void HttpParser::parseRequestLine(std::string &line) { +void HttpParser::parseRequestLine(std::string& line) { + // A request Line is built from: + // ESP_LOGD(LOG_TAG, ">> parseRequestLine: \"%s\" [%d]", line.c_str(), line.length()); std::string::iterator it = line.begin(); @@ -264,3 +267,43 @@ void HttpParser::parseRequestLine(std::string &line) { m_version = toCharToken(it, line, ' '); ESP_LOGD(LOG_TAG, "<< parseRequestLine: method: %s, url: %s, version: %s", m_method.c_str(), m_url.c_str(), m_version.c_str()); } // parseRequestLine + +/** + * @brief Parse a response message. + * @param [in] line The response to parse. + */ +void HttpParser::parseResponse(std::string message) { + // A response is built from: + // A status line, any number of header lines, a body + auto it = message.begin(); + auto line = toStringToken(it, message, lineTerminator); + parseStatusLine(line); + + line = toStringToken(it, message, lineTerminator); + while (!line.empty()) { + ESP_LOGD(LOG_TAG, "Header: \"%s\"", line.c_str()); + m_headers.insert(parseHeader(line)); + line = toStringToken(it, message, lineTerminator); + } + + m_body = message.substr(std::distance(message.begin(), it)); +} // parse + +/** + * @brief Parse A status line. + * @param [in] line The status line to parse. + */ +void HttpParser::parseStatusLine(std::string& line) { + // A status Line is built from: + // + ESP_LOGD(LOG_TAG, ">> ParseStatusLine: \"%s\" [%d]", line.c_str(), line.length()); + std::string::iterator it = line.begin(); + // Get the version + m_version = toCharToken(it, line, ' '); + // Get the version + m_status = toCharToken(it, line, ' '); + // Get the status code + m_reason = toStringToken(it, line, lineTerminator); + + ESP_LOGD(LOG_TAG, "<< ParseStatusLine: method: %s, version: %s, status: %s", m_method.c_str(), m_version.c_str(), m_status.c_str()); +} // parseRequestLine diff --git a/cpp_utils/HttpParser.h b/cpp_utils/HttpParser.h index 77191163..06485293 100644 --- a/cpp_utils/HttpParser.h +++ b/cpp_utils/HttpParser.h @@ -12,14 +12,6 @@ #include "Socket.h" class HttpParser { -private: - std::string m_method; - std::string m_url; - std::string m_version; - std::string m_body; - std::map m_headers; - void dump(); - void parseRequestLine(std::string &line); public: HttpParser(); virtual ~HttpParser(); @@ -29,9 +21,25 @@ class HttpParser { std::string getMethod(); std::string getURL(); std::string getVersion(); + std::string getStatus(); + std::string getReason(); bool hasHeader(const std::string& name); void parse(std::string message); void parse(Socket s); + void parseResponse(std::string message); + +private: + std::string m_method; + std::string m_url; + std::string m_version; + std::string m_body; + std::string m_status; + std::string m_reason; + std::map m_headers; + void dump(); + void parseRequestLine(std::string& line); + void parseStatusLine(std::string& line); + }; #endif /* CPP_UTILS_HTTPPARSER_H_ */ diff --git a/cpp_utils/HttpRequest.cpp b/cpp_utils/HttpRequest.cpp index 5347f195..ff9336fb 100644 --- a/cpp_utils/HttpRequest.cpp +++ b/cpp_utils/HttpRequest.cpp @@ -33,6 +33,7 @@ #include #include +#include #include "HttpResponse.h" #include "HttpRequest.h" #include "GeneralUtils.h" @@ -40,6 +41,8 @@ #include #include +#define STATE_NAME 0 +#define STATE_VALUE 1 static const char* LOG_TAG="HttpRequest"; @@ -78,10 +81,10 @@ const char HttpRequest::HTTP_METHOD_PUT[] = "PUT"; std::string buildWebsocketKeyResponseHash(std::string requestKey) { std::string newKey = requestKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; uint8_t shaData[20]; - esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData); + esp_sha(SHA1, (uint8_t*) newKey.data(), newKey.length(), shaData); //GeneralUtils::hexDump(shaData, 20); std::string retStr; - GeneralUtils::base64Encode(std::string((char*)shaData, sizeof(shaData)), &retStr); + GeneralUtils::base64Encode(std::string((char*) shaData, sizeof(shaData)), &retStr); return retStr; } // buildWebsocketKeyResponseHash @@ -96,11 +99,23 @@ HttpRequest::HttpRequest(Socket clientSocket) { m_parser.parse(clientSocket); // Parse the socket stream to build the HTTP data. + // We have to take some special action on the Connection header. We want to know if it contains "Upgrade" + // however it has come to light that the Connection header can contain multiple parts. For example, it has + // been reported that it can contain "keep-alive,Upgrade". Because of this we can't simply examine the string + // to see if it equals "Upgrade". Our solution is to get the value of Connection string, split it by "," as + // a delimiter and then examine each of the parts to see if any of those are "Upgrade". + std::vector parts = GeneralUtils::split(getHeader(HTTP_HEADER_CONNECTION), ','); + bool upgradeFound = false; + if (std::find(parts.begin(), parts.end(), "Upgrade") != parts.end()) { + upgradeFound = true; + } + // Is this a Web Socket? if (getMethod() == HTTP_METHOD_GET && !getHeader(HTTP_HEADER_HOST).empty() && getHeader(HTTP_HEADER_UPGRADE) == "websocket" && - getHeader(HTTP_HEADER_CONNECTION) == "Upgrade" && + //getHeader(HTTP_HEADER_CONNECTION) == "Upgrade" && + upgradeFound && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_KEY).empty() && !getHeader(HTTP_HEADER_SEC_WEBSOCKET_VERSION).empty()) { ESP_LOGD(LOG_TAG, "Websocket detected!"); @@ -139,9 +154,6 @@ void HttpRequest::close() { } // close_cpp - - - /** * @brief Dump the HttpRequest for debugging purposes. */ @@ -189,8 +201,6 @@ std::string HttpRequest::getPath() { } // getPath -#define STATE_NAME 0 -#define STATE_VALUE 1 /** * @brief Get the query part of the request. * The query is a set of name = value pairs. The return is a map keyed by the name items. @@ -201,7 +211,15 @@ std::map HttpRequest::getQuery() { // Walk through all the characters in the query string maintaining a simple state machine // that lets us know what we are parsing. std::map queryMap; - std::string queryString = ""; + + std::string possibleQueryString = getPath(); + int qindex = possibleQueryString.find_first_of("?"); + if (qindex < 0) { + ESP_LOGD(LOG_TAG, "No query string present"); + return queryMap ; + } + std::string queryString = possibleQueryString.substr(qindex + 1, -1) ; + ESP_LOGD(LOG_TAG, "query string: %s", queryString.c_str()) ; /* * We maintain a simple state machine with states of: @@ -212,7 +230,7 @@ std::map HttpRequest::getQuery() { std::string name = ""; std::string value; // Loop through each character in the query string. - for (int i=0; i HttpRequest::getQuery() { } // getQuery - /** * @brief Get the underlying socket. * @return The underlying socket. @@ -330,11 +347,11 @@ std::vector HttpRequest::pathSplit() { std::istringstream stream(getPath()); std::vector ret; std::string pathPart; - while(std::getline(stream, pathPart, '/')) { + while (std::getline(stream, pathPart, '/')) { ret.push_back(pathPart); } // Debug - for (int i=0; i parseForm(); // Parse the body as a form. std::vector pathSplit(); std::string urlDecode(std::string str); // Decode a URL. +private: + Socket m_clientSocket; // The socket connected to the client. + bool m_isClosed; // Is the client connection closed? + HttpParser m_parser; // The parse to parse HTTP data. + WebSocket* m_pWebSocket; // A possible reference to a WebSocket object instance. + }; #endif /* COMPONENTS_CPP_UTILS_HTTPREQUEST_H_ */ diff --git a/cpp_utils/HttpResponse.cpp b/cpp_utils/HttpResponse.cpp index 70182ca0..46f3e9bd 100644 --- a/cpp_utils/HttpResponse.cpp +++ b/cpp_utils/HttpResponse.cpp @@ -5,6 +5,7 @@ * Author: kolban */ #include +#include #include "HttpRequest.h" #include "HttpResponse.h" #include @@ -25,12 +26,13 @@ const int HttpResponse::HTTP_STATUS_NOT_IMPLEMENTED = 501; const int HttpResponse::HTTP_STATUS_SERVICE_UNAVAILABLE = 503; static std::string lineTerminator = "\r\n"; -HttpResponse::HttpResponse(HttpRequest *request) { +HttpResponse::HttpResponse(HttpRequest* request) { m_request = request; m_status = 200; m_headerCommitted = false; // We have not yet sent a header. } + HttpResponse::~HttpResponse() { } @@ -42,9 +44,7 @@ HttpResponse::~HttpResponse() { * @param [in] value The value of the header. */ void HttpResponse::addHeader(const std::string name, const std::string value) { - if (m_headerCommitted) { - return; - } + if (m_headerCommitted) return; m_responseHeaders.insert(std::pair(name, value)); } // addHeader @@ -56,7 +56,7 @@ void HttpResponse::addHeader(const std::string name, const std::string value) { */ void HttpResponse::close() { // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { sendHeader(); } m_request->close(); @@ -69,9 +69,7 @@ void HttpResponse::close() { * @return The value of the named header. */ std::string HttpResponse::getHeader(std::string name) { - if (m_responseHeaders.find(name) == m_responseHeaders.end()) { - return ""; - } + if (m_responseHeaders.find(name) == m_responseHeaders.end()) return ""; return m_responseHeaders.at(name); } // getHeader @@ -88,28 +86,77 @@ std::map HttpResponse::getHeaders() { * @param [in] data The data to send to the partner. */ void HttpResponse::sendData(std::string data) { + ESP_LOGD(LOG_TAG, ">> sendData"); // If the request is already closed, nothing further to do. if (m_request->isClosed()) { - ESP_LOGE(LOG_TAG, "Request to send more data but the request/response is already closed"); + ESP_LOGE(LOG_TAG, "<< sendData: Request to send more data but the request/response is already closed"); return; } // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { sendHeader(); } // Send the payload data. m_request->getSocket().send(data); + ESP_LOGD(LOG_TAG, "<< sendData"); } // sendData +void HttpResponse::sendData(uint8_t* pData, size_t size) { + ESP_LOGD(LOG_TAG, ">> sendData: 0x%x, size: %d", (uint32_t) pData, size); + // If the request is already closed, nothing further to do. + if (m_request->isClosed()) { + ESP_LOGE(LOG_TAG, "<< sendData: Request to send more data but the request/response is already closed"); + return; + } + + // If we haven't yet sent the header of the data, send that now. + if (!m_headerCommitted) { + sendHeader(); + } + + // Send the payload data. + m_request->getSocket().send(pData, size); + ESP_LOGD(LOG_TAG, "<< sendData"); +} // sendData + +void HttpResponse::sendFile(std::string fileName, size_t bufSize) { + ESP_LOGI(LOG_TAG, "Opening file: %s", fileName.c_str()); + std::ifstream ifStream; + ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. + + // If we failed to open the requested file, then it probably didn't exist so return a not found. + if (!ifStream.is_open()) { + ESP_LOGE(LOG_TAG, "Unable to open file %s for reading", fileName.c_str()); + setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found"); + addHeader(HttpRequest::HTTP_HEADER_CONTENT_TYPE, "text/plain"); + sendData("Not Found"); + close(); + return; // Since we failed to open the file, no further work to be done. + } + + // We now have an open file and want to push the content of that file through to the browser. + // because of defect #252 we have to do some pretty important re-work here. Specifically, we can't host the whole file in + // RAM at one time. Instead what we have to do is ensure that we only have enough data in RAM to be sent. + + setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + uint8_t *pData = new uint8_t[bufSize]; + while (!ifStream.eof()) { + ifStream.read((char*) pData, bufSize); + sendData(pData, ifStream.gcount()); + } + delete[] pData; + ifStream.close(); + close(); +} // sendFile /** * @brief Send the header */ void HttpResponse::sendHeader() { // If we haven't yet sent the header of the data, send that now. - if (m_headerCommitted == false) { + if (!m_headerCommitted) { std::ostringstream oss; oss << m_request->getVersion() << " " << m_status << " " << m_statusMessage << lineTerminator; for (auto it = m_responseHeaders.begin(); it != m_responseHeaders.end(); ++it) { @@ -137,4 +184,3 @@ void HttpResponse::setStatus(const int status, const std::string message) { m_status = status; m_statusMessage = message; } // setStatus - diff --git a/cpp_utils/HttpResponse.h b/cpp_utils/HttpResponse.h index 5c8c5ec0..5dfaa976 100644 --- a/cpp_utils/HttpResponse.h +++ b/cpp_utils/HttpResponse.h @@ -14,15 +14,6 @@ #include "HttpRequest.h" class HttpResponse { -private: - bool m_headerCommitted; // Has the header been sent? - HttpRequest* m_request; // The request associated with this response. - std::map m_responseHeaders; // The headers to be sent with the response. - int m_status; // The status to be sent with the response. - std::string m_statusMessage; // The status message to be sent with the response. - - void sendHeader(); // Send the header to the client. - public: static const int HTTP_STATUS_CONTINUE; static const int HTTP_STATUS_SWITCHING_PROTOCOL; @@ -45,7 +36,19 @@ class HttpResponse { std::string getHeader(std::string name); // Get a named header. std::map getHeaders(); // Get all headers. void sendData(std::string data); // Send data to the client. + void sendData(uint8_t* pData, size_t size); // Send data to the client. void setStatus(int status, std::string message); // Set the response status. + void sendFile(std::string fileName, size_t bufSize = 4 * 1024); // Send file contents if exists. + +private: + bool m_headerCommitted; // Has the header been sent? + HttpRequest* m_request; // The request associated with this response. + std::map m_responseHeaders; // The headers to be sent with the response. + int m_status; // The status to be sent with the response. + std::string m_statusMessage; // The status message to be sent with the response. + + void sendHeader(); // Send the header to the client. + }; #endif /* COMPONENTS_CPP_UTILS_HTTPRESPONSE_H_ */ diff --git a/cpp_utils/HttpServer.cpp b/cpp_utils/HttpServer.cpp index 298ab0c3..fb90fcc9 100644 --- a/cpp_utils/HttpServer.cpp +++ b/cpp_utils/HttpServer.cpp @@ -1,5 +1,10 @@ /* * HttpServer.cpp + * Design: + * This class represents an HTTP server. We create an instance of the class and then it is configured. + * When the user has completed the configuration, they execute the start() method which starts it running. + * A subsequent call to stop() will stop it. When start() is called, a new task is created which listens + * for incoming messages. * * Created on: Aug 30, 2017 * Author: kolban @@ -20,56 +25,17 @@ static const char* LOG_TAG = "HttpServer"; #undef close -/** - * Send a directory listing back to the browser. - * @param [in] path The path of the directory to list. - * @param [in] response The response object to use to send data back to the browser. - */ -static void listDirectory(std::string path, HttpResponse& response) { - // If path ends with a "/" then remove it. - if (GeneralUtils::endsWith(path, '/')) { - path = path.substr(0, path.length()-1); - } - response.addHeader("Content-Type", "text/html"); - response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - response.sendData(""); - if (!GeneralUtils::endsWith(path, '/')) { - response.sendData(""); - } - response.sendData(""); - response.sendData("

" + path + "

"); - response.sendData("
"); - response.sendData("

[To Parent Directory]

"); - response.sendData(""); - auto files = FileSystem::getDirectoryContents(path); - for (auto it = files.begin(); it != files.end(); ++it) { - std::stringstream ss; - ss << ""; - if (it->isDirectory()) { - ss << ""; - } - else { - ss << ""; - } - - ss << ""; - response.sendData(ss.str()); - ESP_LOGD(LOG_TAG, "file: %s", ss.str().c_str()); - } - response.sendData("
" << it->getName() << "<dir>" << it->length() << "
"); - response.sendData("
"); - response.sendData(""); - response.close(); -} // listDirectory /** * Constructor for HTTP Server */ HttpServer::HttpServer() { m_portNumber = 80; // The default port number. + m_clientTimeout = 5; // The default timeout 5 seconds. m_rootPath = ""; // The default path. m_useSSL = false; // Default SSL is no. - setDirectoryListing(false); // Default directory listing is no. + setDirectoryListing(false); // Default directory listing is disabled. + m_fileBufferSize = 4 * 1024; // Default size of the file buffer. } // HttpServer @@ -77,6 +43,7 @@ HttpServer::~HttpServer() { ESP_LOGD(LOG_TAG, "~HttpServer"); } + /** * @brief Be an HTTP server task. * Here we define a Task that will be run when the HTTP server starts. It is this task @@ -85,7 +52,7 @@ HttpServer::~HttpServer() { */ class HttpServerTask: public Task { public: - HttpServerTask(std::string name): Task(name, 16*1024) { + HttpServerTask(std::string name): Task(name, 16 * 1024) { m_pHttpServer = nullptr; }; @@ -100,14 +67,15 @@ class HttpServerTask: public Task { * * If we didn't find a handler, then we are going to behave as a Web Server and try and serve up the * content from the file on the "file system". + * * @param [in] request The HTTP request to process. */ - void processRequest(HttpRequest &request) { + void processRequest(HttpRequest& request) { ESP_LOGD("HttpServerTask", ">> processRequest: Method: %s, Path: %s", request.getMethod().c_str(), request.getPath().c_str()); // Loop over all the path handlers we have looking for the first one that matches. Note that none of them - // may match. If we find one that does, then invoke the handler and that is the end of processing. + // need to match. If we find one that does, then invoke the handler and that is the end of processing. for (auto pathHandlerIterartor = m_pHttpServer->m_pathHandlers.begin(); pathHandlerIterartor != m_pHttpServer->m_pathHandlers.end(); ++pathHandlerIterartor) { @@ -134,42 +102,23 @@ class HttpServerTask: public Task { } // Serve up the content from the file on the file system ... if found ... - std::ifstream ifStream; + std::string fileName = m_pHttpServer->getRootPath() + request.getPath(); // Build the absolute file name to read. // If the file name ends with a '/' then remove it ... we are normalizing to NO trailing slashes. if (GeneralUtils::endsWith(fileName, '/')) { - fileName = fileName.substr(0, fileName.length()-1); + fileName = fileName.substr(0, fileName.length() - 1); } + HttpResponse response(&request); // Test if the path is a directory. if (FileSystem::isDirectory(fileName)) { ESP_LOGD(LOG_TAG, "Path %s is a directory", fileName.c_str()); - HttpResponse response(&request); - listDirectory(fileName, response); + m_pHttpServer->listDirectory(fileName, response); // List the contents of the directory. return; } // Path was a directory. - ESP_LOGD("HttpServerTask", "Opening file: %s", fileName.c_str()); - ifStream.open(fileName, std::ifstream::in | std::ifstream::binary); // Attempt to open the file for reading. - - // If we failed to open the requested file, then it probably didn't exist so return a not found. - if (!ifStream.is_open()) { - ESP_LOGE("HttpServerTask", "Unable to open file %s for reading", fileName.c_str()); - HttpResponse response(&request); - response.setStatus(HttpResponse::HTTP_STATUS_NOT_FOUND, "Not Found"); - response.sendData(""); - return; // Since we failed to open the file, no further work to be done. - } - - // We now have an open file and want to push the content of that file through to the browser. - HttpResponse response(&request); - response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); - std::stringstream ss; - ss << ifStream.rdbuf(); - response.sendData(ss.str()); - ifStream.close(); - + response.sendFile(fileName, m_pHttpServer->getFileBufferSize()); } // processRequest @@ -180,27 +129,29 @@ class HttpServerTask: public Task { * @param [in] data A reference to the HttpServer. */ void run(void* data) { - m_pHttpServer = (HttpServer*)data; // The passed in data is an instance of an HttpServer. + m_pHttpServer = (HttpServer*) data; // The passed in data is an instance of an HttpServer. m_pHttpServer->m_socket.setSSL(m_pHttpServer->m_useSSL); - m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber); + m_pHttpServer->m_socket.listen(m_pHttpServer->m_portNumber, false /* is datagram */, true /* Allow address reuse */); ESP_LOGD("HttpServerTask", "Listening on port %d", m_pHttpServer->getPort()); Socket clientSocket; - while(1) { // Loop forever. - + while (true) { // Loop forever. ESP_LOGD("HttpServerTask", "Waiting for new peer client"); - //Memory::checkIntegrity(); + try { clientSocket = m_pHttpServer->m_socket.accept(); // Block waiting for a new external client connection. - } - catch(std::exception &e) { + clientSocket.setTimeout(m_pHttpServer->getClientTimeout()); + } catch (std::exception& e) { ESP_LOGE("HttpServerTask", "Caught an exception waiting for new client!"); + m_pHttpServer->m_semaphoreServerStarted.give(); // Release the semaphore .. we are now no longer running. return; } - ESP_LOGD("HttpServerTask", "HttpServer listening on port %d received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); - + ESP_LOGD("HttpServerTask", "HttpServer that was listening on port %d has received a new client connection; sockFd=%d", m_pHttpServer->getPort(), clientSocket.getFD()); HttpRequest request(clientSocket); // Build the HTTP Request from the socket. + if (request.isWebsocket()) { // If this is a WebSocket + clientSocket.setTimeout(0); // Clear the timeout. + } request.dump(); // debug. processRequest(request); // Process the request. if (!request.isWebsocket()) { // If this is NOT a WebSocket, then close it as the request @@ -221,7 +172,7 @@ class HttpServerTask: public Task { * Example: * @code{.cpp} * static void handle_REST_WiFi(WebServer::HttpRequest *pRequest, WebServer::HttpResponse *pResponse) { - * ... + * ... * } * * webServer.addPathHandler("GET", "\\/ESP32\\/WiFi", handle_REST_WiFi); @@ -234,7 +185,7 @@ class HttpServerTask: public Task { void HttpServer::addPathHandler( std::string method, std::regex* pathExpr, - void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { + void (*handler)(HttpRequest* pHttpRequest, HttpResponse* pHttpResponse)) { // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); @@ -251,7 +202,7 @@ void HttpServer::addPathHandler( * Example: * @code{.cpp} * static void handle_REST_WiFi(WebServer::HttpRequest *pRequest, WebServer::HttpResponse *pResponse) { - * ... + * ... * } * * webServer.addPathHandler("GET", "/ESP32/WiFi", handle_REST_WiFi); @@ -264,13 +215,25 @@ void HttpServer::addPathHandler( void HttpServer::addPathHandler( std::string method, std::string path, - void (*handler)(HttpRequest *pHttpRequest, HttpResponse *pHttpResponse)) { + void (*handler)(HttpRequest* pHttpRequest, HttpResponse* pHttpResponse)) { // We are maintaining a C++ vector of PathHandler objects. We add a new entry into that vector. m_pathHandlers.push_back(PathHandler(method, path, handler)); } // addPathHandler +/** + * @brief Get the size of the file buffer. + * When serving up a file from the file system, we can't afford to read the whole file into RAM before + * sending it. As such, we must read the file in chunks. The buffer size is the size of a chunk to be + * transmitted before the next chunk is read. + * @return The file buffer size. + */ +size_t HttpServer::getFileBufferSize() { + return m_fileBufferSize; +} // getFileBufferSize + + /** * @brief Get the port number on which the HTTP Server is listening. * @return The port number on which the HTTP server is listening. @@ -289,11 +252,84 @@ std::string HttpServer::getRootPath() { } // getRootPath +/** + * @brief Return whether or not we are using SSL. + * @return True if we are using SSL. + */ bool HttpServer::getSSL() { return m_useSSL; } // getSSL +/** + * Send a directory listing back to the browser. + * @param [in] path The path of the directory to list. + * @param [in] response The response object to use to send data back to the browser. + */ +void HttpServer::listDirectory(std::string path, HttpResponse& response) { + // If path ends with a "/" then remove it. + if (GeneralUtils::endsWith(path, '/')) { + path = path.substr(0, path.length()-1); + } + + // The url path may not be the same as path which is the path on the file system. + // They will differ if we are using a root path. We check to see if the file system path + // starts with the root path, if it does, we remove the root path from the url path. + std::string urlPath = path; + if (urlPath.compare(0, getRootPath().length(), getRootPath()) == 0) { + urlPath = urlPath.substr(getRootPath().length()); + } + + response.addHeader("Content-Type", "text/html"); + response.setStatus(HttpResponse::HTTP_STATUS_OK, "OK"); + response.sendData(""); + if (!GeneralUtils::endsWith(path, '/')) { + response.sendData(""); + } + response.sendData(""); + response.sendData("

" + path + "

"); + response.sendData("
"); + response.sendData("

[To Parent Directory]

"); + response.sendData(""); + auto files = FileSystem::getDirectoryContents(path); + for (auto it = files.begin(); it != files.end(); ++it) { + std::stringstream ss; + ss << ""; + if (it->isDirectory()) { + ss << ""; + } else { + ss << ""; + } + + ss << ""; + response.sendData(ss.str()); + ESP_LOGD(LOG_TAG, "file: %s", ss.str().c_str()); + } + response.sendData("
" << it->getName() << "<dir>" << it->length() << "
"); + response.sendData("
"); + response.sendData(""); + response.close(); +} // listDirectory + + +/** + * @brief Set different socket timeout for new connections. + * @param [in] use Set to true to enable directory listing. + */ +void HttpServer::setClientTimeout(uint32_t timeout) { + m_clientTimeout = timeout; +} + + +/** + * @brief Get current socket's timeout for new connections. + * @param [in] use Set to true to enable directory listing. + */ +uint32_t HttpServer::getClientTimeout() { + return m_clientTimeout; +} + + /** * @brief Set whether or not we will list directories. * @param [in] use Set to true to enable directory listing. @@ -303,6 +339,18 @@ void HttpServer::setDirectoryListing(bool use) { } // setDirectoryListening +/** + * @brief Set the size of the file buffer. + * When serving up a file from the file system, we can't afford to read the whole file into RAM before + * sending it. As such, we must read the file in chunks. The buffer size is the size of a chunk to be + * transmitted before the next chunk is read. + * @param [in] fileBufferSize How large should the file buffer size be? + */ +void HttpServer::setFileBufferSize(size_t fileBufferSize) { + m_fileBufferSize = fileBufferSize; +} // setFileBufferSize + + /** * @brief Set the root path for URL file mapping. * @@ -321,6 +369,11 @@ void HttpServer::setDirectoryListing(bool use) { * @return N/A. */ void HttpServer::setRootPath(std::string path) { + // Should the user have supplied a path that ends in a "/" remove the trailing slash. This also + // means that "/" becomes "". + if (GeneralUtils::endsWith(path, '/')) { + path = path.substr(0, path.length()-1); + } m_rootPath = path; } // setRootPath @@ -336,11 +389,20 @@ void HttpServer::start(uint16_t portNumber, bool useSSL) { // Design: // The start of the HTTP server should be as fast as possible. ESP_LOGD(LOG_TAG, ">> start: port: %d, useSSL: %d", portNumber, useSSL); + + // Take the semaphore that says that we are now running. If we are already running, then end here as + // there is nothing further to do. + if (!m_semaphoreServerStarted.take(100, "start")) { + ESP_LOGD(LOG_TAG, "<< start: Already running"); + return; + } + m_useSSL = useSSL; m_portNumber = portNumber; HttpServerTask* pHttpServerTask = new HttpServerTask("HttpServerTask"); pHttpServerTask->start(this); + ESP_LOGD(LOG_TAG, "<< start"); } // start @@ -352,7 +414,8 @@ void HttpServer::stop() { // that is listening for incoming connections. That will then shutdown all the other // activities. ESP_LOGD(LOG_TAG, ">> stop"); - m_socket.close(); + m_socket.close(); // Close the socket that is being used to watch for incoming requests. + m_semaphoreServerStarted.wait("stop"); // Wait for the server to stop. ESP_LOGD(LOG_TAG, "<< stop"); } // stop @@ -364,7 +427,7 @@ void HttpServer::stop() { * @param [in] pathPattern The path pattern to be matched. * @param [in] webServerRequestHandler The request handler to be called. */ -PathHandler::PathHandler(std::string method, std::regex *pRegex, +PathHandler::PathHandler(std::string method, std::regex* pRegex, void (*pWebServerRequestHandler) ( HttpRequest* pHttpRequest, @@ -394,6 +457,7 @@ PathHandler::PathHandler(std::string method, std::string matchPath, m_method = method; // Save the method we are looking for. m_textPattern = matchPath; m_isRegex = false; + m_pRegex = nullptr; m_pRequestHandler = pWebServerRequestHandler; // The handler to be invoked if the pattern matches. } // PathHandler @@ -406,12 +470,9 @@ PathHandler::PathHandler(std::string method, std::string matchPath, * @return True if the path matches. */ bool PathHandler::match(std::string method, std::string path) { - if (method != m_method) { - return false; - } + if (method != m_method) return false; if (m_isRegex) { ESP_LOGD("PathHandler", "regex matching: %s with %s", m_textPattern.c_str(), path.c_str()); - return std::regex_search(path, *m_pRegex); } ESP_LOGD("PathHandler", "plain matching: %s with %s", m_textPattern.c_str(), path.c_str()); @@ -425,8 +486,6 @@ bool PathHandler::match(std::string method, std::string path) { * @param [in] response An object representing the response. * @return N/A. */ -void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse *response) { +void PathHandler::invokePathHandler(HttpRequest* request, HttpResponse* response) { m_pRequestHandler(request, response); } // invokePathHandler - - diff --git a/cpp_utils/HttpServer.h b/cpp_utils/HttpServer.h index f07abedd..8baa7bbb 100644 --- a/cpp_utils/HttpServer.h +++ b/cpp_utils/HttpServer.h @@ -16,7 +16,7 @@ #include "SockServ.h" #include "HttpRequest.h" #include "HttpResponse.h" -//#include "SockServ.h" +#include "FreeRTOS.h" #include class HttpServerTask; @@ -74,23 +74,31 @@ class HttpServer { HttpRequest* pHttpRequest, HttpResponse* pHttpResponse) ); - uint16_t getPort(); // Get the port on which the Http server is listening. - std::string getRootPath(); // Get the root of the file system path. - bool getSSL(); // Are we using SSL? - void setDirectoryListing(bool use); // Should we list the content of directories? - void setRootPath(std::string path); // Set the root of the file system path. - void start(uint16_t portNumber, bool useSSL=false); + uint32_t getClientTimeout(); // Get client's socket timeout + size_t getFileBufferSize(); // Get the current size of the file buffer. + uint16_t getPort(); // Get the port on which the Http server is listening. + std::string getRootPath(); // Get the root of the file system path. + bool getSSL(); // Are we using SSL? + void setClientTimeout(uint32_t timeout); // Set client's socket timeout + void setDirectoryListing(bool use); // Should we list the content of directories? + void setFileBufferSize(size_t fileBufferSize); // Set the size of the file buffer + void setRootPath(std::string path); // Set the root of the file system path. + void start(uint16_t portNumber, bool useSSL = false); void stop(); // Stop a previously started server. + private: friend class HttpServerTask; friend class WebSocket; - uint16_t m_portNumber; // Port number on which server is listening. + void listDirectory(std::string path, HttpResponse& response); + size_t m_fileBufferSize; // Size of the file buffer. + bool m_directoryListing; // Should we list directory content? std::vector m_pathHandlers; // Vector of path handlers. + uint16_t m_portNumber; // Port number on which server is listening. std::string m_rootPath; // Root path into the file system. - bool m_useSSL; // Is this server listening on an HTTPS port? - bool m_directoryListing; // Should we list directory content? - //SockServ m_sockServ; // Server socket. Socket m_socket; + bool m_useSSL; // Is this server listening on an HTTPS port? + uint32_t m_clientTimeout; // Default Timeout + FreeRTOS::Semaphore m_semaphoreServerStarted = FreeRTOS::Semaphore("ServerStarted"); }; // HttpServer #endif /* COMPONENTS_CPP_UTILS_HTTPSERVER_H_ */ diff --git a/cpp_utils/I2C.cpp b/cpp_utils/I2C.cpp index 3aa740c7..d2413bae 100644 --- a/cpp_utils/I2C.cpp +++ b/cpp_utils/I2C.cpp @@ -67,9 +67,9 @@ void I2C::endTransaction() { ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } - errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000/portTICK_PERIOD_MS); + errRc = ::i2c_master_cmd_begin(m_portNum, m_cmd, 1000 / portTICK_PERIOD_MS); if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "i2c_master_stop: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + ESP_LOGE(LOG_TAG, "i2c_master_cmd_begin: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } ::i2c_cmd_link_delete(m_cmd); m_directionKnown = false; @@ -81,8 +81,7 @@ void I2C::endTransaction() { * * @return The address of the %I2C slave. */ -uint8_t I2C::getAddress() const -{ +uint8_t I2C::getAddress() const { return m_address; } @@ -95,7 +94,7 @@ uint8_t I2C::getAddress() const * @param [in] sclPin The pin to use for SCL clock. * @return N/A. */ -void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum) { +void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t clockSpeed, i2c_port_t portNum, bool pullup) { ESP_LOGD(LOG_TAG, ">> I2c::init. address=%d, sda=%d, scl=%d, clockSpeed=%d, portNum=%d", address, sdaPin, sclPin, clockSpeed, portNum); assert(portNum < I2C_NUM_MAX); m_portNum = portNum; @@ -107,9 +106,9 @@ void I2C::init(uint8_t address, gpio_num_t sdaPin, gpio_num_t sclPin, uint32_t c conf.mode = I2C_MODE_MASTER; conf.sda_io_num = sdaPin; conf.scl_io_num = sclPin; - conf.sda_pullup_en = GPIO_PULLUP_ENABLE; - conf.scl_pullup_en = GPIO_PULLUP_ENABLE; - conf.master.clk_speed = 100000; + conf.sda_pullup_en = pullup ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.scl_pullup_en = pullup ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.master.clk_speed = clockSpeed; esp_err_t errRc = ::i2c_param_config(m_portNum, &conf); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_param_config: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); @@ -136,14 +135,14 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=%d, ack=%d)", length, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } - esp_err_t errRc = ::i2c_master_read(m_cmd, bytes, length, !ack); + esp_err_t errRc = ::i2c_master_read(m_cmd, bytes, length, ack?I2C_MASTER_ACK:I2C_MASTER_NACK); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_read: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } @@ -157,18 +156,18 @@ void I2C::read(uint8_t* bytes, size_t length, bool ack) { * @param [in] ack Whether or not we should send an ACK to the slave after reading a byte. * @return N/A. */ -void I2C::read(uint8_t *byte, bool ack) { +void I2C::read(uint8_t* byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "read(size=1, ack=%d)", ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_READ, !ack); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_write_byte: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } - ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, !ack)); + ESP_ERROR_CHECK(::i2c_master_read_byte(m_cmd, byte, ack?I2C_MASTER_ACK:I2C_MASTER_NACK)); } // readByte @@ -180,12 +179,11 @@ void I2C::read(uint8_t *byte, bool ack) { * @return N/A. */ void I2C::scan() { - uint8_t i; printf("Data Pin: %d, Clock Pin: %d\n", this->m_sdaPin, this->m_sclPin); printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); printf("00: "); - for (i=3; i<0x78; i++) { - if (i%16 == 0) { + for (uint8_t i = 3; i < 0x78; i++) { + if (i % 16 == 0) { printf("\n%.2x:", i); } if (slavePresent(i)) { @@ -203,8 +201,7 @@ void I2C::scan() { * * @param [in] address The address of the %I2C slave. */ -void I2C::setAddress(uint8_t address) -{ +void I2C::setAddress(uint8_t address) { this->m_address = address; } // setAddress @@ -230,7 +227,7 @@ bool I2C::slavePresent(uint8_t address) { ::i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, 1 /* expect ack */); ::i2c_master_stop(cmd); - esp_err_t espRc = ::i2c_master_cmd_begin(m_portNum, cmd, 100/portTICK_PERIOD_MS); + esp_err_t espRc = ::i2c_master_cmd_begin(m_portNum, cmd, 100 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); return espRc == 0; // Return true if the slave is present and false otherwise. } // slavePresent @@ -248,6 +245,7 @@ void I2C::start() { if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "i2c_master_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } + m_directionKnown = false; } // start @@ -278,7 +276,7 @@ void I2C::write(uint8_t byte, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(val=0x%.2x, ack=%d)", byte, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); if (errRc != ESP_OK) { @@ -304,7 +302,7 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { if (debug) { ESP_LOGD(LOG_TAG, "write(length=%d, ack=%d)", length, ack); } - if (m_directionKnown == false) { + if (!m_directionKnown) { m_directionKnown = true; esp_err_t errRc = ::i2c_master_write_byte(m_cmd, (m_address << 1) | I2C_MASTER_WRITE, !ack); if (errRc != ESP_OK) { @@ -316,5 +314,3 @@ void I2C::write(uint8_t *bytes, size_t length, bool ack) { ESP_LOGE(LOG_TAG, "i2c_master_write: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } } // write - - diff --git a/cpp_utils/I2C.h b/cpp_utils/I2C.h index 9886d63a..64b63427 100644 --- a/cpp_utils/I2C.h +++ b/cpp_utils/I2C.h @@ -17,14 +17,6 @@ * @brief Interface to %I2C functions. */ class I2C { -private: - uint8_t m_address; - i2c_cmd_handle_t m_cmd; - bool m_directionKnown; - gpio_num_t m_sdaPin; - gpio_num_t m_sclPin; - i2c_port_t m_portNum; - public: /** * @brief The default SDA pin. @@ -45,17 +37,26 @@ class I2C { void beginTransaction(); void endTransaction(); uint8_t getAddress() const; - void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0); - void read(uint8_t* bytes, size_t length, bool ack=true); - void read(uint8_t* byte, bool ack=true); + void init(uint8_t address, gpio_num_t sdaPin = DEFAULT_SDA_PIN, gpio_num_t sclPin = DEFAULT_CLK_PIN, uint32_t clkSpeed = DEFAULT_CLK_SPEED, i2c_port_t portNum = I2C_NUM_0, bool pullup = true); + void read(uint8_t* bytes, size_t length, bool ack = true); + void read(uint8_t* byte, bool ack = true); void scan(); void setAddress(uint8_t address); void setDebug(bool enabled); bool slavePresent(uint8_t address); void start(); void stop(); - void write(uint8_t byte, bool ack=true); - void write(uint8_t* bytes, size_t length, bool ack=true); + void write(uint8_t byte, bool ack = true); + void write(uint8_t* bytes, size_t length, bool ack = true); + +private: + uint8_t m_address; + i2c_cmd_handle_t m_cmd; + bool m_directionKnown; + gpio_num_t m_sdaPin; + gpio_num_t m_sclPin; + i2c_port_t m_portNum; + }; #endif /* MAIN_I2C_H_ */ diff --git a/cpp_utils/I2S.cpp b/cpp_utils/I2S.cpp index c5bcb997..15ba2fa7 100644 --- a/cpp_utils/I2S.cpp +++ b/cpp_utils/I2S.cpp @@ -27,7 +27,6 @@ static const char* LOG_TAG = "I2S"; static intr_handle_t i2s_intr_handle; - /** * A representation of a DMA buffer. */ @@ -67,6 +66,7 @@ DMABuffer::~DMABuffer() { delete[] m_desc.buf; } // ~DMABuffer + /** * @brief Dump the state of the buffer. * @return N/A @@ -74,7 +74,7 @@ DMABuffer::~DMABuffer() { void DMABuffer::dump() { std::ostringstream ss; ss << "size: " << m_desc.size; - ss << ", buf: 0x" << std::hex << (uint32_t)m_desc.buf << std::dec; + ss << ", buf: 0x" << std::hex << (uint32_t) m_desc.buf << std::dec; ss << ", length: " << m_desc.length; ss << ", offset: " << m_desc.offset; ss << ", sosf: " << m_desc.sosf; @@ -82,10 +82,8 @@ void DMABuffer::dump() { ss << ", owner: " << m_desc.owner; ESP_LOGD(LOG_TAG, "Desc: %s", ss.str().c_str()); int length = 100; - if (length > m_desc.length) { - length = m_desc.length; - } - GeneralUtils::hexDump((uint8_t*)m_desc.buf, length); + if (length > m_desc.length) length = m_desc.length; + GeneralUtils::hexDump((uint8_t*) m_desc.buf, length); } /** @@ -95,23 +93,15 @@ void DMABuffer::dump() { * @return The number of bytes actually copied. */ uint32_t DMABuffer::getData(uint8_t* pData, uint32_t length) { - uint8_t* pBuf = (uint8_t*)m_desc.buf; - if (length > getLength()) { - length = getLength(); - } + uint8_t* pBuf = (uint8_t*) m_desc.buf; + if (length > getLength()) length = getLength(); uint32_t i; - // // The descriptor buffer is filled with data that contains: - // // b1 00 b0 00 b3 00 b2 00 b5 00 b4 00 b7 00 b6 00 - // // Our goal is to populate the passed in buffer with data of the form: - // // b0 b1 b2 b3 b4 b5 b6 b7 ... - // // The following alogrithm does that. - // - for (i=0; igetDesc()); - pCurrentDMABuffer = pCurrentDMABuffer->getNext(); - I2S0.int_clr.val = I2S0.int_raw.val; - pI2S->m_dmaSemaphore.giveFromISR(); +static void IRAM_ATTR i2s_isr(void* arg) { + I2S* pI2S = (I2S*) arg; + //ESP_EARLY_LOGV(LOG_TAG, "I2S isr"); + pLastDMABuffer = pCurrentDMABuffer; + //logI2SIntr(); + //logDesc(pCurrentDMABuffer->getDesc()); + pCurrentDMABuffer = pCurrentDMABuffer->getNext(); + I2S0.int_clr.val = I2S0.int_raw.val; + pI2S->m_dmaSemaphore.giveFromISR(); } /** @@ -261,20 +246,20 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { const uint32_t const_high = 0x38; - gpio_matrix_in(config.pin_d0, I2S0I_DATA_IN0_IDX, false); - gpio_matrix_in(config.pin_d1, I2S0I_DATA_IN1_IDX, false); - gpio_matrix_in(config.pin_d2, I2S0I_DATA_IN2_IDX, false); - gpio_matrix_in(config.pin_d3, I2S0I_DATA_IN3_IDX, false); - gpio_matrix_in(config.pin_d4, I2S0I_DATA_IN4_IDX, false); - gpio_matrix_in(config.pin_d5, I2S0I_DATA_IN5_IDX, false); - gpio_matrix_in(config.pin_d6, I2S0I_DATA_IN6_IDX, false); - gpio_matrix_in(config.pin_d7, I2S0I_DATA_IN7_IDX, false); - gpio_matrix_in(config.pin_vsync, I2S0I_V_SYNC_IDX, true); - gpio_matrix_in(config.pin_href, I2S0I_H_SYNC_IDX, false); - //gpio_matrix_in(const_high, I2S0I_V_SYNC_IDX, false); - //gpio_matrix_in(const_high, I2S0I_H_SYNC_IDX, false); - gpio_matrix_in(const_high, I2S0I_H_ENABLE_IDX, false); - gpio_matrix_in(config.pin_pclk, I2S0I_WS_IN_IDX, false); + gpio_matrix_in(config.pin_d0, I2S0I_DATA_IN0_IDX, false); + gpio_matrix_in(config.pin_d1, I2S0I_DATA_IN1_IDX, false); + gpio_matrix_in(config.pin_d2, I2S0I_DATA_IN2_IDX, false); + gpio_matrix_in(config.pin_d3, I2S0I_DATA_IN3_IDX, false); + gpio_matrix_in(config.pin_d4, I2S0I_DATA_IN4_IDX, false); + gpio_matrix_in(config.pin_d5, I2S0I_DATA_IN5_IDX, false); + gpio_matrix_in(config.pin_d6, I2S0I_DATA_IN6_IDX, false); + gpio_matrix_in(config.pin_d7, I2S0I_DATA_IN7_IDX, false); + gpio_matrix_in(config.pin_vsync, I2S0I_V_SYNC_IDX, true); + gpio_matrix_in(config.pin_href, I2S0I_H_SYNC_IDX, false); +// gpio_matrix_in(const_high, I2S0I_V_SYNC_IDX, false); +// gpio_matrix_in(const_high, I2S0I_H_SYNC_IDX, false); + gpio_matrix_in(const_high, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(config.pin_pclk, I2S0I_WS_IN_IDX, false); // Enable and configure I2S peripheral periph_module_enable(PERIPH_I2S0_MODULE); @@ -345,10 +330,10 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { ESP_LOGD(LOG_TAG, "Initializing %d descriptors", desc_count); - DMABuffer *pFirst = new DMABuffer(); - DMABuffer *pLast = pFirst; - for (int i=1; isetNext(pNewDMABuffer); pLast = pNewDMABuffer; } @@ -356,45 +341,45 @@ void I2S::cameraMode(dma_config_t config, int desc_count, int sample_count) { pCurrentDMABuffer = pFirst; // I2S_RX_EOF_NUM_REG - I2S0.rx_eof_num = sample_count; - - // I2S_IN_LINK_REG -> I2S_INLINK_ADDR - I2S0.in_link.addr = (uint32_t) pFirst; - - // I2S_IN_LINK_REG -> I2S_INLINK_START - I2S0.in_link.start = 1; - - I2S0.int_clr.val = I2S0.int_raw.val; - I2S0.int_ena.val = 0; - I2S0.int_ena.in_done = 1; - - // Register the interrupt handler. - esp_intr_alloc( - ETS_I2S0_INTR_SOURCE, - ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, - &i2s_isr, - this, - &i2s_intr_handle); - - m_dmaSemaphore.take(); - // Start the interrupt handler - esp_intr_enable(i2s_intr_handle); - - I2S0.conf.rx_start = 1; - - /* - while(1) { - m_dmaSemaphore.wait(); - uint32_t dataLength = pLastDMABuffer->getLength(); - ESP_LOGD(LOG_TAG, "Got a DMA buffer; length=%d", dataLength); - //pLastDMABuffer->dump(); - uint8_t *pData = new uint8_t[dataLength]; - pLastDMABuffer->getData(pData, dataLength); + I2S0.rx_eof_num = sample_count; + + // I2S_IN_LINK_REG -> I2S_INLINK_ADDR + I2S0.in_link.addr = (uint32_t) pFirst; + + // I2S_IN_LINK_REG -> I2S_INLINK_START + I2S0.in_link.start = 1; + + I2S0.int_clr.val = I2S0.int_raw.val; + I2S0.int_ena.val = 0; + I2S0.int_ena.in_done = 1; + + // Register the interrupt handler. + esp_intr_alloc( + ETS_I2S0_INTR_SOURCE, + ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, + &i2s_isr, + this, + &i2s_intr_handle); + + m_dmaSemaphore.take(); + // Start the interrupt handler + esp_intr_enable(i2s_intr_handle); + + I2S0.conf.rx_start = 1; + + /* + while(1) { + m_dmaSemaphore.wait(); + uint32_t dataLength = pLastDMABuffer->getLength(); + ESP_LOGD(LOG_TAG, "Got a DMA buffer; length=%d", dataLength); + //pLastDMABuffer->dump(); + uint8_t *pData = new uint8_t[dataLength]; + pLastDMABuffer->getData(pData, dataLength); GeneralUtils::hexDump(pData, dataLength); delete[] pData; m_dmaSemaphore.take(); - } - */ + } + */ ESP_LOGD(LOG_TAG, "<< cameraMode"); } diff --git a/cpp_utils/IFTTT.cpp b/cpp_utils/IFTTT.cpp index 0cf7a6d9..6af2279d 100644 --- a/cpp_utils/IFTTT.cpp +++ b/cpp_utils/IFTTT.cpp @@ -37,7 +37,7 @@ void IFTTT::trigger( std::string value2, std::string value3) { m_restClient.setURL("https://maker.ifttt.com/trigger/" + event + "/with/key/" + m_key); - cJSON *root; + cJSON* root; root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "value1", value1.c_str()); diff --git a/cpp_utils/JSON.cpp b/cpp_utils/JSON.cpp index 72a8da70..31f8cdb1 100644 --- a/cpp_utils/JSON.cpp +++ b/cpp_utils/JSON.cpp @@ -97,7 +97,7 @@ void JsonArray::addDouble(double value) { * @param [in] value The int value to add to the array. */ void JsonArray::addInt(int value) { - cJSON_AddItemToArray(m_node, cJSON_CreateDouble((double)value, value)); + cJSON_AddItemToArray(m_node, cJSON_CreateNumber((double)value)); } // addInt @@ -125,11 +125,8 @@ void JsonArray::addString(std::string value) { * @return The boolean value at the given index. */ bool JsonArray::getBoolean(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); - if (node->valueint == 0) { - return false; - } - return true; + cJSON* node = cJSON_GetArrayItem(m_node, item); + return (node->valueint != 0); } // getBoolean @@ -139,7 +136,7 @@ bool JsonArray::getBoolean(int item) { * @return The double value at the given index. */ double JsonArray::getDouble(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return node->valuedouble; } // getDouble @@ -150,7 +147,7 @@ double JsonArray::getDouble(int item) { * @return The int value at the given index. */ int JsonArray::getInt(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return node->valueint; } // getInt @@ -161,7 +158,7 @@ int JsonArray::getInt(int item) { * @return The object value at the given index. */ JsonObject JsonArray::getObject(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return JsonObject(node); } // getObject @@ -172,7 +169,7 @@ JsonObject JsonArray::getObject(int item) { * @return The object value at the given index. */ std::string JsonArray::getString(int item) { - cJSON *node = cJSON_GetArrayItem(m_node, item); + cJSON* node = cJSON_GetArrayItem(m_node, item); return std::string(node->valuestring); } // getString @@ -182,13 +179,25 @@ std::string JsonArray::getString(int item) { * @return A JSON string representation of the array. */ std::string JsonArray::toString() { - char *data = cJSON_Print(m_node); + char* data = cJSON_Print(m_node); std::string ret(data); free(data); return ret; } // toString +/** + * @brief Build an unformatted string representation. + * @return A string representation. + */ +std::string JsonArray::toStringUnformatted() { + char* data = cJSON_PrintUnformatted(m_node); + std::string ret(data); + free(data); + return ret; +} // toStringUnformatted + + /** * @brief Get the number of elements from the array. * @return The int value that represents the number of elements. @@ -205,7 +214,7 @@ JsonObject::JsonObject(cJSON* node) { } // JsonObject JsonArray JsonObject::getArray(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); return JsonArray(node); } @@ -216,11 +225,9 @@ JsonArray JsonObject::getArray(std::string name) { * @return The boolean value from the object. */ bool JsonObject::getBoolean(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); - if (node->valueint == 0) { - return false; - } - return true; + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return false; + return cJSON_IsTrue(node); } // getBoolean @@ -230,7 +237,8 @@ bool JsonObject::getBoolean(std::string name) { * @return The double value from the object. */ double JsonObject::getDouble(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return 0.0; return node->valuedouble; } // getDouble @@ -241,7 +249,8 @@ double JsonObject::getDouble(std::string name) { * @return The int value from the object. */ int JsonObject::getInt(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return 0; return node->valueint; } // getInt @@ -252,7 +261,7 @@ int JsonObject::getInt(std::string name) { * @return The object value from the object. */ JsonObject JsonObject::getObject(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); return JsonObject(node); } // getObject @@ -260,10 +269,11 @@ JsonObject JsonObject::getObject(std::string name) { /** * @brief Get the named string value from the object. * @param [in] name The name of the object property. - * @return The string value from the object. + * @return The string value from the object. A zero length string is returned when the object is not present. */ std::string JsonObject::getString(std::string name) { - cJSON *node = cJSON_GetObjectItem(m_node, name.c_str()); + cJSON* node = cJSON_GetObjectItem(m_node, name.c_str()); + if (node == nullptr) return ""; return std::string(node->valuestring); } // getString @@ -304,7 +314,7 @@ void JsonObject::setArray(std::string name, JsonArray array) { * @return N/A. */ void JsonObject::setBoolean(std::string name, bool value) { - cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateBool(value)); + cJSON_AddItemToObject(m_node, name.c_str(), value ? cJSON_CreateTrue() : cJSON_CreateFalse()); } // setBoolean @@ -326,7 +336,7 @@ void JsonObject::setDouble(std::string name, double value) { * @return N/A. */ void JsonObject::setInt(std::string name, int value) { - cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateDouble((double)value, value)); + cJSON_AddItemToObject(m_node, name.c_str(), cJSON_CreateNumber((double) value)); } // setInt @@ -357,9 +367,20 @@ void JsonObject::setString(std::string name, std::string value) { * @return A JSON string representation of the object. */ std::string JsonObject::toString() { - char *data = cJSON_Print(m_node); + char* data = cJSON_Print(m_node); std::string ret(data); free(data); return ret; } // toString + +/** + * @brief Build an unformatted string representation. + * @return A string representation. + */ +std::string JsonObject::toStringUnformatted() { + char* data = cJSON_PrintUnformatted(m_node); + std::string ret(data); + free(data); + return ret; +} // toStringUnformatted diff --git a/cpp_utils/JSON.h b/cpp_utils/JSON.h index 6f4be9b9..f132a0a7 100644 --- a/cpp_utils/JSON.h +++ b/cpp_utils/JSON.h @@ -25,6 +25,7 @@ class JSON { static void deleteArray(JsonArray jsonArray); static JsonObject parseObject(std::string text); static JsonArray parseArray(std::string text); + }; // JSON @@ -33,7 +34,6 @@ class JSON { */ class JsonArray { public: - int getInt(int item); JsonObject getObject(int item); std::string getString(int item); @@ -45,7 +45,9 @@ class JsonArray { void addObject(JsonObject value); void addString(std::string value); std::string toString(); + std::string toStringUnformatted(); std::size_t size(); + private: JsonArray(cJSON* node); friend class JSON; @@ -53,7 +55,8 @@ class JsonArray { /** * @brief The underlying cJSON node. */ - cJSON *m_node; + cJSON* m_node; + }; // JsonArray @@ -77,6 +80,7 @@ class JsonObject { void setObject(std::string name, JsonObject value); void setString(std::string name, std::string value); std::string toString(); + std::string toStringUnformatted(); private: JsonObject(cJSON* node); @@ -86,6 +90,7 @@ class JsonObject { * @brief The underlying cJSON node. */ cJSON* m_node; + }; // JsonObject diff --git a/cpp_utils/MAX7219.cpp b/cpp_utils/MAX7219.cpp index d83615d9..4cac347d 100644 --- a/cpp_utils/MAX7219.cpp +++ b/cpp_utils/MAX7219.cpp @@ -76,7 +76,7 @@ const static uint8_t charTable[] = { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }; -MAX7219::MAX7219(SPI *spi, int numDevices) { +MAX7219::MAX7219(SPI* spi, int numDevices) { assert(spi != nullptr); this->spi = spi; if (numDevices <= 0 || numDevices > 8) { @@ -84,7 +84,7 @@ MAX7219::MAX7219(SPI *spi, int numDevices) { } maxDevices = numDevices; - for (int i = 0; i < 64; i++) { + for (uint8_t i = 0; i < 64; i++) { status[i] = 0x00; } for (int i = 0; i < maxDevices; i++) { @@ -106,21 +106,18 @@ int MAX7219::getDeviceCount() { void MAX7219::shutdown(bool b, int addr) { - if (addr < 0 || addr >= maxDevices) - return; + if (addr < 0 || addr >= maxDevices) return; + if (b) { spiTransfer(addr, OP_SHUTDOWN, 0); - } - else { + } else { spiTransfer(addr, OP_SHUTDOWN, 1); } } void MAX7219::setScanLimit(int limit, int addr) { - if (addr < 0 || addr >= maxDevices) { - return; - } + if (addr < 0 || addr >= maxDevices) return; if (limit >= 0 && limit < 8) { spiTransfer(addr, OP_SCANLIMIT, limit); } @@ -128,9 +125,7 @@ void MAX7219::setScanLimit(int limit, int addr) { void MAX7219::setIntensity(int intensity, int addr) { - if (addr < 0 || addr >= maxDevices) { - return; - } + if (addr < 0 || addr >= maxDevices) return; if (intensity >= 0 && intensity < 16) { spiTransfer(addr, OP_INTENSITY, intensity); } @@ -138,13 +133,11 @@ void MAX7219::setIntensity(int intensity, int addr) { void MAX7219::clearDisplay(int addr) { - int offset; + if (addr < 0 || addr >= maxDevices) return; - if (addr < 0 || addr >= maxDevices) { - return; - } + int offset; offset = addr * 8; - for (int i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { status[offset + i] = 0; spiTransfer(addr, i + 1, status[offset + i]); } @@ -152,12 +145,11 @@ void MAX7219::clearDisplay(int addr) { void MAX7219::setLed(int row, int column, bool state, int addr) { + if (addr < 0 || addr >= maxDevices) return; + int offset; - uint8_t val = 0x00; + uint8_t val = 0; - if (addr < 0 || addr >= maxDevices) { - return; - } if (row < 0 || row > 7 || column < 0 || column > 7) { return; } @@ -165,8 +157,7 @@ void MAX7219::setLed(int row, int column, bool state, int addr) { val = 0b10000000 >> column; if (state) { status[offset + row] = status[offset + row] | val; - } - else { + } else { val = ~val; status[offset + row] = status[offset + row] & val; } @@ -175,28 +166,20 @@ void MAX7219::setLed(int row, int column, bool state, int addr) { void MAX7219::setRow(int row, uint8_t value, int addr) { - int offset; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (row < 0 || row > 7) { - return; - } - offset = addr * 8; + if (addr < 0 || addr >= maxDevices) return; + if (row < 0 || row > 7) return; + + int offset = addr * 8; status[offset + row] = value; spiTransfer(addr, row + 1, status[offset + row]); } void MAX7219::setColumn(int col, uint8_t value, int addr) { - uint8_t val; + if (addr < 0 || addr >= maxDevices) return; + if (col < 0 || col > 7) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (col < 0 || col > 7) { - return; - } + uint8_t val; for (int row = 0; row < 8; row++) { val = value >> (7 - row); val = val & 0x01; @@ -206,17 +189,11 @@ void MAX7219::setColumn(int col, uint8_t value, int addr) { void MAX7219::setDigit(int digit, uint8_t value, bool dp, int addr) { - int offset; - uint8_t v; + if (addr < 0 || addr >= maxDevices) return; + if (digit < 0 || digit > 7 || value > 15) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (digit < 0 || digit > 7 || value > 15) { - return; - } - offset = addr * 8; - v = charTable[value]; + int offset = addr * 8; + uint8_t v = charTable[value]; if (dp) { v |= 0b10000000; } @@ -226,22 +203,13 @@ void MAX7219::setDigit(int digit, uint8_t value, bool dp, int addr) { void MAX7219::setChar(int digit, char value, bool dp, int addr) { - int offset; - uint8_t index, v; + if (addr < 0 || addr >= maxDevices) return; + if (digit < 0 || digit > 7) return; - if (addr < 0 || addr >= maxDevices) { - return; - } - if (digit < 0 || digit > 7) { - return; - } - offset = addr * 8; - index = (uint8_t) value; - if (index > 127) { - //no defined beyond index 127, so we use the space char - index = 32; - } - v = charTable[index]; + int offset = addr * 8; + uint8_t index = (uint8_t) value; + if (index > 127) index = 32; // not defined beyond index 127, so we use the space char + uint8_t v = charTable[index]; if (dp) { v |= 0b10000000; } @@ -250,7 +218,7 @@ void MAX7219::setChar(int digit, char value, bool dp, int addr) { } -void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t data) { +void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t data) { //Create an array with the data to shift out int offset = addr * 2; int maxbytes = maxDevices * 2; @@ -268,13 +236,13 @@ void MAX7219::spiTransfer(int addr, volatile uint8_t opcode, volatile uint8_t da } void MAX7219::setNumber(uint32_t number, int addr) { - //number = number % (uint32_t)pow(10, maxDevices); - for (auto i=0; i<8; i++) { - if (number == 0 && i > 0){ + // number = number % (uint32_t) pow(10, maxDevices); + for (uint8_t i = 0; i < 8; i++) { + if (number == 0 && i > 0) { setChar(i, ' ', addr); } else { - setDigit(i, number%10, addr); - number = number/10; + setDigit(i, number % 10, addr); + number = number / 10; } } } diff --git a/cpp_utils/MAX7219.h b/cpp_utils/MAX7219.h index b5ae41f4..d3faf353 100644 --- a/cpp_utils/MAX7219.h +++ b/cpp_utils/MAX7219.h @@ -50,18 +50,6 @@ * and setColumn(). */ class MAX7219 { -private: - - /* Send out a single command to the device */ - void spiTransfer(int addr, uint8_t opcode, uint8_t data); - - /* We keep track of the led-status for all 8 devices in this array */ - uint8_t status[64]; - - /* The maximum number of devices we use */ - int maxDevices; - SPI *spi; - public: /** * @brief Create a new %MAX7219 controller @@ -70,7 +58,7 @@ class MAX7219 { * @param [in] numDevices maximum number of devices that can be controlled that are * daisy chained together. */ - MAX7219(SPI *spi, int numDevices = 1); + MAX7219(SPI* spi, int numDevices = 1); /** @@ -101,7 +89,7 @@ class MAX7219 { * @param [in] dp Sets the decimal point. * @param [in] addr Address of the display. */ - void setChar(int digit, char value, bool dp=false, int addr=0); + void setChar(int digit, char value, bool dp=false, int addr = 0); /** @@ -111,7 +99,7 @@ class MAX7219 { * @param [in] value each bit set to 1 will light up the corresponding Led. * @param [in] addr address of the display. */ - void setColumn(int col, uint8_t value, int addr=0); + void setColumn(int col, uint8_t value, int addr = 0); /** @@ -122,7 +110,7 @@ class MAX7219 { * @param [in] dp Sets the decimal point. * @param [in] addr Address of the display. */ - void setDigit(int digit, uint8_t value, bool dp=false, int addr=0); + void setDigit(int digit, uint8_t value, bool dp=false, int addr = 0); /** @@ -131,7 +119,7 @@ class MAX7219 { * @param [in] intensity the brightness of the display. (0..15). * @param [in] addr The address of the display to control. */ - void setIntensity(int intensity, int addr=0); + void setIntensity(int intensity, int addr = 0); /** @@ -142,7 +130,7 @@ class MAX7219 { * @param [in] state If true the led is switched on, if false it is switched off. * @param [in] addr Address of the display. */ - void setLed(int row, int col, bool state, int addr=0); + void setLed(int row, int col, bool state, int addr = 0); /** @@ -153,7 +141,7 @@ class MAX7219 { * @param [in] number The number to display. * @param [in] addr Address of the display. */ - void setNumber(uint32_t number, int addr=0); + void setNumber(uint32_t number, int addr = 0); /** @@ -163,7 +151,7 @@ class MAX7219 { * @param [in] value Each bit set to 1 will light up the corresponding Led. * @param [in] addr Address of the display. */ - void setRow(int row, uint8_t value, int addr=0); + void setRow(int row, uint8_t value, int addr = 0); /** @@ -175,7 +163,7 @@ class MAX7219 { * @param [in] limit Number of digits to be displayed (1..8). * @param [in] addr Address of the display to control. */ - void setScanLimit(int limit, int addr=0); + void setScanLimit(int limit, int addr = 0); /** @@ -185,7 +173,18 @@ class MAX7219 { * for normal operation. * @param [in] addr The address of the display to control. */ - void shutdown(bool status, int addr=0); + void shutdown(bool status, int addr = 0); + +private: + /* Send out a single command to the device */ + void spiTransfer(int addr, uint8_t opcode, uint8_t data); + + /* We keep track of the led-status for all 8 devices in this array */ + uint8_t status[64]; + + /* The maximum number of devices we use */ + int maxDevices; + SPI* spi; }; diff --git a/cpp_utils/MFRC522.cpp b/cpp_utils/MFRC522.cpp index a860e9ba..940c170c 100644 --- a/cpp_utils/MFRC522.cpp +++ b/cpp_utils/MFRC522.cpp @@ -18,8 +18,8 @@ #include "MFRC522.h" #include "MFRC522Debug.h" -#include -#include +#include "FreeRTOS.h" +#include "GPIO.h" #include #include #include @@ -41,29 +41,37 @@ static const char LOG_TAG[] = "MFRC522"; /** * Writes a byte to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to write to. One of the PCD_Register enums. + * @param value The value to write. */ -void MFRC522::PCD_WriteRegister( - PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. - byte value ///< The value to write. - ) { +void MFRC522::PCD_WriteRegister(PCD_Register reg, byte value) { uint8_t data[2]; data[0] = reg; data[1] = value; + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transfer(data, 2); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + } // End PCD_WriteRegister() + /** * Writes a number of bytes to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to write to. One of the PCD_Register enums. + * @param count The number of bytes to write to the register + * @param values The values to write. Byte array. */ -void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. - byte count, ///< The number of bytes to write to the register - byte *values ///< The values to write. Byte array. - ) { - uint8_t* pData = new uint8_t[count+1]; +void MFRC522::PCD_WriteRegister(PCD_Register reg, byte count, byte* values) { + uint8_t* pData = new uint8_t[count + 1]; pData[0] = reg; - memcpy(pData+1, values, count); - m_spi.transfer(pData, count+1); + memcpy(pData + 1, values, count); + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave + m_spi.transfer(pData, count + 1); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + delete[] pData; } // End PCD_WriteRegister() @@ -71,32 +79,38 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to /** * Reads a byte from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to read from. One of the PCD_Register enums. */ -byte MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. - ) { +byte MFRC522::PCD_ReadRegister(PCD_Register reg) { uint8_t data[2]; data[0] = reg | 0x80; data[1] = 0; + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transfer(data, 2); + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave + return data[1]; } // End PCD_ReadRegister() + /** * Reads a number of bytes from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. + * @param reg The register to read from. One of the PCD_Register enums. + * @param count The number of bytes to read + * @param values Byte array to store the values in. + * @param rxAlign Only bit positions rxAlign..7 in values[0] are updated. */ -void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from. One of the PCD_Register enums. - byte count, ///< The number of bytes to read - byte *values, ///< Byte array to store the values in. - byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. - ) { - if (count == 0) { - return; - } +void MFRC522::PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte rxAlign) { + if (count == 0) return; + //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); byte address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. byte index = 0; // Index in values array. count--; // One read is performed outside of the loop + + ESP32CPP::GPIO::low((gpio_num_t)_chipSelectPin); // Select slave m_spi.transferByte(address); // Tell MFRC522 which address we want to read if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] // Create bit mask for bit positions rxAlign..7 @@ -112,25 +126,28 @@ void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from index++; } values[index] = m_spi.transferByte(0); // Read the final byte. Send 0 to stop reading. + ESP32CPP::GPIO::high((gpio_num_t)_chipSelectPin); // Release slave } // End PCD_ReadRegister() + /** * Sets the bits given in mask in register reg. + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to set. */ -void MFRC522::PCD_SetRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to set. - ) { +void MFRC522::PCD_SetRegisterBitMask(PCD_Register reg, byte mask) { byte tmp; tmp = PCD_ReadRegister(reg); - PCD_WriteRegister(reg, tmp | mask); // set bit mask + PCD_WriteRegister(reg, tmp | mask); // set bit mask } // End PCD_SetRegisterBitMask() + /** * Clears the bits given in mask from register reg. + * @param reg The register to update. One of the PCD_Register enums. + * @param mask The bits to clear. */ -void MFRC522::PCD_ClearRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. - byte mask ///< The bits to clear. - ) { +void MFRC522::PCD_ClearRegisterBitMask(PCD_Register reg, byte mask) { byte tmp; tmp = PCD_ReadRegister(reg); PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask @@ -139,13 +156,12 @@ void MFRC522::PCD_ClearRegisterBitMask( PCD_Register reg, ///< The register to u /** * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. - * + * @param data In: Pointer to the data to transfer to the FIFO for CRC calculation. + * @param length In: The number of bytes to transfer. + * @param result Out: Pointer to result buffer. Result is written to result[0..1], low byte first. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. - byte length, ///< In: The number of bytes to transfer. - byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. - ) { +MFRC522::StatusCode MFRC522::PCD_CalculateCRC(byte* data, byte length, byte* result) { PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit PCD_WriteRegister(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization @@ -159,7 +175,7 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to for (uint16_t i = 5000; i > 0; i--) { // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved byte n = PCD_ReadRegister(DivIrqReg); - if (n & 0x04) { // CRCIRq bit set - calculation done + if (n & 0x04) { // CRCIRq bit set - calculation done PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. // Transfer the result from the registers to the result buffer result[0] = PCD_ReadRegister(CRCResultRegL); @@ -172,32 +188,31 @@ MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to } // End PCD_CalculateCRC() -///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 -///////////////////////////////////////////////////////////////////////////////////// + /** * Initializes the MFRC522 chip. */ void MFRC522::PCD_Init() { //m_spi.setHost(VSPI_HOST); + + ESP32CPP::GPIO::setOutput((gpio_num_t)_chipSelectPin); m_spi.init(); bool hardReset = false; - // Set the chipSelectPin as digital output, do not select the slave yet -/* - pinMode(_chipSelectPin, OUTPUT); - digitalWrite(_chipSelectPin, HIGH); -*/ + // pinMode(_chipSelectPin, OUTPUT); + // digitalWrite(_chipSelectPin, HIGH); + // If a valid pin number has been set, pull device out of power down / reset state. if (_resetPowerDownPin != UNUSED_PIN) { // Set the resetPowerDownPin as digital output, do not reset or power down. //pinMode(_resetPowerDownPin, OUTPUT); ESP32CPP::GPIO::setInput((gpio_num_t)_resetPowerDownPin); - if (ESP32CPP::GPIO::read((gpio_num_t)_resetPowerDownPin) == false) { // The MFRC522 chip is in power down mode. + if (!ESP32CPP::GPIO::read((gpio_num_t)_resetPowerDownPin)) { // The MFRC522 chip is in power down mode. ESP32CPP::GPIO::setOutput((gpio_num_t)_resetPowerDownPin); ESP32CPP::GPIO::high((gpio_num_t)_resetPowerDownPin); // Exit power down mode. This triggers a hard reset. // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let us be generous: 50ms. @@ -221,27 +236,30 @@ void MFRC522::PCD_Init() { // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds - PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. PCD_WriteRegister(TReloadRegL, 0xE8); - PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) - PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) } // End PCD_Init() + /** * Initializes the MFRC522 chip. + * @param chipSelectPin Pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + * @param resetPowerDownPin Pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) */ -void MFRC522::PCD_Init( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) - byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) - ) { +void MFRC522::PCD_Init(byte chipSelectPin, byte resetPowerDownPin) { _chipSelectPin = chipSelectPin; _resetPowerDownPin = resetPowerDownPin; // Set the chipSelectPin as digital output, do not select the slave yet PCD_Init(); } // End PCD_Init() + /** * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. */ @@ -252,11 +270,12 @@ void MFRC522::PCD_Reset() { // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let us be generous: 50ms. FreeRTOS::sleep(50); // Wait for the PowerDown bit in CommandReg to be cleared - while (PCD_ReadRegister(CommandReg) & (1<<4)) { + while (PCD_ReadRegister(CommandReg) & (1 << 4)) { // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. } } // End PCD_Reset() + /** * Turns the antenna on by enabling pins TX1 and TX2. * After a reset these pins are disabled. @@ -268,6 +287,7 @@ void MFRC522::PCD_AntennaOn() { } } // End PCD_AntennaOn() + /** * Turns the antenna off by disabling pins TX1 and TX2. */ @@ -275,6 +295,7 @@ void MFRC522::PCD_AntennaOff() { PCD_ClearRegisterBitMask(TxControlReg, 0x03); } // End PCD_AntennaOff() + /** * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf @@ -283,21 +304,23 @@ void MFRC522::PCD_AntennaOff() { * @return Value of the RxGain, scrubbed to the 3 bits used. */ byte MFRC522::PCD_GetAntennaGain() { - return PCD_ReadRegister(RFCfgReg) & (0x07<<4); + return PCD_ReadRegister(RFCfgReg) & (0x07 << 4); } // End PCD_GetAntennaGain() + /** * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. */ void MFRC522::PCD_SetAntennaGain(byte mask) { - if (PCD_GetAntennaGain() != mask) { // only bother if there is a change - PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern + if (PCD_GetAntennaGain() != mask) { // only bother if there is a change + PCD_ClearRegisterBitMask(RFCfgReg, (0x07 << 4)); // clear needed to allow 000 pattern PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits } } // End PCD_SetAntennaGain() + /** * Performs a self-test of the MFRC522 * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf @@ -335,9 +358,7 @@ bool MFRC522::PCD_PerformSelfTest() { // It is reported that some devices does not trigger CRCIRq flag // during selftest. n = PCD_ReadRegister(FIFOLevelReg); - if (n >= 64) { - break; - } + if (n >= 64) break; } PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. @@ -373,9 +394,7 @@ bool MFRC522::PCD_PerformSelfTest() { // Verify that the results match up to our expectations for (uint8_t i = 0; i < 64; i++) { - if (result[i] != (reference[i])) { - return false; - } + if (result[i] != (reference[i])) return false; } // Test passed; all is good. @@ -383,45 +402,47 @@ bool MFRC522::PCD_PerformSelfTest() { } // End PCD_PerformSelfTest() ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs ///////////////////////////////////////////////////////////////////////////////////// + /** * Executes the Transceive command. * CRC validation can only be done if backData and backLen are specified. - * + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData nullptr or pointer to buffer if data should be read back after executing the command. + * @param backLen In: Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default nullptr. + * @param rxAlign In: Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default nullptr. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { +MFRC522::StatusCode MFRC522::PCD_TransceiveData(byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { byte waitIRq = 0x30; // RxIRq and IdleIRq return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); } // End PCD_TransceiveData() + /** * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. * CRC validation can only be done if backData and backLen are specified. * + * @param command The command to execute. One of the PCD_Command enums. + * @param waitIRq The bits in the ComIrqReg register that signals successful completion of the command. + * @param sendData Pointer to the data to transfer to the FIFO. + * @param sendLen Number of bytes to transfer to the FIFO. + * @param backData nullptr or pointer to buffer if data should be read back after executing the command. + * @param backLen In: Max number of bytes to write to *backData. Out: The number of bytes returned. + * @param validBits In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + * @param rxAlign In: Defines the bit position in backData[0] for the first bit received. Default 0. + * @param checkCRC In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. - byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. - byte *sendData, ///< Pointer to the data to transfer to the FIFO. - byte sendLen, ///< Number of bytes to transfer to the FIFO. - byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. - byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. - byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. - byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. - ) { +MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC(byte command, byte waitIRq, byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits, byte rxAlign, bool checkCRC) { // Prepare values for BitFramingReg - byte txLastBits = validBits ? *validBits : 0; + byte txLastBits = validBits ? *validBits : (byte) 0; byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. @@ -449,9 +470,7 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co } } // 35.7ms and nothing happend. Communication with the MFRC522 might be down. - if (i == 0) { - return STATUS_TIMEOUT; - } + if (i == 0) return STATUS_TIMEOUT; // Stop now if any errors except collisions were detected. byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr @@ -464,10 +483,8 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co // If the caller wants data back, get it from the MFRC522. if (backData && backLen) { byte n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO - if (n > *backLen) { - return STATUS_NO_ROOM; - } - *backLen = n; // Number of bytes returned + if (n > *backLen) return STATUS_NO_ROOM; + *backLen = n; // Number of bytes returned PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. if (validBits) { @@ -483,19 +500,13 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co // Perform CRC_A validation if requested. if (backData && backLen && checkCRC) { // In this case a MIFARE Classic NAK is not OK. - if (*backLen == 1 && _validBits == 4) { - return STATUS_MIFARE_NACK; - } + if (*backLen == 1 && _validBits == 4) return STATUS_MIFARE_NACK; // We need at least the CRC_A value and all 8 bits of the last byte must be received. - if (*backLen < 2 || _validBits != 0) { - return STATUS_CRC_WRONG; - } + if (*backLen < 2 || _validBits != 0) return STATUS_CRC_WRONG; // Verify CRC_A - do our own calculation and store the control in controlBuffer. byte controlBuffer[2]; MFRC522::StatusCode status = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); - if (status != STATUS_OK) { - return status; - } + if (status != STATUS_OK) return status; if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { return STATUS_CRC_WRONG; } @@ -504,58 +515,57 @@ MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The co return STATUS_OK; } // End PCD_CommunicateWithPICC() + /** * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_RequestA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_RequestA(byte* bufferATQA, byte* bufferSize) { return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); } // End PICC_RequestA() + /** * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_WakeupA(byte* bufferATQA, byte* bufferSize) { return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); } // End PICC_WakeupA() + /** * Transmits REQA or WUPA commands. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * + * @param command The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + * @param bufferATQA The buffer to store the ATQA (Answer to request) in + * @param bufferSize Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA - byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in - byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA(byte command, byte* bufferATQA, byte* bufferSize) { byte validBits; MFRC522::StatusCode status; - if (bufferATQA == nullptr || *bufferSize < 2) { // The ATQA response is 2 bytes long. - return STATUS_NO_ROOM; - } PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + if (bufferATQA == nullptr || *bufferSize < 2) return STATUS_NO_ROOM; // The ATQA response is 2 bytes long. status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); - if (status != STATUS_OK) { - return status; - } - if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. - return STATUS_ERROR; - } + if (status != STATUS_OK) return status; + + if (*bufferSize != 2 || validBits != 0) return STATUS_ERROR; // ATQA must be exactly 16 bits. return STATUS_OK; } // End PICC_REQA_or_WUPA() + /** * Transmits SELECT/ANTICOLLISION commands to select a single PICC. * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). @@ -573,9 +583,7 @@ MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command * * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. - byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. - ) { +MFRC522::StatusCode MFRC522::PICC_Select(Uid* uid, byte validBits) { bool uidComplete; bool selectDone; bool useCascadeTag; @@ -589,7 +597,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. - byte *responseBuffer; + byte* responseBuffer; byte responseLength; // Description of buffer structure: @@ -615,9 +623,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct // 3 uid6 uid7 uid8 uid9 // Sanity checks - if (validBits > 80) { - return STATUS_INVALID; - } + if (validBits > 80) return STATUS_INVALID; // Prepare MFRC522 PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. @@ -632,22 +638,18 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct uidIndex = 0; useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes break; - case 2: buffer[0] = PICC_CMD_SEL_CL2; uidIndex = 3; useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes break; - case 3: buffer[0] = PICC_CMD_SEL_CL3; uidIndex = 6; useCascadeTag = false; // Never used in CL3. break; - default: return STATUS_INTERNAL_ERROR; - break; } // How many UID bits are known in this Cascade Level? @@ -686,16 +688,13 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 7, &buffer[7]); - if (result != STATUS_OK) { - return result; - } txLastBits = 0; // 0 => All 8 bits are valid. bufferUsed = 9; + if (result != STATUS_OK) return result; // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) responseBuffer = &buffer[6]; responseLength = 3; - } - else { // This is an ANTICOLLISION. + } else { // This is an ANTICOLLISION. //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); txLastBits = currentLevelKnownBits % 8; count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. @@ -715,31 +714,25 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] - if (valueOfCollReg & 0x20) { // CollPosNotValid - return STATUS_COLLISION; // Without a valid collision position we cannot continue - } + if (valueOfCollReg & 0x20) return STATUS_COLLISION; // CollPosNotValid, Without a valid collision position we cannot continue byte collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32. if (collisionPos == 0) { collisionPos = 32; } - if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen - return STATUS_INTERNAL_ERROR; - } + if (collisionPos <= currentLevelKnownBits) return STATUS_INTERNAL_ERROR; // No progress - should not happen + // Choose the PICC with the bit set. currentLevelKnownBits = collisionPos; count = (currentLevelKnownBits - 1) % 8; // The bit to modify index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. buffer[index] |= (1 << count); } - else if (result != STATUS_OK) { - return result; - } + else if (result != STATUS_OK) return result; else { // STATUS_OK if (currentLevelKnownBits >= 32) { // This was a SELECT. selectDone = true; // No more anticollision // We continue below outside the while. - } - else { // This was an ANTICOLLISION. + } else { // This was an ANTICOLLISION. // We now have all 32 bits of the UID in this Cascade Level currentLevelKnownBits = 32; // Run loop again to do the SELECT. @@ -757,21 +750,18 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct } // Check response SAK (Select Acknowledge) - if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). - return STATUS_ERROR; - } + if (responseLength != 3 || txLastBits != 0) return STATUS_ERROR; // SAK must be exactly 24 bits (1 byte + CRC_A). + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { return STATUS_CRC_WRONG; } if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes cascadeLevel++; - } - else { + } else { uidComplete = true; uid->sak = responseBuffer[0]; } @@ -783,6 +773,7 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct return STATUS_OK; } // End PICC_Select() + /** * Instructs a PICC in state ACTIVE(*) to go to state HALT. * @@ -797,9 +788,7 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { buffer[1] = 0; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Send the command. // The standard says: @@ -807,12 +796,10 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { // HLTA command, this response shall be interpreted as 'not acknowledge'. // We interpret that this way: Only STATUS_TIMEOUT is a success. result = PCD_TransceiveData(buffer, sizeof(buffer), nullptr, 0); - if (result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) - return STATUS_ERROR; - } + + if (result == STATUS_TIMEOUT) return STATUS_OK; + if (result == STATUS_OK) return STATUS_ERROR; // That is ironically NOT ok in this case ;-) + return result; } // End PICC_HaltA() @@ -820,6 +807,7 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// + /** * Executes the MFRC522 MFAuthent command. * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. @@ -830,13 +818,13 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { * * All keys are set to FFFFFFFFFFFFh at chip delivery. * + * @param command PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + * @param blockAddr The block number. See numbering in the comments in the .h file. + * @param key Pointer to the Crypteo1 key to use (6 bytes) + * @param uid Pointer to Uid struct. The first 4 bytes of the UID is used. * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. */ -MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B - byte blockAddr, ///< The block number. See numbering in the comments in the .h file. - MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) - Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. - ) { +MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key* key, Uid* uid) { byte waitIRq = 0x10; // IdleIRq // Build command buffer @@ -844,20 +832,21 @@ MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AU sendData[0] = command; sendData[1] = blockAddr; for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes - sendData[2+i] = key->keyByte[i]; + sendData[2 + i] = key->keyByte[i]; } // Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf // section 3.2.5 "MIFARE Classic Authentication". // The only missed case is the MF1Sxxxx shortcut activation, // but it requires cascade tag (CT) byte, that is not part of uid. - for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID - sendData[8+i] = uid->uidByte[i+uid->size-4]; + for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID + sendData[8 + i] = uid->uidByte[i + uid->size - 4]; } // Start the authentication. return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); } // End PCD_Authenticate() + /** * Used to exit the PCD from its authenticated state. * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. @@ -867,6 +856,7 @@ void MFRC522::PCD_StopCrypto1() { PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] } // End PCD_StopCrypto1() + /** * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. * @@ -881,32 +871,29 @@ void MFRC522::PCD_StopCrypto1() { * The buffer must be at least 18 bytes because a CRC_A is also returned. * Checks the CRC_A before returning STATUS_OK. * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + * @param buffer The buffer to store the data in + * @param bufferSize Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. - byte *buffer, ///< The buffer to store the data in - byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Read(byte blockAddr, byte* buffer, byte* bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || *bufferSize < 18) { - return STATUS_NO_ROOM; - } + if (buffer == nullptr || *bufferSize < 18) return STATUS_NO_ROOM; // Build command buffer buffer[0] = PICC_CMD_MF_READ; buffer[1] = blockAddr; // Calculate CRC_A result = PCD_CalculateCRC(buffer, 2, &buffer[2]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Transmit the buffer and receive the response, validate CRC_A. return PCD_TransceiveData(buffer, 4, buffer, bufferSize, nullptr, 0, true); } // End MIFARE_Read() + /** * Writes 16 bytes to the active PICC. * @@ -915,19 +902,16 @@ MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. - * * + * @param blockAddr MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + * @param buffer The 16 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. - byte *buffer, ///< The 16 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Write(byte blockAddr, byte* buffer, byte bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || bufferSize < 16) { - return STATUS_INVALID; - } + if (buffer == nullptr || bufferSize < 16) return STATUS_INVALID; // Mifare Classic protocol requires two communications to perform a write. // Step 1: Tell the PICC we want to write to block blockAddr. @@ -935,34 +919,28 @@ MFRC522::StatusCode MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: cmdBuffer[0] = PICC_CMD_MF_WRITE; cmdBuffer[1] = blockAddr; result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; // Step 2: Transfer the data result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_Write() + /** * Writes a 4 byte page to the active MIFARE Ultralight PICC. - * + * @param page The page (2-15) to write to. + * @param buffer The 4 bytes to write to the PICC + * @param bufferSize Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. - byte *buffer, ///< The 4 bytes to write to the PICC - byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write(byte page, byte* buffer, byte bufferSize) { MFRC522::StatusCode result; // Sanity check - if (buffer == nullptr || bufferSize < 4) { - return STATUS_INVALID; - } + if (buffer == nullptr || bufferSize < 4) return STATUS_INVALID; // Build commmand buffer byte cmdBuffer[6]; @@ -972,106 +950,105 @@ MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page // Perform the write result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; + return STATUS_OK; } // End MIFARE_Ultralight_Write() + /** * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. + * @param blockAddr The block (0-0xff) number. + * @param delta This number is subtracted from the value of block blockAddr. * * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. - int32_t delta ///< This number is subtracted from the value of block blockAddr. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Decrement(byte blockAddr, int32_t delta) { return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); } // End MIFARE_Decrement() + /** * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. * + * @param blockAddr The block (0-0xff) number. + * @param delta This number is added to the value of block blockAddr. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. - int32_t delta ///< This number is added to the value of block blockAddr. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Increment(byte blockAddr, int32_t delta) { return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); } // End MIFARE_Increment() + /** * MIFARE Restore copies the value of the addressed block into a volatile memory. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * Use MIFARE_Transfer() to store the result in a block. * + * @param blockAddr The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Restore(byte blockAddr) { // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. // Doing only a single step does not work, so I chose to transfer 0L in step two. return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); } // End MIFARE_Restore() + /** * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. * + * @param command The command to use + * @param blockAddr The block (0-0xff) number. + * @param data The data to transfer in step 2 * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use - byte blockAddr, ///< The block (0-0xff) number. - int32_t data ///< The data to transfer in step 2 - ) { +MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper(byte command, byte blockAddr, int32_t data) { MFRC522::StatusCode result; byte cmdBuffer[2]; // We only need room for 2 bytes. // Step 1: Tell the PICC the command and block address cmdBuffer[0] = command; cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) return result; // Step 2: Transfer the data - result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive((byte*) &data, 4, true); // Adds CRC_A and accept timeout as success. + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_TwoStepHelper() + /** * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * + * @param blockAddr The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. - ) { +MFRC522::StatusCode MFRC522::MIFARE_Transfer(byte blockAddr) { MFRC522::StatusCode result; byte cmdBuffer[2]; // We only need room for 2 bytes. // Tell the PICC we want to transfer the result into block blockAddr. cmdBuffer[0] = PICC_CMD_MF_TRANSFER; cmdBuffer[1] = blockAddr; - result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. - if (result != STATUS_OK) { - return result; - } + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) return result; return STATUS_OK; } // End MIFARE_Transfer() + /** * Helper routine to read the current value from a Value Block. * @@ -1083,7 +1060,7 @@ MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0 * @param[out] value Current value of the Value Block. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { +MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t* value) { MFRC522::StatusCode status; byte buffer[18]; byte size = sizeof(buffer); @@ -1092,11 +1069,12 @@ MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { status = MIFARE_Read(blockAddr, buffer, &size); if (status == STATUS_OK) { // Extract the value - *value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); + *value = (int32_t(buffer[3]) << 24) | (int32_t(buffer[2]) << 16) | (int32_t(buffer[1]) << 8) | int32_t(buffer[0]); } return status; } // End MIFARE_GetValue() + /** * Helper routine to write a specific value into a Value Block. * @@ -1129,6 +1107,7 @@ MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { return MIFARE_Write(blockAddr, buffer, 16); } // End MIFARE_SetValue() + /** * Authenticate with a NTAG216. * @@ -1138,8 +1117,7 @@ MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { * @param[in] pACK result success???. * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Authenticate with 32bit password -{ +MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) { //Authenticate with 32bit password // TODO: Fix cmdBuffer length and rxlength. They really should match. // (Better still, rxlength should not even be necessary.) @@ -1148,14 +1126,13 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut cmdBuffer[0] = 0x1B; //Comando de autentificacion - for (byte i = 0; i<4; i++) - cmdBuffer[i+1] = passWord[i]; + for (byte i = 0; i < 4; i++) { + cmdBuffer[i + 1] = passWord[i]; + } result = PCD_CalculateCRC(cmdBuffer, 5, &cmdBuffer[5]); - if (result!=STATUS_OK) { - return result; - } + if (result!=STATUS_OK) return result; // Transceive the data, store the reply in cmdBuffer[] byte waitIRq = 0x30; // RxIRq and IdleIRq @@ -1167,9 +1144,7 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut pACK[0] = cmdBuffer[0]; pACK[1] = cmdBuffer[1]; - if (result!=STATUS_OK) { - return result; - } + if (result!=STATUS_OK) return result; return STATUS_OK; } // End PCD_NTAG216_AUTH() @@ -1179,30 +1154,27 @@ MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Aut // Support functions ///////////////////////////////////////////////////////////////////////////////////// + /** * Wrapper for MIFARE protocol communication. * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. * + * @param sendData Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + * @param sendLen Number of bytes in sendData. + * @param acceptTimeout True => A timeout is also success * @return STATUS_OK on success, STATUS_??? otherwise. */ -MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. - byte sendLen, ///< Number of bytes in sendData. - bool acceptTimeout ///< True => A timeout is also success - ) { +MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive(byte* sendData, byte sendLen, bool acceptTimeout) { MFRC522::StatusCode result; byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. // Sanity check - if (sendData == nullptr || sendLen > 16) { - return STATUS_INVALID; - } + if (sendData == nullptr || sendLen > 16) return STATUS_INVALID; // Copy sendData[] to cmdBuffer[] and add CRC_A memcpy(cmdBuffer, sendData, sendLen); result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); - if (result != STATUS_OK) { - return result; - } + if (result != STATUS_OK) return result; sendLen += 2; // Transceive the data, store the reply in cmdBuffer[] @@ -1210,19 +1182,11 @@ MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointe byte cmdBufferSize = sizeof(cmdBuffer); byte validBits = 0; result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); - if (acceptTimeout && result == STATUS_TIMEOUT) { - return STATUS_OK; - } - if (result != STATUS_OK) { - return result; - } + if (acceptTimeout && result == STATUS_TIMEOUT) return STATUS_OK; + if (result != STATUS_OK) return result; // The PICC must reply with a 4 bit ACK - if (cmdBufferSize != 1 || validBits != 4) { - return STATUS_ERROR; - } - if (cmdBuffer[0] != MF_ACK) { - return STATUS_MIFARE_NACK; - } + if (cmdBufferSize != 1 || validBits != 4) return STATUS_ERROR; + if (cmdBuffer[0] != MF_ACK) return STATUS_MIFARE_NACK; return STATUS_OK; } // End PCD_MIFARE_Transceive() @@ -1230,10 +1194,10 @@ MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointe /** * Translates the SAK (Select Acknowledge) to a PICC type. * + * @param sak The SAK byte returned from PICC_Select(). * @return PICC_Type */ -MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). - ) { +MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak) { // http://www.nxp.com/documents/application_note/AN10833.pdf // 3.2 Coding of Select Acknowledge (SAK) // ignore 8-bit (iso14443 starts with LSBit = bit 1) @@ -1276,19 +1240,21 @@ void MFRC522::PCD_DumpVersionToSerial() { ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); // When 0x00 or 0xFF is returned, communication probably failed - if ((v == 0x00) || (v == 0xFF)) + if ((v == 0x00) || (v == 0xFF)) { ESP_LOGD(LOG_TAG, "WARNING: Communication failure, is the MFRC522 properly connected?"); + } } // End PCD_DumpVersionToSerial() + /** * Dumps debug info about the selected PICC to Serial. * On success the PICC is halted after dumping the data. * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). * @DEPRECATED Kept for bakward compatibility */ -void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). - ) { +void MFRC522::PICC_DumpToSerial(Uid* uid) { MIFARE_Key key; // Dump UID, SAK and Type @@ -1325,17 +1291,17 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro break; // No memory dump here } - ESP_LOGD(LOG_TAG,""); PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. } // End PICC_DumpToSerial() + /** * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). * @DEPRECATED kept for backward compatibility */ -void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). - ) { +void MFRC522::PICC_DumpDetailsToSerial(Uid* uid) { // UID std::ostringstream oss; oss << std::hex << std::setfill('0'); @@ -1351,7 +1317,6 @@ void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct retur oss << "" << "Card SAK: " << std::setw(2) << (int)uid->sak; ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); - // (suggested) PICC type PICC_Type piccType = PICC_GetType(uid->sak); oss.str(""); @@ -1359,31 +1324,30 @@ void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct retur ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } // End PICC_DumpDetailsToSerial() + /** * Dumps memory contents of a MIFARE Classic PICC. * On success the PICC is halted after dumping the data. + * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). + * @param piccType One of the PICC_Type enums. + * @param key Key A used for all sectors. */ -void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - PICC_Type piccType, ///< One of the PICC_Type enums. - MIFARE_Key *key ///< Key A used for all sectors. - ) { +void MFRC522::PICC_DumpMifareClassicToSerial(Uid* uid, PICC_Type piccType, MIFARE_Key* key) { byte no_of_sectors = 0; switch (piccType) { case PICC_TYPE_MIFARE_MINI: // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. no_of_sectors = 5; break; - case PICC_TYPE_MIFARE_1K: // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. no_of_sectors = 16; break; - case PICC_TYPE_MIFARE_4K: // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. no_of_sectors = 40; break; - default: // Should not happen. Ignore. break; } @@ -1399,15 +1363,17 @@ void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid st PCD_StopCrypto1(); } // End PICC_DumpMifareClassicToSerial() + /** * Dumps memory contents of a sector of a MIFARE Classic PICC. * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. + * + * @param uid Pointer to Uid struct returned from a successful PICC_Select(). + * @param key Key A for the sector. + * @param sector The sector to dump, 0..39. */ -void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). - MIFARE_Key *key, ///< Key A for the sector. - byte sector ///< The sector to dump, 0..39. - ) { +void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid* uid, MIFARE_Key* key, byte sector) { MFRC522::StatusCode status; byte firstBlock; // Address of lowest address to dump actually last block dumped) byte no_of_blocks; // Number of blocks in sector @@ -1436,8 +1402,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U else if (sector < 40) { // Sectors 32-39 has 16 blocks each no_of_blocks = 16; firstBlock = 128 + (sector - 32) * no_of_blocks; - } - else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. + } else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. return; } @@ -1452,28 +1417,23 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U // Sector number - only on first line std::ostringstream oss; if (isSectorTrailer) { - if(sector < 10) { + if (sector < 10) { oss << " "; // Pad with spaces - } - else { + } else { oss << " "; // Pad with spaces } - oss << (int)sector; - + oss << (int) sector; oss << " "; - } - else { oss << " "; + } else { } // Block number - if(blockAddr < 10) { + if (blockAddr < 10) { oss << " "; // Pad with spaces - } - else { - if(blockAddr < 100) { + } else { + if (blockAddr < 100) { oss << " "; // Pad with spaces - } - else { + } else { oss << " "; // Pad with spaces } } @@ -1502,7 +1462,6 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U oss << std::hex << std::setfill('0'); for (byte index = 0; index < 16; index++) { oss << " " << std::setw(2) << (int)buffer[index]; - if ((index % 4) == 3) { oss << " "; } @@ -1527,8 +1486,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (no_of_blocks == 4) { group = blockOffset; firstInGroup = true; - } - else { + } else { group = blockOffset / 5; firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); } @@ -1536,15 +1494,15 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (firstInGroup) { // Print access bits oss << std::dec; - oss << " [ " << (int)((g[group] >> 2) & 1) << " " << (int)((g[group] >> 1) & 1) << " " << (int) ((g[group] >> 0) & 1) << " ] "; + oss << " [ " << ((g[group] >> 2) & 1) << " " << ((g[group] >> 1) & 1) << " " << ((g[group] >> 0) & 1) << " ] "; if (invertedError) { oss << " Inverted access bits did not match! "; } } if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block - int32_t value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); - oss << " Value=0x" << std::hex << value << " Adr=0x" << (int)buffer[12]; + int32_t value = (int32_t(buffer[3]) << 24) | (int32_t(buffer[2]) << 16) | (int32_t(buffer[1]) << 8) | int32_t(buffer[0]); + oss << " Value=0x" << std::hex << value << " Adr=0x" << (int) buffer[12]; } ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } @@ -1552,6 +1510,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U return; } // End PICC_DumpMifareClassicSectorToSerial() + /** * Dumps memory contents of a MIFARE Ultralight PICC. */ @@ -1564,7 +1523,7 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { std::ostringstream oss; oss << "Page 0 1 2 3"; // Try the mpages of the original Ultralight. Ultralight C has more pages. - for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + for (byte page = 0; page < 16; page += 4) { // Read returns data for 4 pages at a time. // Read pages byteCount = sizeof(buffer); status = MIFARE_Read(page, buffer, &byteCount); @@ -1582,22 +1541,24 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { for (byte index = 0; index < 4; index++) { i = 4 * offset + index; - oss << std::hex << std::setw(2) << (int)buffer[i]; + oss << std::hex << std::setw(2) << (int) buffer[i]; } ESP_LOGD(LOG_TAG, "%s", oss.str().c_str()); } } } // End PICC_DumpMifareUltralightToSerial() + /** * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1). + * + * @param accessBitBuffer Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + * @param g0 Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + * @param g1 Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + * @param g2 Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + * @param g3 Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) */ -void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. - byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) - byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) - byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) - byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) - ) { +void MFRC522::MIFARE_SetAccessBits(byte* accessBitBuffer, byte g0, byte g1, byte g2, byte g3) { byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); @@ -1611,6 +1572,7 @@ void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte // Convenience functions - does not add extra functionality ///////////////////////////////////////////////////////////////////////////////////// + /** * Returns true if a PICC responds to PICC_CMD_REQA. * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. @@ -1631,6 +1593,7 @@ bool MFRC522::PICC_IsNewCardPresent() { return (result == STATUS_OK || result == STATUS_COLLISION); } // End PICC_IsNewCardPresent() + /** * Simple wrapper around PICC_Select. * Returns true if a UID could be read. diff --git a/cpp_utils/MFRC522.h b/cpp_utils/MFRC522.h index 2e060712..9e03ba4f 100644 --- a/cpp_utils/MFRC522.h +++ b/cpp_utils/MFRC522.h @@ -76,7 +76,6 @@ #define MFRC522_h - #include #include @@ -258,7 +257,6 @@ class MFRC522 { PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. - PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. // The read/write commands can also be used for MIFARE Ultralight. @@ -329,18 +327,20 @@ class MFRC522 { ///////////////////////////////////////////////////////////////////////////////////// // Functions for setting up the Arduino ///////////////////////////////////////////////////////////////////////////////////// + //MFRC522(); ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(PCD_Register reg, byte value); - void PCD_WriteRegister(PCD_Register reg, byte count, byte *values); + void PCD_WriteRegister(PCD_Register reg, byte count, byte* values); byte PCD_ReadRegister(PCD_Register reg); - void PCD_ReadRegister(PCD_Register reg, byte count, byte *values, byte rxAlign = 0); + void PCD_ReadRegister(PCD_Register reg, byte count, byte* values, byte rxAlign = 0); void PCD_SetRegisterBitMask(PCD_Register reg, byte mask); void PCD_ClearRegisterBitMask(PCD_Register reg, byte mask); - StatusCode PCD_CalculateCRC(byte *data, byte length, byte *result); + StatusCode PCD_CalculateCRC(byte* data, byte length, byte* result); ///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 @@ -357,46 +357,46 @@ class MFRC522 { ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with PICCs ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); - StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = nullptr, byte *backLen = nullptr, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); - StatusCode PICC_RequestA(byte *bufferATQA, byte *bufferSize); - StatusCode PICC_WakeupA(byte *bufferATQA, byte *bufferSize); - StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); - virtual StatusCode PICC_Select(Uid *uid, byte validBits = 0); + StatusCode PCD_TransceiveData(byte* sendData, byte sendLen, byte* backData, byte* backLen, byte* validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte* sendData, byte sendLen, byte* backData = nullptr, byte* backLen = nullptr, byte* validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PICC_RequestA(byte* bufferATQA, byte* bufferSize); + StatusCode PICC_WakeupA(byte* bufferATQA, byte* bufferSize); + StatusCode PICC_REQA_or_WUPA(byte command, byte* bufferATQA, byte* bufferSize); + virtual StatusCode PICC_Select(Uid* uid, byte validBits = 0); StatusCode PICC_HaltA(); ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key* key, Uid* uid); void PCD_StopCrypto1(); - StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); - StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); - StatusCode MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + StatusCode MIFARE_Read(byte blockAddr, byte* buffer, byte* bufferSize); + StatusCode MIFARE_Write(byte blockAddr, byte* buffer, byte bufferSize); + StatusCode MIFARE_Ultralight_Write(byte page, byte* buffer, byte bufferSize); StatusCode MIFARE_Decrement(byte blockAddr, int32_t delta); StatusCode MIFARE_Increment(byte blockAddr, int32_t delta); StatusCode MIFARE_Restore(byte blockAddr); StatusCode MIFARE_Transfer(byte blockAddr); - StatusCode MIFARE_GetValue(byte blockAddr, int32_t *value); + StatusCode MIFARE_GetValue(byte blockAddr, int32_t* value); StatusCode MIFARE_SetValue(byte blockAddr, int32_t value); - StatusCode PCD_NTAG216_AUTH(byte *passWord, byte pACK[]); + StatusCode PCD_NTAG216_AUTH(byte* passWord, byte pACK[]); ///////////////////////////////////////////////////////////////////////////////////// // Support functions ///////////////////////////////////////////////////////////////////////////////////// - StatusCode PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + StatusCode PCD_MIFARE_Transceive(byte* sendData, byte sendLen, bool acceptTimeout = false); static PICC_Type PICC_GetType(byte sak); // Support functions for debuging void PCD_DumpVersionToSerial(); - void PICC_DumpToSerial(Uid *uid); - void PICC_DumpDetailsToSerial(Uid *uid); - void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); - void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpToSerial(Uid* uid); + void PICC_DumpDetailsToSerial(Uid* uid); + void PICC_DumpMifareClassicToSerial(Uid* uid, PICC_Type piccType, MIFARE_Key* key); + void PICC_DumpMifareClassicSectorToSerial(Uid* uid, MIFARE_Key* key, byte sector); void PICC_DumpMifareUltralightToSerial(); // Advanced functions for MIFARE - void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + void MIFARE_SetAccessBits(byte* accessBitBuffer, byte g0, byte g1, byte g2, byte g3); ///////////////////////////////////////////////////////////////////////////////////// // Convenience functions - does not add extra functionality diff --git a/cpp_utils/MFRC522Debug.h b/cpp_utils/MFRC522Debug.h index 6c1c9ac6..e8033495 100644 --- a/cpp_utils/MFRC522Debug.h +++ b/cpp_utils/MFRC522Debug.h @@ -4,11 +4,10 @@ #define MFRC522Debug_h class MFRC522Debug { -private: - public: // Get human readable code and type static const char* PICC_GetTypeName(MFRC522::PICC_Type type); static const char* GetStatusCodeName(MFRC522::StatusCode code); + }; #endif // MFRC522Debug_h diff --git a/cpp_utils/MMU.cpp b/cpp_utils/MMU.cpp new file mode 100644 index 00000000..ee7adb59 --- /dev/null +++ b/cpp_utils/MMU.cpp @@ -0,0 +1,126 @@ +/* + * MMU.cpp + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#include "MMU.h" +#include +#include +#include + +// The following functions are provided by spi_flash.h +extern "C" { + extern void spi_flash_disable_interrupts_caches_and_other_cpu(); + + // Enable cache, enable interrupts (to be added in future), resume scheduler + extern void spi_flash_enable_interrupts_caches_and_other_cpu(); +} + + +typedef struct { + uint32_t low; + uint32_t high; +} addressRange_t; + +static addressRange_t entryNumberToAddressRange(uint32_t entryNumber) { + addressRange_t ret; + if (entryNumber < 64) { + ret.low = 0x3F400000 + 64 * 1024 * entryNumber; + ret.high = 0x3F400000 + 64 * 1024 * (entryNumber + 1) - 1; + return ret; + } + ret.low = 0x40000000 + 64 * 1024 * (entryNumber - 64); + ret.high = 0x40000000 + 64 * 1024 * (entryNumber + 1 - 64) - 1; + return ret; +} + + +static uint32_t flashPageToOffset(uint32_t page) { + return page * 64 * 1024; +} + + + +/* static */ void MMU::dump() { + const uint32_t mappingInvalid = 1 << 8; + + printf("PRO CPU MMU\n"); + for (uint16_t i = 0; i < 256; i++) { + if (!(DPORT_PRO_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff)); + } + } + printf("\n"); + printf("APP CPU MMU\n"); + for (uint16_t i = 0; i < 256; i++) { + if (!(DPORT_APP_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_APP_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_APP_FLASH_MMU_TABLE[i] & 0xff)); + } + } +} // MMU#dumpMMU + +extern "C" { + static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size); +} + +static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size) { + printf(">> MMU::mapFlashToVMA: flash offset: 0x%x, VMA: 0x%x, size: %d\n", flashOffset, (uint32_t)vma, size); + uint32_t mmuEntryStart; // The MMU table entry to start mapping. + uint32_t mmuEntryEnd; // The MMU table entry to end mapping. + + if ((uint32_t) vma >= 0x40000000 && (uint32_t) vma < 0x40C00000) { + mmuEntryStart = (((uint32_t) vma - 0x40000000) / (64 * 1024)) + 64; + mmuEntryEnd = (((uint32_t) vma - 0x40000000 + size) / (64 * 1024)) + 64; + } + else if ((uint32_t) vma >= 0x3F400000 && (uint32_t) vma < 0x3F800000) { + mmuEntryStart = (((uint32_t) vma - 0x3F400000) / (64 * 1024)); + mmuEntryEnd = (((uint32_t) vma - 0x3F400000 + size) / (64 * 1024)); + } else { + printf(" - Unable to map from flash to VMA."); + return; + } + + // At this point we have populated mmuEntryStart and mmuEntryEnd which are the MMU table entries. + uint32_t pFlashStart = flashOffset; + uint32_t pFlashEnd = flashOffset + size; + + printf(" - Mapping flash to VMA via MMU. MMU entries start: %d, end: %d, mapping flash 0x%x (flash page: %d) to 0x%x (flash page: %d)\n", + mmuEntryStart, mmuEntryEnd, pFlashStart, pFlashStart/(64 * 1024), pFlashEnd, pFlashEnd / (64 * 1024)); + + uint32_t flashRegion = pFlashStart / (64 * 1024); // Determine the 64K chunk of flash to be mapped (we map in units of 64K). + + spi_flash_disable_interrupts_caches_and_other_cpu(); + + // For each of the mapping entries, map it to the corresponding flash region. + for (uint32_t i = mmuEntryStart; i <= mmuEntryEnd; i++) { + DPORT_PRO_FLASH_MMU_TABLE[i] = flashRegion; // There are two tables. One for the PRO CPU and one for the APP CPU. + DPORT_APP_FLASH_MMU_TABLE[i] = flashRegion; // Map both of them to the flash region. + flashRegion++; + } + + Cache_Flush(0); + Cache_Flush(1); + spi_flash_enable_interrupts_caches_and_other_cpu(); +} // mapFlashToVMA + +/** + * Map an area of flash memory into VMA. + * @param flashOffset The offset in flash of the start of data. + * @param vma Where in VMA the data should appear. + * @size How much data to map. + */ +void IRAM_ATTR MMU::mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size) { + mapFlashToVMA_Internal(flashOffset, vma, size); +} diff --git a/cpp_utils/MMU.h b/cpp_utils/MMU.h new file mode 100644 index 00000000..d8ba6b64 --- /dev/null +++ b/cpp_utils/MMU.h @@ -0,0 +1,22 @@ +/* + * MMU.h + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_MMU_H_ +#define COMPONENTS_CPP_UTILS_MMU_H_ +#include +#include +#include + +class MMU { +public: + MMU(); + virtual ~MMU(); + static void dump(); + static void mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size); +}; + +#endif /* COMPONENTS_CPP_UTILS_MMU_H_ */ diff --git a/cpp_utils/MPU6050.cpp b/cpp_utils/MPU6050.cpp index 5bcf5bb9..0f6931ae 100644 --- a/cpp_utils/MPU6050.cpp +++ b/cpp_utils/MPU6050.cpp @@ -9,14 +9,15 @@ #define MPU6050_GYRO_XOUT_H 0x43 #define MPU6050_PWR_MGMT_1 0x6B + /** * @brief Construct an %MPU6050 handler. */ MPU6050::MPU6050() { accel_x = accel_y = accel_z = 0; gyro_x = gyro_y = gyro_z = 0; - i2c=nullptr; - inited=false; + i2c = nullptr; + inited = false; } @@ -43,7 +44,7 @@ void MPU6050::readAccel() { uint8_t data[6]; i2c->beginTransaction(); i2c->read(data, 5, true); - i2c->read(data+5, false); + i2c->read(data + 5, false); i2c->endTransaction(); accel_x = (data[0] << 8) | data[1]; @@ -51,6 +52,7 @@ void MPU6050::readAccel() { accel_z = (data[4] << 8) | data[5]; } // readAccel + /** * @brief Read the gyroscopic values from the device. * @@ -66,7 +68,7 @@ void MPU6050::readGyro() { uint8_t data[6]; i2c->beginTransaction(); i2c->read(data, 5, true); - i2c->read(data+5, false); + i2c->read(data + 5, false); i2c->endTransaction(); gyro_x = (data[0] << 8) | data[1]; @@ -74,6 +76,7 @@ void MPU6050::readGyro() { gyro_z = (data[4] << 8) | data[5]; } // readGyro + /** * @brief Initialize the %MPU6050. * @param [in] sdaPin The %GPIO pin to use for %I2C SDA. @@ -94,5 +97,5 @@ void MPU6050::init(gpio_num_t sdaPin, gpio_num_t clkPin) { i2c->write(MPU6050_PWR_MGMT_1); i2c->write(0); i2c->endTransaction(); - inited=true; + inited = true; } diff --git a/cpp_utils/MPU6050.h b/cpp_utils/MPU6050.h index fe69193d..8207f6d6 100644 --- a/cpp_utils/MPU6050.h +++ b/cpp_utils/MPU6050.h @@ -16,61 +16,49 @@ * A call to init() must precede all other API calls. */ class MPU6050 { -private: - I2C *i2c; - short accel_x, accel_y, accel_z; - short gyro_x, gyro_y, gyro_z; - bool inited; public: MPU6050(); virtual ~MPU6050(); - /** * @brief Get the X acceleration value. */ - short getAccelX() const - { + short getAccelX() const { return accel_x; } /** * @brief Get the Y acceleration value. */ - short getAccelY() const - { + short getAccelY() const { return accel_y; } /** * @brief Get the Z acceleration value. */ - short getAccelZ() const - { + short getAccelZ() const { return accel_z; } /** * @brief Get the X gyroscopic value. */ - short getGyroX() const - { + short getGyroX() const { return gyro_x; } /** * @brief Get the Y gyroscopic value. */ - short getGyroY() const - { + short getGyroY() const { return gyro_y; } /** * @brief Get the Z gyroscopic value. */ - short getGyroZ() const - { + short getGyroZ() const { return gyro_z; } @@ -86,10 +74,16 @@ class MPU6050 { return sqrt(accel_x * accel_x + accel_y * accel_y + accel_z * accel_z); } - void init(gpio_num_t sdaPin=I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin=I2C::DEFAULT_CLK_PIN); + void init(gpio_num_t sdaPin = I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin = I2C::DEFAULT_CLK_PIN); void readAccel(); - void readGyro(); + +private: + I2C* i2c; + short accel_x, accel_y, accel_z; + short gyro_x, gyro_y, gyro_z; + bool inited; + }; #endif /* MAIN_MPU6050_H_ */ diff --git a/cpp_utils/MRFC522Debug.cpp b/cpp_utils/MRFC522Debug.cpp index 82cd031d..56ba91d0 100644 --- a/cpp_utils/MRFC522Debug.cpp +++ b/cpp_utils/MRFC522Debug.cpp @@ -1,46 +1,48 @@ #include "MFRC522Debug.h" + /** * Returns a __FlashStringHelper pointer to the PICC type name. * + * @param piccType One of the PICC_Type enums. * @return const __FlashStringHelper * */ -const char* MFRC522Debug::PICC_GetTypeName(MFRC522::PICC_Type piccType ///< One of the PICC_Type enums. -) { +const char* MFRC522Debug::PICC_GetTypeName(MFRC522::PICC_Type piccType) { switch (piccType) { - case MFRC522::PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; + case MFRC522::PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; case MFRC522::PICC_TYPE_ISO_18092: return "PICC compliant with ISO/IEC 18092 (NFC)"; - case MFRC522::PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; + case MFRC522::PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; case MFRC522::PICC_TYPE_MIFARE_1K: return "MIFARE 1KB"; case MFRC522::PICC_TYPE_MIFARE_4K: return "MIFARE 4KB"; case MFRC522::PICC_TYPE_MIFARE_UL: return "MIFARE Ultralight or Ultralight C"; - case MFRC522::PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; - case MFRC522::PICC_TYPE_MIFARE_DESFIRE: return "MIFARE DESFire"; - case MFRC522::PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; - case MFRC522::PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; + case MFRC522::PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; + case MFRC522::PICC_TYPE_MIFARE_DESFIRE: return "MIFARE DESFire"; + case MFRC522::PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; + case MFRC522::PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; case MFRC522::PICC_TYPE_UNKNOWN: - default: return "Unknown type"; + default: return "Unknown type"; } } // End PICC_GetTypeName() + /** * Returns a __FlashStringHelper pointer to a status code name. * + * @param code One of the StatusCode enums. * @return const __FlashStringHelper * */ -const char *MFRC522Debug::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums. -) { +const char *MFRC522Debug::GetStatusCodeName(MFRC522::StatusCode code) { switch (code) { - case MFRC522::STATUS_OK: return "Success."; + case MFRC522::STATUS_OK: return "Success."; case MFRC522::STATUS_ERROR: return "Error in communication."; - case MFRC522::STATUS_COLLISION: return "Collission detected."; - case MFRC522::STATUS_TIMEOUT: return "Timeout in communication."; - case MFRC522::STATUS_NO_ROOM: return "A buffer is not big enough."; - case MFRC522::STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; - case MFRC522::STATUS_INVALID: return "Invalid argument."; + case MFRC522::STATUS_COLLISION: return "Collision detected."; + case MFRC522::STATUS_TIMEOUT: return "Timeout in communication."; + case MFRC522::STATUS_NO_ROOM: return "A buffer is not big enough."; + case MFRC522::STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; + case MFRC522::STATUS_INVALID: return "Invalid argument."; case MFRC522::STATUS_CRC_WRONG: return "The CRC_A does not match."; - case MFRC522::STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; - default: return "Unknown error"; + case MFRC522::STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; + default: return "Unknown error"; } } // End GetStatusCodeName() diff --git a/cpp_utils/Makefile.arduino b/cpp_utils/Makefile.arduino index f9239250..5638b266 100644 --- a/cpp_utils/Makefile.arduino +++ b/cpp_utils/Makefile.arduino @@ -10,12 +10,16 @@ BLE_FILES= \ BLE2902.cpp \ BLE2902.h \ + BLE2904.cpp \ + BLE2904.h \ BLEAddress.cpp \ BLEAddress.h \ BLEAdvertisedDevice.cpp \ BLEAdvertisedDevice.h \ BLEAdvertising.cpp \ BLEAdvertising.h \ + BLEBeacon.cpp \ + BLEBeacon.h \ BLECharacteristic.cpp \ BLECharacteristic.h \ BLECharacteristicMap.cpp \ @@ -28,6 +32,8 @@ BLE_FILES= \ BLEDevice.h \ BLEExceptions.cpp \ BLEExceptions.h \ + BLEHIDDevice.cpp \ + BLEHIDDevice.h \ BLERemoteCharacteristic.cpp \ BLERemoteCharacteristic.h \ BLERemoteDescriptor.cpp \ @@ -41,6 +47,8 @@ BLE_FILES= \ BLEService.cpp \ BLEService.h \ BLEServiceMap.cpp \ + BLESecurity.cpp \ + BLESecurity.h \ BLEUtils.cpp \ BLEUtils.h \ BLEUUID.cpp \ @@ -50,7 +58,9 @@ BLE_FILES= \ FreeRTOS.h \ FreeRTOS.cpp \ GeneralUtils.h \ - GeneralUtils.cpp + GeneralUtils.cpp \ + HIDTypes.h \ + HIDKeyboardTypes.h ARDUINO_LIBS=$(HOME)/Arduino/libraries @@ -70,4 +80,3 @@ build_ble: install: build_ble rm -rf ${ARDUINO_LIBS}/ESP32_BLE unzip Arduino/ESP32_BLE.zip -d $(ARDUINO_LIBS) - diff --git a/cpp_utils/Memory.cpp b/cpp_utils/Memory.cpp index e3be59db..33e5ae80 100644 --- a/cpp_utils/Memory.cpp +++ b/cpp_utils/Memory.cpp @@ -29,7 +29,7 @@ size_t Memory::m_lastHeapSize = 0; */ /* STATIC */ bool Memory::checkIntegrity() { bool rc = ::heap_caps_check_integrity_all(true); - if (rc == false && m_pRecords != nullptr) { + if (!rc && m_pRecords != nullptr) { dumpRanges(); abort(); } @@ -47,7 +47,7 @@ size_t Memory::m_lastHeapSize = 0; /* STATIC */ void Memory::dumpHeapChange(std::string tag) { size_t currentUsage = heap_caps_get_free_size(MALLOC_CAP_8BIT); - int diff = currentUsage - m_lastHeapSize; + size_t diff = currentUsage - m_lastHeapSize; ESP_LOGD(LOG_TAG, "%s: Heap changed by %d bytes (%d to %d)", tag.c_str(), diff, m_lastHeapSize, currentUsage); m_lastHeapSize = currentUsage; } // dumpHeapChange @@ -58,32 +58,29 @@ size_t Memory::m_lastHeapSize = 0; */ /* STATIC */ void Memory::dumpRanges() { // Each record contained in the Heap trace has the following format: - // // * uint32_t ccount – Timestamp of record. // * void* address – Address that was allocated or released. // * size_t size – Size of the block that was requested and allocated. // * void* alloced_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of allocator // * void* freed_by[CONFIG_HEAP_TRACING_STACK_DEPTH] – Call stack of releasor - // - if (m_pRecords == nullptr) { - return; - } + if (m_pRecords == nullptr) return; + esp_log_level_set("*", ESP_LOG_NONE); - size_t count = heap_trace_get_count(); + size_t count = (size_t) heap_trace_get_count(); heap_trace_record_t record; printf(">>> dumpRanges\n"); - for (size_t i=0; i +#include #include "NeoPixelWiFiEventHandler.h" +static const char* LOG_TAG = "NeoPixelWiFiEventHandler"; + NeoPixelWiFiEventHandler::NeoPixelWiFiEventHandler(gpio_num_t gpioPin) { this->gpioPin = gpioPin; ws2812 = new WS2812(gpioPin, 8); @@ -17,42 +20,42 @@ NeoPixelWiFiEventHandler::~NeoPixelWiFiEventHandler() { } esp_err_t NeoPixelWiFiEventHandler::apStart() { - printf("XXX apStart\n"); + ESP_LOGD(LOG_TAG, "XXX apStart"); ws2812->setPixel(0, 0, 00, 64); ws2812->show(); return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staConnected() { - printf("XXX staConnected\n"); +esp_err_t NeoPixelWiFiEventHandler::staConnected(system_event_sta_connected_t info) { + ESP_LOGD(LOG_TAG, "XXX staConnected"); ws2812->setPixel(0, 57, 89, 66); ws2812->show(); return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staDisconnected() { - printf("XXX staDisconnected\n"); +esp_err_t NeoPixelWiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { + ESP_LOGD(LOG_TAG, "XXX staDisconnected"); ws2812->setPixel(0, 64, 0, 0); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::staStart() { - printf("XXX staStart\n"); + ESP_LOGD(LOG_TAG, "XXX staStart"); ws2812->setPixel(0, 64, 64, 0); ws2812->show(); return ESP_OK; } -esp_err_t NeoPixelWiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { - printf("XXX staGotIp\n"); +esp_err_t NeoPixelWiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { + ESP_LOGD(LOG_TAG, "XXX staGotIp"); ws2812->setPixel(0, 0, 64, 0); ws2812->show(); return ESP_OK; } esp_err_t NeoPixelWiFiEventHandler::wifiReady() { - printf("XXX wifiReady\n"); + ESP_LOGD(LOG_TAG, "XXX wifiReady"); ws2812->setPixel(0, 64, 64, 0); ws2812->show(); return ESP_OK; diff --git a/cpp_utils/NeoPixelWiFiEventHandler.h b/cpp_utils/NeoPixelWiFiEventHandler.h index c271e3e3..d59cafff 100644 --- a/cpp_utils/NeoPixelWiFiEventHandler.h +++ b/cpp_utils/NeoPixelWiFiEventHandler.h @@ -23,14 +23,15 @@ class NeoPixelWiFiEventHandler: public WiFiEventHandler { virtual ~NeoPixelWiFiEventHandler(); esp_err_t apStart() override; - esp_err_t staConnected() override; - esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) override; - esp_err_t staDisconnected() override; + esp_err_t staConnected(system_event_sta_connected_t info) override; + esp_err_t staGotIp(system_event_sta_got_ip_t info) override; + esp_err_t staDisconnected(system_event_sta_disconnected_t info) override; esp_err_t wifiReady() override; esp_err_t staStart() override; + private: gpio_num_t gpioPin; - WS2812 *ws2812; + WS2812* ws2812; }; #endif /* MAIN_NEOPIXELWIFIEVENTHANDLER_H_ */ diff --git a/cpp_utils/OV7670.cpp b/cpp_utils/OV7670.cpp index c2de1316..a3c3811d 100644 --- a/cpp_utils/OV7670.cpp +++ b/cpp_utils/OV7670.cpp @@ -14,31 +14,34 @@ #include "GeneralUtils.h" #include "sdkconfig.h" #include + +#define OV7670_I2C_ADDR (0x21) + extern "C" { #include #include } -static const char *LOG_TAG="OV7670"; +static const char* LOG_TAG = "OV7670"; static bool getBit(uint8_t value, uint8_t bitNum) { - return (value & (1<> 3) << 3; uint8_t green = (((byte1 & 0b111) << 3) | ((byte2 & 0b11100000) >> 5)) << 2; uint8_t blue = (byte2 & 0b11111) << 3; - *pLine = (red + green + blue)/3; + *pLine = (red + green + blue) / 3; pLine++; - i+=2; + i += 2; } pLine = pLineSave; - GeneralUtils::hexDump(pLine, length/2); + GeneralUtils::hexDump(pLine, length / 2); } void OV7670::setFormat(uint8_t value) { @@ -120,7 +123,6 @@ void OV7670::setTestPattern(uint8_t value) { * */ -#define OV7670_I2C_ADDR (0x21) /* static void IRAM_ATTR isr_vsync(void* arg) { ESP_EARLY_LOGD(LOG_TAG, "VSYNC"); @@ -183,20 +185,19 @@ OV7670::~OV7670() { } -static esp_err_t camera_enable_out_clock(camera_config_t* config) -{ +static esp_err_t camera_enable_out_clock(camera_config_t* config) { ESP_LOGD(LOG_TAG, ">> camera_enable_out_clock: freq_hz=%d, pin=%d", config->xclk_freq_hz, config->pin_xclk); periph_module_enable(PERIPH_LEDC_MODULE); ledc_timer_config_t timer_conf; - timer_conf.duty_resolution = (ledc_timer_bit_t)1; timer_conf.freq_hz = config->xclk_freq_hz; + timer_conf.duty_resolution = (ledc_timer_bit_t) 1; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; timer_conf.timer_num = config->ledc_timer; esp_err_t err = ledc_timer_config(&timer_conf); if (err != ESP_OK) { - ESP_LOGE(LOG_TAG, "ledc_timer_config failed, rc=%x", err); - return err; + ESP_LOGE(LOG_TAG, "ledc_timer_config failed, rc=%x", err); + return err; } ledc_channel_config_t ch_conf; @@ -209,8 +210,8 @@ static esp_err_t camera_enable_out_clock(camera_config_t* config) err = ledc_channel_config(&ch_conf); if (err != ESP_OK) { - ESP_LOGE(LOG_TAG, "ledc_channel_config failed, rc=%x", err); - return err; + ESP_LOGE(LOG_TAG, "ledc_channel_config failed, rc=%x", err); + return err; } ESP_LOGD(LOG_TAG, "<< camera_enable_out_clock"); return ESP_OK; @@ -274,88 +275,90 @@ void OV7670::dump() { uint32_t outputFormat = getBit(com7, 2) << 1 | getBit(com7, 0); //uint32_t outputFormat = (com7 & (1<<2)) >> 1 | (com7 & (1<<0)); std::string outputFormatString; - switch(outputFormat) { - case 0b00: - outputFormatString = "YUV"; - break; - case 0b10: - outputFormatString = "RGB"; - break; - case 0b01: - outputFormatString = "Raw Bayer RGB"; - break; - case 0b11: - outputFormatString = "Process Bayer RGB"; - break; - default: - outputFormatString = "Unknown"; - break; - } - ESP_LOGD(LOG_TAG, "Output format: %s", outputFormatString.c_str()); - if (outputFormat == 0b10) { - uint8_t com15 = readRegister(OV7670_REG_COM15); - uint8_t rgbType = getBit(com15, 5) << 1 | getBit(com15,4); - char *rgbTypeString; - switch(rgbType) { + switch (outputFormat) { case 0b00: + outputFormatString = "YUV"; + break; case 0b10: - rgbTypeString = (char*)"Normal RGB Output"; + outputFormatString = "RGB"; break; case 0b01: - rgbTypeString = (char*)"RGB 565"; + outputFormatString = "Raw Bayer RGB"; break; case 0b11: - rgbTypeString = (char*)"RGB 555"; + outputFormatString = "Process Bayer RGB"; break; default: - rgbTypeString = (char*)"Unknown"; + outputFormatString = "Unknown"; break; + } + ESP_LOGD(LOG_TAG, "Output format: %s", outputFormatString.c_str()); + if (outputFormat == 0b10) { + uint8_t com15 = readRegister(OV7670_REG_COM15); + uint8_t rgbType = getBit(com15, 5) << 1 | getBit(com15, 4); + char* rgbTypeString; + switch (rgbType) { + case 0b00: + case 0b10: + rgbTypeString = (char*) "Normal RGB Output"; + break; + case 0b01: + rgbTypeString = (char*) "RGB 565"; + break; + case 0b11: + rgbTypeString = (char*) "RGB 555"; + break; + default: + rgbTypeString = (char*) "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Rgb Type: %s", rgbTypeString); } - ESP_LOGD(LOG_TAG, "Color bar: %s", getBit(com7, 1)?"Enabled":"Disabled"); + ESP_LOGD(LOG_TAG, "Color bar: %s", getBit(com7, 1) ? "Enabled" : "Disabled"); uint8_t scaling_xsc = readRegister(OV7670_REG_SCALING_XSC); uint8_t scaling_ysc = readRegister(OV7670_REG_SCALING_YSC); uint32_t testPattern = getBit(scaling_xsc, 7) << 1 | getBit(scaling_ysc, 7); - char *testPatternString; - switch(testPattern) { - case 0b00: - testPatternString = (char*)"No test output"; - break; - case 0b01: - testPatternString = (char*)"Shifting 1"; - break; - case 0b10: - testPatternString = (char*)"8-bar color bar"; - break; - case 0b11: - testPatternString = (char*)"Fade to gray color bar"; - break; - default: - testPatternString = (char*)"Unknown"; - break; + char* testPatternString; + switch (testPattern) { + case 0b00: + testPatternString = (char*) "No test output"; + break; + case 0b01: + testPatternString = (char*) "Shifting 1"; + break; + case 0b10: + testPatternString = (char*) "8-bar color bar"; + break; + case 0b11: + testPatternString = (char*) "Fade to gray color bar"; + break; + default: + testPatternString = (char*) "Unknown"; + break; } ESP_LOGD(LOG_TAG, "Test pattern: %s", testPatternString); ESP_LOGD(LOG_TAG, "Horizontal scale factor: %d", scaling_xsc & 0x3f); ESP_LOGD(LOG_TAG, "Vertical scale factor: %d", scaling_ysc & 0x3f); uint8_t com15 = readRegister(OV7670_REG_COM15); - switch((com15 & 0b11000000) >> 6) { - case 0b00: - case 0b01: - ESP_LOGD(LOG_TAG, "Output range: 0x10 to 0xf0"); - break; - case 0b10: - ESP_LOGD(LOG_TAG, "Output range: 0x01 to 0xfe"); - break; - case 0b11: - ESP_LOGD(LOG_TAG, "Output range: 0x00 to 0xff"); - break; + switch ((com15 & 0b11000000) >> 6) { + case 0b00: + case 0b01: + ESP_LOGD(LOG_TAG, "Output range: 0x10 to 0xf0"); + break; + case 0b10: + ESP_LOGD(LOG_TAG, "Output range: 0x01 to 0xfe"); + break; + case 0b11: + ESP_LOGD(LOG_TAG, "Output range: 0x00 to 0xff"); + break; + default: + break; } } // dump /* -static void log(char *marker) { +static void log(char* marker) { ESP_LOGD(LOG_TAG, "%s", marker); FreeRTOS::sleep(100); } @@ -394,7 +397,7 @@ void OV7670::init(camera_config_t cameraConfig) { // Create the I2C interface. m_i2c = new I2C(); - m_i2c->init(OV7670_I2C_ADDR, (gpio_num_t)m_cameraConfig.pin_sscb_sda, (gpio_num_t)m_cameraConfig.pin_sscb_scl); + m_i2c->init(OV7670_I2C_ADDR, (gpio_num_t) m_cameraConfig.pin_sscb_sda, (gpio_num_t) m_cameraConfig.pin_sscb_scl); m_i2c->scan(); ESP_LOGD(LOG_TAG, "Do you see 0x21 listed?"); resetCamera(); @@ -430,46 +433,42 @@ void OV7670::init(camera_config_t cameraConfig) { } */ - I2S i2s; - dma_config_t dmaConfig; - dmaConfig.pin_d0 = cameraConfig.pin_d0; - dmaConfig.pin_d1 = cameraConfig.pin_d1; - dmaConfig.pin_d2 = cameraConfig.pin_d2; - dmaConfig.pin_d3 = cameraConfig.pin_d3; - dmaConfig.pin_d4 = cameraConfig.pin_d4; - dmaConfig.pin_d5 = cameraConfig.pin_d5; - dmaConfig.pin_d6 = cameraConfig.pin_d6; - dmaConfig.pin_d7 = cameraConfig.pin_d7; - dmaConfig.pin_href = cameraConfig.pin_href; - dmaConfig.pin_pclk = cameraConfig.pin_pclk; - dmaConfig.pin_vsync = cameraConfig.pin_vsync; - i2s.cameraMode(dmaConfig, 50, 360*2); - ESP_LOGD(LOG_TAG, "Waiting for data!"); - while(1) { - DMAData dmaData = i2s.waitForData(); - //GeneralUtils::hexDump(dmaData.getData(), dmaData.getLength()); - toGrayscale(dmaData.getData(), dmaData.getLength()); - dmaData.free(); - } - - ESP_LOGD(LOG_TAG, "Waiting for positive edge on VSYNC"); - while (gpio_get_level(m_cameraConfig.pin_vsync) == 0) { - ; - } - while (gpio_get_level(m_cameraConfig.pin_vsync) != 0) { - ; - } - - - ESP_LOGD(LOG_TAG, "Got VSYNC"); - - - - - while(1) { - FreeRTOS::sleep(1000); - ESP_LOGD(LOG_TAG, "VSYNC Counter: %d, lastHref=%d, pclk=%d", vsyncCounter, lastHref, pclkCounter); - } + I2S i2s; + dma_config_t dmaConfig; + dmaConfig.pin_d0 = cameraConfig.pin_d0; + dmaConfig.pin_d1 = cameraConfig.pin_d1; + dmaConfig.pin_d2 = cameraConfig.pin_d2; + dmaConfig.pin_d3 = cameraConfig.pin_d3; + dmaConfig.pin_d4 = cameraConfig.pin_d4; + dmaConfig.pin_d5 = cameraConfig.pin_d5; + dmaConfig.pin_d6 = cameraConfig.pin_d6; + dmaConfig.pin_d7 = cameraConfig.pin_d7; + dmaConfig.pin_href = cameraConfig.pin_href; + dmaConfig.pin_pclk = cameraConfig.pin_pclk; + dmaConfig.pin_vsync = cameraConfig.pin_vsync; + i2s.cameraMode(dmaConfig, 50, 360 * 2); + ESP_LOGD(LOG_TAG, "Waiting for data!"); + while (true) { + DMAData dmaData = i2s.waitForData(); +// GeneralUtils::hexDump(dmaData.getData(), dmaData.getLength()); + toGrayscale(dmaData.getData(), dmaData.getLength()); + dmaData.free(); + } + + ESP_LOGD(LOG_TAG, "Waiting for positive edge on VSYNC"); + while (gpio_get_level(m_cameraConfig.pin_vsync) == 0) { + ; + } + while (gpio_get_level(m_cameraConfig.pin_vsync) != 0) { + ; + } + + ESP_LOGD(LOG_TAG, "Got VSYNC"); + + while (true) { + FreeRTOS::sleep(1000); + ESP_LOGD(LOG_TAG, "VSYNC Counter: %d, lastHref=%d, pclk=%d", vsyncCounter, lastHref, pclkCounter); + } ESP_LOGD(LOG_TAG, "<< init"); } // init diff --git a/cpp_utils/OV7670.h b/cpp_utils/OV7670.h index b7baf1f9..0fb4fa19 100644 --- a/cpp_utils/OV7670.h +++ b/cpp_utils/OV7670.h @@ -198,11 +198,13 @@ class OV7670 { void setRGBFormat(uint8_t value); void setTestPattern(uint8_t value); void resetCamera(); + private: uint8_t readRegister(uint8_t reg); void writeRegister(uint8_t reg, uint8_t value); camera_config_t m_cameraConfig; - I2C *m_i2c; + I2C* m_i2c; + }; #endif /* CPP_UTILS_OV7670_H_ */ diff --git a/cpp_utils/PCF8574.cpp b/cpp_utils/PCF8574.cpp index 4d850a8b..d246b7a6 100644 --- a/cpp_utils/PCF8574.cpp +++ b/cpp_utils/PCF8574.cpp @@ -18,14 +18,17 @@ * @param [in] address The %I2C address of the device on the %I2C bus. */ PCF8574::PCF8574(uint8_t address) { - i2c.setAddress(address); + i2c = new I2C(); + i2c->setAddress(address); lastWrite = 0; } + /** * @brief Class instance destructor. */ PCF8574::~PCF8574() { + delete i2c; } @@ -35,9 +38,9 @@ PCF8574::~PCF8574() { */ uint8_t PCF8574::read() { uint8_t value; - i2c.beginTransaction(); - i2c.read(&value,true); - i2c.endTransaction(); + i2c->beginTransaction(); + i2c->read(&value,true); + i2c->endTransaction(); return value; } // read @@ -49,11 +52,9 @@ uint8_t PCF8574::read() { * @return True if the pin is high, false otherwise. Undefined if there is no signal on the pin. */ bool PCF8574::readBit(uint8_t bit) { - if (bit > 7) { - return false; - } + if (bit > 7) return false; uint8_t value = read(); - return (value & (1<beginTransaction(); + i2c->write(value, true); + i2c->endTransaction(); lastWrite = value; } // write @@ -83,9 +84,7 @@ void PCF8574::write(uint8_t value) { * @param [in] value The logic level to appear on the identified output pin. */ void PCF8574::writeBit(uint8_t bit, bool value) { - if (bit > 7) { - return; - } + if (bit > 7) return; if (invert) { value = !value; } @@ -117,5 +116,6 @@ void PCF8574::setInvert(bool value) { * @param [in] clkPin The pin to use for the %I2C CLK functions. */ void PCF8574::init(gpio_num_t sdaPin, gpio_num_t clkPin) { - i2c.init(0, sdaPin, clkPin); + uint8_t addr = i2c->getAddress(); + i2c->init(addr, sdaPin, clkPin); } // init diff --git a/cpp_utils/PCF8574.h b/cpp_utils/PCF8574.h index 15b11da9..b8c287f4 100644 --- a/cpp_utils/PCF8574.h +++ b/cpp_utils/PCF8574.h @@ -21,7 +21,7 @@ class PCF8574 { public: PCF8574(uint8_t address); virtual ~PCF8574(); - void init(gpio_num_t sdaPin=I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin=I2C::DEFAULT_CLK_PIN); + void init(gpio_num_t sdaPin = I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin = I2C::DEFAULT_CLK_PIN); uint8_t read(); bool readBit(uint8_t bit); void setInvert(bool value); @@ -29,9 +29,10 @@ class PCF8574 { void writeBit(uint8_t bit, bool value); private: - I2C i2c = I2C(); + I2C* i2c; uint8_t lastWrite; bool invert = false; + }; #endif /* COMPONENTS_CPP_UTILS_PCF8574_H_ */ diff --git a/cpp_utils/PCF8575.cpp b/cpp_utils/PCF8575.cpp new file mode 100644 index 00000000..ea322ef2 --- /dev/null +++ b/cpp_utils/PCF8575.cpp @@ -0,0 +1,123 @@ +/* + * PCF8575.cpp + * + * Created on: Jan 27, 2018 + * Author: kolban + */ + +#include "PCF8575.h" +#include "I2C.h" + + +/** + * @brief Class constructor. + * + * The address is the address of the device on the %I2C bus. This is the value 0x20 plus + * the value of the device input pins `A0`, `A1` and `A2`. This means that the address should + * be between 0x20 and 0x27. + * + * @param [in] address The %I2C address of the device on the %I2C bus. + */ +PCF8575::PCF8575(uint8_t address) { + i2c = new I2C(); + i2c->setAddress(address); + m_lastWrite = 0; +} + + +/** + * @brief Class instance destructor. + */ +PCF8575::~PCF8575() { + delete i2c; +} + + +/** + * @brief Read all the input bits from the device. + * @return A 16 bit value representing the values on each of the input pins. + */ +uint16_t PCF8575::read() { + uint16_t value; + i2c->beginTransaction(); + i2c->read((uint8_t*) &value, true); + i2c->read(((uint8_t*) &value) + 1, true); + i2c->endTransaction(); + return value; +} // read + + +/** + * @brief Read the logic level on a given pin. + * + * @param [in] bit The input pin of the device to read. Values are 0-15. + * @return True if the pin is high, false otherwise. Undefined if there is no signal on the pin. + */ +bool PCF8575::readBit(uint16_t bit) { + if (bit > 7) return false; + uint16_t value = read(); + return (value & (1 << bit)) != 0; +} // readBit + + +/** + * @brief Set the output values of the device. + * + * @param [in] value The bit pattern to set on the output. + */ +void PCF8575::write(uint16_t value) { + if (invert) { + value = ~value; + } + i2c->beginTransaction(); + i2c->write(value & 0xff, true); + i2c->write((value >> 8) & 0xff, true); + i2c->endTransaction(); + m_lastWrite = value; +} // write + + +/** + * @brief Change the output value of a specific pin. + * + * The other bits beyond the one setting retain their values from the previous call to write() or + * previous calls to writeBit(). + * + * @param [in] bit The pin to have its value changed. The pin may be 0-15. + * @param [in] value The logic level to appear on the identified output pin. + */ +void PCF8575::writeBit(uint16_t bit, bool value) { + if (bit > 15) return; + if (invert) { + value = !value; + } + if (value) { + m_lastWrite |= (1 << bit); + } else { + m_lastWrite &= ~(1 << bit); + } + write(m_lastWrite); +} // writeBit + + +/** + * @brief Invert the bit values. + * Normally setting a pin's value to 1 means that a high signal is generated and a 0 means a low + * signal is generated. Setting the inversion to true, inverts that meaning. + * + * @param [in] value True if we wish to invert the signals and false otherwise. + */ +void PCF8575::setInvert(bool value) { + this->invert = value; +} // setInvert + + +/** + * @brief Initialize the PCF8575 device. + * + * @param [in] sdaPin The pin to use for the %I2C SDA functions. + * @param [in] clkPin The pin to use for the %I2C CLK functions. + */ +void PCF8575::init(gpio_num_t sdaPin, gpio_num_t clkPin) { + i2c->init(0, sdaPin, clkPin); +} // init diff --git a/cpp_utils/PCF8575.h b/cpp_utils/PCF8575.h new file mode 100644 index 00000000..0153124c --- /dev/null +++ b/cpp_utils/PCF8575.h @@ -0,0 +1,37 @@ +/* + * PCF8575.h + * + * Created on: Jan 27, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_PCF8575_H_ +#define COMPONENTS_CPP_UTILS_PCF8575_H_ +#include "I2C.h" + +/** + * @brief Encapsulate a %PCF8575 device. + * + * The %PCF8575 is a 16 bit %GPIO expander attached to %I2C. It can read and write 16 bits of data + * and hence has 16 pins that can be used for input or output. + * + * @see [PCF8575 home page](http://www.ti.com/product/PCF8575) + */ +class PCF8575 { +public: + PCF8575(uint8_t address); + virtual ~PCF8575(); + void init(gpio_num_t sdaPin = I2C::DEFAULT_SDA_PIN, gpio_num_t clkPin = I2C::DEFAULT_CLK_PIN); + uint16_t read(); + bool readBit(uint16_t bit); + void setInvert(bool value); + void write(uint16_t value); + void writeBit(uint16_t bit, bool value); + +private: + I2C* i2c; + uint16_t m_lastWrite; + bool invert = false; +}; + +#endif /* COMPONENTS_CPP_UTILS_PCF8575_H_ */ diff --git a/cpp_utils/PWM.cpp b/cpp_utils/PWM.cpp index f7119c4a..e67bd4fd 100644 --- a/cpp_utils/PWM.cpp +++ b/cpp_utils/PWM.cpp @@ -97,7 +97,7 @@ void PWM::setDuty(uint32_t duty) { */ void PWM::setDutyPercentage(uint8_t percent) { uint32_t max; - switch(m_dutyResolution) { + switch (m_dutyResolution) { case LEDC_TIMER_10_BIT: max = 1 << 10; break; @@ -120,15 +120,10 @@ void PWM::setDutyPercentage(uint8_t percent) { max = 1 << 10; break; } - if (percent > 100) { - percent = 100; - } + if (percent > 100) percent = 100; uint32_t value = max * percent / 100; - if (value >= max) { - value = max-1; - } + if (value >= max) value = max - 1; setDuty(value); - } // setDutyPercentage @@ -150,5 +145,5 @@ void PWM::setFrequency(uint32_t freq) { * @return N/A. */ void PWM::stop(bool idleLevel) { - ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, m_channel, idleLevel)); + ESP_ERROR_CHECK(::ledc_stop(LEDC_HIGH_SPEED_MODE, m_channel, idleLevel ? 1 : 0)); } // stop diff --git a/cpp_utils/PWM.h b/cpp_utils/PWM.h index 01c30a48..8cc1ce39 100644 --- a/cpp_utils/PWM.h +++ b/cpp_utils/PWM.h @@ -32,11 +32,13 @@ class PWM { void setDuty(uint32_t duty); void setDutyPercentage(uint8_t percent); void setFrequency(uint32_t freq); - void stop(bool idleLevel=false); + void stop(bool idleLevel = false); + private: ledc_channel_t m_channel; ledc_timer_t m_timer; ledc_timer_bit_t m_dutyResolution; // Bit size of timer. + }; #endif /* COMPONENTS_CPP_UTILS_PWM_H_ */ diff --git a/cpp_utils/PubSubClient.cpp b/cpp_utils/PubSubClient.cpp new file mode 100644 index 00000000..ae95bb33 --- /dev/null +++ b/cpp_utils/PubSubClient.cpp @@ -0,0 +1,852 @@ +/* + PubSubClient.cpp - A simple client for MQTT. + Nick O'Leary + http://knolleary.net + + edit by marcel.seerig + */ +#include "esp_log.h" + +#include "PubSubClient.h" +#include "Task.h" +#include "FreeRTOS.h" +#include "FreeRTOSTimer.h" + +#include "sdkconfig.h" + +static const char* TAG = "PubSubClient"; + +#define pgm_read_byte_near(x) *(x) + +/** + * @brief A task that will handle the PubSubClient. + * + * This Task is started, when we have a valid connection. + * If the connection breaks, we stop this Task. + */ +class PubSubClientTask: public Task { +public: + PubSubClientTask(std::string name) : + Task(name, 16 * 1024) { + taskName = name; + }; + +private: + std::string taskName; + /** + * @brief Loop over the PubSubClient. + * @param [in] data A pointer to an instance of the PubSubClient. + */ + void run(void* data) { + PubSubClient* pPubSubClient = (PubSubClient*) data; + ESP_LOGD("PubSubClientTask", "PubSubClientTask Task started!"); + + while (true) { + if (pPubSubClient->connected()) { + uint16_t len = pPubSubClient->readPacket(); + + if (len > 0) { // if there was data + + pPubSubClient->keepAliveTimer->reset(0); //lastInActivity = t; + + mqtt_message* msg = new mqtt_message; + pPubSubClient->parseData(msg, len); + + //pPubSubClient->dumpData(msg); + ESP_LOGD(TAG, "Message type (%s)!", pPubSubClient->messageType_toString(msg->type).c_str()); + + if (msg->type == PUBLISH) { + if (pPubSubClient->callback) { + if (msg->qos == QOS0) { + pPubSubClient->callback(msg->topic, msg->payload); + } else if (msg->qos == QOS1) { + pPubSubClient->callback(msg->topic, msg->payload); + + pPubSubClient->buffer[0] = PUBACK; + pPubSubClient->buffer[1] = 2; + pPubSubClient->buffer[2] = (msg->msgId >> 8); + pPubSubClient->buffer[3] = (msg->msgId & 0xFF); + + int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 4); + if (rc < 0) pPubSubClient->_state = CONNECTION_LOST; + + pPubSubClient->keepAliveTimer->reset(0); //lastOutActivity = t; + } else if(msg->qos == QOS2) { + ESP_LOGD(TAG, "QOS2 is not supported!"); + } else { + ESP_LOGD(TAG, "QOS-Level unkonwon yet!"); + } + } + } else if (msg->type == PINGREQ) { + pPubSubClient->buffer[0] = PINGRESP; + pPubSubClient->buffer[1] = 0; + int rc = pPubSubClient->_client->send(pPubSubClient->buffer, 2); + if (rc < 0) pPubSubClient->_state = CONNECTION_LOST; + + } else if (msg->type == PINGRESP) { + pPubSubClient->PING_outstanding = false; + } else if (msg->type == SUBACK) { + pPubSubClient->SUBACK_outstanding = false; + pPubSubClient->timeoutTimer->stop(0); + } else if (msg->type == UNSUBACK) { + pPubSubClient->UNSUBACK_Outstanding = false; + pPubSubClient->timeoutTimer->stop(0); + } + + delete(msg); + } + } + } // while (true) + } // run +}; // PubSubClientTask + + +PubSubClient::PubSubClient() { + setup(); + this->_state = DISCONNECTED; + this->_client = new Socket; + setCallback(NULL); +} + + +PubSubClient::PubSubClient(Socket& client) { + setup(); + this->_state = DISCONNECTED; + setClient(client); +} + + +PubSubClient::PubSubClient(std::string addr, uint16_t port) { + setup(); + this->_state = DISCONNECTED; + this->_client = new Socket; + setServer(addr, port); +} + + +PubSubClient::PubSubClient(std::string addr, uint16_t port, Socket& client) { + setup(); + this->_state = DISCONNECTED; + setServer(addr, port); + setClient(client); +} + + +PubSubClient::PubSubClient(std::string addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Socket& client) { + setup(); + this->_state = DISCONNECTED; + setServer(addr, port); + setCallback(callback); + setClient(client); +} + + +PubSubClient::~PubSubClient() { + _client->close(); + keepAliveTimer->stop(0); + timeoutTimer->stop(0); + m_task->stop(); + delete (_client); + delete (keepAliveTimer); + delete (timeoutTimer); + delete (m_task); +} + + +/** + * @brief This is a Timer called routine mapping routine, which calls + * the PubSubClient member function keepAliveChecker. + * @param The FreeRTOSTimer root instance for this callback function. + * @return N/A. + */ +void keepAliveTimerMapper(FreeRTOSTimer* pTimer) { + PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); + m_pubSubClient->keepAliveChecker(); +} //keepAliveChecker + + +/** + * @brief This is a Timer called routine mapping routine, which calls + * the PubSubClient member function timeoutChecker. + * @param The FreeRTOSTimer root instance for this callback function. + * @return N/A. + */ +void timeoutTimerMapper(FreeRTOSTimer* pTimer) { + PubSubClient* m_pubSubClient = (PubSubClient*) pTimer->getData(); + m_pubSubClient->timeoutChecker(); +} //keepAliveChecker + + +/** + * @brief This is a internal setup routine for the PubSubClient. + * @param N/A. + * @return N/A. + */ +void PubSubClient::setup() { + PING_outstanding = false; + SUBACK_outstanding = false; + UNSUBACK_Outstanding = false; + + keepAliveTimer = new FreeRTOSTimer((char*) "keepAliveTimer", + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, pdTRUE, this, + keepAliveTimerMapper); + timeoutTimer = new FreeRTOSTimer((char*) "timeoutTimer", + (MQTT_KEEPALIVE * 1000) / portTICK_PERIOD_MS, pdTRUE, this, + timeoutTimerMapper); + m_task = new PubSubClientTask("PubSubClientTask"); +} // setup + +/** + * @brief This is a Timer called routine, which checks the PING_outstanding flag. + * This flag is set in this function. We send a Keep alive message to the + * server here. If there is a data in, or output, or we receive the MQTT + * ping request, the flag will be set to false by other functions. + * This function is called every MQTT_KEEPALIVE interval. If the flag is + * still true, we have a error with the connection. + * @param N/A. + * @return N/A. + */ + +void PubSubClient::keepAliveChecker() { + if (PING_outstanding && connected()) { + _state = CONNECTION_TIMEOUT; + //_client->close(); + ESP_LOGD(TAG, "KeepAlive TIMEOUT!"); + } else { + buffer[0] = PINGREQ; + buffer[1] = 0; + int rc = _client->send(buffer, 2); + if (rc < 0) _state = CONNECTION_LOST; + ESP_LOGD(TAG, "send KeepAlive REQUEST!"); + PING_outstanding = true; + } +} //keepAliveChecker + +/** + * @brief This is a Timer called routine, which is called, when we reach the timeout. + * Used is this function for all ACK commands, which comes over MQTT. Notice, + * that the KeepAlivePing has his own Timer, to prevent conflicts. Basically, if + * this function is called, we have detected a MQTT timeout! + * @param N/A. + * @return N/A. + */ +void PubSubClient::timeoutChecker() { + if (connected() && (SUBACK_outstanding || UNSUBACK_Outstanding)) { + _state = CONNECTION_TIMEOUT; + //_client->close(); + ESP_LOGD(TAG, "MQTT TIMEOUT!"); + } +} //keepAliveChecker + + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char* id) { + return connect(id, NULL, NULL, 0, 0, 0, 0); +} + + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] my user name. + * [in] my password. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char* id, const char* user, const char* pass) { + return connect(id, user, pass, 0, 0, 0, 0); +} + + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] last will: topic name. + * [in] last will: qos-level. + * [in] last will: as retained message. + * [in] last will: payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage) { + return connect(id, NULL, NULL, willTopic, willQos, willRetain, willMessage); +} + + +/** + * @brief Connect to a MQTT server. + * @param [in] Device id to identify this device. + * [in] my user name. + * [in] my password. + * [in] last will: topic name. + * [in] last will: qos-level. + * [in] last will: as retained message. + * [in] last will: payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage) { + _config.id = id; + + _config.user = user; + _config.pass = pass; + + _config.willTopic = willTopic; + _config.willQos = willQos; + _config.willRetain = willRetain; + _config.willMessage = willMessage; + + return connect(); +} + + +/** + * @brief Connect to a MQTT server with the with the previous settings. + * Note: do not call this function without settings, this will not work! + * For the very first connect process, use the connect function with parameters. + * @param N/A + * @return success (true), or no success (false). + */ +bool PubSubClient::connect() { + if (!connected()) { + ESP_LOGD(TAG, "Connect to mqtt server..."); + ESP_LOGD(TAG, "ip: %s port: %d", _config.ip.c_str(), _config.port); + int result = _client->connect((char *)_config.ip.c_str(), _config.port); + + if (result == 0) { + nextMsgId = 1; + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + +#if MQTT_VERSION == MQTT_VERSION_3_1 + uint8_t d[9] = { 0x00, 0x06, 'M', 'Q', 'I', 's', 'd', 'p', MQTT_VERSION }; +#define MQTT_HEADER_VERSION_LENGTH 9 +#elif MQTT_VERSION == MQTT_VERSION_3_1_1 + uint8_t d[7] = { 0x00, 0x04, 'M', 'Q', 'T', 'T', MQTT_VERSION }; +#define MQTT_HEADER_VERSION_LENGTH 7 +#endif + for (unsigned int j = 0; j < MQTT_HEADER_VERSION_LENGTH; j++) { + buffer[length++] = d[j]; + } + + uint8_t v; + if (_config.willTopic) { + v = 0x06 | (_config.willQos << 3) | (_config.willRetain << 5); + } else { + v = 0x02; + } + + if (_config.user != NULL) { + v = v | 0x80; + + if (_config.pass != NULL) { + v = v | (0x80 >> 1); + } + } + + buffer[length++] = v; + + buffer[length++] = ((MQTT_KEEPALIVE) >> 8); + buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + length = writeString(_config.id, buffer, length); + if (_config.willTopic) { + length = writeString(_config.willTopic, buffer, length); + length = writeString(_config.willMessage, buffer, length); + } + + if (_config.user != NULL) { + length = writeString(_config.user, buffer, length); + if (_config.pass != NULL) { + length = writeString(_config.pass, buffer, length); + } + } + + write(CONNECT, buffer, length - 5); + + // start keepAliveTimer in 1ms... + keepAliveTimer->start(0); //lastInActivity = lastOutActivity = millis(); + + readPacket(); + uint8_t type = buffer[0] & 0xF0; + + if (type == CONNACK) { + ESP_LOGD(TAG, "Connected to mqtt server!"); + + keepAliveTimer->reset(0); //lastInActivity = millis(); + PING_outstanding = false; + _state = CONNECTED; + + m_task->start(this); + return true; + } else { + _state = (mqtt_state) buffer[3]; + ESP_LOGD(TAG, "Error: %d", _state); + } + + } else { + keepAliveTimer->stop(0); + _state = CONNECT_FAILED; + } + return false; + } + return true; +} + + +/** + * @brief Receiving a MQTT packet and store it in the buffer to parse it. + * @param N/A. + * @return Number of received bytes. + */ +size_t PubSubClient::readPacket() { + size_t res = _client->receive(buffer, MQTT_MAX_PACKET_SIZE); + + if (res > MQTT_MAX_PACKET_SIZE) { + res = 0; // This will cause the packet to be ignored. + } + + return (uint16_t) res; +} + + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const char* payload) { + return publish(topic, (const uint8_t*) payload, strlen(payload), false); +} + + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] is this a retained message (true/false) + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const char* payload, bool retained) { + return publish(topic, (const uint8_t*) payload, strlen(payload), retained); +} + + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] length of the message + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { + return publish(topic, payload, plength, false); +} + + +/** + * @brief Publish a MQTT message. + * @param [in] my topic. + * [in] my payload. + * [in] length of the message + * [in] is this a retained message (true/false) + * @return success (true), or no success (false). + */ +bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, bool retained) { + if (connected()) { + if (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength) { + // Too long + return false; + } + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + length = writeString(topic, buffer, length); + uint16_t i; + for (i = 0; i < plength; i++) { + buffer[length++] = payload[i]; + } + uint8_t header = PUBLISH; + if (retained) { + header |= 1; + } + return write(header, buffer, length - 5); + } + return false; +} + + +//bool PubSubClient::publish_P(const char* topic, const uint8_t* payload, +// unsigned int plength, bool retained) { +// uint8_t llen = 0; +// uint8_t digit; +// unsigned int rc = 0; +// uint16_t tlen; +// unsigned int pos = 0; +// unsigned int i; +// uint8_t header; +// unsigned int len; +// +// if (!connected()) { +// return false; +// } +// +// tlen = strlen(topic); +// +// header = PUBLISH; +// if (retained) { +// header |= 1; +// } +// buffer[pos++] = header; +// len = plength + 2 + tlen; +// do { +// digit = len % 128; +// len = len / 128; +// if (len > 0) { +// digit |= 0x80; +// } +// buffer[pos++] = digit; +// llen++; +// } while (len > 0); +// +// pos = writeString(topic, buffer, pos); +// +// rc += _client->send(buffer, pos); +// +// for (i = 0; i < plength; i++) { +// //rc += _client->send((char)pgm_read_byte_near(payload + i)); +// //rc += _client->send((uint8_t*) pgm_read_byte_near(payload + i), 1); +// } +// +// keepAliveTimer->reset(0); //lastOutActivity = millis(); +// +// return rc == tlen + 4 + plength; +//} + + +/** + * @brief Send a MQTT message over socket. + * @param [in] MQTT header. + * [in] Message buffer. + * [in] total length. + * @return success (true), or no success (false). + */ +bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { + uint8_t lenBuf[4]; + uint8_t llen = 0; + uint8_t digit; + uint8_t pos = 0; + int rc; + uint16_t len = length; + do { + digit = len % 128; + len = len / 128; + if (len > 0) { + digit |= 0x80; + } + lenBuf[pos++] = digit; + llen++; + } while (len > 0); + + buf[4 - llen] = header; + for (int i = 0; i < llen; i++) { + buf[5 - llen + i] = lenBuf[i]; + } + +//#ifdef MQTT_MAX_TRANSFER_SIZE +// uint8_t* writeBuf = buf + (4 - llen); +// uint16_t bytesRemaining = length + 1 + llen; //Match the length type +// uint8_t bytesToWrite; +// bool result = true; +// while ((bytesRemaining > 0) && result) { +// bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE) ? MQTT_MAX_TRANSFER_SIZE : bytesRemaining; +// rc = _client->write(writeBuf, bytesToWrite); +// result = (rc == bytesToWrite); +// bytesRemaining -= rc; +// writeBuf += rc; +// } +// return result; +//#else + rc = _client->send(buf + (4 - llen), length + 1 + llen); + if(rc < 0) _state = CONNECTION_LOST; + keepAliveTimer->reset(0); //lastOutActivity = millis(); + return (rc == 1 + llen + length); +//#endif +} + + +/** + * @brief Subscribe a MQTT topic. + * @param [in] my topic + * [in] qos of subscription + * @return request transmitted with success (true), or no success (false). + */ +bool PubSubClient::subscribe(const char* topic, bool ack) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) return false; // Too long + + if (connected()) { + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer, length); + buffer[length++] = QOS1; + + if (write(SUBSCRIBE | QOS1, buffer, length - 5)) { + SUBACK_outstanding = true; + if (ack) timeoutTimer->start(0); + return true; + } + } + return false; +} + + +/** + * @brief Check the state of subscription. If there was received a subscription + * ACK, we return a true here. + * @return Is subscription validated with ACK (true/false) + */ +bool PubSubClient::isSubscribeDone() { + return !SUBACK_outstanding; +} + + +/** + * @brief Unsubscribe a MQTT topic. + * @param [in] my topic + * [in] qos of unsubscription + * @return request transmitted with success (true), or no success (false). + */ +bool PubSubClient::unsubscribe(const char* topic, bool ack) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) return false; // Too long + + if (connected()) { + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer, length); + + if (write(UNSUBSCRIBE | QOS1, buffer, length - 5)) { + UNSUBACK_Outstanding = true; + if (ack) timeoutTimer->start(0); + return true; + } + } + return false; +} + + +/** + * @brief Check the state of unsubscription. If there was received a unsubscription + * ACK, we return a true here. + * @return Is unsubscription validated with ACK (true/false) + */ +bool PubSubClient::isUnsubscribeDone() { + return !UNSUBACK_Outstanding; +} + + +/** + * @brief Disconnect form MQTT server and close the socket. + * @return N/A. + */ +void PubSubClient::disconnect() { + buffer[0] = DISCONNECT; + buffer[1] = 0; + _client->send(buffer, 2); + _state = DISCONNECTED; + _client->close(); + keepAliveTimer->stop(0); //lastInActivity = lastOutActivity = millis(); + timeoutTimer->stop(0); +} + + +/** + * @brief calculation help to send a string. + */ +uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) { + const char* idp = string; + uint16_t i = 0; + pos += 2; + while (*idp) { + buf[pos++] = *idp++; + i++; + } + buf[pos - i - 2] = (i >> 8); + buf[pos - i - 1] = (i & 0xFF); + return pos; +} + + +/** + * @brief Check the connection to the MQTT server. + * @return connected (true/false) + */ +bool PubSubClient::connected() { + if (this->_state != CONNECTED) return false; + bool rc = true; + + if (_client == nullptr) { + rc = false; + } else if (!_client->isValid()) { + rc = false; + + this->_state = CONNECTION_LOST; + + if (_client->isValid()) _client->close(); + keepAliveTimer->stop(0); + timeoutTimer->stop(0); + } + return rc; +} + + +/** + * @brief Set server ip and Port of my MQTT server. + * @param [in] ip of the distant MQTT server. + * [in] the port of the distant MQTT port. + * @return My instance. + */ +PubSubClient& PubSubClient::setServer(std::string ip, uint16_t port) { + _config.ip = ip; + _config.port = port; + return *this; +} + + +/** + * @brief Set the callback function for incoming data. + * @param [in] callback function + * @return My instance. + */ +PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) { + this->callback = callback; + return *this; +} + + +/** + * @brief Set the socket, which we want to use for our MQTT communication. + * @param [in] the new socket instance + * @return My instance. + */ +PubSubClient& PubSubClient::setClient(Socket& client) { + this->_client = &client; + return *this; +} + + +/** + * @brief Get the current MYTT state form the instance. + * @param N/A. + * @return current MQTT state. + */ +int PubSubClient::state() { + return this->_state; +} + + +/** + * @brief Parsing the received data in to the internal message struct. + */ +void PubSubClient::parseData(mqtt_message* msg, uint16_t len) { + /********* Parse Fixed header *********/ + msg->type = buffer[0] & 0xF0; + + /* read DUP-Flag */ + if (msg->type == PUBLISH) { + msg->dup = (bool) (buffer[0] & 0x18) >> 3; + } + + /* read QoS-Level */ + if(msg->type == PUBLISH) { + msg->qos = (buffer[0] & 0x06); + } + + /* read RETAIN-Frag */ + if(msg->type == PUBLISH) { + msg->retained = (bool) (buffer[0] & 0x01); + } + + uint8_t remainingLength = buffer[1]; + + /********* Parse Variable header *********/ + int pos = 2; + + /* read topic name */ + if (msg->type == PUBLISH) { + uint16_t topicLen = (buffer[2] << 8) + buffer[3]; + + msg->topic = ""; + for (int i = 4; i < (topicLen + 4); i++) { + msg->topic += (char) buffer[i]; + pos++; + } + } + + /* read Message ID */ + if (msg->type == PUBLISH || msg->type == PUBACK || msg->type == PUBREC || msg->type == PUBCOMP || msg->type == SUBACK || msg->type == UNSUBACK) { + msg->msgId = (buffer[pos] << 8) + (buffer[pos + 1]); + pos += 2; + } + + /********* read Payload *********/ + if (msg->type == PUBLISH) { + msg->payload = ""; + for (int i = pos; i < remainingLength + 2; i++) { + msg->payload += (char) buffer[i]; + } + } +} + + +/** + * @brief Dump the message struct. + */ +void PubSubClient::dumpData(mqtt_message* msg) { + ESP_LOGD(TAG, "mqtt_message_type: %s", messageType_toString(msg->type).c_str()); + ESP_LOGD(TAG, "mqtt_qos: %d", msg->qos); + ESP_LOGD(TAG, "retained: %d", msg->retained); + ESP_LOGD(TAG, "dup: %d", msg->dup); + ESP_LOGD(TAG, "topic: %s", msg->topic.c_str()); + ESP_LOGD(TAG, "payload: %s", msg->payload.c_str()); + ESP_LOGD(TAG, "msgId: %d", msg->msgId); +} + + +/** + * @brief Convert the MQTT message type to string. + * @param [in] message type byte. + * @return message type as std::string. + */ +std::string PubSubClient::messageType_toString(uint8_t type) { + std::string str = "Not in list!"; + switch (type) { + case CONNECT : str = "CONNECT"; break; + case CONNACK : str = "CONNACK"; break; + case PUBLISH : str = "PUBLISH"; break; + case PUBACK : str = "PUBACK"; break; + case PUBREC : str = "PUBREC"; break; + case PUBREL : str = "PUBREL"; break; + case PUBCOMP : str = "PUBCOMP"; break; + case SUBSCRIBE : str = "SUBSCRIBE"; break; + case SUBACK : str = "SUBACK"; break; + case UNSUBSCRIBE: str = "UNSUBSCRIBE"; break; + case UNSUBACK : str = "UNSUBACK"; break; + case PINGREQ : str = "PINGREQ"; break; + case PINGRESP : str = "PINGRESP"; break; + case DISCONNECT : str = "DISCONNECT"; break; + case Reserved : str = "Reserved"; break; + default : break; + } + return str; +} diff --git a/cpp_utils/PubSubClient.h b/cpp_utils/PubSubClient.h new file mode 100644 index 00000000..aad1103e --- /dev/null +++ b/cpp_utils/PubSubClient.h @@ -0,0 +1,172 @@ +/* + PubSubClient.h - A simple client for MQTT. + Nick O'Leary + http://knolleary.net + + edit by marcel.seerig +*/ + +#ifndef PubSubClient_h +#define PubSubClient_h + +#include +#include "Socket.h" +#include "FreeRTOSTimer.h" + +#define MQTT_VERSION_3_1 3 +#define MQTT_VERSION_3_1_1 4 + +// MQTT_VERSION : Pick the version +//#define MQTT_VERSION MQTT_VERSION_3_1 +#ifndef MQTT_VERSION +#define MQTT_VERSION MQTT_VERSION_3_1_1 +#endif + +// MQTT_MAX_PACKET_SIZE : Maximum packet size +#ifndef MQTT_MAX_PACKET_SIZE +#define MQTT_MAX_PACKET_SIZE 128 +#endif + +// MQTT_KEEPALIVE : keepAlive interval in Seconds +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 15 +#endif + +// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds +#ifndef MQTT_SOCKET_TIMEOUT +#define MQTT_SOCKET_TIMEOUT 15 +#endif + +// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client +// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to +// pass the entire MQTT packet in each write call. +//#define MQTT_MAX_TRANSFER_SIZE 80 + +// Possible values for client.state() + +struct mqtt_InitTypeDef{ + std::string ip; + uint16_t port; + + const char* user; + const char* pass; + + const char* id; + const char* willTopic; + uint8_t willQos; + bool willRetain; + const char* willMessage; +}; + +typedef enum { + CONNECTION_TIMEOUT = -4, + CONNECTION_LOST = -3, + CONNECT_FAILED = -2, + DISCONNECTED = -1, + CONNECTED = 0, + CONNECT_BAD_PROTOCOL = 1, + CONNECT_BAD_CLIENT_ID = 2, + CONNECT_UNAVAILABLE = 3, + CONNECT_BAD_CREDENTIALS = 4, + CONNECT_UNAUTHORIZED = 5, +} mqtt_state; + +typedef enum { + CONNECT = 1 << 4, // Client request to connect to Server + CONNACK = 2 << 4, // Connect Acknowledgment + PUBLISH = 3 << 4, // Publish message + PUBACK = 4 << 4, // Publish Acknowledgment + PUBREC = 5 << 4, // Publish Received (assured delivery part 1) + PUBREL = 6 << 4, // Publish Release (assured delivery part 2) + PUBCOMP = 7 << 4, // Publish Complete (assured delivery part 3) + SUBSCRIBE = 8 << 4, // Client Subscribe request + SUBACK = 9 << 4, // Subscribe Acknowledgment + UNSUBSCRIBE = 10 << 4, // Client Unsubscribe request + UNSUBACK = 11 << 4, // Unsubscribe Acknowledgment + PINGREQ = 12 << 4, // PING Request + PINGRESP = 13 << 4, // PING Response + DISCONNECT = 14 << 4, // Client is Disconnecting + Reserved = 15 << 4, // Reserved +} mqtt_message_type; + +typedef enum { + QOS0 = (0 << 1), + QOS1 = (1 << 1), + QOS2 = (2 << 1), +} mqtt_qos; + +struct mqtt_message { + uint8_t type; + uint8_t qos; + bool retained; + bool dup; + std::string topic; + std::string payload; + uint16_t msgId; +}; + +#define MQTT_CALLBACK_SIGNATURE void (*callback) (std::string, std::string) + +class PubSubClientTask; + +class PubSubClient { +public: + PubSubClient(); + PubSubClient(Socket& client); + PubSubClient(std::string ip, uint16_t port); + PubSubClient(std::string ip, uint16_t port, Socket& client); + PubSubClient(std::string ip, uint16_t port, MQTT_CALLBACK_SIGNATURE,Socket& client); + ~PubSubClient(); + + PubSubClient& setServer(std::string ip, uint16_t port); + PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); + PubSubClient& setClient(Socket& client); + + bool connect(const char* id); + bool connect(const char* id, const char* user, const char* pass); + bool connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage); + bool connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage); + bool connect(); + void disconnect(); + bool publish(const char* topic, const char* payload); + bool publish(const char* topic, const char* payload, bool retained); + bool publish(const char* topic, const uint8_t* payload, unsigned int plength); + bool publish(const char* topic, const uint8_t* payload, unsigned int plength, bool retained); + //bool publish_P(const char* topic, const uint8_t * payload, unsigned int plength, bool retained); + + bool subscribe(const char* topic, bool ack = false); + bool unsubscribe(const char* topic, bool ack = false); + bool isSubscribeDone(); + bool isUnsubscribeDone(); + + bool connected(); + int state(); + void keepAliveChecker(); + void timeoutChecker(); + +private: + friend class PubSubClientTask; + PubSubClientTask* m_task; + Socket* _client; + mqtt_InitTypeDef _config; + mqtt_state _state; + uint8_t buffer[MQTT_MAX_PACKET_SIZE]; + uint16_t nextMsgId; + bool PING_outstanding; + bool SUBACK_outstanding; + bool UNSUBACK_Outstanding; + FreeRTOSTimer* keepAliveTimer; + FreeRTOSTimer* timeoutTimer; + + MQTT_CALLBACK_SIGNATURE; + void setup(); + size_t readPacket(); + bool write(uint8_t header, uint8_t* buf, uint16_t length); + uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + void parseData(mqtt_message* msg, uint16_t len); + void dumpData(mqtt_message* msg); + std::string messageType_toString(uint8_t type); + +}; + +#endif diff --git a/cpp_utils/RESTClient.cpp b/cpp_utils/RESTClient.cpp index 20b5bc3d..a72f80b5 100644 --- a/cpp_utils/RESTClient.cpp +++ b/cpp_utils/RESTClient.cpp @@ -17,7 +17,7 @@ #include "RESTClient.h" -static char tag[] = "RESTClient"; +static const char* LOG_TAG = "RESTClient"; RESTClient::RESTClient() { @@ -36,13 +36,16 @@ RESTClient::~RESTClient() { /** * @brief Perform an HTTP GET request. */ -void RESTClient::get() { +long RESTClient::get() { + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_HTTPGET, 1); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { - ESP_LOGE(tag, "get(): %s", getErrorMessage().c_str()); + ESP_LOGE(LOG_TAG, "get(): %s", getErrorMessage().c_str()); } + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code); // Added return response_code 2018_4_12 + return response_code; // Added return response_code 2018_4_12 } // get @@ -52,13 +55,16 @@ void RESTClient::get() { * @param [in] body The body of the payload to send with the post request. * */ -void RESTClient::post(std::string body) { +long RESTClient::post(std::string body) { + long response_code; // Added return response_code 2018_4_12 prepForCall(); ::curl_easy_setopt(m_curlHandle, CURLOPT_POSTFIELDS, body.c_str()); int rc = ::curl_easy_perform(m_curlHandle); if (rc != CURLE_OK) { - ESP_LOGE(tag, "post(): %s", getErrorMessage().c_str()); + ESP_LOGE(LOG_TAG, "post(): %s", getErrorMessage().c_str()); } + curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &response_code);// Added return response_code 2018_4_12 + return response_code;// Added return response_code 2018_4_12 } // post @@ -84,10 +90,10 @@ std::string RESTClient::getErrorMessage() { * * @return The number of bytes of data processed. */ -size_t RESTClient::handleData(void *buffer, size_t size, size_t nmemb, void *userp) { +size_t RESTClient::handleData(void* buffer, size_t size, size_t nmemb, void* userp) { //printf("handleData: size: %d, num: %d\n", size, nmemb); - RESTClient *pClient = (RESTClient *)userp; - pClient->m_response.append((const char *)buffer, size*nmemb); + RESTClient* pClient = (RESTClient*) userp; + pClient->m_response.append((const char*) buffer, size * nmemb); return size * nmemb; } // handleData @@ -133,7 +139,7 @@ void RESTClient::prepForCall() { } // prepForCall -RESTTimings::RESTTimings(RESTClient *client) { +RESTTimings::RESTTimings(RESTClient* client) { this->client = client; } diff --git a/cpp_utils/RESTClient.h b/cpp_utils/RESTClient.h index 02279c40..f44612ac 100644 --- a/cpp_utils/RESTClient.h +++ b/cpp_utils/RESTClient.h @@ -19,9 +19,10 @@ class RESTClient; */ class RESTTimings { public: - RESTTimings(RESTClient *client); + RESTTimings(RESTClient* client); void refresh(); std::string toString(); + private: double m_namelookup = 0; double m_connect = 0; @@ -29,7 +30,8 @@ class RESTTimings { double m_pretransfer = 0; double m_starttransfer = 0; double m_total = 0; - RESTClient *client = nullptr; + RESTClient* client = nullptr; + }; /** @@ -68,7 +70,7 @@ class RESTClient { RESTClient(); virtual ~RESTClient(); void addHeader(std::string name, std::string value); - void get(); + long get(); // Added return response_code 2018_4_12 std::string getErrorMessage(); /** * @brief Get the response payload data from the last REST call. @@ -86,7 +88,7 @@ class RESTClient { return m_timings; } - void post(std::string body); + long post(std::string body); // Added return response_code 2018_4_12 /** * @brief Set the URL for the target. @@ -107,19 +109,18 @@ class RESTClient { m_verbose = value; }; - - private: - CURL *m_curlHandle; + CURL* m_curlHandle; std::string m_url; char m_errbuf[CURL_ERROR_SIZE]; - struct curl_slist *m_headers = nullptr; + struct curl_slist* m_headers = nullptr; bool m_verbose = false; friend class RESTTimings; - RESTTimings *m_timings; + RESTTimings* m_timings; std::string m_response; - static size_t handleData(void *buffer, size_t size, size_t nmemb, void *userp); + static size_t handleData(void* buffer, size_t size, size_t nmemb, void* userp); void prepForCall(); + }; #endif /* CONFIG_LIBCURL_PRESENT */ #endif /* MAIN_RESTCLIENT_H_ */ diff --git a/cpp_utils/RMT.cpp b/cpp_utils/RMT.cpp index 750da1a6..9f9a5950 100644 --- a/cpp_utils/RMT.cpp +++ b/cpp_utils/RMT.cpp @@ -8,6 +8,7 @@ #include #include "RMT.h" + //static char tag[] = "RMT"; /** * @brief Create a class instance. @@ -22,7 +23,7 @@ RMT::RMT(gpio_num_t pin, rmt_channel_t channel) { config.rmt_mode = RMT_MODE_TX; config.channel = channel; config.gpio_num = pin; - config.mem_block_num = 8-this->channel; + config.mem_block_num = 8 - this->channel; config.clk_div = 8; config.tx_config.loop_en = 0; config.tx_config.carrier_en = 0; @@ -32,7 +33,6 @@ RMT::RMT(gpio_num_t pin, rmt_channel_t channel) { config.tx_config.carrier_level = (rmt_carrier_level_t)1; config.tx_config.carrier_duty_percent = 50; - ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(this->channel, 0, 0)); } @@ -45,6 +45,7 @@ RMT::~RMT() { ESP_ERROR_CHECK(::rmt_driver_uninstall(this->channel)); } + /** * @brief Start receiving. */ @@ -52,6 +53,7 @@ void RMT::rxStart() { ESP_ERROR_CHECK(::rmt_rx_start(this->channel, true)); } + /** * @brief Stop receiving. */ @@ -59,6 +61,7 @@ void RMT::rxStop() { ESP_ERROR_CHECK(::rmt_rx_stop(this->channel)); } + /** * @brief Start transmitting. */ @@ -66,6 +69,7 @@ void RMT::txStart() { ESP_ERROR_CHECK(::rmt_tx_start(this->channel, true)); } + /** * @brief Stop transmitting. */ @@ -73,6 +77,7 @@ void RMT::txStop() { ESP_ERROR_CHECK(::rmt_tx_stop(this->channel)); } + /** * @brief Write the items out through the RMT. * @@ -81,7 +86,7 @@ void RMT::txStop() { * is cleared. */ void RMT::write() { - add(false,0); + add(false, 0); ESP_ERROR_CHECK(::rmt_write_items(this->channel, &items[0], items.size(), true)); clear(); } @@ -100,12 +105,13 @@ void RMT::add(bool level, uint32_t duration) { item.duration0 = duration; items.push_back(item); } else { - items.at(bitCount/2).level1 = level; - items.at(bitCount/2).duration1 = duration; + items.at(bitCount / 2).level1 = level; + items.at(bitCount / 2).duration1 = duration; } bitCount++; } + /** * @brief Clear any previously written level/duration pairs that have not been sent. */ diff --git a/cpp_utils/RMT.h b/cpp_utils/RMT.h index 90e59007..8c079f80 100644 --- a/cpp_utils/RMT.h +++ b/cpp_utils/RMT.h @@ -15,7 +15,7 @@ */ class RMT { public: - RMT(gpio_num_t pin, rmt_channel_t channel=RMT_CHANNEL_0); + RMT(gpio_num_t pin, rmt_channel_t channel = RMT_CHANNEL_0); virtual ~RMT(); void add(bool level, uint32_t duration); void clear(); @@ -25,11 +25,11 @@ class RMT { void txStop(); void write(); - private: rmt_channel_t channel; std::vector items; int bitCount = 0; + }; #endif /* COMPONENTS_CPP_UTILS_RMT_H_ */ diff --git a/cpp_utils/SOC.cpp b/cpp_utils/SOC.cpp index 2f4d3a9a..fa12d4e6 100644 --- a/cpp_utils/SOC.cpp +++ b/cpp_utils/SOC.cpp @@ -34,9 +34,9 @@ void SOC::I2S::dump() { I2S0.clkm_conf.clka_en); uint32_t clockSpeed; if (I2S0.clkm_conf.clkm_div_a == 0) { - clockSpeed = 160000000/I2S0.clkm_conf.clkm_div_num; + clockSpeed = 160000000 / I2S0.clkm_conf.clkm_div_num; } else { - clockSpeed = 160000000/(I2S0.clkm_conf.clkm_div_num + I2S0.clkm_conf.clkm_div_b/I2S0.clkm_conf.clkm_div_a); + clockSpeed = 160000000 / (I2S0.clkm_conf.clkm_div_num + I2S0.clkm_conf.clkm_div_b / I2S0.clkm_conf.clkm_div_a); } printf("Clock speed: %d\n", clockSpeed); printf("\n"); @@ -44,8 +44,8 @@ void SOC::I2S::dump() { printf("I2S_CONF_REG\n"); printf("------------\n"); printf("tx_slave_mod: %s, rx_slave_mod: %s, rx_msb_right: %d, rx_right_first: %d\n", - I2S0.conf.tx_slave_mod==0?"Master":"Slave", - I2S0.conf.rx_slave_mod==0?"Master":"Slave", + (I2S0.conf.tx_slave_mod == 0) ? "Master" : "Slave", + (I2S0.conf.rx_slave_mod == 0) ? "Master" : "Slave", I2S0.conf.rx_msb_right, I2S0.conf.rx_right_first); printf("\n"); diff --git a/cpp_utils/SPI.cpp b/cpp_utils/SPI.cpp index 56b87502..1312e706 100644 --- a/cpp_utils/SPI.cpp +++ b/cpp_utils/SPI.cpp @@ -54,6 +54,7 @@ void SPI::init(int mosiPin, int misoPin, int clkPin, int csPin) { bus_config.quadwp_io_num = -1; // Not used bus_config.quadhd_io_num = -1; // Not used bus_config.max_transfer_sz = 0; // 0 means use default. + bus_config.flags = (SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO); ESP_LOGI(LOG_TAG, "... Initializing bus; host=%d", m_host); @@ -78,7 +79,7 @@ void SPI::init(int mosiPin, int misoPin, int clkPin, int csPin) { dev_config.cs_ena_pretrans = 0; dev_config.clock_speed_hz = 100000; dev_config.spics_io_num = csPin; - dev_config.flags = 0; + dev_config.flags = SPI_DEVICE_NO_DUMMY; dev_config.queue_size = 1; dev_config.pre_cb = NULL; dev_config.post_cb = NULL; @@ -100,6 +101,7 @@ void SPI::setHost(spi_host_device_t host) { m_host = host; } // setHost + /** * @brief Send and receive data through %SPI. This is a blocking call. * @@ -110,7 +112,7 @@ void SPI::transfer(uint8_t* data, size_t dataLen) { assert(data != nullptr); assert(dataLen > 0); #ifdef DEBUG - for (auto i=0; i %2d %.2x", i, data[i]); } #endif @@ -140,5 +142,3 @@ uint8_t SPI::transferByte(uint8_t value) { transfer(&value, 1); return value; } // transferByte - - diff --git a/cpp_utils/SPI.h b/cpp_utils/SPI.h index d2de5d99..6ecf64e1 100644 --- a/cpp_utils/SPI.h +++ b/cpp_utils/SPI.h @@ -22,32 +22,33 @@ class SPI { int clkPin = DEFAULT_CLK_PIN, int csPin = DEFAULT_CS_PIN); void setHost(spi_host_device_t host); - void transfer(uint8_t *data, size_t dataLen); + void transfer(uint8_t* data, size_t dataLen); uint8_t transferByte(uint8_t value); + /** * @brief The default MOSI pin. */ - static const int DEFAULT_MOSI_PIN = GPIO_NUM_13; - - /** - * @brief The default MISO pin. - */ - static const int DEFAULT_MISO_PIN = GPIO_NUM_12; - - /** - * @brief The default CLK pin. - */ - static const int DEFAULT_CLK_PIN = GPIO_NUM_14; - - /** - * @brief The default CS pin. - */ - static const int DEFAULT_CS_PIN = GPIO_NUM_15; - - /** - * @brief Value of unset pin. - */ - static const int PIN_NOT_SET = -1; + static const int DEFAULT_MOSI_PIN = GPIO_NUM_13; + + /** + * @brief The default MISO pin. + */ + static const int DEFAULT_MISO_PIN = GPIO_NUM_12; + + /** + * @brief The default CLK pin. + */ + static const int DEFAULT_CLK_PIN = GPIO_NUM_14; + + /** + * @brief The default CS pin. + */ + static const int DEFAULT_CS_PIN = GPIO_NUM_15; + + /** + * @brief Value of unset pin. + */ + static const int PIN_NOT_SET = -1; private: spi_device_handle_t m_handle; diff --git a/cpp_utils/SSLUtils.cpp b/cpp_utils/SSLUtils.cpp index 92ebcfc4..36621b3e 100644 --- a/cpp_utils/SSLUtils.cpp +++ b/cpp_utils/SSLUtils.cpp @@ -20,7 +20,7 @@ SSLUtils::~SSLUtils() { void SSLUtils::setCertificate(std::string certificate) { size_t len = certificate.length(); - m_certificate = (char*)malloc(len + 1); + m_certificate = (char*) malloc(len + 1); memcpy(m_certificate, certificate.data(), len); m_certificate[len] = '\0'; } @@ -31,7 +31,7 @@ char* SSLUtils::getCertificate() { void SSLUtils::setKey(std::string key) { size_t len = key.length(); - m_key = (char*)malloc(len + 1); + m_key = (char*) malloc(len + 1); memcpy(m_key, key.data(), len); m_key[len] = '\0'; } diff --git a/cpp_utils/SmartLED.cpp b/cpp_utils/SmartLED.cpp index e0267585..430c6317 100644 --- a/cpp_utils/SmartLED.cpp +++ b/cpp_utils/SmartLED.cpp @@ -9,13 +9,13 @@ #include "string.h" #include -const char* LOG_TAG = "SmartLED"; +static const char* LOG_TAG = "SmartLED"; SmartLED::SmartLED() { m_brightness = 100; m_pixelCount = 0; m_pixels = nullptr; - m_colorOrder = (char *)"GRB"; + m_colorOrder = (char*) "GRB"; } // SmartLED @@ -32,7 +32,7 @@ SmartLED::~SmartLED() { * The LEDs are not actually updated until a call to show() is subsequently made. */ void SmartLED::clear() { - for (auto i=0; im_pixelCount; i++) { + for (auto i = 0; i < this->m_pixelCount; i++) { m_pixels[i].red = 0; m_pixels[i].green = 0; m_pixels[i].blue = 0; @@ -48,6 +48,7 @@ uint32_t SmartLED::getBrightness() { return m_brightness; } // getBrightness + /** * @brief Return the number of pixels in the chain. * @return The number of pixels in the chain as previously set by setPixelCount(). @@ -65,6 +66,7 @@ void SmartLED::setBrightness(uint32_t percent) { m_brightness = percent; } // setBrightness + /** * @brief Set the color order of data sent to the LEDs. * @@ -77,7 +79,7 @@ void SmartLED::setBrightness(uint32_t percent) { * an alternate order by supply an alternate three character string made up of 'R', 'G' and 'B' * for example "RGB". */ -void SmartLED::setColorOrder(char *colorOrder) { +void SmartLED::setColorOrder(char* colorOrder) { if (colorOrder != nullptr && strlen(colorOrder) == 3) { m_colorOrder = colorOrder; } @@ -94,9 +96,8 @@ void SmartLED::setColorOrder(char *colorOrder) { * @param [in] green The amount of green in the pixel. * @param [in] blue The amount of blue in the pixel. */ -void SmartLED::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { +void SmartLED::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { //assert(index < m_pixelCount); - m_pixels[index].red = red; m_pixels[index].green = green; m_pixels[index].blue = blue; @@ -127,12 +128,12 @@ void SmartLED::setPixel(uint16_t index, pixel_t pixel) { */ void SmartLED::setPixel(uint16_t index, uint32_t pixel) { //assert(index < m_pixelCount); - m_pixels[index].red = pixel & 0xff; m_pixels[index].green = (pixel & 0xff00) >> 8; m_pixels[index].blue = (pixel & 0xff0000) >> 16; } // setPixel + void SmartLED::setPixelCount(uint16_t pixelCount) { ESP_LOGD(LOG_TAG, ">> setPixelCount: %d", pixelCount); if (m_pixels != nullptr) { @@ -143,6 +144,7 @@ void SmartLED::setPixelCount(uint16_t pixelCount) { ESP_LOGD(LOG_TAG, "<< setPixelCount"); } + /** * @brief Set the given pixel to the specified HSB color. * @@ -153,66 +155,60 @@ void SmartLED::setPixelCount(uint16_t pixelCount) { * @param [in] saturation The amount of saturation in the pixel (0-255). * @param [in] brightness The amount of brightness in the pixel (0-255). */ -void SmartLED::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { - double sat_red; - double sat_green; - double sat_blue; - double ctmp_red; - double ctmp_green; - double ctmp_blue; - double new_red; - double new_green; - double new_blue; - double dSaturation=(double)saturation/255; - double dBrightness=(double)brightness/255; - - //assert(index < pixelCount); - - if (hue < 120) { - sat_red = (120 - hue) / 60.0; - sat_green = hue / 60.0; - sat_blue = 0; - } else if (hue < 240) { - sat_red = 0; - sat_green = (240 - hue) / 60.0; - sat_blue = (hue - 120) / 60.0; - } else { - sat_red = (hue - 240) / 60.0; - sat_green = 0; - sat_blue = (360 - hue) / 60.0; - } - - if (sat_red>1.0) { - sat_red = 1.0; - } - if (sat_green>1.0) { - sat_green = 1.0; - } - if (sat_blue>1.0) { - sat_blue = 1.0; - } - - ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); - ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); - ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); - - if (dBrightness < 0.5) { - new_red = dBrightness * ctmp_red; - new_green = dBrightness * ctmp_green; - new_blue = dBrightness * ctmp_blue; - } else { - new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; - new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; - new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; - } - - m_pixels[index].red = (uint8_t)(new_red*255); - m_pixels[index].green = (uint8_t)(new_green*255); - m_pixels[index].blue = (uint8_t)(new_blue*255); -} // setHSBPixel - - - +void SmartLED::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { + double sat_red; + double sat_green; + double sat_blue; + double ctmp_red; + double ctmp_green; + double ctmp_blue; + double new_red; + double new_green; + double new_blue; + double dSaturation=(double) saturation / 255; + double dBrightness=(double) brightness / 255; + + //assert(index < pixelCount); + + if (hue < 120) { + sat_red = (120 - hue) / 60.0; + sat_green = hue / 60.0; + sat_blue = 0; + } else if (hue < 240) { + sat_red = 0; + sat_green = (240 - hue) / 60.0; + sat_blue = (hue - 120) / 60.0; + } else { + sat_red = (hue - 240) / 60.0; + sat_green = 0; + sat_blue = (360 - hue) / 60.0; + } + if (sat_red>1.0) { + sat_red = 1.0; + } + if (sat_green>1.0) { + sat_green = 1.0; + } + if (sat_blue>1.0) { + sat_blue = 1.0; + } + ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); + ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); + ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); + + if (dBrightness < 0.5) { + new_red = dBrightness * ctmp_red; + new_green = dBrightness * ctmp_green; + new_blue = dBrightness * ctmp_blue; + } else { + new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; + new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; + new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; + } + m_pixels[index].red = (uint8_t)(new_red * 255); + m_pixels[index].green = (uint8_t)(new_green * 255); + m_pixels[index].blue = (uint8_t)(new_blue * 255); +} // setHSBPixel diff --git a/cpp_utils/SmartLED.h b/cpp_utils/SmartLED.h index e70c4843..7903f697 100644 --- a/cpp_utils/SmartLED.h +++ b/cpp_utils/SmartLED.h @@ -36,13 +36,14 @@ class SmartLED { virtual void init() = 0; virtual void show() = 0; void setBrightness(uint32_t percent); - void setColorOrder(char *order); + void setColorOrder(char* order); void setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue); void setPixel(uint16_t index, pixel_t pixel); void setPixel(uint16_t index, uint32_t pixel); void setPixelCount(uint16_t pixelCount); void setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness); void clear(); + protected: uint32_t m_brightness; char* m_colorOrder; diff --git a/cpp_utils/SockServ.cpp b/cpp_utils/SockServ.cpp index 6d35a387..020ed096 100644 --- a/cpp_utils/SockServ.cpp +++ b/cpp_utils/SockServ.cpp @@ -7,15 +7,15 @@ #include #include -#include #include #include - #include + #include #include #include "sdkconfig.h" +#include "FreeRTOS.h" #include "SockServ.h" #include "Socket.h" @@ -30,7 +30,6 @@ static const char* LOG_TAG = "SockServ"; */ SockServ::SockServ(uint16_t port) : SockServ() { this->m_port = port; - } // SockServ @@ -61,28 +60,26 @@ SockServ::~SockServ() { * socket is placed on a queue and a semaphore signaled that a new client is available. */ /* static */ void SockServ::acceptTask(void* data) { - SockServ* pSockServ = (SockServ*)data; - try { - while(1) { - ESP_LOGD(LOG_TAG, "Waiting on accept") + SockServ* pSockServ = (SockServ*) data; + while (true) { + try { + ESP_LOGD(LOG_TAG, "Waiting on accept"); Socket tempSock = pSockServ->m_serverSocket.accept(); - if (!tempSock.isValid()) { - continue; - } + if (!tempSock.isValid()) continue; pSockServ->m_clientSet.insert(tempSock); xQueueSendToBack(pSockServ->m_acceptQueue, &tempSock, portMAX_DELAY); pSockServ->m_clientSemaphore.give(); + } catch (std::exception e) { + ESP_LOGD(LOG_TAG, "acceptTask ending"); + pSockServ->m_clientSemaphore.give(); // Wake up any waiting clients. + FreeRTOS::deleteTask(); + break; } - } catch(std::exception e) { - ESP_LOGD(LOG_TAG, "acceptTask ending"); - pSockServ->m_clientSemaphore.give(); // Wake up any waiting clients. - FreeRTOS::deleteTask(); } } // acceptTask - /** * @brief Determine the number of connected partners. * @@ -117,7 +114,7 @@ bool SockServ::getSSL() { * @return The amount of data returned or 0 if there was an error. */ size_t SockServ::receiveData(Socket s, void* pData, size_t maxData) { - int rc = s.receive((uint8_t*)pData, maxData); + size_t rc = s.receive((uint8_t*) pData, maxData); if (rc == -1) { ESP_LOGE(LOG_TAG, "recv(): %s", strerror(errno)); return 0; @@ -132,7 +129,7 @@ size_t SockServ::receiveData(Socket s, void* pData, size_t maxData) { * @param[in] str A string from which sequence of bytes will be used to send to the partner. */ void SockServ::sendData(std::string str) { - sendData((uint8_t *)str.data(), str.size()); + sendData((uint8_t*) str.data(), str.size()); } // sendData @@ -144,7 +141,7 @@ void SockServ::sendData(std::string str) { */ void SockServ::sendData(uint8_t* data, size_t length) { for (auto it = m_clientSet.begin(); it != m_clientSet.end(); ++it) { - (*it).send(data, length); + (*it).send(data, length); } } // sendData @@ -173,7 +170,7 @@ void SockServ::start() { //m_serverSocket.setSSL(m_useSSL); m_serverSocket.listen(m_port); // Create a socket and start listening on it. ESP_LOGD(LOG_TAG, "Now listening on port %d", m_port); - FreeRTOS::startTask(acceptTask, "acceptTask", this, 8*1024); + FreeRTOS::startTask(acceptTask, "acceptTask", this, 8 * 1024); } // start @@ -193,7 +190,7 @@ Socket SockServ::waitForData(std::set& socketSet) { fd_set readSet; int maxFd = -1; - for ( auto it = socketSet.begin(); it != socketSet.end(); ++it) { + for (auto it = socketSet.begin(); it != socketSet.end(); ++it) { FD_SET(it->getFD(), &readSet); if (it->getFD() > maxFd) { maxFd = it->getFD(); @@ -201,11 +198,11 @@ Socket SockServ::waitForData(std::set& socketSet) { } // End for int rc = ::select( - maxFd+1, // Number of sockets to scan - &readSet, // Set of read sockets - nullptr, // Set of write sockets - nullptr, // Set of exception sockets - nullptr // Timeout + maxFd+1, // Number of sockets to scan + &readSet, // Set of read sockets + nullptr, // Set of write sockets + nullptr, // Set of exception sockets + nullptr // Timeout ); if (rc == -1) { ESP_LOGE(LOG_TAG, "Error with select"); @@ -213,7 +210,7 @@ Socket SockServ::waitForData(std::set& socketSet) { return s; } - for ( auto it = socketSet.begin(); it != socketSet.end(); ++it) { + for (auto it = socketSet.begin(); it != socketSet.end(); ++it) { if (FD_ISSET(it->getFD(), &readSet)) { return *it; } @@ -229,7 +226,7 @@ Socket SockServ::waitForData(std::set& socketSet) { * or can return immediately is there is already a client connection in existence. */ Socket SockServ::waitForNewClient() { - ESP_LOGD(LOG_TAG, ">> waitForNewClient") + ESP_LOGD(LOG_TAG, ">> waitForNewClient"); m_clientSemaphore.wait("waitForNewClient"); // Unlocked in acceptTask. m_clientSemaphore.take("waitForNewClient"); Socket tempSocket; @@ -241,5 +238,3 @@ Socket SockServ::waitForNewClient() { ESP_LOGD(LOG_TAG, "<< waitForNewClient"); return tempSocket; } // waitForNewClient - - diff --git a/cpp_utils/SockServ.h b/cpp_utils/SockServ.h index e0ffee76..770ec954 100644 --- a/cpp_utils/SockServ.h +++ b/cpp_utils/SockServ.h @@ -51,11 +51,12 @@ class SockServ { void sendData(uint8_t* data, size_t length); void sendData(std::string str); void setPort(uint16_t port); - void setSSL(bool use=true); + void setSSL(bool use = true); void start(); void stop(); Socket waitForData(std::set& socketSet); Socket waitForNewClient(); + }; #endif /* MAIN_SOCKSERV_H_ */ diff --git a/cpp_utils/Socket.cpp b/cpp_utils/Socket.cpp index b7c41c1e..db5991ab 100644 --- a/cpp_utils/Socket.cpp +++ b/cpp_utils/Socket.cpp @@ -13,8 +13,6 @@ #include - - #include #include #include @@ -32,11 +30,11 @@ static const char* LOG_TAG = "Socket"; #undef bind static void my_debug( - void *ctx, + void* ctx, int level, - const char *file, + const char* file, int line, - const char *str) { + const char* str) { ((void) level); ((void) ctx); @@ -60,8 +58,10 @@ Socket Socket::accept() { struct sockaddr addr; getBind(&addr); ESP_LOGD(LOG_TAG, ">> accept: Accepting on %s; sockFd: %d, using SSL: %d", addressToString(&addr).c_str(), m_sock, getSSL()); - - int clientSockFD = ::lwip_accept_r(m_sock, nullptr, nullptr); + struct sockaddr_in client_addr; + socklen_t sin_size; + int clientSockFD = ::lwip_accept_r(m_sock, (struct sockaddr*) &client_addr, &sin_size); + //printf("------> new connection client %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (clientSockFD == -1) { SocketException se(errno); ESP_LOGE(LOG_TAG, "accept(): %s, m_sock=%d", strerror(errno), m_sock); @@ -90,7 +90,7 @@ Socket Socket::accept() { * @return A string representation of the address. */ std::string Socket::addressToString(struct sockaddr* addr) { - struct sockaddr_in *pInAddr = (struct sockaddr_in *)addr; + struct sockaddr_in* pInAddr = (struct sockaddr_in*) addr; char temp[30]; char ip[20]; inet_ntop(AF_INET, &pInAddr->sin_addr, ip, sizeof(ip)); @@ -105,9 +105,9 @@ std::string Socket::addressToString(struct sockaddr* addr) { * Specify an address of INADDR_ANY to use the local server IP. * @param [in] port Port number to bind. * @param [in] address Address to bind. - * @return N/A + * @return Returns 0 on success. */ -void Socket::bind(uint16_t port, uint32_t address) { +int Socket::bind(uint16_t port, uint32_t address) { ESP_LOGD(LOG_TAG, ">> bind: port=%d, address=0x%x", port, address); if (m_sock == -1) { @@ -117,36 +117,40 @@ void Socket::bind(uint16_t port, uint32_t address) { serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(address); serverAddress.sin_port = htons(port); - int rc = ::lwip_bind_r(m_sock, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); - if (rc == -1) { + int rc = ::lwip_bind_r(m_sock, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); + if (rc != 0) { ESP_LOGE(LOG_TAG, "<< bind: bind[socket=%d]: %d: %s", m_sock, errno, strerror(errno)); - return; + return rc; } ESP_LOGD(LOG_TAG, "<< bind"); + return rc; } // bind /** * @brief Close the socket. * - * @return N/A. + * @return Returns 0 on success. */ -void Socket::close() { +int Socket::close() { ESP_LOGD(LOG_TAG, "close: m_sock=%d, ssl: %d", m_sock, getSSL()); + int rc; if (getSSL()) { - int rc = mbedtls_ssl_close_notify(&m_sslContext); + rc = mbedtls_ssl_close_notify(&m_sslContext); if (rc < 0) { ESP_LOGD(LOG_TAG, "mbedtls_ssl_close_notify: %d", rc); } } + rc = 0; if (m_sock != -1) { ESP_LOGD(LOG_TAG, "Calling lwip_close on %d", m_sock); - int rc = lwip_close_r(m_sock); + rc = ::lwip_close_r(m_sock); if (rc != 0) { - ESP_LOGE(LOG_TAG, "Error with lwip_close"); + ESP_LOGE(LOG_TAG, "Error with lwip_close: %d", rc); } } m_sock = -1; + return rc; } // close @@ -166,7 +170,7 @@ int Socket::connect(struct in_addr address, uint16_t port) { inet_ntop(AF_INET, &address, msg, sizeof(msg)); ESP_LOGD(LOG_TAG, "Connecting to %s:[%d]", msg, port); createSocket(); - int rc = ::lwip_connect_r(m_sock, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + int rc = ::lwip_connect_r(m_sock, (struct sockaddr*) &serverAddress, sizeof(struct sockaddr_in)); if (rc == -1) { ESP_LOGE(LOG_TAG, "connect_cpp: Error: %s", strerror(errno)); close(); @@ -187,7 +191,7 @@ int Socket::connect(struct in_addr address, uint16_t port) { */ int Socket::connect(char* strAddress, uint16_t port) { struct in_addr address; - inet_pton(AF_INET, (char *)strAddress, &address); + inet_pton(AF_INET, strAddress, &address); return connect(address, port); } @@ -201,8 +205,7 @@ int Socket::createSocket(bool isDatagram) { ESP_LOGD(LOG_TAG, ">> createSocket: isDatagram: %d", isDatagram); if (isDatagram) { m_sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - } - else { + } else { m_sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } if (m_sock == -1) { @@ -251,20 +254,28 @@ bool Socket::isValid() { * @brief Create a listening socket. * @param [in] port The port number to listen upon. * @param [in] isDatagram True if we are listening on a datagram. The default is false. + * @return Returns 0 on success. */ -void Socket::listen(uint16_t port, bool isDatagram) { +int Socket::listen(uint16_t port, bool isDatagram, bool reuseAddress) { ESP_LOGD(LOG_TAG, ">> listen: port: %d, isDatagram: %d", port, isDatagram); createSocket(isDatagram); - bind(port, 0); + setReuseAddress(reuseAddress); + int rc = bind(port, 0); + if (rc != 0) { + ESP_LOGE(LOG_TAG, "<< listen: Error in bind: %s", strerror(errno)); + return rc; + } // For a datagram socket, we don't execute a listen call. That is is only for connection oriented // sockets. if (!isDatagram) { - int rc = ::lwip_listen_r(m_sock, 5); + rc = ::lwip_listen_r(m_sock, 5); if (rc == -1) { ESP_LOGE(LOG_TAG, "<< listen: %s", strerror(errno)); + return rc; } } ESP_LOGD(LOG_TAG, "<< listen"); + return 0; } // listen @@ -273,25 +284,46 @@ bool Socket::operator <(const Socket& other) const { } +/** + * @brief Set the socket option. + */ +int Socket::setSocketOption(int option, void* value, size_t len) { + int res = ::setsockopt(m_sock, SOL_SOCKET, option, value, len); + if (res < 0) { + ESP_LOGE(LOG_TAG, "%X : %d", option, errno); + } + return res; +} // setSocketOption + + +/** + * @brief Socket timeout. + * @param [in] seconds to wait. + */ +int Socket::setTimeout(uint32_t seconds) { + struct timeval tv; + tv.tv_sec = seconds; + tv.tv_usec = 0; + if (setSocketOption(SO_RCVTIMEO, (char*) &tv, sizeof(struct timeval)) < 0) { + return -1; + } + return setSocketOption(SO_SNDTIMEO, (char*) &tv, sizeof(struct timeval)); +} + + std::string Socket::readToDelim(std::string delim) { std::string ret; std::string part; auto it = delim.begin(); - while(1) { + while (true) { uint8_t val; int rc = receive(&val, 1); - if (rc == -1) { - return ""; - } - if (rc == 0) { - return ret+part; - } + if (rc == -1) return ""; + if (rc == 0) return ret + part; if (*it == val) { part+= val; ++it; - if (it == delim.end()) { - return ret; - } + if (it == delim.end()) return ret; } else { if (part.empty()) { ret += part; @@ -304,7 +336,6 @@ std::string Socket::readToDelim(std::string delim) { } // readToDelim - /** * @brief Receive data from the partner. * Receive data from the socket partner. If exact = false, we read as much data as @@ -317,13 +348,13 @@ std::string Socket::readToDelim(std::string delim) { */ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { //ESP_LOGD(LOG_TAG, ">> receive: sockFd: %d, length: %d, exact: %d", m_sock, length, exact); - if (exact == false) { + if (!exact) { int rc; if (getSSL()) { do { rc = mbedtls_ssl_read(&m_sslContext, data, length); ESP_LOGD(LOG_TAG, "rc=%d, MBEDTLS_ERR_SSL_WANT_READ=%d", rc, MBEDTLS_ERR_SSL_WANT_READ); - } while(rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); + } while (rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); } else { rc = ::lwip_recv_r(m_sock, data, length, 0); if (rc == -1) { @@ -332,16 +363,16 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { } //GeneralUtils::hexDump(data, rc); //ESP_LOGD(LOG_TAG, "<< receive: rc: %d", rc); - return rc; + return (size_t) rc; } // Read what we can, doesn't need to be an exact amount. size_t amountToRead = length; int rc; - while(amountToRead > 0) { + while (amountToRead > 0) { if (getSSL()) { do { rc = mbedtls_ssl_read(&m_sslContext, data, amountToRead); - } while(rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); + } while (rc == MBEDTLS_ERR_SSL_WANT_WRITE || rc == MBEDTLS_ERR_SSL_WANT_READ); } else { rc = ::lwip_recv_r(m_sock, data, amountToRead, 0); } @@ -349,9 +380,7 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { ESP_LOGE(LOG_TAG, "receive: %s", strerror(errno)); return 0; } - if (rc == 0) { - break; - } + if (rc == 0) break; amountToRead -= rc; data += rc; } @@ -368,7 +397,7 @@ size_t Socket::receive(uint8_t* data, size_t length, bool exact) { * @param [in] pAddr An area into which we can store the address of the partner. * @return The length of the data received. */ -int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { +int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { socklen_t addrLen = sizeof(struct sockaddr); int rc = ::recvfrom(m_sock, data, length, 0, pAddr, &addrLen); return rc; @@ -386,14 +415,34 @@ int Socket::receiveFrom(uint8_t* data, size_t length, struct sockaddr *pAddr) { int Socket::send(const uint8_t* data, size_t length) const { ESP_LOGD(LOG_TAG, "send: Raw binary of length: %d", length); //GeneralUtils::hexDump(data, length); - int rc; - if (getSSL()) { - rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); - } else { - rc = ::lwip_send_r(m_sock, data, length, 0); - } - if (rc == -1) { - ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); + int rc = ERR_OK; + while (length > 0) { + if (getSSL()) { + rc = mbedtls_ssl_write((mbedtls_ssl_context*)&m_sslContext, data, length); + // retry with same parameters if MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ + if ((rc != MBEDTLS_ERR_SSL_WANT_WRITE) && (rc != MBEDTLS_ERR_SSL_WANT_READ)) { + if (rc < 0) { + // no cure for other errors - log and exit + ESP_LOGE(LOG_TAG, "send: SSL write error %d", rc); + return rc; + } else { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } + } else { + rc = ::lwip_send_r(m_sock, data, length, 0); + if ((rc < 0) && (errno != EAGAIN)) { + // no cure for errors other than EAGAIN - log and exit + ESP_LOGE(LOG_TAG, "send: socket=%d, %s", m_sock, strerror(errno)); + return rc; + } else if (rc > 0) { + // not all data was written, try again for the remainder + length -= rc; + data += rc; + } + } } return rc; } // send @@ -407,19 +456,19 @@ int Socket::send(const uint8_t* data, size_t length) const { */ int Socket::send(std::string value) const { ESP_LOGD(LOG_TAG, "send: Binary of length: %d", value.length()); - return send((uint8_t *)value.data(), value.size()); + return send((uint8_t*) value.data(), value.size()); } // send int Socket::send(uint16_t value) { ESP_LOGD(LOG_TAG, "send: 16bit value: %.2x", value); - return send((uint8_t *)&value, sizeof(value)); + return send((uint8_t*) &value, sizeof(value)); } // send_cpp int Socket::send(uint32_t value) { ESP_LOGD(LOG_TAG, "send: 32bit value: %.2x", value); - return send((uint8_t *)&value, sizeof(value)); + return send((uint8_t*) &value, sizeof(value)); } // send @@ -442,6 +491,18 @@ void Socket::sendTo(const uint8_t* data, size_t length, struct sockaddr* pAddr) } // sendTo +/** + * @brief Flag the socket address as re-usable. + * @param [in] value True to mark the address as re-usable, false otherwise. + */ +void Socket::setReuseAddress(bool value) { + ESP_LOGD(LOG_TAG, ">> setReuseAddress: %d", value); + int val = value ? 1 : 0; + setSocketOption(SO_REUSEADDR, &val, sizeof(val)); + ESP_LOGD(LOG_TAG, "<< setReuseAddress"); +} // setReuseAddress + + /** * @brief Flag the socket as using SSL * @param [in] sslValue True if we wish to use SSL. @@ -451,9 +512,9 @@ void Socket::setSSL(bool sslValue) { ESP_LOGD(LOG_TAG, ">> setSSL: %s", sslValue?"Yes":"No"); m_useSSL = sslValue; - if (sslValue == true) { - char* pvtKey = SSLUtils::getKey(); - char* certificate = SSLUtils::getCertificate(); + if (sslValue) { + char *pvtKey = SSLUtils::getKey(); + char *certificate = SSLUtils::getCertificate(); if (pvtKey == nullptr) { ESP_LOGE(LOG_TAG, "No private key file"); return; @@ -471,66 +532,56 @@ void Socket::setSSL(bool sslValue) { mbedtls_entropy_init(&m_entropy); mbedtls_ctr_drbg_init(&m_ctr_drbg); - int ret = mbedtls_x509_crt_parse(&m_srvcert, (unsigned char*)certificate, strlen(certificate)+1); - if( ret != 0 ) { - ESP_LOGD(LOG_TAG, "mbedtls_x509_crt_parse returned 0x%x", -ret ); + int ret = mbedtls_x509_crt_parse(&m_srvcert, (unsigned char *) certificate, strlen(certificate) + 1); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_x509_crt_parse returned 0x%x", -ret); goto exit; } - ret = mbedtls_pk_parse_key(&m_pkey, (unsigned char*)pvtKey, strlen(pvtKey)+1, NULL, 0 ); - if( ret != 0 ) { + ret = mbedtls_pk_parse_key(&m_pkey, (unsigned char *) pvtKey, strlen(pvtKey) + 1, NULL, 0); + if (ret != 0) { ESP_LOGD(LOG_TAG, "mbedtls_pk_parse_key returned 0x%x", -ret); goto exit; } - ret = mbedtls_ctr_drbg_seed( &m_ctr_drbg, mbedtls_entropy_func, &m_entropy, (const unsigned char*) pers, strlen(pers)); - if( ret != 0 ) { - ESP_LOGD(LOG_TAG, "! mbedtls_ctr_drbg_seed returned %d\n",ret); - goto exit; - } + ret = mbedtls_ctr_drbg_seed(&m_ctr_drbg, mbedtls_entropy_func, &m_entropy, (const unsigned char*) pers, strlen(pers)); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } ret = mbedtls_ssl_config_defaults(&m_conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if(ret != 0 ) { + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { ESP_LOGD(LOG_TAG, "mbedtls_ssl_config_defaults returned %d\n\n", ret); goto exit; } mbedtls_ssl_conf_authmode(&m_conf, MBEDTLS_SSL_VERIFY_NONE); - mbedtls_ssl_conf_rng(&m_conf, mbedtls_ctr_drbg_random, &m_ctr_drbg); +// mbedtls_ssl_conf_ca_chain( &m_conf, m_srvcert.next, NULL); + ret = mbedtls_ssl_conf_own_cert(&m_conf, &m_srvcert, &m_pkey); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + goto exit; + } -// mbedtls_ssl_conf_ca_chain( &m_conf, m_srvcert.next, NULL); - ret = mbedtls_ssl_conf_own_cert( &m_conf, &m_srvcert, &m_pkey); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_conf_own_cert returned %d\n\n", ret); - goto exit; - } - - mbedtls_ssl_conf_dbg(&m_conf, my_debug, nullptr); + mbedtls_ssl_conf_dbg(&m_conf, my_debug, nullptr); #ifdef CONFIG_MBEDTLS_DEBUG - mbedtls_debug_set_threshold(4); + mbedtls_debug_set_threshold(4); #endif - ret = mbedtls_ssl_setup(&m_sslContext, &m_conf); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_setup returned %d\n\n", ret ); - goto exit; - } -/* - ret = mbedtls_ssl_set_hostname(&m_sslContext, "192.168.1.99"); - if(ret != 0) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_set_hostname returned %d\n\n", ret ); - goto exit; - } - */ - - exit: - return; + ret = mbedtls_ssl_setup(&m_sslContext, &m_conf); + if (ret != 0) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } } +exit: + return; } // setSSL @@ -543,14 +594,12 @@ void Socket::sslHandshake() { ESP_LOGD(LOG_TAG, " - Reset complete"); mbedtls_ssl_set_bio(&m_sslContext, &m_sslSock, mbedtls_net_send, mbedtls_net_recv, NULL); - while(1) { + while (true) { int ret = mbedtls_ssl_handshake(&m_sslContext); - if (ret == 0) { - break; - } + if (ret == 0) break; - if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - ESP_LOGD(LOG_TAG, "mbedtls_ssl_handshake returned %d\n\n", ret ); + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGD(LOG_TAG, "mbedtls_ssl_handshake returned %d\n\n", ret); return; } } // End while @@ -599,13 +648,10 @@ SocketInputRecordStreambuf::~SocketInputRecordStreambuf() { * */ SocketInputRecordStreambuf::int_type SocketInputRecordStreambuf::underflow() { - if (m_sizeRead >= m_dataLength) { - return EOF; - } - int bytesRead = m_socket.receive((uint8_t*)m_buffer, m_bufferSize, true); - if (bytesRead == 0) { - return EOF; - } + if (m_sizeRead >= m_dataLength) return EOF; + int bytesRead = m_socket.receive((uint8_t*) m_buffer, m_bufferSize, true); + if (bytesRead == 0) return EOF; + m_sizeRead += bytesRead; setg(m_buffer, m_buffer, m_buffer + bytesRead); return traits_type::to_int_type(*gptr()); diff --git a/cpp_utils/Socket.h b/cpp_utils/Socket.h index 6eef11db..f9a2bac6 100644 --- a/cpp_utils/Socket.h +++ b/cpp_utils/Socket.h @@ -45,10 +45,13 @@ class SocketException: public std::exception { public: SocketException(int myErrno); + private: int m_errno; + }; + /** * @brief Encapsulate a socket. * @@ -63,52 +66,58 @@ class Socket { Socket accept(); static std::string addressToString(struct sockaddr* addr); - void bind(uint16_t port, uint32_t address); - void close(); + int bind(uint16_t port, uint32_t address); + int close(); int connect(struct in_addr address, uint16_t port); int connect(char* address, uint16_t port); int createSocket(bool isDatagram = false); + void setReuseAddress(bool value); + int setSocketOption(int option, void* value, size_t len); + int setTimeout(uint32_t seconds); void getBind(struct sockaddr* pAddr); int getFD() const; bool getSSL() const; bool isValid(); - void listen(uint16_t port, bool isDatagram=false); + int listen(uint16_t port, bool isDatagram = false, bool reuseAddress = false); bool operator<(const Socket& other) const; std::string readToDelim(std::string delim); - size_t receive(uint8_t* data, size_t length, bool exact=false); + size_t receive(uint8_t* data, size_t length, bool exact = false); int receiveFrom(uint8_t* data, size_t length, struct sockaddr* pAddr); int send(std::string value) const; int send(const uint8_t* data, size_t length) const; int send(uint16_t value); int send(uint32_t value); void sendTo(const uint8_t* data, size_t length, struct sockaddr* pAddr); - void setSSL(bool sslValue=true); + void setSSL(bool sslValue = true); std::string toString(); private: int m_sock; // The underlying TCP/IP socket bool m_useSSL; // Should we use SSL - mbedtls_net_context m_sslSock; - mbedtls_entropy_context m_entropy; - mbedtls_ctr_drbg_context m_ctr_drbg; - mbedtls_ssl_context m_sslContext; - mbedtls_ssl_config m_conf; - mbedtls_x509_crt m_srvcert; - mbedtls_pk_context m_pkey; - void sslHandshake(); + mbedtls_net_context m_sslSock; + mbedtls_entropy_context m_entropy; + mbedtls_ctr_drbg_context m_ctr_drbg; + mbedtls_ssl_context m_sslContext; + mbedtls_ssl_config m_conf; + mbedtls_x509_crt m_srvcert; + mbedtls_pk_context m_pkey; + void sslHandshake(); + }; class SocketInputRecordStreambuf : public std::streambuf { public: - SocketInputRecordStreambuf(Socket socket, size_t dataLength, size_t bufferSize=512); + SocketInputRecordStreambuf(Socket socket, size_t dataLength, size_t bufferSize = 512); ~SocketInputRecordStreambuf(); int_type underflow(); + private: char* m_buffer; Socket m_socket; size_t m_dataLength; size_t m_bufferSize; size_t m_sizeRead; + }; diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index 1993be4c..2e24f88b 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -7,11 +7,127 @@ #include "System.h" #include +#include +#include extern "C" { #include } +typedef volatile struct { + union { + struct { + uint32_t mcu_oe: 1; + uint32_t slp_sel: 1; + uint32_t mcu_wpd: 1; + uint32_t mcu_wpu: 1; + uint32_t mcu_ie: 1; + uint32_t mcu_drb: 2; + uint32_t func_wpd: 1; + uint32_t func_wpu: 1; + uint32_t func_ie: 1; + uint32_t func_drv: 2; + uint32_t mcu_sel: 3; + uint32_t reserved15: 17; + }; + uint32_t val; + }; +} io_mux_reg_t; + + +typedef volatile struct { + union { + struct { + uint32_t clk1: 4; + uint32_t clk2: 4; + uint32_t clk3: 4; + uint32_t reserved12: 20; + }; + uint32_t val; + } pin_ctrl; + + // The 36 exposed pads. + io_mux_reg_t pad_gpio36; // GPIO36 + io_mux_reg_t pad_gpio37; // GPIO37 + io_mux_reg_t pad_gpio38; // GPIO38 + io_mux_reg_t pad_gpio39; // GPIO39 + io_mux_reg_t pad_gpio34; // GPIO34 + io_mux_reg_t pad_gpio35; // GPIO35 + io_mux_reg_t pad_gpio32; // GPIO32 + io_mux_reg_t pad_gpio33; // GPIO33 + io_mux_reg_t pad_gpio25; // GPIO25 + io_mux_reg_t pad_gpio26; // GPIO26 + io_mux_reg_t pad_gpio27; // GPIO27 + io_mux_reg_t pad_mtms; // GPIO14 + io_mux_reg_t pad_mtdi; // GPIO12 + io_mux_reg_t pad_mtck; // GPIO13 + io_mux_reg_t pad_mtdo; // GPIO15 + io_mux_reg_t pad_gpio2; // GPIO2 + io_mux_reg_t pad_gpio0; // GPIO0 + io_mux_reg_t pad_gpio4; // GPIO4 + io_mux_reg_t pad_gpio16; // GPIO16 + io_mux_reg_t pad_gpio17; // GPIO17 + io_mux_reg_t pad_sd_data2; // GPIO9 + io_mux_reg_t pad_sd_data3; // GPIO10 + io_mux_reg_t pad_sd_cmd; // GPIO11 + io_mux_reg_t pad_sd_clk; // GPIO6 + io_mux_reg_t pad_sd_data0; // GPIO7 + io_mux_reg_t pad_sd_data1; // GPIO8 + io_mux_reg_t pad_gpio5; // GPIO5 + io_mux_reg_t pad_gpio18; // GPIO18 + io_mux_reg_t pad_gpio19; // GPIO19 + io_mux_reg_t pad_gpio20; // GPIO20 + io_mux_reg_t pad_gpio21; // GPIO21 + io_mux_reg_t pad_gpio22; // GPIO22 + io_mux_reg_t pad_u0rxd; // GPIO3 + io_mux_reg_t pad_u0txd; // GPIO1 + io_mux_reg_t pad_gpio23; // GPIO23 + io_mux_reg_t pad_gpio24; // GPIO24 +} io_mux_dev_t; + +static io_mux_dev_t* IO_MUX = (io_mux_dev_t*) 0x3ff49000; + +static const io_mux_reg_t* io_mux_translate[] = { + &IO_MUX->pad_gpio0, // 0 + &IO_MUX->pad_u0txd, // 1 + &IO_MUX->pad_gpio2, // 2 + &IO_MUX->pad_u0rxd, // 3 + &IO_MUX->pad_gpio4, // 4 + &IO_MUX->pad_gpio5, // 5 + &IO_MUX->pad_sd_clk, // 6 + &IO_MUX->pad_sd_data0, // 7 + &IO_MUX->pad_sd_data1, // 8 + &IO_MUX->pad_sd_data2, // 9 + &IO_MUX->pad_sd_data3, // 10 + &IO_MUX->pad_sd_cmd, // 11 + &IO_MUX->pad_mtdi, // 12 + &IO_MUX->pad_mtck, // 13 + &IO_MUX->pad_mtms, // 14 + &IO_MUX->pad_mtdo, // 15 + &IO_MUX->pad_gpio16, // 16 + &IO_MUX->pad_gpio17, // 17 + &IO_MUX->pad_gpio18, // 18 + &IO_MUX->pad_gpio19, // 19 + &IO_MUX->pad_gpio20, // 20 + &IO_MUX->pad_gpio21, // 21 + nullptr, // 28 + nullptr, // 29 + nullptr, // 30 + nullptr, // 31 + &IO_MUX->pad_gpio32, // 32 + &IO_MUX->pad_gpio33, // 33 + &IO_MUX->pad_gpio34, // 34 + &IO_MUX->pad_gpio35, // 35 + &IO_MUX->pad_gpio36, // 36 + &IO_MUX->pad_gpio37, // 37 + &IO_MUX->pad_gpio38, // 38 + &IO_MUX->pad_gpio39 // 39 +}; + +static const io_mux_reg_t* gpioToIoMux(int gpio) { + return io_mux_translate[gpio]; +} + System::System() { // TODO Auto-generated constructor stub @@ -21,12 +137,323 @@ System::~System() { // TODO Auto-generated destructor stub } +const static char* outSignalStrings[] = { + "SPICLK_out", // 0 + "SPIQ_out", // 1 + "SPID_out", // 2 + "SPIHD_out", //3 + "SPIWP_out", // 4 + "SPICS0_out", // 5 + "SPICS1_out", // 6 + "SPICS2_out", // 7 + "HSPICLK_out", // 8 + "HSPIQ_out", // 9 + "HSPID_out", // 10 + "HSPICS0_out", // 11 + "HSPIHD_out", // 12 + "HSPIWP_out", // 13 + "U0TXD_out", // 14 + "U0RTS_out", // 15 + "U0DTR_out", // 16 + "U1TXD_out", // 17 + "U1RTS_out", // 18 + "", // 19 + "", // 20 + "", // 21 + "", // 22 + "I2S0O_BCK_out", // 23 + "I2S1O_BCK_out", // 24 + "I2S0O_WS_out", // 25 + "I2S1O_WS_out", // 26 + "I2S0I_BCK_out", // 27 + "I2S0I_WS_out", // 28 + "I2CEXT0_SCL_out", // 29 + "I2CEXT0_SDA_out", // 30 + "sdio_tohost_int_out", // 31 + "pwm0_out0a", // 32 + "pwm0_out0b", // 33 + "pwm0_out1a", // 34 + "pwm0_out1b", // 35 + "pwm0_out2a", // 36 + "pwm0_out2b", //37 + "", // 38 + "", // 39 + "", // 40 + "", // 41 + "", // 42 + "", // 43 + "", // 44 + "", // 45 + "", // 46 + "", // 47 + "", // 48 + "", // 49 + "", // 50 + "", // 51 + "", // 52 + "", // 53 + "", // 54 + "", // 55 + "", // 56 + "", // 57 + "", // 58 + "", // 59 + "", // 60 + "HSPICS1_out", // 61 + "HSPICS2_out", // 62 + "VSPICLK_out_mux", // 63 + "VSPIQ_out", // 64 + "VSPID_out", // 65 + "VSPIHD_out", // 66 + "VSPIWP_out", // 67 + "VSPICS0_out", // 68 + "VSPICS1_out", // 69 + "VSPICS2_out", // 70 + "ledc_hs_sig_out0", // 71 + "ledc_hs_sig_out1", // 72 + "ledc_hs_sig_out2", // 73 + "ledc_hs_sig_out3", // 74 + "ledc_hs_sig_out4", // 75 + "ledc_hs_sig_out5", // 76 + "ledc_hs_sig_out6", // 77 + "ledc_hs_sig_out7", // 78 + "edc_ls_sig_out0", // 79 + "ledc_ls_sig_out1", // 80 + "ledc_ls_sig_out2", // 81 + "ledc_ls_sig_out3", // 82 + "ledc_ls_sig_out4", // 83 + "ledc_ls_sig_out5", // 84 + "ledc_ls_sig_out6", // 85 + "ledc_ls_sig_out7", // 86 + "rmt_sig_out0", // 87 + "rmt_sig_out1", // 88 + "rmt_sig_out2", // 89 + "rmt_sig_out3", // 90 + "rmt_sig_out4", // 91 + "rmt_sig_out5", // 92 + "rmt_sig_out6", // 93 + "rmt_sig_out7", // 94 + "I2CEXT1_SCL_out", // 95 + "I2CEXT1_SCL_out", // 96 + "host_ccmd_od_pullup_en_n", // 97 + "host_rst_n_1", // 98 + "host_rst_n_2", // 99 + "gpio_sd0_out", // 100 + "gpio_sd1_out", // 101 + "gpio_sd2_out", // 102 + "gpio_sd3_out", // 103 + "gpio_sd4_out", // 104 + "gpio_sd5_out", // 105 + "gpio_sd6_out", // 106 + "gpio_sd7_out", // 107 + "pwm1_out0a", // 108 + "pwm1_out0b", // 109 + "pwm1_out1a", // 110 + "pwm1_out1b", // 111 + "pwm1_out2a", // 112 + "pwm1_out2b", // 113 + "pwm2_out1h", // 114 + "pwm2_out1l", // 115 + "pwm2_out2h", // 116 + "pwm2_out2l", // 117 + "pwm2_out3h", // 118 + "pwm2_out3l", // 119 + "pwm2_out4h", // 120 + "pwm2_out4l", // 121 + "", // 122 + "", // 123 + "", // 124 + "", // 125 + "", // 126 + "", // 127 + "", // 128 + "", // 129 + "", // 130 + "", // 131 + "", // 132 + "", // 133 + "", // 134 + "", // 135 + "", // 136 + "", // 137 + "", // 138 + "", // 139 + "I2S0O_DATA_out0", // 140 + "I2S0O_DATA_out1", // 141 + "I2S0O_DATA_out2", // 142 + "I2S0O_DATA_out3", // 143 + "I2S0O_DATA_out4", // 144 + "I2S0O_DATA_out5", // 145 + "I2S0O_DATA_out6", // 146 + "I2S0O_DATA_out7", // 147 + "I2S0O_DATA_out8", // 148 + "I2S0O_DATA_out9", // 149 + "I2S0O_DATA_out10", // 150 + "I2S0O_DATA_out11", // 151 + "I2S0O_DATA_out12", // 152 + "I2S0O_DATA_out13", // 153 + "I2S0O_DATA_out14", // 154 + "I2S0O_DATA_out15", // 155 + "I2S0O_DATA_out16", // 156 + "I2S0O_DATA_out17", // 157 + "I2S0O_DATA_out18", // 158 + "I2S0O_DATA_out19", // 159 + "I2S0O_DATA_out20", // 160 + "I2S0O_DATA_out21", // 161 + "I2S0O_DATA_out22", // 162 + "I2S0O_DATA_out23", // 163 + "I2S1I_BCK_out", // 164 + "I2S1I_WS_out", // 165 + "I2S1O_DATA_out0", // 166 + "I2S1O_DATA_out1", // 167 + "I2S1O_DATA_out2", // 168 + "I2S1O_DATA_out3", // 169 + "I2S1O_DATA_out4", // 170 + "I2S1O_DATA_out5", // 171 + "I2S1O_DATA_out6", // 172 + "I2S1O_DATA_out7", // 173 + "I2S1O_DATA_out8", // 174 + "I2S1O_DATA_out9", // 175 + "I2S1O_DATA_out10", // 176 + "I2S1O_DATA_out11", // 177 + "I2S1O_DATA_out12", // 178 + "I2S1O_DATA_out13", // 179 + "I2S1O_DATA_out14", // 180 + "I2S1O_DATA_out15", // 181 + "I2S1O_DATA_out16", // 182 + "I2S1O_DATA_out17", // 183 + "I2S1O_DATA_out18", // 184 + "I2S1O_DATA_out19", // 185 + "I2S1O_DATA_out20", // 186 + "I2S1O_DATA_out21", // 187 + "I2S1O_DATA_out22", // 188 + "I2S1O_DATA_out23", // 189 + "pwm3_out1h", // 190 + "pwm3_out1l", // 191 + "pwm3_out2h", // 192 + "pwm3_out2l", // 193 + "pwm3_out3h", // 194 + "pwm3_out3l", // 195 + "pwm3_out4h", // 196 + "pwm3_out4l", // 197 + "U2TXD_out", // 198 + "U2RTS_out", // 199 + "emac_mdc_o", // 200 + "emac_mdo_o", // 201 + "emac_crs_o", // 202 + "emac_col_o", // 203 + "bt_audio0_irq", // 204 + "bt_audio1_irq", // 205 + "bt_audio2_irq", // 206 + "ble_audio0_irq", // 207 + "ble_audio1_irq", // 208 + "ble_audio2_irq", // 209 + "pcmfsync_out", // 210 + "pcmclk_out", // 211 + "pcmdout", // 212 + "ble_audio_sync0_p", // 213 + "ble_audio_sync1_p", // 214 + "ble_audio_sync2_p", // 215 + "", // 216 + "", // 217 + "", // 218 + "", // 219 + "", // 220 + "", // 221 + "", // 222 + "", // 223 + "sig_in_func224", // 224 + "sig_in_func225", // 225 + "sig_in_func226", // 226 + "sig_in_func227", // 227 + "sig_in_func228", // 228 + "", // 229 + "", // 230 + "", // 231 + "", // 232 + "", // 233 + "", // 234 + "", // 235 + "", // 236 + "", // 237 + "", // 238 + "", // 239 + "", // 240 + "", // 241 + "", // 242 + "", // 243 + "", // 244 + "", // 245 + "", // 246 + "", // 247 + "", // 248 + "", // 249 + "", // 250 + "", // 251 + "", // 252 + "", // 253 + "", // 254 + "", // 255 +}; + + +/** + * Dump the mappings for GPIO pins. + */ +/* static */ void System::dumpPinMapping() { + const int numPins = 40; + printf("GPIO_FUNCn_OUT_SEL_CFG_REG\n"); + printf("--------------------------\n"); + printf("%3s %4s\n", "Pin", "Func"); + for (uint8_t i = 0; i < numPins; i++) { + const char *signal; + if (GPIO.func_out_sel_cfg[i].func_sel == 256) { + signal = (char*) "[GPIO]"; + } else if (GPIO.func_out_sel_cfg[i].func_sel == 257) { + signal = (char*) "N/A"; + } else { + signal = outSignalStrings[GPIO.func_out_sel_cfg[i].func_sel]; + } + printf("%2d %4d %s\n", i, GPIO.func_out_sel_cfg[i].func_sel, signal); + const io_mux_reg_t* io_mux = gpioToIoMux(i); + if (GPIO.func_out_sel_cfg[i].func_sel == 256 && io_mux != nullptr) { + printf("0x%x - function: %d, ie: %d\n", (uint32_t) io_mux, io_mux->mcu_sel + 1, io_mux->func_ie); + } + } + +} // System#dumpPinMapping + + +/** + * Dump the storage stats for the heap. + */ +/* static */ void System::dumpHeapInfo() { + multi_heap_info_t heapInfo; + + printf(" %10s %10s %10s %10s %13s %11s %12s\n", "Free", "Allocated", "Largest", "Minimum", "Alloc Blocks", "Free Blocks", "Total Blocks"); + heap_caps_get_info(&heapInfo, MALLOC_CAP_EXEC); + printf("EXEC %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_32BIT); + printf("32BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_8BIT); + printf("8BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DMA); + printf("DMA %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_SPIRAM); + printf("SPISRAM %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_INTERNAL); + printf("INTERNAL %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DEFAULT); + printf("DEFAULT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); +} // System#dumpHeapInfo + + /** * @brief Get the information about the device. * @param [out] info The structure to be populated on return. * @return N/A. */ -void System::getChipInfo(esp_chip_info_t *info) { +void System::getChipInfo(esp_chip_info_t* info) { ::esp_chip_info(info); } // getChipInfo @@ -58,6 +485,7 @@ size_t System::getMinimumFreeHeapSize() { return heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT); } // getMinimumFreeHeapSize + /** * @brief Restart the ESP32. */ diff --git a/cpp_utils/System.h b/cpp_utils/System.h index 6459509d..a3bed334 100644 --- a/cpp_utils/System.h +++ b/cpp_utils/System.h @@ -18,7 +18,9 @@ class System { public: System(); virtual ~System(); - static void getChipInfo(esp_chip_info_t *info); + static void dumpPinMapping(); // Dump the mappings of pins to functions. + static void dumpHeapInfo(); + static void getChipInfo(esp_chip_info_t* info); static size_t getFreeHeapSize(); static std::string getIDFVersion(); static size_t getMinimumFreeHeapSize(); diff --git a/cpp_utils/TFTP.cpp b/cpp_utils/TFTP.cpp index 4e395591..b1591a9f 100644 --- a/cpp_utils/TFTP.cpp +++ b/cpp_utils/TFTP.cpp @@ -9,13 +9,13 @@ #include "TFTP.h" #include -#include -#include +#include "FreeRTOS.h" +#include "GeneralUtils.h" #include #include #include #include -#include +#include "Socket.h" #include "sdkconfig.h" @@ -26,7 +26,7 @@ extern "C" { extern uint32_t lwip_htonl(uint32_t); } -static char tag[] = "TFTP"; +static const char* LOG_TAG = "TFTP"; enum opcode { TFTP_OPCODE_RRQ = 1, // Read request @@ -50,7 +50,7 @@ enum ERRORCODE { /** * Size of the TFTP data payload. */ -const int TFTP_DATA_SIZE=512; +const int TFTP_DATA_SIZE = 512; struct data_packet { uint16_t blockNumber; @@ -91,15 +91,15 @@ void TFTP::TFTP_Transaction::processRRQ() { * ---------------------------------- * */ - FILE *file; + FILE* file; bool finished = false; - ESP_LOGD(tag, "Reading TFTP data from file: %s", m_filename.c_str()); + ESP_LOGD(LOG_TAG, "Reading TFTP data from file: %s", m_filename.c_str()); std::string tmpName = m_baseDir + "/" + m_filename; /* struct stat buf; if (stat(tmpName.c_str(), &buf) != 0) { - ESP_LOGE(tag, "Stat file: %s: %s", tmpName.c_str(), strerror(errno)); + ESP_LOGE(LOG_TAG, "Stat file: %s: %s", tmpName.c_str(), strerror(errno)); return; } int length = buf.st_size; @@ -109,23 +109,28 @@ void TFTP::TFTP_Transaction::processRRQ() { file = fopen(tmpName.c_str(), "r"); if (file == nullptr) { - ESP_LOGE(tag, "Failed to open file for reading: %s: %s", tmpName.c_str(), strerror(errno)); + ESP_LOGE(LOG_TAG, "Failed to open file for reading: %s: %s", tmpName.c_str(), strerror(errno)); sendError(ERROR_CODE_FILE_NOT_FOUND, tmpName); return; } - uint8_t buf[TFTP_DATA_SIZE + 2 + 2]; // Buffer data size is packet size (512) + 2 bytes for opcode + 2 bytes for blocknumber. + struct { + uint16_t opCode; + uint16_t blockNumber; + uint8_t buf[TFTP_DATA_SIZE]; + } record; - *(uint16_t *)(&buf[0]) = htons(TFTP_OPCODE_DATA); // Set the op code to be DATA. - while(!finished) { - *(uint16_t *)(&buf[2]) = htons(blockNumber); + record.opCode = htons(TFTP_OPCODE_DATA); // Set the op code to be DATA. + + while (!finished) { + record.blockNumber = htons(blockNumber); - int sizeRead = fread(&buf[4], 1, TFTP_DATA_SIZE, file); + int sizeRead = fread(record.buf, 1, TFTP_DATA_SIZE, file); - ESP_LOGD(tag, "Sending data to %s, blockNumber=%d, size=%d", + ESP_LOGD(LOG_TAG, "Sending data to %s, blockNumber=%d, size=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber, sizeRead); - m_partnerSocket.sendTo(buf, sizeRead+4, &m_partnerAddress); + m_partnerSocket.sendTo((uint8_t*) &record, sizeRead + 4, &m_partnerAddress); if (sizeRead < TFTP_DATA_SIZE) { @@ -135,7 +140,7 @@ void TFTP::TFTP_Transaction::processRRQ() { } blockNumber++; // Increment the block number. } - ESP_LOGD(tag, "File sent"); + ESP_LOGD(LOG_TAG, "File sent"); } // processRRQ @@ -161,27 +166,27 @@ void TFTP::TFTP_Transaction::processWRQ() { uint8_t dataBuffer[TFTP_DATA_SIZE + 2 + 2]; bool finished = false; - FILE *file; + FILE* file; - ESP_LOGD(tag, "Writing TFTP data to file: %s", m_filename.c_str()); + ESP_LOGD(LOG_TAG, "Writing TFTP data to file: %s", m_filename.c_str()); std::string tmpName = m_baseDir + "/" + m_filename; file = fopen(tmpName.c_str(), "w"); if (file == nullptr) { - ESP_LOGE(tag, "Failed to open file for writing: %s: %s", tmpName.c_str(), strerror(errno)); + ESP_LOGE(LOG_TAG, "Failed to open file for writing: %s: %s", tmpName.c_str(), strerror(errno)); return; } while(!finished) { - pRecv_data = (struct recv_data *)dataBuffer; + pRecv_data = (struct recv_data*) dataBuffer; int receivedSize = m_partnerSocket.receiveFrom(dataBuffer, sizeof(dataBuffer), &recvAddr); if (receivedSize == -1) { - ESP_LOGE(tag, "rc == -1 from receive_from"); + ESP_LOGE(LOG_TAG, "rc == -1 from receive_from"); } struct data_packet dp; dp.blockNumber = ntohs(pRecv_data->blockNumber); - dp.data = std::string((char *)&pRecv_data->data, receivedSize-4); + dp.data = std::string((char*) &pRecv_data->data, receivedSize - 4); fwrite(dp.data.data(), dp.data.length(), 1, file); sendAck(dp.blockNumber); - ESP_LOGD(tag, "Block size: %d", dp.data.length()); + ESP_LOGD(LOG_TAG, "Block size: %d", dp.data.length()); if (dp.data.length() < TFTP_DATA_SIZE) { finished = true; } @@ -207,8 +212,8 @@ void TFTP::TFTP_Transaction::sendAck(uint16_t blockNumber) { ackData.opCode = htons(TFTP_OPCODE_ACK); ackData.blockNumber = htons(blockNumber); - ESP_LOGD(tag, "Sending ack to %s, blockNumber=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber); - m_partnerSocket.sendTo((uint8_t *)&ackData, sizeof(ackData), &m_partnerAddress); + ESP_LOGD(LOG_TAG, "Sending ack to %s, blockNumber=%d", Socket::addressToString(&m_partnerAddress).c_str(), blockNumber); + m_partnerSocket.sendTo((uint8_t*) &ackData, sizeof(ackData), &m_partnerAddress); } // sendAck @@ -227,26 +232,29 @@ void TFTP::start(uint16_t port) { * or write a file to the server. Once we have received a request we then call the appropriate * handler to handle that type of request. When the request has been completed, we start again. */ - ESP_LOGD(tag, "Starting TFTP::start() on port %d", port); + ESP_LOGD(LOG_TAG, "Starting TFTP::start() on port %d", port); Socket serverSocket; serverSocket.listen(port, true); // Create a listening socket that is a datagram. - while(true) { + while (true) { // This would be a good place to start a transaction in the background. - TFTP_Transaction *pTFTPTransaction = new TFTP_Transaction(); + TFTP_Transaction* pTFTPTransaction = new TFTP_Transaction(); pTFTPTransaction->setBaseDir(m_baseDir); uint16_t receivedOpCode = pTFTPTransaction->waitForRequest(&serverSocket); - switch(receivedOpCode) { - // Handle the write request (client file upload) + switch (receivedOpCode) { + // Handle the write request (client file upload) case opcode::TFTP_OPCODE_WRQ: { pTFTPTransaction->processWRQ(); break; } - // Handle the read request (server file download) + // Handle the read request (server file download) case opcode::TFTP_OPCODE_RRQ: { pTFTPTransaction->processRRQ(); break; } + default: + ESP_LOGE(LOG_TAG, "Unknown opcode: %d", receivedOpCode); + break; } delete pTFTPTransaction; } // End while loop @@ -286,12 +294,12 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { uint16_t blockNumber; } ackData; - ESP_LOGD(tag, "TFTP: Waiting for an acknowledgment request"); - int sizeRead = m_partnerSocket.receiveFrom((uint8_t *)&ackData, sizeof(ackData), &m_partnerAddress); - ESP_LOGD(tag, "TFTP: Received some data."); + ESP_LOGD(LOG_TAG, "TFTP: Waiting for an acknowledgment request"); + int sizeRead = m_partnerSocket.receiveFrom((uint8_t*) &ackData, sizeof(ackData), &m_partnerAddress); + ESP_LOGD(LOG_TAG, "TFTP: Received some data."); if (sizeRead != sizeof(ackData)) { - ESP_LOGE(tag, "waitForAck: Received %d but expected %d", sizeRead, sizeof(ackData)); + ESP_LOGE(LOG_TAG, "waitForAck: Received %d but expected %d", sizeRead, sizeof(ackData)); sendError(ERROR_CODE_NOTDEFINED, "Ack not correct size"); return; } @@ -300,12 +308,12 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { ackData.blockNumber = ntohs(ackData.blockNumber); if (ackData.opCode != opcode::TFTP_OPCODE_ACK) { - ESP_LOGE(tag, "waitForAck: Received opcode %d but expected %d", ackData.opCode, opcode::TFTP_OPCODE_ACK); + ESP_LOGE(LOG_TAG, "waitForAck: Received opcode %d but expected %d", ackData.opCode, opcode::TFTP_OPCODE_ACK); return; } if (ackData.blockNumber != blockNumber) { - ESP_LOGE(tag, "waitForAck: Blocknumber received %d but expected %d", ackData.blockNumber, blockNumber); + ESP_LOGE(LOG_TAG, "waitForAck: Blocknumber received %d but expected %d", ackData.blockNumber, blockNumber); return; } } // waitForAck @@ -320,26 +328,28 @@ void TFTP::TFTP_Transaction::waitForAck(uint16_t blockNumber) { * @param pServerSocket The server socket on which to listen for client requests. * @return The op code received. */ -uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { /* * 2 bytes string 1 byte string 1 byte * ----------------------------------------------- * RRQ/ | 01/02 | Filename | 0 | Mode | 0 | * WRQ ----------------------------------------------- */ - uint8_t buf[TFTP_DATA_SIZE]; +uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket* pServerSocket) { + union { + uint8_t buf[TFTP_DATA_SIZE]; + uint16_t opCode; + } record; size_t length = 100; - ESP_LOGD(tag, "TFTP: Waiting for a request"); - pServerSocket->receiveFrom(buf, length, &m_partnerAddress); + ESP_LOGD(LOG_TAG, "TFTP: Waiting for a request"); + pServerSocket->receiveFrom(record.buf, length, &m_partnerAddress); // Save the filename, mode and op code. - m_filename = std::string((char *)(buf+2)); - m_mode = std::string((char *)(buf + 3 + m_filename.length())); - m_opCode = ntohs(*(uint16_t *)buf); - switch(m_opCode) { - + m_filename = std::string((char*) (record.buf + 2)); + m_mode = std::string((char*) (record.buf + 3 + m_filename.length())); + m_opCode = ntohs(record.opCode); + switch (m_opCode) { // Handle the Write Request command. case TFTP_OPCODE_WRQ: { m_partnerSocket.createSocket(true); @@ -348,7 +358,6 @@ uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { break; } - // Handle the Read request command. case TFTP_OPCODE_RRQ: { m_partnerSocket.createSocket(true); @@ -357,7 +366,7 @@ uint16_t TFTP::TFTP_Transaction::waitForRequest(Socket *pServerSocket) { } default: { - ESP_LOGD(tag, "Un-handled opcode: %d", m_opCode); + ESP_LOGD(LOG_TAG, "Un-handled opcode: %d", m_opCode); break; } } @@ -378,10 +387,10 @@ void TFTP::TFTP_Transaction::sendError(uint16_t code, std::string message) { * ----------------------------------------- */ int size = 2 + 2 + message.length() + 1; - uint8_t *buf = (uint8_t *)malloc(size); - *(uint16_t *)(&buf[0]) = htons(opcode::TFTP_OPCODE_ERROR); - *(uint16_t *)(&buf[2]) = htons(code); - strcpy((char *)(&buf[4]), message.c_str()); + uint8_t* buf = (uint8_t*) malloc(size); + *(uint16_t*) (&buf[0]) = htons(opcode::TFTP_OPCODE_ERROR); + *(uint16_t*) (&buf[2]) = htons(code); + strcpy((char*) (&buf[4]), message.c_str()); m_partnerSocket.sendTo(buf, size, &m_partnerAddress); free(buf); } // sendError diff --git a/cpp_utils/TFTP.h b/cpp_utils/TFTP.h index 3f40bd2b..56733598 100644 --- a/cpp_utils/TFTP.h +++ b/cpp_utils/TFTP.h @@ -36,7 +36,7 @@ class TFTP { public: TFTP(); virtual ~TFTP(); - void start(uint16_t port=TFTP_DEFAULT_PORT); + void start(uint16_t port = TFTP_DEFAULT_PORT); void setBaseDir(std::string baseDir); /** * @brief Internal class for %TFTP processing. @@ -50,7 +50,8 @@ class TFTP { void sendError(uint16_t code, std::string message); void setBaseDir(std::string baseDir); void waitForAck(uint16_t blockNumber); - uint16_t waitForRequest(Socket *pServerSocket); + uint16_t waitForRequest(Socket* pServerSocket); + private: /** * Socket on which the server will communicate with the client.. @@ -61,9 +62,12 @@ class TFTP { std::string m_filename; // The name of the file. std::string m_mode; std::string m_baseDir; // The base directory. + }; + private: std::string m_baseDir; + }; #endif /* COMPONENTS_CPP_UTILS_TFTP_H_ */ diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index 4520478a..43f587e2 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -14,7 +14,7 @@ #include "Task.h" #include "sdkconfig.h" -static char tag[] = "Task"; +static const char* LOG_TAG = "Task"; /** @@ -30,6 +30,7 @@ Task::Task(std::string taskName, uint16_t stackSize, uint8_t priority) { m_priority = priority; m_taskData = nullptr; m_handle = nullptr; + m_coreId = tskNO_AFFINITY; } // Task Task::~Task() { @@ -42,8 +43,8 @@ Task::~Task() { * @return N/A. */ -void Task::delay(int ms) { - ::vTaskDelay(ms/portTICK_PERIOD_MS); +/* static */ void Task::delay(int ms) { + ::vTaskDelay(ms / portTICK_PERIOD_MS); } // delay /** @@ -53,10 +54,10 @@ void Task::delay(int ms) { * @param [in] pTaskInstance The task to run. */ void Task::runTask(void* pTaskInstance) { - Task* pTask = (Task*)pTaskInstance; - ESP_LOGD(tag, ">> runTask: taskName=%s", pTask->m_taskName.c_str()); + Task* pTask = (Task*) pTaskInstance; + ESP_LOGD(LOG_TAG, ">> runTask: taskName=%s", pTask->m_taskName.c_str()); pTask->run(pTask->m_taskData); - ESP_LOGD(tag, "<< runTask: taskName=%s", pTask->m_taskName.c_str()); + ESP_LOGD(LOG_TAG, "<< runTask: taskName=%s", pTask->m_taskName.c_str()); pTask->stop(); } // runTask @@ -68,10 +69,10 @@ void Task::runTask(void* pTaskInstance) { */ void Task::start(void* taskData) { if (m_handle != nullptr) { - ESP_LOGW(tag, "Task::start - There might be a task already running!"); + ESP_LOGW(LOG_TAG, "Task::start - There might be a task already running!"); } m_taskData = taskData; - ::xTaskCreate(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle); + ::xTaskCreatePinnedToCore(&runTask, m_taskName.c_str(), m_stackSize, this, m_priority, &m_handle, m_coreId); } // start @@ -81,9 +82,7 @@ void Task::start(void* taskData) { * @return N/A. */ void Task::stop() { - if (m_handle == nullptr) { - return; - } + if (m_handle == nullptr) return; xTaskHandle temp = m_handle; m_handle = nullptr; ::vTaskDelete(temp); @@ -118,3 +117,14 @@ void Task::setPriority(uint8_t priority) { void Task::setName(std::string name) { m_taskName = name; } // setName + +/** + * @brief Set the core number the task has to be executed on. + * If the core number is not set, tskNO_AFFINITY will be used + * + * @param [in] coreId The id of the core. + * @return N/A. + */ +void Task::setCore(BaseType_t coreId) { + m_coreId = coreId; +} diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 74d7f332..5eb4966b 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -33,12 +33,13 @@ */ class Task { public: - Task(std::string taskName="Task", uint16_t stackSize=10000, uint8_t priority=5); + Task(std::string taskName = "Task", uint16_t stackSize = 10000, uint8_t priority = 5); virtual ~Task(); void setStackSize(uint16_t stackSize); void setPriority(uint8_t priority); void setName(std::string name); - void start(void* taskData=nullptr); + void setCore(BaseType_t coreId); + void start(void* taskData = nullptr); void stop(); /** * @brief Body of the task to execute. @@ -49,16 +50,18 @@ class Task { * * @param [in] data The data passed in to the newly started task. */ - virtual void run(void *data) = 0; // Make run pure virtual - void delay(int ms); + virtual void run(void* data) = 0; // Make run pure virtual + static void delay(int ms); private: xTaskHandle m_handle; void* m_taskData; - static void runTask(void *data); + static void runTask(void* data); std::string m_taskName; uint16_t m_stackSize; uint8_t m_priority; + BaseType_t m_coreId; + }; #endif /* COMPONENTS_CPP_UTILS_TASK_H_ */ diff --git a/cpp_utils/U8G2.h b/cpp_utils/U8G2.h index db57ba64..386272fc 100644 --- a/cpp_utils/U8G2.h +++ b/cpp_utils/U8G2.h @@ -24,7 +24,7 @@ class U8G2 { u8g2_ClearBuffer(&m_u8g2); } - void drawBitmap(uint32_t x, uint32_t y, uint32_t cnt, uint32_t h, const uint8_t *bitmap) { + void drawBitmap(uint32_t x, uint32_t y, uint32_t cnt, uint32_t h, const uint8_t* bitmap) { u8g2_DrawBitmap(&m_u8g2, x, y, cnt, h, bitmap); } @@ -75,7 +75,6 @@ class U8G2 { u8g2_DrawRFrame(&m_u8g2, x, y, w, h, r); } - uint32_t drawStr(uint32_t x, uint32_t y, std::string s) { return u8g2_DrawStr(&m_u8g2, x, y, s.c_str()); } @@ -92,11 +91,11 @@ class U8G2 { u8g2_DrawVLine(&m_u8g2, x, y, h); } - int8_t getAscent(void) { + int8_t getAscent() { return u8g2_GetAscent(&m_u8g2); } - int8_t getDescent(void) { + int8_t getDescent() { return u8g2_GetDescent(&m_u8g2); } @@ -107,19 +106,23 @@ class U8G2 { void initDisplay() { u8g2_InitDisplay(&m_u8g2); } + void sendBuffer() { u8g2_SendBuffer(&m_u8g2); } - void setFont(const uint8_t *font) { + void setFont(const uint8_t* font) { u8g2_SetFont(&m_u8g2, font); } + void setPowerSave(uint8_t is_enable) { u8g2_SetPowerSave(&m_u8g2, is_enable); // wake up display } private: u8g2_t m_u8g2; + }; + #endif // CONFIG_U8G2_PRESENT #endif /* COMPONENTS_CPP_UTILS_U8G2_H_ */ diff --git a/cpp_utils/WS2812.cpp b/cpp_utils/WS2812.cpp index 624480f0..af4bc512 100644 --- a/cpp_utils/WS2812.cpp +++ b/cpp_utils/WS2812.cpp @@ -33,7 +33,7 @@ static const char* LOG_TAG = "WS2812"; * a logic 1 for 0.7us * a logic 0 for 0.6us */ -static void setItem1(rmt_item32_t *pItem) { +static void setItem1(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 1; pItem->duration0 = 10; @@ -49,7 +49,7 @@ static void setItem1(rmt_item32_t *pItem) { * a logic 1 for 0.35us * a logic 0 for 0.8us */ -static void setItem0(rmt_item32_t *pItem) { +static void setItem0(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 1; pItem->duration0 = 4; @@ -61,7 +61,7 @@ static void setItem0(rmt_item32_t *pItem) { /** * Add an RMT terminator into the RMT data. */ -static void setTerminator(rmt_item32_t *pItem) { +static void setTerminator(rmt_item32_t* pItem) { assert(pItem != nullptr); pItem->level0 = 0; pItem->duration0 = 0; @@ -74,7 +74,7 @@ static void setTerminator(rmt_item32_t *pItem) { * type which should be one of 'R', 'G' or 'B'. */ static uint8_t getChannelValueByType(char type, pixel_t pixel) { - switch(type) { + switch (type) { case 'r': case 'R': return pixel.red; @@ -84,13 +84,13 @@ static uint8_t getChannelValueByType(char type, pixel_t pixel) { case 'g': case 'G': return pixel.green; + default: + ESP_LOGW(LOG_TAG, "Unknown color channel 0x%2x", type); + return 0; } - ESP_LOGW(LOG_TAG, "Unknown color channel 0x%2x", type); - return 0; } // getChannelValueByType - /** * @brief Construct a wrapper for the pixels. * @@ -113,7 +113,7 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { assert(ESP32CPP::GPIO::inRange(dinPin)); this->pixelCount = pixelCount; - this->channel = (rmt_channel_t)channel; + this->channel = (rmt_channel_t) channel; // The number of items is number of pixels * 24 bits per pixel + the terminator. // Remember that an item is TWO RMT output bits ... for NeoPixels this is correct because @@ -121,24 +121,23 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { this->items = new rmt_item32_t[pixelCount * 24 + 1]; this->pixels = new pixel_t[pixelCount]; - this->colorOrder = (char *)"GRB"; + this->colorOrder = (char*) "GRB"; clear(); rmt_config_t config; config.rmt_mode = RMT_MODE_TX; config.channel = this->channel; config.gpio_num = dinPin; - config.mem_block_num = 8-this->channel; + config.mem_block_num = 8 - this->channel; config.clk_div = 8; config.tx_config.loop_en = 0; config.tx_config.carrier_en = 0; config.tx_config.idle_output_en = 1; - config.tx_config.idle_level = (rmt_idle_level_t)0; + config.tx_config.idle_level = (rmt_idle_level_t) 0; config.tx_config.carrier_freq_hz = 10000; config.tx_config.carrier_level = (rmt_carrier_level_t)1; config.tx_config.carrier_duty_percent = 50; - ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(this->channel, 0, 0)); } // WS2812 @@ -152,19 +151,19 @@ WS2812::WS2812(gpio_num_t dinPin, uint16_t pixelCount, int channel) { void WS2812::show() { auto pCurrentItem = this->items; - for (auto i=0; ipixelCount; i++) { + for (uint16_t i = 0; i < this->pixelCount; i++) { uint32_t currentPixel = (getChannelValueByType(this->colorOrder[0], this->pixels[i]) << 16) | (getChannelValueByType(this->colorOrder[1], this->pixels[i]) << 8) | (getChannelValueByType(this->colorOrder[2], this->pixels[i])); ESP_LOGD(LOG_TAG, "Pixel value: %x", currentPixel); - for (int j=23; j>=0; j--) { + for (int8_t j = 23; j >= 0; j--) { // We have 24 bits of data representing the red, green amd blue channels. The value of the // 24 bits to output is in the variable current_pixel. We now need to stream this value // through RMT in most significant bit first. To do this, we iterate through each of the 24 // bits from MSB to LSB. - if (currentPixel & (1<channel, this->items, this->pixelCount*24, 1 /* wait till done */)); + ESP_ERROR_CHECK(rmt_write_items(this->channel, this->items, this->pixelCount * 24, 1 /* wait till done */)); } // show @@ -191,7 +190,7 @@ void WS2812::show() { * an alternate order by supply an alternate three character string made up of 'R', 'G' and 'B' * for example "RGB". */ -void WS2812::setColorOrder(char *colorOrder) { +void WS2812::setColorOrder(char* colorOrder) { if (colorOrder != nullptr && strlen(colorOrder) == 3) { this->colorOrder = colorOrder; } @@ -208,14 +207,14 @@ void WS2812::setColorOrder(char *colorOrder) { * @param [in] green The amount of green in the pixel. * @param [in] blue The amount of blue in the pixel. */ -void WS2812::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { +void WS2812::setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue) { assert(index < pixelCount); - this->pixels[index].red = red; this->pixels[index].green = green; this->pixels[index].blue = blue; } // setPixel + /** * @brief Set the given pixel to the specified color. * @@ -240,7 +239,6 @@ void WS2812::setPixel(uint16_t index, pixel_t pixel) { */ void WS2812::setPixel(uint16_t index, uint32_t pixel) { assert(index < pixelCount); - this->pixels[index].red = pixel & 0xff; this->pixels[index].green = (pixel & 0xff00) >> 8; this->pixels[index].blue = (pixel & 0xff0000) >> 16; @@ -256,64 +254,65 @@ void WS2812::setPixel(uint16_t index, uint32_t pixel) { * @param [in] saturation The amount of saturation in the pixel (0-255). * @param [in] brightness The amount of brightness in the pixel (0-255). */ -void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { - double sat_red; - double sat_green; - double sat_blue; - double ctmp_red; - double ctmp_green; - double ctmp_blue; - double new_red; - double new_green; - double new_blue; - double dSaturation=(double)saturation/255; - double dBrightness=(double)brightness/255; - - assert(index < pixelCount); - - if (hue < 120) { - sat_red = (120 - hue) / 60.0; - sat_green = hue / 60.0; - sat_blue = 0; - } else if (hue < 240) { - sat_red = 0; - sat_green = (240 - hue) / 60.0; - sat_blue = (hue - 120) / 60.0; - } else { - sat_red = (hue - 240) / 60.0; - sat_green = 0; - sat_blue = (360 - hue) / 60.0; - } - - if (sat_red>1.0) { - sat_red=1.0; - } - if (sat_green>1.0) { - sat_green=1.0; - } - if (sat_blue>1.0) { - sat_blue=1.0; - } - - ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); - ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); - ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); - - if (dBrightness < 0.5) { - new_red = dBrightness * ctmp_red; - new_green = dBrightness * ctmp_green; - new_blue = dBrightness * ctmp_blue; - } else { - new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; - new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; - new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; - } - - this->pixels[index].red = (uint8_t)(new_red*255); - this->pixels[index].green = (uint8_t)(new_green*255); - this->pixels[index].blue = (uint8_t)(new_blue*255); +void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness) { + double sat_red; + double sat_green; + double sat_blue; + double ctmp_red; + double ctmp_green; + double ctmp_blue; + double new_red; + double new_green; + double new_blue; + double dSaturation = (double) saturation / 255; + double dBrightness = (double) brightness / 255; + + assert(index < pixelCount); + + if (hue < 120) { + sat_red = (120 - hue) / 60.0; + sat_green = hue / 60.0; + sat_blue = 0; + } else if (hue < 240) { + sat_red = 0; + sat_green = (240 - hue) / 60.0; + sat_blue = (hue - 120) / 60.0; + } else { + sat_red = (hue - 240) / 60.0; + sat_green = 0; + sat_blue = (360 - hue) / 60.0; + } + + if (sat_red > 1.0) { + sat_red = 1.0; + } + if (sat_green > 1.0) { + sat_green = 1.0; + } + if (sat_blue > 1.0) { + sat_blue = 1.0; + } + + ctmp_red = 2 * dSaturation * sat_red + (1 - dSaturation); + ctmp_green = 2 * dSaturation * sat_green + (1 - dSaturation); + ctmp_blue = 2 * dSaturation * sat_blue + (1 - dSaturation); + + if (dBrightness < 0.5) { + new_red = dBrightness * ctmp_red; + new_green = dBrightness * ctmp_green; + new_blue = dBrightness * ctmp_blue; + } else { + new_red = (1 - dBrightness) * ctmp_red + 2 * dBrightness - 1; + new_green = (1 - dBrightness) * ctmp_green + 2 * dBrightness - 1; + new_blue = (1 - dBrightness) * ctmp_blue + 2 * dBrightness - 1; + } + + this->pixels[index].red = (uint8_t)(new_red * 255); + this->pixels[index].green = (uint8_t)(new_green * 255); + this->pixels[index].blue = (uint8_t)(new_blue * 255); } // setHSBPixel + /** * @brief Clear all the pixel colors. * @@ -321,13 +320,14 @@ void WS2812::setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8 * The LEDs are not actually updated until a call to show(). */ void WS2812::clear() { - for (auto i=0; ipixelCount; i++) { + for (uint16_t i = 0; i < this->pixelCount; i++) { this->pixels[i].red = 0; this->pixels[i].green = 0; this->pixels[i].blue = 0; } } // clear + /** * @brief Class instance destructor. */ diff --git a/cpp_utils/WS2812.h b/cpp_utils/WS2812.h index f599c1b7..f11cbc89 100644 --- a/cpp_utils/WS2812.h +++ b/cpp_utils/WS2812.h @@ -48,21 +48,23 @@ typedef struct { */ class WS2812 { public: - WS2812(gpio_num_t gpioNum, uint16_t pixelCount, int channel=RMT_CHANNEL_0); + WS2812(gpio_num_t gpioNum, uint16_t pixelCount, int channel = RMT_CHANNEL_0); void show(); - void setColorOrder(char *order); + void setColorOrder(char* order); void setPixel(uint16_t index, uint8_t red, uint8_t green, uint8_t blue); void setPixel(uint16_t index, pixel_t pixel); void setPixel(uint16_t index, uint32_t pixel); void setHSBPixel(uint16_t index, uint16_t hue, uint8_t saturation, uint8_t brightness); void clear(); virtual ~WS2812(); + private: - char *colorOrder; + char* colorOrder; uint16_t pixelCount; rmt_channel_t channel; - rmt_item32_t *items; - pixel_t *pixels; + rmt_item32_t* items; + pixel_t* pixels; + }; #endif /* MAIN_WS2812_H_ */ diff --git a/cpp_utils/WebServer.cpp b/cpp_utils/WebServer.cpp index c250a4d5..0fc666af 100644 --- a/cpp_utils/WebServer.cpp +++ b/cpp_utils/WebServer.cpp @@ -18,107 +18,113 @@ #include #include -static char tag[] = "WebServer"; +#define STATE_NAME 0 +#define STATE_VALUE 1 + +static const char* LOG_TAG = "WebServer"; struct WebServerUserData { - WebServer *pWebServer; - WebServer::HTTPMultiPart *pMultiPart; - WebServer::WebSocketHandler *pWebSocketHandler; - void *originalUserData; + WebServer* pWebServer; + WebServer::HTTPMultiPart* pMultiPart; + WebServer::WebSocketHandler* pWebSocketHandler; + void* originalUserData; }; + /** * @brief Convert a Mongoose event type to a string. * @param [in] event The received event type. * @return The string representation of the event. */ static std::string mongoose_eventToString(int event) { - switch (event) { - case MG_EV_CONNECT: - return "MG_EV_CONNECT"; - case MG_EV_ACCEPT: - return "MG_EV_ACCEPT"; - case MG_EV_CLOSE: - return "MG_EV_CLOSE"; - case MG_EV_SEND: - return "MG_EV_SEND"; - case MG_EV_RECV: - return "MG_EV_RECV"; - case MG_EV_POLL: - return "MG_EV_POLL"; - case MG_EV_TIMER: - return "MG_EV_TIMER"; - case MG_EV_HTTP_PART_DATA: - return "MG_EV_HTTP_PART_DATA"; - case MG_EV_HTTP_MULTIPART_REQUEST: - return "MG_EV_HTTP_MULTIPART_REQUEST"; - case MG_EV_HTTP_PART_BEGIN: - return "MG_EV_HTTP_PART_BEGIN"; - case MG_EV_HTTP_PART_END: - return "MG_EV_HTTP_PART_END"; - case MG_EV_HTTP_MULTIPART_REQUEST_END: - return "MG_EV_HTTP_MULTIPART_REQUEST_END"; - case MG_EV_HTTP_REQUEST: - return "MG_EV_HTTP_REQUEST"; - case MG_EV_HTTP_REPLY: - return "MG_EV_HTTP_REPLY"; - case MG_EV_HTTP_CHUNK: - return "MG_EV_HTTP_CHUNK"; - case MG_EV_MQTT_CONNACK: - return "MG_EV_MQTT_CONNACK"; - case MG_EV_MQTT_CONNECT: - return "MG_EV_MQTT_CONNECT"; - case MG_EV_MQTT_DISCONNECT: - return "MG_EV_MQTT_DISCONNECT"; - case MG_EV_MQTT_PINGREQ: - return "MG_EV_MQTT_PINGREQ"; - case MG_EV_MQTT_PINGRESP: - return "MG_EV_MQTT_PINGRESP"; - case MG_EV_MQTT_PUBACK: - return "MG_EV_MQTT_PUBACK"; - case MG_EV_MQTT_PUBCOMP: - return "MG_EV_MQTT_PUBCOMP"; - case MG_EV_MQTT_PUBLISH: - return "MG_EV_MQTT_PUBLISH"; - case MG_EV_MQTT_PUBREC: - return "MG_EV_MQTT_PUBREC"; - case MG_EV_MQTT_PUBREL: - return "MG_EV_MQTT_PUBREL"; - case MG_EV_MQTT_SUBACK: - return "MG_EV_MQTT_SUBACK"; - case MG_EV_MQTT_SUBSCRIBE: - return "MG_EV_MQTT_SUBSCRIBE"; - case MG_EV_MQTT_UNSUBACK: - return "MG_EV_MQTT_UNSUBACK"; - case MG_EV_MQTT_UNSUBSCRIBE: - return "MG_EV_MQTT_UNSUBSCRIBE"; - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: - return "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"; - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: - return "MG_EV_WEBSOCKET_HANDSHAKE_DONE"; - case MG_EV_WEBSOCKET_FRAME: - return "MG_EV_WEBSOCKET_FRAME"; - case MG_EV_WEBSOCKET_CONTROL_FRAME: - return "MG_EV_WEBSOCKET_CONTROL_FRAME"; - } - std::string s; - s += "Unknown event: "; - s += event; - return s; + switch (event) { + case MG_EV_CONNECT: + return "MG_EV_CONNECT"; + case MG_EV_ACCEPT: + return "MG_EV_ACCEPT"; + case MG_EV_CLOSE: + return "MG_EV_CLOSE"; + case MG_EV_SEND: + return "MG_EV_SEND"; + case MG_EV_RECV: + return "MG_EV_RECV"; + case MG_EV_POLL: + return "MG_EV_POLL"; + case MG_EV_TIMER: + return "MG_EV_TIMER"; + case MG_EV_HTTP_PART_DATA: + return "MG_EV_HTTP_PART_DATA"; + case MG_EV_HTTP_MULTIPART_REQUEST: + return "MG_EV_HTTP_MULTIPART_REQUEST"; + case MG_EV_HTTP_PART_BEGIN: + return "MG_EV_HTTP_PART_BEGIN"; + case MG_EV_HTTP_PART_END: + return "MG_EV_HTTP_PART_END"; + case MG_EV_HTTP_MULTIPART_REQUEST_END: + return "MG_EV_HTTP_MULTIPART_REQUEST_END"; + case MG_EV_HTTP_REQUEST: + return "MG_EV_HTTP_REQUEST"; + case MG_EV_HTTP_REPLY: + return "MG_EV_HTTP_REPLY"; + case MG_EV_HTTP_CHUNK: + return "MG_EV_HTTP_CHUNK"; + case MG_EV_MQTT_CONNACK: + return "MG_EV_MQTT_CONNACK"; + case MG_EV_MQTT_CONNECT: + return "MG_EV_MQTT_CONNECT"; + case MG_EV_MQTT_DISCONNECT: + return "MG_EV_MQTT_DISCONNECT"; + case MG_EV_MQTT_PINGREQ: + return "MG_EV_MQTT_PINGREQ"; + case MG_EV_MQTT_PINGRESP: + return "MG_EV_MQTT_PINGRESP"; + case MG_EV_MQTT_PUBACK: + return "MG_EV_MQTT_PUBACK"; + case MG_EV_MQTT_PUBCOMP: + return "MG_EV_MQTT_PUBCOMP"; + case MG_EV_MQTT_PUBLISH: + return "MG_EV_MQTT_PUBLISH"; + case MG_EV_MQTT_PUBREC: + return "MG_EV_MQTT_PUBREC"; + case MG_EV_MQTT_PUBREL: + return "MG_EV_MQTT_PUBREL"; + case MG_EV_MQTT_SUBACK: + return "MG_EV_MQTT_SUBACK"; + case MG_EV_MQTT_SUBSCRIBE: + return "MG_EV_MQTT_SUBSCRIBE"; + case MG_EV_MQTT_UNSUBACK: + return "MG_EV_MQTT_UNSUBACK"; + case MG_EV_MQTT_UNSUBSCRIBE: + return "MG_EV_MQTT_UNSUBSCRIBE"; + case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: + return "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"; + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: + return "MG_EV_WEBSOCKET_HANDSHAKE_DONE"; + case MG_EV_WEBSOCKET_FRAME: + return "MG_EV_WEBSOCKET_FRAME"; + case MG_EV_WEBSOCKET_CONTROL_FRAME: + return "MG_EV_WEBSOCKET_CONTROL_FRAME"; + } + std::string s; + s += "Unknown event: "; + s += event; + return s; } //eventToString -static void dumpHttpMessage(struct http_message *pHttpMessage) { - ESP_LOGD(tag, "HTTP Message"); - ESP_LOGD(tag, "Message: %.*s", (int)pHttpMessage->message.len, pHttpMessage->message.p); + +static void dumpHttpMessage(struct http_message* pHttpMessage) { + ESP_LOGD(LOG_TAG, "HTTP Message"); + ESP_LOGD(LOG_TAG, "Message: %.*s", (int) pHttpMessage->message.len, pHttpMessage->message.p); } /* -static struct mg_str uploadFileNameHandler(struct mg_connection *mgConnection, struct mg_str fname) { - ESP_LOGD(tag, "uploadFileNameHandler: %s", mgStrToString(fname).c_str()); - return fname; +static struct mg_str uploadFileNameHandler(struct mg_connection* mgConnection, struct mg_str fname) { + ESP_LOGD(LOG_TAG, "uploadFileNameHandler: %s", mgStrToString(fname).c_str()); + return fname; } */ + /** * @brief Mongoose event handler. * The event handler is called when an event occurs associated with the WebServer @@ -129,147 +135,139 @@ static struct mg_str uploadFileNameHandler(struct mg_connection *mgConnection, s * @param [in] eventData Data associated with the event. * @return N/A. */ -static void mongoose_event_handler_web_server( - struct mg_connection *mgConnection, // The network connection associated with the event. - int event, // The type of event. - void *eventData // Data associated with the event. -) { - if (event == MG_EV_POLL) { - return; - } - ESP_LOGD(tag, "Event: %s [%d]", mongoose_eventToString(event).c_str(), mgConnection->sock); - switch (event) { - case MG_EV_SEND: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - pWebServer->continueConnection(mgConnection); - break; - } - - case MG_EV_HTTP_REQUEST: { - struct http_message *message = (struct http_message *) eventData; - dumpHttpMessage(message); - - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - pWebServer->processRequest(mgConnection, message); - break; - } // MG_EV_HTTP_REQUEST - - case MG_EV_HTTP_MULTIPART_REQUEST: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - ESP_LOGD(tag, "User_data address 0x%d", (uint32_t)pWebServerUserData); - WebServer *pWebServer = pWebServerUserData->pWebServer; - if (pWebServer->m_pMultiPartFactory == nullptr) { - return; - } - WebServer::HTTPMultiPart *pMultiPart = pWebServer->m_pMultiPartFactory->newInstance(); - struct WebServerUserData *p2 = new WebServerUserData(); - ESP_LOGD(tag, "New User_data address 0x%d", (uint32_t)p2); - p2->originalUserData = pWebServerUserData; - p2->pWebServer = pWebServerUserData->pWebServer; - p2->pMultiPart = pMultiPart; - p2->pWebSocketHandler = nullptr; - mgConnection->user_data = p2; - //struct http_message *message = (struct http_message *) eventData; - //dumpHttpMessage(message); - break; - } // MG_EV_HTTP_MULTIPART_REQUEST - - case MG_EV_HTTP_MULTIPART_REQUEST_END: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pMultiPart != nullptr) { - delete pWebServerUserData->pMultiPart; - pWebServerUserData->pMultiPart = nullptr; - } - mgConnection->user_data = pWebServerUserData->originalUserData; - delete pWebServerUserData; - WebServer::HTTPResponse httpResponse = WebServer::HTTPResponse(mgConnection); - httpResponse.setStatus(200); - httpResponse.sendData(""); - break; - } // MG_EV_HTTP_MULTIPART_REQUEST_END - - case MG_EV_HTTP_PART_BEGIN: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->begin(std::string(part->var_name), std::string(part->file_name)); - } - break; - } // MG_EV_HTTP_PART_BEGIN - - case MG_EV_HTTP_PART_DATA: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->data(std::string(part->data.p, part->data.len)); - } - break; - } // MG_EV_HTTP_PART_DATA - - case MG_EV_HTTP_PART_END: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - struct mg_http_multipart_part *part = (struct mg_http_multipart_part *)eventData; - ESP_LOGD(tag, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", - part->file_name, part->var_name, part->status, (uint32_t)part->user_data); - if (pWebServerUserData->pMultiPart != nullptr) { - pWebServerUserData->pMultiPart->end(); - } - break; - } // MG_EV_HTTP_PART_END - - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - WebServer *pWebServer = pWebServerUserData->pWebServer; - if (pWebServer->m_pWebSocketHandlerFactory != nullptr) { - if (pWebServerUserData->pWebSocketHandler != nullptr) { - ESP_LOGD(tag, "Warning: MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: pWebSocketHandler was NOT null"); - } - struct WebServerUserData *p2 = new WebServerUserData(); - ESP_LOGD(tag, "New User_data address 0x%d", (uint32_t)p2); - p2->originalUserData = pWebServerUserData; - p2->pWebServer = pWebServerUserData->pWebServer; - p2->pWebSocketHandler = pWebServer->m_pWebSocketHandlerFactory->newInstance(); - mgConnection->user_data = p2; - } else { - ESP_LOGD(tag, "We received a WebSocket request but we have no handler factory!"); - } - break; - } // MG_EV_WEBSOCKET_HANDSHAKE_REQUEST - - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pWebSocketHandler == nullptr) { - ESP_LOGE(tag, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); - return; - } - pWebServerUserData->pWebSocketHandler->onCreated(); - break; - } // MG_EV_WEBSOCKET_HANDSHAKE_DONE - - - /* - * When we receive a MG_EV_WEBSOCKET_FRAME then we have received a chunk of data over the network. - * Our goal will be to send this to the web socket handler (if one exists). - */ - case MG_EV_WEBSOCKET_FRAME: { - struct WebServerUserData *pWebServerUserData = (struct WebServerUserData *)mgConnection->user_data; - if (pWebServerUserData->pWebSocketHandler == nullptr) { - ESP_LOGE(tag, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); - return; - } - struct websocket_message *ws_message = (websocket_message *)eventData; - ESP_LOGD(tag, "Received data length: %d", ws_message->size); - pWebServerUserData->pWebSocketHandler->onMessage(std::string((char *)ws_message->data, ws_message->size)); - break; - } // MG_EV_WEBSOCKET_FRAME - - } // End of switch +static void mongoose_event_handler_web_server(struct mg_connection* mgConnection, int event, void* eventData) { + if (event == MG_EV_POLL) return; + ESP_LOGD(LOG_TAG, "Event: %s [%d]", mongoose_eventToString(event).c_str(), mgConnection->sock); + switch (event) { + case MG_EV_SEND: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + pWebServer->continueConnection(mgConnection); + break; + } + + case MG_EV_HTTP_REQUEST: { + struct http_message* message = (struct http_message*) eventData; + dumpHttpMessage(message); + + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + pWebServer->processRequest(mgConnection, message); + break; + } // MG_EV_HTTP_REQUEST + + case MG_EV_HTTP_MULTIPART_REQUEST: { + struct WebServerUserData *pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + ESP_LOGD(LOG_TAG, "User_data address 0x%d", (uint32_t) pWebServerUserData); + WebServer* pWebServer = pWebServerUserData->pWebServer; + if (pWebServer->m_pMultiPartFactory == nullptr) return; + WebServer::HTTPMultiPart* pMultiPart = pWebServer->m_pMultiPartFactory->newInstance(); + struct WebServerUserData* p2 = new WebServerUserData(); + ESP_LOGD(LOG_TAG, "New User_data address 0x%d", (uint32_t) p2); + p2->originalUserData = pWebServerUserData; + p2->pWebServer = pWebServerUserData->pWebServer; + p2->pMultiPart = pMultiPart; + p2->pWebSocketHandler = nullptr; + mgConnection->user_data = p2; + //struct http_message* message = (struct http_message*) eventData; + //dumpHttpMessage(message); + break; + } // MG_EV_HTTP_MULTIPART_REQUEST + + case MG_EV_HTTP_MULTIPART_REQUEST_END: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pMultiPart != nullptr) { + delete pWebServerUserData->pMultiPart; + pWebServerUserData->pMultiPart = nullptr; + } + mgConnection->user_data = pWebServerUserData->originalUserData; + delete pWebServerUserData; + WebServer::HTTPResponse httpResponse = WebServer::HTTPResponse(mgConnection); + httpResponse.setStatus(200); + httpResponse.sendData(""); + break; + } // MG_EV_HTTP_MULTIPART_REQUEST_END + + case MG_EV_HTTP_PART_BEGIN: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t) part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->begin(std::string(part->var_name), std::string(part->file_name)); + } + break; + } // MG_EV_HTTP_PART_BEGIN + + case MG_EV_HTTP_PART_DATA: { + struct WebServerUserData *pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t) part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->data(std::string(part->data.p, part->data.len)); + } + break; + } // MG_EV_HTTP_PART_DATA + + case MG_EV_HTTP_PART_END: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + struct mg_http_multipart_part* part = (struct mg_http_multipart_part*) eventData; + ESP_LOGD(LOG_TAG, "file_name: \"%s\", var_name: \"%s\", status: %d, user_data: 0x%d", + part->file_name, part->var_name, part->status, (uint32_t)part->user_data); + if (pWebServerUserData->pMultiPart != nullptr) { + pWebServerUserData->pMultiPart->end(); + } + break; + } // MG_EV_HTTP_PART_END + + case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + WebServer* pWebServer = pWebServerUserData->pWebServer; + if (pWebServer->m_pWebSocketHandlerFactory != nullptr) { + if (pWebServerUserData->pWebSocketHandler != nullptr) { + ESP_LOGD(LOG_TAG, "Warning: MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: pWebSocketHandler was NOT null"); + } + struct WebServerUserData* p2 = new WebServerUserData(); + ESP_LOGD(LOG_TAG, "New User_data address 0x%d", (uint32_t) p2); + p2->originalUserData = pWebServerUserData; + p2->pWebServer = pWebServerUserData->pWebServer; + p2->pWebSocketHandler = pWebServer->m_pWebSocketHandlerFactory->newInstance(); + mgConnection->user_data = p2; + } else { + ESP_LOGD(LOG_TAG, "We received a WebSocket request but we have no handler factory!"); + } + break; + } // MG_EV_WEBSOCKET_HANDSHAKE_REQUEST + + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pWebSocketHandler == nullptr) { + ESP_LOGE(LOG_TAG, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); + return; + } + pWebServerUserData->pWebSocketHandler->onCreated(); + break; + } // MG_EV_WEBSOCKET_HANDSHAKE_DONE + + + /* + * When we receive a MG_EV_WEBSOCKET_FRAME then we have received a chunk of data over the network. + * Our goal will be to send this to the web socket handler (if one exists). + */ + case MG_EV_WEBSOCKET_FRAME: { + struct WebServerUserData* pWebServerUserData = (struct WebServerUserData*) mgConnection->user_data; + if (pWebServerUserData->pWebSocketHandler == nullptr) { + ESP_LOGE(LOG_TAG, "Error: MG_EV_WEBSOCKET_FRAME: pWebSocketHandler is null"); + return; + } + struct websocket_message* ws_message = (websocket_message*) eventData; + ESP_LOGD(LOG_TAG, "Received data length: %d", ws_message->size); + pWebServerUserData->pWebSocketHandler->onMessage(std::string((char*) ws_message->data, ws_message->size)); + break; + } // MG_EV_WEBSOCKET_FRAME + + } // End of switch } // End of mongoose_event_handler @@ -277,9 +275,9 @@ static void mongoose_event_handler_web_server( * @brief Constructor. */ WebServer::WebServer() { - m_rootPath = ""; - m_pMultiPartFactory = nullptr; - m_pWebSocketHandlerFactory = nullptr; + m_rootPath = ""; + m_pMultiPartFactory = nullptr; + m_pWebSocketHandlerFactory = nullptr; } // WebServer @@ -292,7 +290,7 @@ WebServer::~WebServer() { * @return The current root path. */ const std::string& WebServer::getRootPath() { - return m_rootPath; + return m_rootPath; } // getRootPath @@ -317,15 +315,17 @@ const std::string& WebServer::getRootPath() { * @param [in] handler The callback function to be invoked when a request arrives. */ void WebServer::addPathHandler(const std::string& method, const std::string& pathExpr, - void (* handler)(WebServer::HTTPRequest* pHttpRequest, + void (*handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); + m_pathHandlers.push_back(PathHandler(method, pathExpr, handler)); } // addPathHandler -void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, void (* handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_pathHandlers.push_back(PathHandler(std::move(method), pathExpr, handler)); + +void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr, void (*handler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_pathHandlers.push_back(PathHandler(std::move(method), pathExpr, handler)); } // addPathHandler + /** * @brief Run the web server listening at the given port. * @@ -335,31 +335,31 @@ void WebServer::addPathHandler(std::string&& method, const std::string& pathExpr * @return N/A. */ void WebServer::start(uint16_t port) { - ESP_LOGD(tag, "WebServer task starting"); - struct mg_mgr mgr; - mg_mgr_init(&mgr, NULL); - - std::stringstream stringStream; - stringStream << ':' << port; - struct mg_connection *mgConnection = mg_bind(&mgr, stringStream.str().c_str(), mongoose_event_handler_web_server); - - if (mgConnection == NULL) { - ESP_LOGE(tag, "No connection from the mg_bind()"); - vTaskDelete(NULL); - return; - } - - struct WebServerUserData *pWebServerUserData = new WebServerUserData(); - pWebServerUserData->pWebServer = this; - pWebServerUserData->pMultiPart = nullptr; - mgConnection->user_data = pWebServerUserData; // Save the WebServer instance reference in user_data. - ESP_LOGD(tag, "start: User_data address 0x%d", (uint32_t)pWebServerUserData); - mg_set_protocol_http_websocket(mgConnection); - - ESP_LOGD(tag, "WebServer listening on port %d", port); - while (1) { - mg_mgr_poll(&mgr, 2000); - } + ESP_LOGD(LOG_TAG, "WebServer task starting"); + struct mg_mgr mgr; + mg_mgr_init(&mgr, NULL); + + std::stringstream stringStream; + stringStream << ':' << port; + struct mg_connection *mgConnection = mg_bind(&mgr, stringStream.str().c_str(), mongoose_event_handler_web_server); + + if (mgConnection == NULL) { + ESP_LOGE(LOG_TAG, "No connection from the mg_bind()"); + vTaskDelete(NULL); + return; + } + + struct WebServerUserData* pWebServerUserData = new WebServerUserData(); + pWebServerUserData->pWebServer = this; + pWebServerUserData->pMultiPart = nullptr; + mgConnection->user_data = pWebServerUserData; // Save the WebServer instance reference in user_data. + ESP_LOGD(LOG_TAG, "start: User_data address 0x%d", (uint32_t)pWebServerUserData); + mg_set_protocol_http_websocket(mgConnection); + + ESP_LOGD(LOG_TAG, "WebServer listening on port %d", port); + while (true) { + mg_mgr_poll(&mgr, 2000); + } } // run @@ -368,7 +368,7 @@ void WebServer::start(uint16_t port) { * @param [in] pMultiPart A pointer to the multi part factory. */ void WebServer::setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory) { - m_pMultiPartFactory = pMultiPartFactory; + m_pMultiPartFactory = pMultiPartFactory; } @@ -390,11 +390,12 @@ void WebServer::setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory) { * @return N/A. */ void WebServer::setRootPath(const std::string& path) { - m_rootPath = path; + m_rootPath = path; } // setRootPath + void WebServer::setRootPath(std::string&& path) { - m_rootPath = std::move(path); + m_rootPath = std::move(path); } // setRootPath @@ -405,7 +406,7 @@ void WebServer::setRootPath(std::string&& path) { * @return N/A. */ void WebServer::setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHandlerFactory) { - m_pWebSocketHandlerFactory = pWebSocketHandlerFactory; + m_pWebSocketHandlerFactory = pWebSocketHandlerFactory; } // setWebSocketHandlerFactory @@ -414,9 +415,9 @@ void WebServer::setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHa * @param [in] nc The network connection for the response. */ WebServer::HTTPResponse::HTTPResponse(struct mg_connection* nc) { - m_nc = nc; - m_status = 200; - m_dataSent = false; + m_nc = nc; + m_status = 200; + m_dataSent = false; } // HTTPResponse @@ -426,11 +427,11 @@ WebServer::HTTPResponse::HTTPResponse(struct mg_connection* nc) { * @param [in] value The value of the header. */ void WebServer::HTTPResponse::addHeader(const std::string& name, const std::string& value) { - m_headers[name] = value; + m_headers[name] = value; } // addHeader void WebServer::HTTPResponse::addHeader(std::string&& name, std::string&& value) { - m_headers[std::move(name)] = std::move(value); + m_headers[std::move(name)] = std::move(value); } // addHeader @@ -442,9 +443,10 @@ std::string WebServer::HTTPResponse::buildHeaders() { std::string headers; unsigned long headers_len = 0; - for(auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers_len += 2; + for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { + if (iter != m_headers.begin()) { + headers_len += 2; + } headers_len += iter->first.length(); headers_len += 2; headers_len += iter->second.length(); @@ -453,8 +455,9 @@ std::string WebServer::HTTPResponse::buildHeaders() { headers.resize(headers_len); // Will not have to resize and recopy during the next loop, we have 2 loops but it still ends up being faster for (auto iter = m_headers.begin(); iter != m_headers.end(); iter++) { - if(iter != m_headers.begin()) - headers += "\r\n"; + if (iter != m_headers.begin()) { + headers += "\r\n"; + } headers += iter->first; headers += ": "; headers += iter->second; @@ -462,6 +465,7 @@ std::string WebServer::HTTPResponse::buildHeaders() { return headers; } // buildHeaders + /** * @brief Send data to the HTTP caller. * Send the data to the HTTP caller. No further data should be sent after this call. @@ -469,13 +473,10 @@ std::string WebServer::HTTPResponse::buildHeaders() { * @return N/A. */ void WebServer::HTTPResponse::sendData(const std::string& data) { - sendData((uint8_t *)data.data(), data.length()); + sendData((uint8_t*) data.data(), data.length()); } // sendData - - - /** * @brief Send data to the HTTP caller. * Send the data to the HTTP caller. No further data should be sent after this call. @@ -484,15 +485,15 @@ void WebServer::HTTPResponse::sendData(const std::string& data) { * @return N/A. */ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { - if (m_dataSent) { - ESP_LOGE(tag, "HTTPResponse: Data already sent! Attempt to send again/more."); - return; - } - m_dataSent = true; - - mg_send_head(m_nc, m_status, length, buildHeaders().c_str()); - mg_send(m_nc, pData, length); - m_nc->flags |= MG_F_SEND_AND_CLOSE; + if (m_dataSent) { + ESP_LOGE(LOG_TAG, "HTTPResponse: Data already sent! Attempt to send again/more."); + return; + } + m_dataSent = true; + + mg_send_head(m_nc, m_status, length, buildHeaders().c_str()); + mg_send(m_nc, pData, length); + m_nc->flags |= MG_F_SEND_AND_CLOSE; } // sendData @@ -500,7 +501,7 @@ void WebServer::HTTPResponse::sendData(const uint8_t* pData, size_t length) { * */ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { - sendData((uint8_t*) pData, length); + sendData((uint8_t*) pData, length); } // sendData @@ -508,11 +509,11 @@ void WebServer::HTTPResponse::sendData(const char* pData, size_t length) { * */ void WebServer::HTTPResponse::sendChunkHead() { - if(m_dataSent) { - ESP_LOGE(tag, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); - } - m_dataSent = true; - mg_send_head(m_nc, m_status, -1, buildHeaders().c_str()); + if (m_dataSent) { + ESP_LOGE(LOG_TAG, "HTTPResponse: Chunk headers already sent! Attempt to send again/more."); + } + m_dataSent = true; + mg_send_head(m_nc, m_status, -1, buildHeaders().c_str()); } // sendChunkHead @@ -520,7 +521,7 @@ void WebServer::HTTPResponse::sendChunkHead() { * */ void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { - mg_send_http_chunk(m_nc, pData, length); + mg_send_http_chunk(m_nc, pData, length); } // sendChunkHead @@ -528,7 +529,7 @@ void WebServer::HTTPResponse::sendChunk(const char* pData, size_t length) { * */ void WebServer::HTTPResponse::closeConnection() { - m_nc->flags |= MG_F_SEND_AND_CLOSE; + m_nc->flags |= MG_F_SEND_AND_CLOSE; } // closeConnection @@ -538,11 +539,12 @@ void WebServer::HTTPResponse::closeConnection() { * @return N/A. */ void WebServer::HTTPResponse::setHeaders(const std::map& headers) { - m_headers = headers; + m_headers = headers; } // setHeaders + void WebServer::HTTPResponse::setHeaders(std::map&& headers) { - m_headers = std::move(headers); + m_headers = std::move(headers); } // setHeaders @@ -551,7 +553,7 @@ void WebServer::HTTPResponse::setHeaders(std::map&& he * @return The current root path. */ const std::string& WebServer::HTTPResponse::getRootPath() const { - return m_rootPath; + return m_rootPath; } // getRootPath @@ -561,13 +563,15 @@ const std::string& WebServer::HTTPResponse::getRootPath() const { * @return N/A. */ void WebServer::HTTPResponse::setRootPath(const std::string& path) { - m_rootPath = path; + m_rootPath = path; } // setRootPath + void WebServer::HTTPResponse::setRootPath(std::string&& path) { - m_rootPath = std::move(path); + m_rootPath = std::move(path); } // setRootPath + /** * @brief Set the status value in the HTTP response. * @@ -576,7 +580,7 @@ void WebServer::HTTPResponse::setRootPath(std::string&& path) { * @return N/A. */ void WebServer::HTTPResponse::setStatus(int status) { - m_status = status; + m_status = status; } // setStatus @@ -590,74 +594,73 @@ void WebServer::HTTPResponse::setStatus(int status) { * @param [in] mgConnection The network connection on which the request was received. * @param [in] message The message representing the request. */ -void WebServer::processRequest(struct mg_connection *mgConnection, struct http_message* message) { - ESP_LOGD(tag, "WebServer::processRequest: Matching: %.*s", (int)message->uri.len, message->uri.p); - HTTPResponse httpResponse = HTTPResponse(mgConnection); - - /* - * Iterate through each of the path handlers looking for a match with the method and specified path. - */ - std::vector::iterator it; - for (it = m_pathHandlers.begin(); it != m_pathHandlers.end(); ++it) { - if ((*it).match(message->method.p, message->method.len, message->uri.p)) { - HTTPRequest httpRequest(message); - (*it).invoke(&httpRequest, &httpResponse); - ESP_LOGD(tag, "Found a match!!"); - return; - } - } // End of examine path handlers. - - // Because we reached here, it means that we did NOT match a handler. Now we want to attempt - // to retrieve the corresponding file content. - std::string filePath; - filePath.reserve(httpResponse.getRootPath().length() + message->uri.len + 1); - filePath += httpResponse.getRootPath(); - filePath.append(message->uri.p, message->uri.len); - ESP_LOGD(tag, "Opening file: %s", filePath.c_str()); - FILE* file = nullptr; - - if(strcmp(filePath.c_str(), "/") != 0) - file = fopen(filePath.c_str(), "rb"); - if (file != nullptr) { - auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); - size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); - - if(read >= MAX_CHUNK_LENGTH) { - httpResponse.sendChunkHead(); - httpResponse.sendChunk((char*)pData, read); - fclose(unfinishedConnection[mgConnection->sock]); - unfinishedConnection[mgConnection->sock] = file; - } else { - fclose(file); - httpResponse.sendData(pData, read); - } - free(pData); - } else { - // Handle unable to open file - httpResponse.setStatus(404); // Not found - httpResponse.sendData(""); - } +void WebServer::processRequest(struct mg_connection* mgConnection, struct http_message* message) { + ESP_LOGD(LOG_TAG, "WebServer::processRequest: Matching: %.*s", (int) message->uri.len, message->uri.p); + HTTPResponse httpResponse = HTTPResponse(mgConnection); + + /* + * Iterate through each of the path handlers looking for a match with the method and specified path. + */ + std::vector::iterator it; + for (it = m_pathHandlers.begin(); it != m_pathHandlers.end(); ++it) { + if ((*it).match(message->method.p, message->method.len, message->uri.p)) { + HTTPRequest httpRequest(message); + (*it).invoke(&httpRequest, &httpResponse); + ESP_LOGD(LOG_TAG, "Found a match!!"); + return; + } + } // End of examine path handlers. + + // Because we reached here, it means that we did NOT match a handler. Now we want to attempt + // to retrieve the corresponding file content. + std::string filePath; + filePath.reserve(httpResponse.getRootPath().length() + message->uri.len + 1); + filePath += httpResponse.getRootPath(); + filePath.append(message->uri.p, message->uri.len); + ESP_LOGD(LOG_TAG, "Opening file: %s", filePath.c_str()); + FILE* file = nullptr; + + if (strcmp(filePath.c_str(), "/") != 0) { + file = fopen(filePath.c_str(), "rb"); + } + if (file != nullptr) { + auto pData = (uint8_t*)malloc(MAX_CHUNK_LENGTH); + size_t read = fread(pData, 1, MAX_CHUNK_LENGTH, file); + + if (read >= MAX_CHUNK_LENGTH) { + httpResponse.sendChunkHead(); + httpResponse.sendChunk((char*) pData, read); + fclose(unfinishedConnection[mgConnection->sock]); + unfinishedConnection[mgConnection->sock] = file; + } else { + fclose(file); + httpResponse.sendData(pData, read); + } + free(pData); + } else { + // Handle unable to open file + httpResponse.setStatus(404); // Not found + httpResponse.sendData(""); + } } // processRequest void WebServer::continueConnection(struct mg_connection* mgConnection) { - if(unfinishedConnection.count(mgConnection->sock) == 0) { - return; - } - - HTTPResponse httpResponse = HTTPResponse(mgConnection); - - FILE* file = unfinishedConnection[mgConnection->sock]; - auto pData = (char*) malloc(MAX_CHUNK_LENGTH); - size_t length = fread(pData, 1, MAX_CHUNK_LENGTH, file); - - httpResponse.sendChunk(pData, length); - if(length < MAX_CHUNK_LENGTH) { - fclose(file); - httpResponse.closeConnection(); - unfinishedConnection.erase(mgConnection->sock); - httpResponse.sendChunk("", 0); - } - free(pData); + if (unfinishedConnection.count(mgConnection->sock) == 0) return; + + HTTPResponse httpResponse = HTTPResponse(mgConnection); + + FILE* file = unfinishedConnection[mgConnection->sock]; + auto pData = (char*) malloc(MAX_CHUNK_LENGTH); + size_t length = fread(pData, 1, MAX_CHUNK_LENGTH, file); + + httpResponse.sendChunk(pData, length); + if (length < MAX_CHUNK_LENGTH) { + fclose(file); + httpResponse.closeConnection(); + unfinishedConnection.erase(mgConnection->sock); + httpResponse.sendChunk("", 0); + } + free(pData); } @@ -668,16 +671,16 @@ void WebServer::continueConnection(struct mg_connection* mgConnection) { * @param [in] pathPattern The path pattern to be matched. * @param [in] webServerRequestHandler The request handler to be called. */ -WebServer::PathHandler::PathHandler(const std::string& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_method = method; - m_pattern = std::regex(pathPattern); - m_requestHandler = webServerRequestHandler; +WebServer::PathHandler::PathHandler(const std::string& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_method = method; + m_pattern = std::regex(pathPattern); + m_requestHandler = webServerRequestHandler; } // PathHandler -WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { - m_method = std::move(method); - m_pattern = std::regex(pathPattern); - m_requestHandler = webServerRequestHandler; +WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)) { + m_method = std::move(method); + m_pattern = std::regex(pathPattern); + m_requestHandler = webServerRequestHandler; } // PathHandler @@ -689,13 +692,12 @@ WebServer::PathHandler::PathHandler(std::string&& method, const std::string& pat * @param [in] path The path to be matched. * @return True if the path matches. */ - bool WebServer::PathHandler::match(const char* method, size_t method_len, const char* path) { - //ESP_LOGD(tag, "match: %s with %s", m_pattern.c_str(), path.c_str()); - if (method_len != m_method.length() || strncmp(method, m_method.c_str(), method_len) != 0) { - return false; - } - return std::regex_search(path, m_pattern); + //ESP_LOGD(LOG_TAG, "match: %s with %s", m_pattern.c_str(), path.c_str()); + if (method_len != m_method.length() || strncmp(method, m_method.c_str(), method_len) != 0) { + return false; + } + return std::regex_search(path, m_pattern); } // match @@ -705,8 +707,8 @@ bool WebServer::PathHandler::match(const char* method, size_t method_len, const * @param [in] response An object representing the response. * @return N/A. */ -void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer::HTTPResponse *response) { - m_requestHandler(request, response); +void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer::HTTPResponse* response) { + m_requestHandler(request, response); } // invoke @@ -717,7 +719,7 @@ void WebServer::PathHandler::invoke(WebServer::HTTPRequest* request, WebServer:: * @param [in] message The description of the underlying Mongoose message. */ WebServer::HTTPRequest::HTTPRequest(struct http_message* message) { - m_message = message; + m_message = message; } // HTTPRequest @@ -729,9 +731,10 @@ WebServer::HTTPRequest::HTTPRequest(struct http_message* message) { * @return The body of the request. */ const char* WebServer::HTTPRequest::getBody() const { - return m_message->body.p; + return m_message->body.p; } // getBody + /** * @brief Get the length of the body of the request. * When an HTTP request is either PUT or POST then it may contain a payload that is also @@ -739,7 +742,7 @@ const char* WebServer::HTTPRequest::getBody() const { * @return The length of the body of the request. */ size_t WebServer::HTTPRequest::getBodyLen() const { - return m_message->body.len; + return m_message->body.len; } // getBodyLen @@ -751,16 +754,17 @@ size_t WebServer::HTTPRequest::getBodyLen() const { * @return The body of the request. */ const char* WebServer::HTTPRequest::getMethod() const { - return m_message->method.p; + return m_message->method.p; } // getMethod + /** * @brief Get the length of the method of the request. * An HTTP request contains a request method which is one of GET, PUT, POST, etc. * @return The length of the method of the request. */ size_t WebServer::HTTPRequest::getMethodLen() const { - return m_message->method.len; + return m_message->method.len; } // getMethodLen @@ -773,9 +777,10 @@ size_t WebServer::HTTPRequest::getMethodLen() const { * @return The path of the request. */ const char* WebServer::HTTPRequest::getPath() const { - return m_message->uri.p; + return m_message->uri.p; } // getPath + /** * @brief Get the path of the request. * The path of an HTTP request is the portion of the URL that follows the hostname/port pair @@ -783,11 +788,10 @@ const char* WebServer::HTTPRequest::getPath() const { * @return The path of the request. */ size_t WebServer::HTTPRequest::getPathLen() const { - return m_message->uri.len; + return m_message->uri.len; } // getPath -#define STATE_NAME 0 -#define STATE_VALUE 1 + /** * @brief Get the query part of the request. * The query is a set of name = value pairs. The return is a map keyed by the name items. @@ -795,48 +799,47 @@ size_t WebServer::HTTPRequest::getPathLen() const { * @return The query part of the request. */ std::map WebServer::HTTPRequest::getQuery() const { - // Walk through all the characters in the query string maintaining a simple state machine - // that lets us know what we are parsing. - std::map queryMap; - const char* queryString = m_message->query_string.p; - size_t queryStringLen = m_message->query_string.len; - int i=0; - - /* - * We maintain a simple state machine with states of: - * * STATE_NAME - We are parsing a name. - * * STATE_VALUE - We are parsing a value. - */ - int state = STATE_NAME; - std::string name = ""; - std::string value; - // Loop through each character in the query string. - for (i=0; i queryMap; + const char* queryString = m_message->query_string.p; + size_t queryStringLen = m_message->query_string.len; + + /* + * We maintain a simple state machine with states of: + * * STATE_NAME - We are parsing a name. + * * STATE_VALUE - We are parsing a value. + */ + int state = STATE_NAME; + std::string name = ""; + std::string value; + // Loop through each character in the query string. + for (size_t i = 0; i < queryStringLen; i++) { + char currentChar = queryString[i]; + if (state == STATE_NAME) { + if (currentChar != '=') { + name += currentChar; + } else { + state = STATE_VALUE; + value = ""; + } + } // End state = STATE_NAME + else { // if (state == STATE_VALUE) + if (currentChar != '&') { + value += currentChar; + } else { + //ESP_LOGD(LOG_TAG, "name=%s, value=%s", name.c_str(), value.c_str()); + queryMap[name] = value; + state = STATE_NAME; + name = ""; + } + } // End state = STATE_VALUE + } // End for loop + if (state == STATE_VALUE) { + //ESP_LOGD(LOG_TAG, "name=%s, value=%s", name.c_str(), value.c_str()); + queryMap[name] = value; + } + return queryMap; } // getQuery @@ -860,17 +863,17 @@ std::map WebServer::HTTPRequest::getQuery() const { * @return A vector of the constituent parts of the path. */ std::vector WebServer::HTTPRequest::pathSplit() const { - std::istringstream stream(std::string(getPath(), getPathLen())); // I don't know if there's a better istringstream constructor for this - std::vector ret; - std::string pathPart; - while(std::getline(stream, pathPart, '/')) { - ret.push_back(pathPart); - } - // Debug - for (int i=0; i ret; + std::string pathPart; + while (std::getline(stream, pathPart, '/')) { + ret.push_back(pathPart); + } + // Debug + for (int i = 0; i < ret.size(); i++) { + ESP_LOGD(LOG_TAG, "part[%d]: %s", i, ret[i].c_str()); + } + return ret; } // pathSplit /** @@ -882,8 +885,8 @@ std::vector WebServer::HTTPRequest::pathSplit() const { * @return N/A. */ void WebServer::HTTPMultiPart::begin(const std::string& varName, const std::string& fileName) { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::begin(varName=\"%s\", fileName=\"%s\")", - varName.c_str(), fileName.c_str()); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::begin(varName=\"%s\", fileName=\"%s\")", + varName.c_str(), fileName.c_str()); } // WebServer::HTTPMultiPart::begin @@ -893,7 +896,7 @@ void WebServer::HTTPMultiPart::begin(const std::string& varName, const std::stri * @return N/A. */ void WebServer::HTTPMultiPart::end() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::end()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::end()"); } // WebServer::HTTPMultiPart::end @@ -905,7 +908,7 @@ void WebServer::HTTPMultiPart::end() { * @return N/A. */ void WebServer::HTTPMultiPart::data(const std::string& data) { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::data(), length=%d", data.length()); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::data(), length=%d", data.length()); } // WebServer::HTTPMultiPart::data @@ -914,7 +917,7 @@ void WebServer::HTTPMultiPart::data(const std::string& data) { * @return N/A. */ void WebServer::HTTPMultiPart::multipartEnd() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::multipartEnd()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::multipartEnd()"); } // WebServer::HTTPMultiPart::multipartEnd @@ -923,7 +926,7 @@ void WebServer::HTTPMultiPart::multipartEnd() { * @return N/A. */ void WebServer::HTTPMultiPart::multipartStart() { - ESP_LOGD(tag, "WebServer::HTTPMultiPart::multipartStart()"); + ESP_LOGD(LOG_TAG, "WebServer::HTTPMultiPart::multipartStart()"); } // WebServer::HTTPMultiPart::multipartStart @@ -932,7 +935,6 @@ void WebServer::HTTPMultiPart::multipartStart() { * @return N/A. */ void WebServer::WebSocketHandler::onCreated() { - } // onCreated @@ -942,7 +944,6 @@ void WebServer::WebSocketHandler::onCreated() { * @return N/A. */ void WebServer::WebSocketHandler::onMessage(const std::string& message){ - } // onMessage /** @@ -950,7 +951,6 @@ void WebServer::WebSocketHandler::onMessage(const std::string& message){ * @return N/A */ void WebServer::WebSocketHandler::onClosed() { - } // onClosed @@ -960,12 +960,13 @@ void WebServer::WebSocketHandler::onClosed() { * @return N/A. */ void WebServer::WebSocketHandler::sendData(const std::string& message) { - ESP_LOGD(tag, "WebSocketHandler::sendData(length=%d)", message.length()); - mg_send_websocket_frame(m_mgConnection, - WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, - message.data(), message.length()); + ESP_LOGD(LOG_TAG, "WebSocketHandler::sendData(length=%d)", message.length()); + mg_send_websocket_frame(m_mgConnection, + WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, + message.data(), message.length()); } // sendData + /** * @brief Send data down the WebSocket * @param [in] data The message to send down the socket. @@ -973,9 +974,9 @@ void WebServer::WebSocketHandler::sendData(const std::string& message) { * @return N/A. */ void WebServer::WebSocketHandler::sendData(const uint8_t* data, uint32_t size) { - mg_send_websocket_frame(m_mgConnection, - WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, - data, size); + mg_send_websocket_frame(m_mgConnection, + WEBSOCKET_OP_BINARY | WEBSOCKET_OP_CONTINUE, + data, size); } // sendData @@ -986,9 +987,7 @@ void WebServer::WebSocketHandler::sendData(const uint8_t* data, uint32_t size) { * @return N/A. */ void WebServer::WebSocketHandler::close() { - mg_send_websocket_frame(m_mgConnection, WEBSOCKET_OP_CLOSE, nullptr, 0); + mg_send_websocket_frame(m_mgConnection, WEBSOCKET_OP_CLOSE, nullptr, 0); } // close - - #endif // CONFIG_MONGOOSE_PRESENT diff --git a/cpp_utils/WebServer.h b/cpp_utils/WebServer.h index ae06647b..961b6662 100644 --- a/cpp_utils/WebServer.h +++ b/cpp_utils/WebServer.h @@ -27,183 +27,196 @@ class WebServer; */ class WebServer { public: - /** - * @brief Request wrapper for an HTTP request. - */ - class HTTPRequest { - public: - HTTPRequest(struct http_message* message); - const char* getMethod() const; - const char* getPath() const; - const char* getBody() const; - size_t getMethodLen() const; - size_t getPathLen() const; - size_t getBodyLen() const; - std::map getQuery() const; - std::vector pathSplit() const; - private: - struct http_message* m_message; - }; // HTTPRequest - - /** - * @brief Response wrapper for an HTTP response. - */ - class HTTPResponse { - public: - HTTPResponse(struct mg_connection *nc); - void addHeader(const std::string& name, const std::string& value); - void addHeader(std::string&& name, std::string&& value); - void setStatus(int status); - void setHeaders(const std::map& headers); - void setHeaders(std::map&& headers); - void sendData(const std::string& data); - void sendData(const uint8_t* pData, size_t length); - void sendData(const char* pData, size_t length); - const std::string& getRootPath() const; - void setRootPath(const std::string& path); - void setRootPath(std::string&& path); - void sendChunkHead(); - void sendChunk(const char* pData, size_t length); - void closeConnection(); - private: - struct mg_connection *m_nc; - std::string m_rootPath; - int m_status; - std::map m_headers; - bool m_dataSent; - std::string buildHeaders(); - }; // HTTPResponse - - /** - * @brief Handler for a Multipart. - * - * This class is usually subclassed to provide your own implementation. Typically - * a factory is implemented based on HTTPMultiPartFactory that creates instances. This - * is then registered with the WebServer. When done, when ever the WebServer receives multi - * part requests, this handler for Multipart is called. The call sequence is usually: - * ~~~ - * multipartStart - * begin - * data* - * end - * begin - * data* - * end - * ... - * multipartEnd - * ~~~ - * - * There can be multiple begin, data, data, ..., end groups. - */ - class HTTPMultiPart { - public: - virtual ~HTTPMultiPart() = default; - virtual void begin(const std::string& varName, const std::string& fileName); - virtual void end(); - virtual void data(const std::string& data); - virtual void multipartEnd(); - virtual void multipartStart(); - }; // HTTPMultiPart - - /** - * @brief Factory for creating Multipart instances. - * This class is meant to be implemented to provide a constructor for a custom - * HTTPMultiPart instance. - * @code{.cpp} - * class MyMultiPart : public WebServer::HTTPMultiPart { - * public: - * void begin(std::string varName, std::string fileName) { - * ESP_LOGD(tag, "MyMultiPart begin(): varName=%s, fileName=%s", - * varName.c_str(), fileName.c_str()); - * } - * - * void end() { - * ESP_LOGD(tag, "MyMultiPart end()"); - * } - * - * void data(std::string data) { - * ESP_LOGD(tag, "MyMultiPart data(): length=%d", data.length()); - * } - * - * void multipartEnd() { - * ESP_LOGD(tag, "MyMultiPart multipartEnd()"); - * } - * - * void multipartStart() { - * ESP_LOGD(tag, "MyMultiPart multipartStart()"); - * } - * }; - * - * class MyMultiPartFactory : public WebServer::HTTPMultiPartFactory { - * WebServer::HTTPMultiPart *createNew() { - * return new MyMultiPart(); - * } - * }; - * @endcode - */ - class HTTPMultiPartFactory { - public: - /** - * @brief Create a new HTTPMultiPart instance. - * @return A new HTTPMultiPart instance. - */ - virtual HTTPMultiPart *newInstance() = 0; - }; - - /** - * @brief The handler for path matching. - * - */ - class PathHandler { - public: - PathHandler(const std::string& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - PathHandler(std::string&& method, const std::string& pathPattern, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - bool match(const char* method, size_t method_len, const char* path); - void invoke(HTTPRequest *request, HTTPResponse *response); - private: - std::string m_method; - std::regex m_pattern; - void (*m_requestHandler)(WebServer::HTTPRequest *pHttpRequest, WebServer::HTTPResponse *pHttpResponse); - }; // PathHandler - - /** - * @brief A WebSocket handler for handling WebSockets. - */ - class WebSocketHandler { - public: - void onCreated(); - virtual void onMessage(const std::string& message); - void onClosed(); - void sendData(const std::string& message); - void sendData(const uint8_t* data, uint32_t size); - void close(); - private: - struct mg_connection *m_mgConnection; - }; - - class WebSocketHandlerFactory { - public: - virtual WebSocketHandler *newInstance() = 0; - }; - - WebServer(); - virtual ~WebServer(); - void addPathHandler(const std::string& method, const std::string& pathExpr, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - void addPathHandler(std::string&& method, const std::string& pathExpr, void (* webServerRequestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); - const std::string& getRootPath(); - void setMultiPartFactory(HTTPMultiPartFactory *pMultiPartFactory); - void setRootPath(const std::string& path); - void setRootPath(std::string&& path); - void setWebSocketHandlerFactory(WebSocketHandlerFactory *pWebSocketHandlerFactory); - void start(unsigned short port = 80); - void processRequest(struct mg_connection *mgConnection, struct http_message *message); - void continueConnection(struct mg_connection* mgConnection); - HTTPMultiPartFactory *m_pMultiPartFactory; - WebSocketHandlerFactory *m_pWebSocketHandlerFactory; + /** + * @brief Request wrapper for an HTTP request. + */ + class HTTPRequest { + public: + HTTPRequest(struct http_message* message); + const char* getMethod() const; + const char* getPath() const; + const char* getBody() const; + size_t getMethodLen() const; + size_t getPathLen() const; + size_t getBodyLen() const; + std::map getQuery() const; + std::vector pathSplit() const; + + private: + struct http_message* m_message; + + }; // HTTPRequest + + /** + * @brief Response wrapper for an HTTP response. + */ + class HTTPResponse { + public: + HTTPResponse(struct mg_connection* nc); + void addHeader(const std::string& name, const std::string& value); + void addHeader(std::string&& name, std::string&& value); + void setStatus(int status); + void setHeaders(const std::map& headers); + void setHeaders(std::map&& headers); + void sendData(const std::string& data); + void sendData(const uint8_t* pData, size_t length); + void sendData(const char* pData, size_t length); + const std::string& getRootPath() const; + void setRootPath(const std::string& path); + void setRootPath(std::string&& path); + void sendChunkHead(); + void sendChunk(const char* pData, size_t length); + void closeConnection(); + + private: + struct mg_connection* m_nc; + std::string m_rootPath; + int m_status; + std::map m_headers; + bool m_dataSent; + std::string buildHeaders(); + + }; // HTTPResponse + + /** + * @brief Handler for a Multipart. + * + * This class is usually subclassed to provide your own implementation. Typically + * a factory is implemented based on HTTPMultiPartFactory that creates instances. This + * is then registered with the WebServer. When done, when ever the WebServer receives multi + * part requests, this handler for Multipart is called. The call sequence is usually: + * ~~~ + * multipartStart + * begin + * data* + * end + * begin + * data* + * end + * ... + * multipartEnd + * ~~~ + * + * There can be multiple begin, data, data, ..., end groups. + */ + class HTTPMultiPart { + public: + virtual ~HTTPMultiPart() = default; + virtual void begin(const std::string& varName, const std::string& fileName); + virtual void end(); + virtual void data(const std::string& data); + virtual void multipartEnd(); + virtual void multipartStart(); + + }; // HTTPMultiPart + + /** + * @brief Factory for creating Multipart instances. + * This class is meant to be implemented to provide a constructor for a custom + * HTTPMultiPart instance. + * @code{.cpp} + * class MyMultiPart : public WebServer::HTTPMultiPart { + * public: + * void begin(std::string varName, std::string fileName) { + * ESP_LOGD(tag, "MyMultiPart begin(): varName=%s, fileName=%s", + * varName.c_str(), fileName.c_str()); + * } + * + * void end() { + * ESP_LOGD(tag, "MyMultiPart end()"); + * } + * + * void data(std::string data) { + * ESP_LOGD(tag, "MyMultiPart data(): length=%d", data.length()); + * } + * + * void multipartEnd() { + * ESP_LOGD(tag, "MyMultiPart multipartEnd()"); + * } + * + * void multipartStart() { + * ESP_LOGD(tag, "MyMultiPart multipartStart()"); + * } + * }; + * + * class MyMultiPartFactory : public WebServer::HTTPMultiPartFactory { + * WebServer::HTTPMultiPart *createNew() { + * return new MyMultiPart(); + * } + * }; + * @endcode + */ + class HTTPMultiPartFactory { + public: + /** + * @brief Create a new HTTPMultiPart instance. + * @return A new HTTPMultiPart instance. + */ + virtual HTTPMultiPart* newInstance() = 0; + + }; + + /** + * @brief The handler for path matching. + * + */ + class PathHandler { + public: + PathHandler(const std::string& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + PathHandler(std::string&& method, const std::string& pathPattern, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + bool match(const char* method, size_t method_len, const char* path); + void invoke(HTTPRequest* request, HTTPResponse* response); + + private: + std::string m_method; + std::regex m_pattern; + void (*m_requestHandler)(WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse); + + }; // PathHandler + + /** + * @brief A WebSocket handler for handling WebSockets. + */ + class WebSocketHandler { + public: + void onCreated(); + virtual void onMessage(const std::string& message); + void onClosed(); + void sendData(const std::string& message); + void sendData(const uint8_t* data, uint32_t size); + void close(); + + private: + struct mg_connection* m_mgConnection; + + }; + + class WebSocketHandlerFactory { + public: + virtual WebSocketHandler* newInstance() = 0; + + }; + + WebServer(); + virtual ~WebServer(); + void addPathHandler(const std::string& method, const std::string& pathExpr, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + void addPathHandler(std::string&& method, const std::string& pathExpr, void (*webServerRequestHandler) (WebServer::HTTPRequest* pHttpRequest, WebServer::HTTPResponse* pHttpResponse)); + const std::string& getRootPath(); + void setMultiPartFactory(HTTPMultiPartFactory* pMultiPartFactory); + void setRootPath(const std::string& path); + void setRootPath(std::string&& path); + void setWebSocketHandlerFactory(WebSocketHandlerFactory* pWebSocketHandlerFactory); + void start(unsigned short port = 80); + void processRequest(struct mg_connection* mgConnection, struct http_message* message); + void continueConnection(struct mg_connection* mgConnection); + HTTPMultiPartFactory* m_pMultiPartFactory; + WebSocketHandlerFactory* m_pWebSocketHandlerFactory; + private: - std::string m_rootPath; - std::vector m_pathHandlers; - std::map unfinishedConnection; + std::string m_rootPath; + std::vector m_pathHandlers; + std::map unfinishedConnection; + }; #endif // CONFIG_MONGOOSE_PRESENT diff --git a/cpp_utils/WebSocket.cpp b/cpp_utils/WebSocket.cpp index 791be38c..6930f3b6 100644 --- a/cpp_utils/WebSocket.cpp +++ b/cpp_utils/WebSocket.cpp @@ -21,12 +21,12 @@ extern "C" { static const char* LOG_TAG = "WebSocket"; // WebSocket op codes as found in a WebSocket frame. -static const int OPCODE_CONTINUE = 0x00; -static const int OPCODE_TEXT = 0x01; -static const int OPCODE_BINARY = 0x02; -static const int OPCODE_CLOSE = 0x08; -static const int OPCODE_PING = 0x09; -static const int OPCODE_PONG = 0x0a; +static const uint8_t OPCODE_CONTINUE = 0x00; +static const uint8_t OPCODE_TEXT = 0x01; +static const uint8_t OPCODE_BINARY = 0x02; +static const uint8_t OPCODE_CLOSE = 0x08; +static const uint8_t OPCODE_PING = 0x09; +static const uint8_t OPCODE_PONG = 0x0a; // Structure definition for the WebSocket frame. @@ -50,8 +50,8 @@ struct Frame { */ static void dumpFrame(Frame frame) { std::ostringstream oss; - oss << "Fin: " << (int)frame.fin << ", OpCode: " << (int)frame.opCode; - switch(frame.opCode) { + oss << "Fin: " << (int) frame.fin << ", OpCode: " << (int) frame.opCode; + switch (frame.opCode) { case OPCODE_BINARY: { oss << " BINARY"; break; @@ -81,7 +81,7 @@ static void dumpFrame(Frame frame) { break; } } - oss << ", Mask: " << (int)frame.mask << ", len: " << (int)frame.len; + oss << ", Mask: " << (int) frame.mask << ", len: " << (int) frame.len; ESP_LOGD(LOG_TAG, "WebSocket frame: %s", oss.str().c_str()); } // dumpFrame @@ -101,6 +101,7 @@ class WebSocketReader: public Task { void end() { m_end = true; } + private: bool m_end; /** @@ -114,14 +115,12 @@ class WebSocketReader: public Task { Socket peerSocket = pWebSocket->getSocket(); Frame frame; - while(1) { - if (m_end) { - break; - } + while (true) { + if (m_end) break; ESP_LOGD("WebSocketReader", "Waiting on socket data for socket %s", peerSocket.toString().c_str()); - int length = peerSocket.receive((uint8_t*)&frame, sizeof(frame), true); // Read exact + int length = peerSocket.receive((uint8_t*) &frame, sizeof(frame), true); // Read exact if (length != sizeof(frame)) { - ESP_LOGD(LOG_TAG, "Socket read error"); + ESP_LOGD("WebSocketReader", "Socket read error"); pWebSocket->close(); return; } @@ -135,12 +134,12 @@ class WebSocketReader: public Task { payloadLen = frame.len; } else if (frame.len == 126) { uint16_t tempLen; - peerSocket.receive((uint8_t*)&tempLen, sizeof(tempLen), true); + peerSocket.receive((uint8_t*) &tempLen, sizeof(tempLen), true); payloadLen = ntohs(tempLen); } else if (frame.len == 127) { uint64_t tempLen; - peerSocket.receive((uint8_t*)&tempLen, sizeof(tempLen), true); - payloadLen = ntohl((uint32_t)tempLen); + peerSocket.receive((uint8_t*) &tempLen, sizeof(tempLen), true); + payloadLen = ntohl((uint32_t) tempLen); } if (frame.mask == 1) { peerSocket.receive(mask, sizeof(mask), true); @@ -152,13 +151,13 @@ class WebSocketReader: public Task { ESP_LOGD("WebSocketReader", "Web socket payload, length=%d:", payloadLen); } - WebSocketHandler *pWebSocketHandler = pWebSocket->getHandler(); - switch(frame.opCode) { + WebSocketHandler* pWebSocketHandler = pWebSocket->getHandler(); + switch (frame.opCode) { case OPCODE_TEXT: case OPCODE_BINARY: { if (pWebSocketHandler != nullptr) { - WebSocketInputStreambuf streambuf(pWebSocket->getSocket(), payloadLen, frame.mask==1?mask:nullptr); - pWebSocketHandler->onMessage(&streambuf); + WebSocketInputStreambuf streambuf(pWebSocket->getSocket(), payloadLen, (frame.mask == 1) ? mask : nullptr); + pWebSocketHandler->onMessage(&streambuf, pWebSocket); //streambuf.discard(); } break; @@ -187,12 +186,12 @@ class WebSocketReader: public Task { } default: { - ESP_LOGD("WebSocketReader", "Unknown opcode: %d", frame.opCode); + ESP_LOGD("WebSocketReader", "Unknown opcode: %d", frame.opCode); break; } } // Switch opCode - } // While (1) + } // while (true) ESP_LOGD("WebSocketReader", "<< run"); } // run }; // WebSocketReader @@ -203,7 +202,8 @@ class WebSocketReader: public Task { * If no over-riding handler is provided for the "close" event, this method is called. */ void WebSocketHandler::onClose() { - ESP_LOGD("WebSocketHandler", ">> WebSocketHandler:onClose()"); + ESP_LOGD("WebSocketHandler", ">> onClose"); + ESP_LOGD("WebSocketHandler", "<< onClose"); } // onClose @@ -217,8 +217,9 @@ void WebSocketHandler::onClose() { * ``` * This will read the whole message into the string stream. */ -void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf) { - ESP_LOGD("WebSocketHandler", ">> onMessage") +void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreambuf, WebSocket* pWebSocket) { + ESP_LOGD("WebSocketHandler", ">> onMessage"); + ESP_LOGD("WebSocketHandler", "<< onMessage"); } // onData @@ -227,7 +228,8 @@ void WebSocketHandler::onMessage(WebSocketInputStreambuf* pWebSocketInputStreamb * If no over-riding handler is provided for the "error" event, this method is called. */ void WebSocketHandler::onError(std::string error) { - ESP_LOGD("WebSocketHandler", ">> WebSocketHandler:onError()"); + ESP_LOGD("WebSocketHandler", ">> onError: %s", error.c_str()); + ESP_LOGD("WebSocketHandler", "<< onError"); } // onError @@ -276,7 +278,7 @@ void WebSocket::close(uint16_t status, std::string message) { frame.opCode = OPCODE_CLOSE; frame.mask = 0; frame.len = message.length() + 2; - int rc = m_socket.send((uint8_t *)&frame, sizeof(frame)); + int rc = m_socket.send((uint8_t*) &frame, sizeof(frame)); if (rc > 0) { rc = m_socket.send(status); @@ -326,21 +328,50 @@ void WebSocket::send(std::string data, uint8_t sendType) { frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; - frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; + frame.opCode = (sendType == SEND_TYPE_TEXT) ? OPCODE_TEXT : OPCODE_BINARY; frame.mask = 0; if (data.length() < 126) { frame.len = data.length(); - m_socket.send((uint8_t *)&frame, sizeof(frame)); + m_socket.send((uint8_t*) &frame, sizeof(frame)); } else { frame.len = 126; - m_socket.send((uint8_t *)&frame, sizeof(frame)); - m_socket.send((uint16_t)data.length()); + m_socket.send((uint8_t*) &frame, sizeof(frame)); + m_socket.send(htons((uint16_t) data.length())); // Convert to network byte order from host byte order } m_socket.send((uint8_t*)data.data(), data.length()); ESP_LOGD(LOG_TAG, "<< send"); } // send_cpp +/** + * @brief Send data down the web socket + * See the WebSocket spec (RFC6455) section "6.1 Sending Data". + * We build a WebSocket frame, send the frame followed by the data. + * @param [in] data The data to send down the WebSocket. + * @param [in] sendType The type of payload. Either SEND_TYPE_TEXT or SEND_TYPE_BINARY. + */ +void WebSocket::send(uint8_t* data, uint16_t length, uint8_t sendType) { + ESP_LOGD(LOG_TAG, ">> send: Length: %d", length); + Frame frame; + frame.fin = 1; + frame.rsv1 = 0; + frame.rsv2 = 0; + frame.rsv3 = 0; + frame.opCode = (sendType==SEND_TYPE_TEXT) ? OPCODE_TEXT : OPCODE_BINARY; + frame.mask = 0; + if (length < 126) { + frame.len = length; + m_socket.send((uint8_t*) &frame, sizeof(frame)); + } else { + frame.len = 126; + m_socket.send((uint8_t*) &frame, sizeof(frame)); + m_socket.send(htons(length)); // Convert to network byte order from host byte order + } + m_socket.send(data, length); + ESP_LOGD(LOG_TAG, "<< send"); +} + + /** * @brief Set the Web socket handler associated with this Websocket. * @@ -373,7 +404,7 @@ void WebSocket::startReader() { WebSocketInputStreambuf::WebSocketInputStreambuf( Socket socket, size_t dataLength, - uint8_t *pMask, + uint8_t* pMask, size_t bufferSize) { m_socket = socket; // The socket we will be reading from m_dataLength = dataLength; // The size of the record we wish to read. @@ -444,7 +475,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { // We wish to refill the buffer. We want to read data from the socket. We want to read either // the size of the buffer to fill it or the maximum number of bytes remaining to be read. // We will choose which ever is smaller as the number of bytes to read into the buffer. - int remainingBytes = getRecordSize()-m_sizeRead; + size_t remainingBytes = getRecordSize() - m_sizeRead; size_t sizeToRead; if (remainingBytes < m_bufferSize) { sizeToRead = remainingBytes; @@ -453,7 +484,7 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { } ESP_LOGD("WebSocketInputRecordStreambuf", "- getting next buffer of data; size request: %d", sizeToRead); - int bytesRead = m_socket.receive((uint8_t*)m_buffer, sizeToRead, true); + size_t bytesRead = m_socket.receive((uint8_t*) m_buffer, sizeToRead, true); if (bytesRead == 0) { ESP_LOGD("WebSocketInputRecordStreambuf", "<< underflow: Read 0 bytes"); return EOF; @@ -461,8 +492,8 @@ WebSocketInputStreambuf::int_type WebSocketInputStreambuf::underflow() { // If the WebSocket frame shows that we have a mask bit set then we have to unmask the data. if (m_pMask != nullptr) { - for (int i=0; i> onMessage"); // Test to see if we are currently active. If not, this is the start of a transfer. if (!m_active) { - ESP_LOGD("FileTransferWebSocketHandler", "Not yet active!"); // Read a chunk of data into memory. std::stringstream buffer; @@ -82,7 +71,7 @@ class FileTransferWebSocketHandler : public WebSocketHandler { // "length": // Length of file. Optional. // } JsonObject jo = JSON::parseObject(buffer.str()); - m_fileName = jo.getString("name"); + m_fileName = jo.getString("name"); assert(m_fileName.length() > 0); // Doesn't make any sense to receive a zero length file name. if (jo.hasItem("length")) { m_fileLength = jo.getInt("length"); @@ -91,9 +80,9 @@ class FileTransferWebSocketHandler : public WebSocketHandler { ESP_LOGD("FileTransferWebSocketHandler", "Target file is %s", fileName.c_str()); // If the file to create ends in a "/" then we are being asked to create a directory. - if (m_fileName.substr(m_fileName.size()-1)=="/") { + if (m_fileName.substr(m_fileName.size() - 1) == "/") { ESP_LOGD("FileTransferWebSocketHandler", "Is a directory!!"); - fileName = fileName.substr(0, fileName.size()-1); // Remove the trailing slash + fileName = fileName.substr(0, fileName.size() - 1); // Remove the trailing slash struct stat statbuf; if (stat(fileName.c_str(), &statbuf) == 0) { if (S_ISREG(statbuf.st_mode)) { @@ -114,7 +103,7 @@ class FileTransferWebSocketHandler : public WebSocketHandler { return; } } - m_active = true; + m_active = true; ESP_LOGD("FileTransferWebSocketHandler", "Filename: %s, length: %d", fileName.c_str(), m_fileLength); } // !active --- Not active else { @@ -133,7 +122,6 @@ class FileTransferWebSocketHandler : public WebSocketHandler { } } // onMessage - /** * @brief Handle a close event on the web socket. */ @@ -146,8 +134,17 @@ class FileTransferWebSocketHandler : public WebSocketHandler { if (m_ofStream.is_open()) { m_ofStream.close(); // Close the file now that we have finished writing to it. } - delete this; // Delete ourself. + delete this; // Delete ourselves. } // onClose + +private: + std::string m_fileName; // The name of the file we are receiving. + uint32_t m_fileLength; // We may optionally receive a file length. + uint32_t m_sizeReceived; // The size of the data actually received so far. + bool m_active; // Are we actively processing a file. + std::ofstream m_ofStream; // The file stream we are writing to when active. + std::string m_rootPath; // The root path for file names. + }; // FileTransferWebSocketHandler } // End un-named namespace diff --git a/cpp_utils/WebSocketFileTransfer.h b/cpp_utils/WebSocketFileTransfer.h index 98cbda9b..6eb8de43 100644 --- a/cpp_utils/WebSocketFileTransfer.h +++ b/cpp_utils/WebSocketFileTransfer.h @@ -17,7 +17,7 @@ class WebSocketFileTransfer { public: WebSocketFileTransfer(std::string rootPath); - void start(WebSocket *pWebSocket); + void start(WebSocket* pWebSocket); }; #endif /* COMPONENTS_CPP_UTILS_WEBSOCKETFILETRANSFER_H_ */ diff --git a/cpp_utils/WiFi.cpp b/cpp_utils/WiFi.cpp index 4054dfe5..3c6d4112 100644 --- a/cpp_utils/WiFi.cpp +++ b/cpp_utils/WiFi.cpp @@ -47,23 +47,27 @@ static void setDNSServer(char *ip) { * @brief Creates and uses a default event handler */ WiFi::WiFi() - : ip(0) - , gw(0) - , netmask(0) - , m_pWifiEventHandler(nullptr) + : ip(0) + , gw(0) + , netmask(0) + , m_pWifiEventHandler(nullptr) { m_eventLoopStarted = false; m_initCalled = false; - m_pWifiEventHandler = new WiFiEventHandler(); - m_apConnected = false; // Are we connected to an access point? + //m_pWifiEventHandler = new WiFiEventHandler(); + m_apConnectionStatus = UINT8_MAX; // Are we connected to an access point? } // WiFi + /** * @brief Deletes the event handler that was used by the class */ WiFi::~WiFi() { - delete m_pWifiEventHandler; -} + if (m_pWifiEventHandler != nullptr) { + delete m_pWifiEventHandler; + m_pWifiEventHandler = nullptr; + } +} // ~WiFi /** @@ -90,14 +94,14 @@ void WiFi::addDNSServer(const std::string& ip) { void WiFi::addDNSServer(const char* ip) { ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { + if (inet_pton(AF_INET, ip, &dns_server)) { addDNSServer(ip); } } // addDNSServer void WiFi::addDNSServer(ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*) (&ip))[0], ((uint8_t*) (&ip))[1], ((uint8_t*) (&ip))[2], ((uint8_t*) (&ip))[3]); init(); ::dns_setserver(m_dnsCount, &ip); m_dnsCount++; @@ -128,14 +132,14 @@ void WiFi::setDNSServer(int numdns, const std::string& ip) { void WiFi::setDNSServer(int numdns, const char* ip) { ip_addr_t dns_server; - if(inet_pton(AF_INET, ip, &dns_server)) { + if (inet_pton(AF_INET, ip, &dns_server)) { setDNSServer(numdns, dns_server); } } // setDNSServer void WiFi::setDNSServer(int numdns, ip_addr_t ip) { - ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*)(&ip))[0], ((uint8_t*)(&ip))[1], ((uint8_t*)(&ip))[2], ((uint8_t*)(&ip))[3]); + ESP_LOGD(LOG_TAG, "Setting DNS[%d] to %d.%d.%d.%d", m_dnsCount, ((uint8_t*) (&ip))[0], ((uint8_t*) (&ip))[1], ((uint8_t*) (&ip))[2], ((uint8_t*) (&ip))[3]); init(); ::dns_setserver(numdns, &ip); } // setDNSServer @@ -149,26 +153,27 @@ void WiFi::setDNSServer(int numdns, ip_addr_t ip) { * @param [in] ssid The network SSID of the access point to which we wish to connect. * @param [in] password The password of the access point to which we wish to connect. * @param [in] waitForConnection Block until the connection has an outcome. - * @return N/A. + * @param [in] mode WIFI_MODE_AP for normal or WIFI_MODE_APSTA if you want to keep an Access Point running while you connect + * @return ESP_OK if we are now connected and wifi_err_reason_t if not. */ -bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection){ +uint8_t WiFi::connectAP(const std::string& ssid, const std::string& password, bool waitForConnection, wifi_mode_t mode){ ESP_LOGD(LOG_TAG, ">> connectAP"); - m_apConnected = false; + m_apConnectionStatus = UINT8_MAX; init(); if (ip != 0 && gw != 0 && netmask != 0) { - ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client + ::tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); // Don't run a DHCP client - tcpip_adapter_ip_info_t ipInfo; - ipInfo.ip.addr = ip; - ipInfo.gw.addr = gw; - ipInfo.netmask.addr = netmask; + tcpip_adapter_ip_info_t ipInfo; + ipInfo.ip.addr = ip; + ipInfo.gw.addr = gw; + ipInfo.netmask.addr = netmask; - ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); + ::tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo); } - esp_err_t errRc = ::esp_wifi_set_mode(WIFI_MODE_STA); + esp_err_t errRc = ::esp_wifi_set_mode(mode); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_wifi_set_mode: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); @@ -190,20 +195,22 @@ bool WiFi::connectAP(const std::string& ssid, const std::string& password, bool } m_connectFinished.take("connectAP"); // Take the semaphore to wait for a connection. - - errRc = ::esp_wifi_connect(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); + do { + ESP_LOGD(LOG_TAG, "esp_wifi_connect"); + errRc = ::esp_wifi_connect(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_connect: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } + while (!m_connectFinished.take(5000, "connectAP")); // retry if not connected within 5s + m_connectFinished.give(); - m_connectFinished.wait("connectAP"); // Wait for the completion of the connection. ESP_LOGD(LOG_TAG, "<< connectAP"); - return m_apConnected; // Return true if we are now connected and false if not. + return m_apConnectionStatus; // Return ESP_OK if we are now connected and wifi_err_reason_t if not. } // connectAP - /** * @brief Dump diagnostics to the log. */ @@ -217,6 +224,14 @@ void WiFi::dump() { } // dump +/** + * @brief Returns whether wifi is connected to an access point + */ +bool WiFi::isConnectedToAP() { + return m_apConnectionStatus; +} // isConnected + + /** * @brief Primary event handler interface. */ @@ -226,18 +241,23 @@ void WiFi::dump() { // processing. We can then retrieve the specific/custom event handler from within it and invoke that. This then makes this // an indirection vector to the real caller. - WiFi *pWiFi = (WiFi *)ctx; // retrieve the WiFi object from the passed in context. + WiFi* pWiFi = (WiFi*) ctx; // retrieve the WiFi object from the passed in context. // Invoke the event handler. - esp_err_t rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + esp_err_t rc; + if (pWiFi->m_pWifiEventHandler != nullptr) { + rc = pWiFi->m_pWifiEventHandler->getEventHandler()(pWiFi->m_pWifiEventHandler, event); + } else { + rc = ESP_OK; + } // If the event we received indicates that we now have an IP address or that a connection was disconnected then unlock the mutex that // indicates we are waiting for a connection complete. if (event->event_id == SYSTEM_EVENT_STA_GOT_IP || event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the flag. - pWiFi->m_apConnected = true; + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { // If we connected and have an IP, change the status to ESP_OK. Otherwise, change it to the reason code. + pWiFi->m_apConnectionStatus = ESP_OK; } else { - pWiFi->m_apConnected = false; + pWiFi->m_apConnectionStatus = event->event_info.disconnected.reason; } pWiFi->m_connectFinished.give(); } @@ -280,10 +300,43 @@ std::string WiFi::getApSSID() { wifi_config_t conf; //init(); esp_wifi_get_config(WIFI_IF_AP, &conf); - return std::string((char *)conf.sta.ssid); + return std::string((char*) conf.sta.ssid); } // getApSSID +/** + * @brief Get the current ESP32 IP form AP. + * @return The ESP32 IP. + */ +std::string WiFi::getApIp() { + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + +/** + * @brief Get the current AP netmask. + * @return The Netmask IP. + */ +std::string WiFi::getApNetmask() { + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + +/** + * @brief Get the current AP Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getApGateway() { + tcpip_adapter_ip_info_t ipInfo = getApIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + /** * @brief Lookup an IP address by host name. * @@ -298,13 +351,13 @@ struct in_addr WiFi::getHostByName(const std::string& hostName) { struct in_addr WiFi::getHostByName(const char* hostName) { struct in_addr retAddr; - struct hostent *he = gethostbyname(hostName); + struct hostent* he = gethostbyname(hostName); if (he == nullptr) { retAddr.s_addr = 0; ESP_LOGD(LOG_TAG, "Unable to resolve %s - %d", hostName, h_errno); } else { - retAddr = *(struct in_addr *)(he->h_addr_list[0]); - ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t *)&retAddr); + retAddr = *(struct in_addr*) (he->h_addr_list[0]); + ESP_LOGD(LOG_TAG, "resolved %s to %.8x", hostName, *(uint32_t*) &retAddr); } return retAddr; } // getHostByName @@ -317,7 +370,7 @@ struct in_addr WiFi::getHostByName(const char* hostName) { std::string WiFi::getMode() { wifi_mode_t mode; esp_wifi_get_mode(&mode); - switch(mode) { + switch (mode) { case WIFI_MODE_NULL: return "WIFI_MODE_NULL"; case WIFI_MODE_STA: @@ -343,6 +396,42 @@ tcpip_adapter_ip_info_t WiFi::getStaIpInfo() { } // getStaIpInfo +/** + * @brief Get the current ESP32 IP form STA. + * @return The ESP32 IP. + */ +std::string WiFi::getStaIp() { + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.ip.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaIp + + +/** + * @brief Get the current STA netmask. + * @return The Netmask IP. + */ +std::string WiFi::getStaNetmask() { + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.netmask.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaNetmask + + +/** + * @brief Get the current STA Gateway IP. + * @return The Gateway IP. + */ +std::string WiFi::getStaGateway() { + tcpip_adapter_ip_info_t ipInfo = getStaIpInfo(); + char ipAddrStr[30]; + inet_ntop(AF_INET, &ipInfo.gw.addr, ipAddrStr, sizeof(ipAddrStr)); + return std::string(ipAddrStr); +} // getStaGateway + + /** * @brief Get the MAC address of the STA interface. * @return The MAC address of the STA interface. @@ -363,7 +452,7 @@ std::string WiFi::getStaMac() { std::string WiFi::getStaSSID() { wifi_config_t conf; esp_wifi_get_config(WIFI_IF_STA, &conf); - return std::string((char *)conf.ap.ssid); + return std::string((char*) conf.ap.ssid); } // getStaSSID @@ -371,7 +460,6 @@ std::string WiFi::getStaSSID() { * @brief Initialize WiFi. */ /* PRIVATE */ void WiFi::init() { - // If we have already started the event loop, then change the handler otherwise // start the event loop. if (m_eventLoopStarted) { @@ -441,15 +529,20 @@ std::vector WiFi::scan() { esp_err_t rc = ::esp_wifi_scan_start(&conf, true); if (rc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); - return apRecords; + ESP_LOGE(LOG_TAG, "esp_wifi_scan_start: %d", rc); + return apRecords; } uint16_t apCount; // Number of access points available. rc = ::esp_wifi_scan_get_ap_num(&apCount); - ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_wifi_scan_get_ap_num: %d", rc); + return apRecords; + } else { + ESP_LOGD(LOG_TAG, "Count of found access points: %d", apCount); + } - wifi_ap_record_t* list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + wifi_ap_record_t* list = (wifi_ap_record_t*) malloc(sizeof(wifi_ap_record_t) * apCount); if (list == nullptr) { ESP_LOGE(LOG_TAG, "Failed to allocate memory"); return apRecords; @@ -461,18 +554,18 @@ std::vector WiFi::scan() { abort(); } - for (auto i=0; i rhs.m_rssi;}); + [](const WiFiAPRecord& lhs, const WiFiAPRecord& rhs){ return lhs.m_rssi > rhs.m_rssi; }); return apRecords; } // scan @@ -482,9 +575,38 @@ std::vector WiFi::scan() { * * @param[in] ssid The SSID to use to advertize for stations. * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @return N/A. + */ +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth) { + startAP(ssid, password, auth, 0, false, 4); +} // startAP + + +/** + * @brief Start being an access point. + * + * @param[in] ssid The SSID to use to advertize for stations. + * @param[in] password The password to use for station connections. + * @param[in] auth The authorization mode for access to this access point. Options are: + * * WIFI_AUTH_OPEN + * * WIFI_AUTH_WPA_PSK + * * WIFI_AUTH_WPA2_PSK + * * WIFI_AUTH_WPA_WPA2_PSK + * * WIFI_AUTH_WPA2_ENTERPRISE + * * WIFI_AUTH_WEP + * @param[in] channel from the access point. + * @param[in] is the ssid hidden, ore not. + * @param[in] limiting number of clients. * @return N/A. */ -void WiFi::startAP(const std::string& ssid, const std::string& password) { +void WiFi::startAP(const std::string& ssid, const std::string& password, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection) { ESP_LOGD(LOG_TAG, ">> startAP: ssid: %s", ssid.c_str()); init(); @@ -501,10 +623,10 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { ::memcpy(apConfig.ap.ssid, ssid.data(), ssid.size()); apConfig.ap.ssid_len = ssid.size(); ::memcpy(apConfig.ap.password, password.data(), password.size()); - apConfig.ap.channel = 0; - apConfig.ap.authmode = WIFI_AUTH_OPEN; - apConfig.ap.ssid_hidden = 0; - apConfig.ap.max_connection = 4; + apConfig.ap.channel = channel; + apConfig.ap.authmode = auth; + apConfig.ap.ssid_hidden = (uint8_t) ssid_hidden; + apConfig.ap.max_connection = max_connection; apConfig.ap.beacon_interval = 100; errRc = ::esp_wifi_set_config(WIFI_IF_AP, &apConfig); @@ -518,18 +640,23 @@ void WiFi::startAP(const std::string& ssid, const std::string& password) { ESP_LOGE(LOG_TAG, "esp_wifi_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); abort(); } + + errRc = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "tcpip_adapter_dhcps_start: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< startAP"); } // startAP - /** * @brief Set the event handler to use to process detected events. * @param[in] wifiEventHandler The class that will be used to process events. */ void WiFi::setWifiEventHandler(WiFiEventHandler* wifiEventHandler) { - ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t)wifiEventHandler); - this->m_pWifiEventHandler = wifiEventHandler; - ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); + ESP_LOGD(LOG_TAG, ">> setWifiEventHandler: 0x%d", (uint32_t) wifiEventHandler); + this->m_pWifiEventHandler = wifiEventHandler; + ESP_LOGD(LOG_TAG, "<< setWifiEventHandler"); } // setWifiEventHandler @@ -555,19 +682,16 @@ void WiFi::setIPInfo(const std::string& ip, const std::string& gw, const std::st } // setIPInfo - void WiFi::setIPInfo(const char* ip, const char* gw, const char* netmask) { uint32_t new_ip; uint32_t new_gw; uint32_t new_netmask; - auto success = (bool)inet_pton(AF_INET, ip, &new_ip); + auto success = (bool) inet_pton(AF_INET, ip, &new_ip); success = success && inet_pton(AF_INET, gw, &new_gw); success = success && inet_pton(AF_INET, netmask, &new_netmask); - if(!success) { - return; - } + if (!success) return; setIPInfo(new_ip, new_gw, new_netmask); } // setIPInfo @@ -586,7 +710,7 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { this->gw = gw; this->netmask = netmask; - if(ip != 0 && gw != 0 && netmask != 0) { + if (ip != 0 && gw != 0 && netmask != 0) { tcpip_adapter_ip_info_t ipInfo; ipInfo.ip.addr = ip; ipInfo.gw.addr = gw; @@ -607,7 +731,7 @@ void WiFi::setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask) { */ std::string WiFiAPRecord::toString() { std::string auth; - switch(getAuthMode()) { + switch (getAuthMode()) { case WIFI_AUTH_OPEN: auth = "WIFI_AUTH_OPEN"; break; @@ -623,17 +747,19 @@ std::string WiFiAPRecord::toString() { case WIFI_AUTH_WPA_WPA2_PSK: auth = "WIFI_AUTH_WPA_WPA2_PSK"; break; - default: - auth = ""; - break; - } -// std::stringstream s; -// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; - auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); - sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); - return std::string(std::move(info_str)); + default: + auth = ""; + break; + } +// std::stringstream s; +// s<< "ssid: " << m_ssid << ", auth: " << auth << ", rssi: " << m_rssi; + auto info_str = (char*) malloc(6 + 32 + 8 + 22 + 8 + 3 + 1); + sprintf(info_str, "ssid: %s, auth: %s, rssi: %d", m_ssid.c_str(), auth.c_str(), (int) m_rssi); + return std::string(std::move(info_str)); } // toString + +/* MDNS::MDNS() { esp_err_t errRc = ::mdns_init(TCPIP_ADAPTER_IF_STA, &m_mdns_server); if (errRc != ESP_OK) { @@ -642,12 +768,15 @@ MDNS::MDNS() { } } + MDNS::~MDNS() { if (m_mdns_server != nullptr) { mdns_free(m_mdns_server); } m_mdns_server = nullptr; } +*/ + /** * @brief Define the service for mDNS. @@ -657,24 +786,26 @@ MDNS::~MDNS() { * @param [in] port * @return N/A. */ +/* void MDNS::serviceAdd(const std::string& service, const std::string& proto, uint16_t port) { - serviceAdd(service.c_str(), proto.c_str(), port); + serviceAdd(service.c_str(), proto.c_str(), port); } // serviceAdd void MDNS::serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance) { - serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); + serviceInstanceSet(service.c_str(), proto.c_str(), instance.c_str()); } // serviceInstanceSet void MDNS::servicePortSet(const std::string& service, const std::string& proto, uint16_t port) { - servicePortSet(service.c_str(), proto.c_str(), port); + servicePortSet(service.c_str(), proto.c_str(), port); } // servicePortSet void MDNS::serviceRemove(const std::string& service, const std::string& proto) { - serviceRemove(service.c_str(), proto.c_str()); + serviceRemove(service.c_str(), proto.c_str()); } // serviceRemove +*/ /** @@ -683,9 +814,11 @@ void MDNS::serviceRemove(const std::string& service, const std::string& proto) { * @param [in] hostname The host name to set against the mDNS. * @return N/A. */ +/* void MDNS::setHostname(const std::string& hostname) { - setHostname(hostname.c_str()); + setHostname(hostname.c_str()); } // setHostname +*/ /** @@ -694,9 +827,12 @@ void MDNS::setHostname(const std::string& hostname) { * @param [in] instance The instance name to set against the mDNS. * @return N/A. */ +/* void MDNS::setInstance(const std::string& instance) { - setInstance(instance.c_str()); + setInstance(instance.c_str()); } // setInstance +*/ + /** * @brief Define the service for mDNS. @@ -706,6 +842,7 @@ void MDNS::setInstance(const std::string& instance) { * @param [in] port * @return N/A. */ +/* void MDNS::serviceAdd(const char* service, const char* proto, uint16_t port) { esp_err_t errRc = ::mdns_service_add(m_mdns_server, service, proto, port); if (errRc != ESP_OK) { @@ -734,12 +871,13 @@ void MDNS::servicePortSet(const char* service, const char* proto, uint16_t port) void MDNS::serviceRemove(const char* service, const char* proto) { - esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + esp_err_t errRc = ::mdns_service_remove(m_mdns_server, service, proto); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_service_remove: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // serviceRemove +*/ /** @@ -748,13 +886,15 @@ void MDNS::serviceRemove(const char* service, const char* proto) { * @param [in] hostname The host name to set against the mDNS. * @return N/A. */ +/* void MDNS::setHostname(const char* hostname) { esp_err_t errRc = ::mdns_set_hostname(m_mdns_server,hostname); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_hostname: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setHostname +*/ /** @@ -763,10 +903,12 @@ void MDNS::setHostname(const char* hostname) { * @param [in] instance The instance name to set against the mDNS. * @return N/A. */ +/* void MDNS::setInstance(const char* instance) { - esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - abort(); - } + esp_err_t errRc = ::mdns_set_instance(m_mdns_server, instance); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "mdns_set_instance: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + abort(); + } } // setInstance +*/ diff --git a/cpp_utils/WiFi.h b/cpp_utils/WiFi.h index e9af3d67..c0bfa07a 100644 --- a/cpp_utils/WiFi.h +++ b/cpp_utils/WiFi.h @@ -19,63 +19,68 @@ /** * @brief Manage mDNS server. */ +/* class MDNS { public: - MDNS(); - ~MDNS(); - void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); - void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); - void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); - void serviceRemove(const std::string& service, const std::string& proto); - void setHostname(const std::string& hostname); - void setInstance(const std::string& instance); - // If we the above functions with a basic char*, a copy would be created into an std::string, - // making the whole thing require twice as much processing power and speed - void serviceAdd(const char* service, const char* proto, uint16_t port); - void serviceInstanceSet(const char* service, const char* proto, const char* instance); - void servicePortSet(const char* service, const char* proto, uint16_t port); - void serviceRemove(const char* service, const char* proto); - void setHostname(const char* hostname); - void setInstance(const char* instance); + MDNS(); + ~MDNS(); + void serviceAdd(const std::string& service, const std::string& proto, uint16_t port); + void serviceInstanceSet(const std::string& service, const std::string& proto, const std::string& instance); + void servicePortSet(const std::string& service, const std::string& proto, uint16_t port); + void serviceRemove(const std::string& service, const std::string& proto); + void setHostname(const std::string& hostname); + void setInstance(const std::string& instance); + // If we the above functions with a basic char*, a copy would be created into an std::string, + // making the whole thing require twice as much processing power and speed + void serviceAdd(const char* service, const char* proto, uint16_t port); + void serviceInstanceSet(const char* service, const char* proto, const char* instance); + void servicePortSet(const char* service, const char* proto, uint16_t port); + void serviceRemove(const char* service, const char* proto); + void setHostname(const char* hostname); + void setInstance(const char* instance); + private: - mdns_server_t *m_mdns_server = nullptr; + mdns_server_t* m_mdns_server = nullptr; + }; +*/ class WiFiAPRecord { public: - friend class WiFi; - - /** - * @brief Get the auth mode. - * @return The auth mode. - */ - wifi_auth_mode_t getAuthMode() { - return m_authMode; - } - - /** - * @brief Get the RSSI. - * @return the RSSI. - */ - int8_t getRSSI() { - return m_rssi; - } - - /** - * @brief Get the SSID. - * @return the SSID. - */ - std::string getSSID() { - return m_ssid; - } - - std::string toString(); + friend class WiFi; + + /** + * @brief Get the auth mode. + * @return The auth mode. + */ + wifi_auth_mode_t getAuthMode() { + return m_authMode; + } + + /** + * @brief Get the RSSI. + * @return the RSSI. + */ + int8_t getRSSI() { + return m_rssi; + } + + /** + * @brief Get the SSID. + * @return the SSID. + */ + std::string getSSID() { + return m_ssid; + } + + std::string toString(); private: - uint8_t m_bssid[6]; - int8_t m_rssi; - std::string m_ssid; - wifi_auth_mode_t m_authMode; + uint8_t m_bssid[6]; + int8_t m_rssi; + std::string m_ssid; + wifi_auth_mode_t m_authMode; + }; /** @@ -105,44 +110,53 @@ class WiFiAPRecord { */ class WiFi { private: - static esp_err_t eventHandler(void* ctx, system_event_t* event); - void init(); - uint32_t ip; - uint32_t gw; - uint32_t netmask; - WiFiEventHandler* m_pWifiEventHandler; - uint8_t m_dnsCount=0; - bool m_eventLoopStarted; - bool m_initCalled; - bool m_apConnected; // Are we connected to an access point? + static esp_err_t eventHandler(void* ctx, system_event_t* event); + void init(); + uint32_t ip; + uint32_t gw; + uint32_t netmask; + WiFiEventHandler* m_pWifiEventHandler; + uint8_t m_dnsCount = 0; + bool m_eventLoopStarted; + bool m_initCalled; + uint8_t m_apConnectionStatus; // ESP_OK = we are connected to an access point. Otherwise receives wifi_err_reason_t. FreeRTOS::Semaphore m_connectFinished = FreeRTOS::Semaphore("ConnectFinished"); public: - WiFi(); - ~WiFi(); - void addDNSServer(const std::string& ip); - void addDNSServer(const char* ip); - void addDNSServer(ip_addr_t ip); - void setDNSServer(int numdns, const std::string& ip); - void setDNSServer(int numdns, const char* ip); - void setDNSServer(int numdns, ip_addr_t ip); - struct in_addr getHostByName(const std::string& hostName); - struct in_addr getHostByName(const char* hostName); - bool connectAP(const std::string& ssid, const std::string& password, bool waitForConnection=true); - void dump(); - static std::string getApMac(); - static tcpip_adapter_ip_info_t getApIpInfo(); - static std::string getApSSID(); - static std::string getMode(); - static tcpip_adapter_ip_info_t getStaIpInfo(); - static std::string getStaMac(); - static std::string getStaSSID(); - std::vector scan(); - void startAP(const std::string& ssid, const std::string& passwd); - void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); - void setIPInfo(const char* ip, const char* gw, const char* netmask); - void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); - void setWifiEventHandler(WiFiEventHandler *wifiEventHandler); + WiFi(); + ~WiFi(); + void addDNSServer(const std::string& ip); + void addDNSServer(const char* ip); + void addDNSServer(ip_addr_t ip); + void setDNSServer(int numdns, const std::string& ip); + void setDNSServer(int numdns, const char* ip); + void setDNSServer(int numdns, ip_addr_t ip); + struct in_addr getHostByName(const std::string& hostName); + struct in_addr getHostByName(const char* hostName); + uint8_t connectAP(const std::string& ssid, const std::string& password, bool waitForConnection = true, wifi_mode_t mode = WIFI_MODE_STA); + void dump(); + bool isConnectedToAP(); + static std::string getApMac(); + static tcpip_adapter_ip_info_t getApIpInfo(); + static std::string getApSSID(); + static std::string getApIp(); + static std::string getApNetmask(); + static std::string getApGateway(); + static std::string getMode(); + static tcpip_adapter_ip_info_t getStaIpInfo(); + static std::string getStaMac(); + static std::string getStaSSID(); + static std::string getStaIp(); + static std::string getStaNetmask(); + static std::string getStaGateway(); + std::vector scan(); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth = WIFI_AUTH_OPEN); + void startAP(const std::string& ssid, const std::string& passwd, wifi_auth_mode_t auth, uint8_t channel, bool ssid_hidden, uint8_t max_connection); + void setIPInfo(const std::string& ip, const std::string& gw, const std::string& netmask); + void setIPInfo(const char* ip, const char* gw, const char* netmask); + void setIPInfo(uint32_t ip, uint32_t gw, uint32_t netmask); + void setWifiEventHandler(WiFiEventHandler* wifiEventHandler); + }; #endif /* MAIN_WIFI_H_ */ diff --git a/cpp_utils/WiFiEventHandler.cpp b/cpp_utils/WiFiEventHandler.cpp index 405058ad..204a0661 100644 --- a/cpp_utils/WiFiEventHandler.cpp +++ b/cpp_utils/WiFiEventHandler.cpp @@ -24,30 +24,52 @@ static const char* LOG_TAG = "WiFiEventHandler"; * @return ESP_OK if the event was handled otherwise an error. */ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { - ESP_LOGD(LOG_TAG, ">> eventHandler called: ctx=0x%x, event=0x%x", (uint32_t)ctx, (uint32_t)event); - WiFiEventHandler *pWiFiEventHandler = (WiFiEventHandler *)ctx; + ESP_LOGD(LOG_TAG, ">> eventHandler called: ctx=0x%x, event=0x%x", (uint32_t) ctx, (uint32_t) event); + WiFiEventHandler* pWiFiEventHandler = (WiFiEventHandler*) ctx; if (ctx == nullptr) { ESP_LOGD(LOG_TAG, "No context"); return ESP_OK; } esp_err_t rc = ESP_OK; - switch(event->event_id) { + switch (event->event_id) { case SYSTEM_EVENT_AP_START: { rc = pWiFiEventHandler->apStart(); break; } + case SYSTEM_EVENT_AP_STOP: { rc = pWiFiEventHandler->apStop(); break; } + + case SYSTEM_EVENT_AP_STACONNECTED: { + rc = pWiFiEventHandler->apStaConnected(event->event_info.sta_connected); + break; + } + + case SYSTEM_EVENT_AP_STADISCONNECTED: { + rc = pWiFiEventHandler->apStaDisconnected(event->event_info.sta_disconnected); + break; + } + + case SYSTEM_EVENT_SCAN_DONE: { + rc = pWiFiEventHandler->staScanDone(event->event_info.scan_done); + break; + } + + case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { + rc = pWiFiEventHandler->staAuthChange(event->event_info.auth_change); + break; + } + case SYSTEM_EVENT_STA_CONNECTED: { - rc = pWiFiEventHandler->staConnected(); + rc = pWiFiEventHandler->staConnected(event->event_info.connected); break; } case SYSTEM_EVENT_STA_DISCONNECTED: { - rc = pWiFiEventHandler->staDisconnected(); + rc = pWiFiEventHandler->staDisconnected(event->event_info.disconnected); break; } @@ -73,15 +95,15 @@ esp_err_t WiFiEventHandler::eventHandler(void* ctx, system_event_t* event) { default: break; - } - - if (pWiFiEventHandler->m_nextHandler != nullptr) { - printf("Found a next handler\n"); - rc = eventHandler(pWiFiEventHandler->m_nextHandler, event); - } else { - //printf("NOT Found a next handler\n"); - } - return rc; + } + + if (pWiFiEventHandler->m_nextHandler != nullptr) { + ESP_LOGD(LOG_TAG, "Found a next handler"); + rc = eventHandler(pWiFiEventHandler->m_nextHandler, event); + } else { + //ESP_LOGD(LOG_TAG, "NOT Found a next handler"); + } + return rc; } // eventHandler @@ -111,9 +133,9 @@ system_event_cb_t WiFiEventHandler::getEventHandler() { * @param [in] event_sta_got_ip The Station Got IP event. * @return An indication of whether or not we processed the event successfully. */ -esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { - ESP_LOGD(LOG_TAG, "default staGotIp"); - return ESP_OK; +esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t info) { + ESP_LOGD(LOG_TAG, "default staGotIp"); + return ESP_OK; } // staGotIp @@ -123,8 +145,8 @@ esp_err_t WiFiEventHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStart() { - ESP_LOGD(LOG_TAG, "default apStart"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStart"); + return ESP_OK; } // apStart @@ -134,52 +156,100 @@ esp_err_t WiFiEventHandler::apStart() { * @return An indication of whether or not we processed the event successfully. */ esp_err_t WiFiEventHandler::apStop() { - ESP_LOGD(LOG_TAG, "default apStop"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default apStop"); + return ESP_OK; } // apStop esp_err_t WiFiEventHandler::wifiReady() { - ESP_LOGD(LOG_TAG, "default wifiReady"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default wifiReady"); + return ESP_OK; } // wifiReady esp_err_t WiFiEventHandler::staStart() { - ESP_LOGD(LOG_TAG, "default staStart"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staStart"); + return ESP_OK; } // staStart esp_err_t WiFiEventHandler::staStop() { - ESP_LOGD(LOG_TAG, "default staStop"); - return ESP_OK; + ESP_LOGD(LOG_TAG, "default staStop"); + return ESP_OK; } // staStop -esp_err_t WiFiEventHandler::staConnected() { - ESP_LOGD(LOG_TAG, "default staConnected"); - return ESP_OK; +/** + * @brief Handle the Station Connected event. + * Handle having connected to remote AP. + * @param [in] event_connected system_event_sta_connected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staConnected(system_event_sta_connected_t info) { + ESP_LOGD(LOG_TAG, "default staConnected"); + return ESP_OK; } // staConnected -esp_err_t WiFiEventHandler::staDisconnected() { - ESP_LOGD(LOG_TAG, "default staDisconnected"); - return ESP_OK; +/** + * @brief Handle the Station Disconnected event. + * Handle having disconnected from remote AP. + * @param [in] event_disconnected system_event_sta_disconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staDisconnected(system_event_sta_disconnected_t info) { + ESP_LOGD(LOG_TAG, "default staDisconnected"); + return ESP_OK; } // staDisconnected -esp_err_t WiFiEventHandler::apStaConnected() { - ESP_LOGD(LOG_TAG, "default apStaConnected"); - return ESP_OK; +/** + * @brief Handle a Station Connected to AP event. + * Handle having a station connected to ESP32 soft-AP. + * @param [in] event_sta_connected system_event_ap_staconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::apStaConnected(system_event_ap_staconnected_t info) { + ESP_LOGD(LOG_TAG, "default apStaConnected"); + return ESP_OK; } // apStaConnected -esp_err_t WiFiEventHandler::apStaDisconnected() { - ESP_LOGD(LOG_TAG, "default apStaDisconnected"); - return ESP_OK; +/** + * @brief Handle a Station Disconnected from AP event. + * Handle having a station disconnected from ESP32 soft-AP. + * @param [in] event_sta_disconnected system_event_ap_stadisconnected_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::apStaDisconnected(system_event_ap_stadisconnected_t info) { + ESP_LOGD(LOG_TAG, "default apStaDisconnected"); + return ESP_OK; } // apStaDisconnected +/** + * @brief Handle a Scan for APs done event. + * Handle having an ESP32 station scan (APs) done. + * @param [in] event_scan_done system_event_sta_scan_done_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staScanDone(system_event_sta_scan_done_t info) { + ESP_LOGD(LOG_TAG, "default staScanDone"); + return ESP_OK; +} // staScanDone + + +/** + * @brief Handle the auth mode of APs change event. + * Handle having the auth mode of AP ESP32 station connected to changed. + * @param [in] event_auth_change system_event_sta_authmode_change_t. + * @return An indication of whether or not we processed the event successfully. + */ +esp_err_t WiFiEventHandler::staAuthChange(system_event_sta_authmode_change_t info) { + ESP_LOGD(LOG_TAG, "default staAuthChange"); + return ESP_OK; +} // staAuthChange + + WiFiEventHandler::~WiFiEventHandler() { } // ~WiFiEventHandler diff --git a/cpp_utils/WiFiEventHandler.h b/cpp_utils/WiFiEventHandler.h index 612dc8f3..9efb837f 100644 --- a/cpp_utils/WiFiEventHandler.h +++ b/cpp_utils/WiFiEventHandler.h @@ -22,13 +22,15 @@ * wifi.startAP("MYSSID", "password"); * * The overridable functions are: - * * esp_err_t apStaConnected() - * * esp_err_t apStaDisconnected() + * * esp_err_t apStaConnected(system_event_ap_staconnected_t info) + * * esp_err_t apStaDisconnected(system_event_ap_stadisconnected_t info) * * esp_err_t apStart() * * esp_err_t apStop() - * * esp_err_t staConnected() - * * esp_err_t staDisconnected() - * * esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) + * * esp_err_t staConnected(system_event_sta_connected_t info) + * * esp_err_t staDisconnected(system_event_sta_disconnected_t info) + * * esp_err_t staGotIp(system_event_sta_got_ip_t info) + * * esp_err_t staScanDone(system_event_sta_scan_done_t info) + * * esp_err_t staAuthChange(system_event_sta_authmode_change_t info) * * esp_err_t staStart() * * esp_err_t staStop() * * esp_err_t wifiReady() @@ -56,11 +58,19 @@ * return ESP_OK; * } * - * esp_err_t MyHandler::staConnected() { + * esp_err_t MyHandler::staConnected(system_event_sta_connected_t info) { * return ESP_OK; * } * - * esp_err_t MyHandler::staDisconnected() { + * esp_err_t MyHandler::staDisconnected(system_event_sta_disconnected_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::apStaConnected(system_event_ap_staconnected_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::apStaDisconnected(system_event_ap_stadisconnected_t info) { * return ESP_OK; * } * @@ -68,7 +78,15 @@ * return ESP_OK; * } * - * esp_err_t MyHandler::staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { + * esp_err_t MyHandler::staGotIp(system_event_sta_got_ip_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::staScanDone(system_event_sta_scan_done_t info) { + * return ESP_OK; + * } + * + * esp_err_t MyHandler::staAuthChange(system_event_sta_authmode_change_t info) { * return ESP_OK; * } * @@ -81,14 +99,16 @@ class WiFiEventHandler { public: WiFiEventHandler(); virtual ~WiFiEventHandler(); - virtual esp_err_t apStaConnected(); - virtual esp_err_t apStaDisconnected(); + virtual esp_err_t apStaConnected(system_event_ap_staconnected_t info); + virtual esp_err_t apStaDisconnected(system_event_ap_stadisconnected_t info); virtual esp_err_t apStart(); virtual esp_err_t apStop(); system_event_cb_t getEventHandler(); - virtual esp_err_t staConnected(); - virtual esp_err_t staDisconnected(); - virtual esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip); + virtual esp_err_t staConnected(system_event_sta_connected_t info); + virtual esp_err_t staDisconnected(system_event_sta_disconnected_t info); + virtual esp_err_t staGotIp(system_event_sta_got_ip_t info); + virtual esp_err_t staScanDone(system_event_sta_scan_done_t info); + virtual esp_err_t staAuthChange(system_event_sta_authmode_change_t info); virtual esp_err_t staStart(); virtual esp_err_t staStop(); virtual esp_err_t wifiReady(); @@ -97,9 +117,10 @@ class WiFiEventHandler { * Get the next WiFi event handler in the chain, if there is one. * @return The next WiFi event handler in the chain or nullptr if there is none. */ - WiFiEventHandler *getNextHandler() { + WiFiEventHandler* getNextHandler() { return m_nextHandler; } + /** * Set the next WiFi event handler in the chain. * @param [in] nextHandler The next WiFi event handler in the chain. @@ -112,6 +133,7 @@ class WiFiEventHandler { friend class WiFi; WiFiEventHandler *m_nextHandler; static esp_err_t eventHandler(void* ctx, system_event_t* event); + }; #endif /* MAIN_WIFIEVENTHANDLER_H_ */ diff --git a/cpp_utils/cpp_utils b/cpp_utils/cpp_utils deleted file mode 120000 index 5d96b78d..00000000 --- a/cpp_utils/cpp_utils +++ /dev/null @@ -1 +0,0 @@ -/home/kolban/esp32/esptest/apps/workspace/esp32-snippets/cpp_utils \ No newline at end of file diff --git a/cpp_utils/mainpage.dox b/cpp_utils/mainpage.dox index 3597471d..5701909e 100644 --- a/cpp_utils/mainpage.dox +++ b/cpp_utils/mainpage.dox @@ -5,4 +5,4 @@ * functions. * * The Github repository is [https://github.com/nkolban/esp32-snippets](https://github.com/nkolban/esp32-snippets). - */ \ No newline at end of file + */ diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino index c0b6163c..7bce73f1 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_client/BLE_client.ino @@ -6,9 +6,9 @@ //#include "BLEScan.h" // The remote service we wish to connect to. -static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); // The characteristic of the remote service we are interested in. -static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); static BLEAddress *pServerAddress; static boolean doConnect = false; @@ -62,6 +62,8 @@ bool connectToServer(BLEAddress pAddress) { Serial.println(value.c_str()); pRemoteCharacteristic->registerForNotify(notifyCallback); + + return true; } /** * Scan for BLE servers and find the first one that advertises the service we are looking for. diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 00000000..2c2ec6a3 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,104 @@ +/* + 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 "BLEServer.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 is no longer required to crete beacon + // BLEServer *pServer = BLEDevice::createServer(); + + 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() { +} diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino index 8d329c20..5e915bea 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_notify/BLE_notify.ino @@ -23,8 +23,10 @@ #include #include -BLECharacteristic *pCharacteristic; +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; bool deviceConnected = false; +bool oldDeviceConnected = false; uint8_t value = 0; // See the following for generating UUIDs: @@ -53,7 +55,7 @@ void setup() { BLEDevice::init("MyESP32"); // Create the BLE Server - BLEServer *pServer = BLEDevice::createServer(); + pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service @@ -81,13 +83,23 @@ void setup() { } void loop() { - - if (deviceConnected) { - Serial.printf("*** NOTIFY: %d ***\n", value); - pCharacteristic->setValue(&value, 1); - pCharacteristic->notify(); - //pCharacteristic->indicate(); - value++; - } - delay(2000); + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue(&value, 1); + pCharacteristic->notify(); + value++; + 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; + } } \ No newline at end of file diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino index 38224a67..79793975 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_server/BLE_server.ino @@ -29,6 +29,7 @@ void setup() { pCharacteristic->setValue("Hello World says Neil"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->start(); Serial.println("Characteristic defined! Now you can read it in your phone!"); } @@ -36,4 +37,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: delay(2000); -} \ No newline at end of file +} diff --git a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino index a348a666..ec014db7 100644 --- a/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino +++ b/cpp_utils/tests/BLETests/Arduino/BLE_uart/BLE_uart.ino @@ -24,8 +24,10 @@ #include #include -BLECharacteristic *pCharacteristic; +BLEServer *pServer = NULL; +BLECharacteristic * pTxCharacteristic; bool deviceConnected = false; +bool oldDeviceConnected = false; uint8_t txValue = 0; // See the following for generating UUIDs: @@ -70,42 +72,55 @@ void setup() { BLEDevice::init("UART Service"); // Create the BLE Server - BLEServer *pServer = BLEDevice::createServer(); + 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_TX, - BLECharacteristic::PROPERTY_NOTIFY - ); + pTxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_TX, + BLECharacteristic::PROPERTY_NOTIFY + ); - pCharacteristic->addDescriptor(new BLE2902()); + pTxCharacteristic->addDescriptor(new BLE2902()); - BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_RX, - BLECharacteristic::PROPERTY_WRITE - ); + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + BLECharacteristic::PROPERTY_WRITE + ); - pCharacteristic->setCallbacks(new MyCallbacks()); + pRxCharacteristic->setCallbacks(new MyCallbacks()); // Start the service pService->start(); // Start advertising + pServer->getAdvertising()->addServiceUUID(pService->getUUID()); pServer->getAdvertising()->start(); Serial.println("Waiting a client connection to notify..."); } void loop() { - if (deviceConnected) { - Serial.printf("*** Sent Value: %d ***\n", txValue); - pCharacteristic->setValue(&txValue, 1); - pCharacteristic->notify(); - txValue++; - } - delay(1000); + 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; + } } diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino new file mode 100644 index 00000000..e77d774f --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_encrypted/BLE_client_encrypted.ino @@ -0,0 +1,138 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +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); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + 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()); + 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()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * 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.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = 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 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // 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(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } 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()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino new file mode 100644 index 00000000..846a664b --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_numeric_confirmation/BLE_client_numeric_confirmation.ino @@ -0,0 +1,169 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +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); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + 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()); + 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()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * 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.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = 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 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // 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(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } 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()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino new file mode 100644 index 00000000..763a87d2 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_client/BLE_client_passkey/BLE_client_passkey.ino @@ -0,0 +1,168 @@ +/** + * A BLE client example that is rich in capabilities. + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +static BLEAddress *pServerAddress; +static boolean doConnect = false; +static boolean connected = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + +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); +} + +bool connectToServer(BLEAddress pAddress) { + Serial.print("Forming a connection to "); + Serial.println(pAddress.toString().c_str()); + + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Connect to the remove BLE Server. + pClient->connect(pAddress); + 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()); + 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()); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + + pRemoteCharacteristic->registerForNotify(notifyCallback); +} +/** + * 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.getServiceUUID().equals(serviceUUID)) { + + // + Serial.print("Found our device! address: "); + advertisedDevice.getScan()->stop(); + + pServerAddress = new BLEAddress(advertisedDevice.getAddress()); + doConnect = 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 30 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(30); +} // 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(*pServerAddress)) { + Serial.println("We are now connected to the BLE Server."); + connected = true; + } 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()); + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino new file mode 100644 index 00000000..75a461af --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_authorization/BLE_server_authorization.ino @@ -0,0 +1,44 @@ +/* + 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 +*/ + +#include +#include +#include + +// 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("ESP32"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + /* + * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests + */ + + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino new file mode 100644 index 00000000..43ffd2c9 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_encrypted/BLE_server_encrypted.ino @@ -0,0 +1,43 @@ +/* + 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 +*/ + +#include +#include +#include + +// 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("ESP32"); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + 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(); + pAdvertising->start(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino new file mode 100644 index 00000000..186019e8 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_numeric_confirmation/BLE_server_numeric_confirmation.ino @@ -0,0 +1,75 @@ +/* + 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 +*/ + +#include +#include +#include + +// 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 MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("ESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + 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(); + pAdvertising->start(); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} diff --git a/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino new file mode 100644 index 00000000..93f8c89f --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/BLE_server/BLE_server_passkey/BLE_server_passkey.ino @@ -0,0 +1,78 @@ +/* + 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 +*/ + +#include +#include +#include + +// 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 MySecurity : public BLESecurityCallbacks { + + bool onConfirmPIN(uint32_t pin){ + return false; + } + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("ESP32"); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + 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(); + pAdvertising->start(); + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} diff --git a/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino b/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino new file mode 100644 index 00000000..d89c14f9 --- /dev/null +++ b/cpp_utils/tests/BLETests/Arduino/security/StaticPIN/StaticPIN.ino @@ -0,0 +1,47 @@ +/* + 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 +*/ + +#include +#include +#include + +// 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("ESP32"); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setStaticPIN(123456); + + //set static passkey + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} diff --git a/cpp_utils/tests/BLETests/SampleAsyncScan.cpp b/cpp_utils/tests/BLETests/SampleAsyncScan.cpp new file mode 100644 index 00000000..2973b652 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleAsyncScan.cpp @@ -0,0 +1,58 @@ +/** + * Perform an async scanning for BLE advertised servers. + */ +#include "BLEUtils.h" +#include "BLEScan.h" +#include + +#include "BLEDevice.h" +#include "BLEAdvertisedDevice.h" +#include "sdkconfig.h" + +/** + * Callback for each detected advertised device. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice advertisedDevice) { + printf("Advertised Device: %s\n", advertisedDevice.toString().c_str()); + } +}; + + +/** + * Callback invoked when scanning has completed. + */ +static void scanCompleteCB(BLEScanResults scanResults) { + printf("Scan complete!\n"); + printf("We found %d devices\n", scanResults.getCount()); + scanResults.dump(); +} // scanCompleteCB + +/** + * Run the sample. + */ +static void run() { + printf("Async Scanning sample starting\n"); + BLEDevice::init(""); + + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false); + pBLEScan->setActiveScan(true); + printf("About to start scanning for 10 seconds\n"); + pBLEScan->start(10, scanCompleteCB); + printf("Now scanning in the background ... scanCompleteCB() will be called when done.\n"); + + // + // Now going into a loop logging that we are still alive. + // + while(1) { + printf("Tick! - still alive\n"); + FreeRTOS::sleep(1000); + } + printf("Scanning sample ended\n"); +} + +void SampleAsyncScan(void) +{ + run(); +} // app_main diff --git a/cpp_utils/tests/BLETests/SampleHIDDevice.cpp b/cpp_utils/tests/BLETests/SampleHIDDevice.cpp new file mode 100644 index 00000000..c3a4df1a --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleHIDDevice.cpp @@ -0,0 +1,220 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#include "SampleKeyboardTypes.h" +#include +#include +#include + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleHIDDevice"; + +static BLEHIDDevice* hid; + +class MyTask : public Task { + void run(void*){ + vTaskDelay(10000); + const char* hello = "Hello world from esp32 hid keyboard!!!"; + while(*hello){ + KEYMAP map = keymap[(uint8_t)*hello]; + uint8_t a[] = {map.modifier, 0x0, map.usage, 0x0,0x0,0x0,0x0,0x0}; + hid->inputReport(NULL)->setValue(a,sizeof(a)); + hid->inputReport(NULL)->notify(); + + hello++; + } + uint8_t v[] = {0x0, 0x0, 0x0, 0x0,0x0,0x0,0x0,0x0}; + hid->inputReport(NULL)->setValue(v, sizeof(v)); + hid->inputReport(NULL)->notify(); + vTaskDelete(NULL); + } +}; + MyTask *task; + + class MyCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer){ + task->start(); + } + + void onDisconnect(BLEServer* pServer){ + + } + }; +uint32_t passKey = 0; + class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGE(LOG_TAG, "The passkey request %d", passKey); + + vTaskDelay(25000); + return passKey; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + passKey = pass_key; + } + bool onSecurityRequest(){ + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } + }; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + task = new MyTask(); + BLEDevice::init("ESP32"); + BLEServer *pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyCallbacks()); + pServer->setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + pServer->setSecurityCallbacks(new MySecurity()); + + /* + * Instantiate hid device + */ + hid = new BLEHIDDevice(pServer); + + /* + * Set manufacturer name + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.manufacturer_name_string.xml + */ + std::string name = "esp-community"; + hid->manufacturer()->setValue(name); + + /* + * Set pnp parameters + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.pnp_id.xml + */ + const uint8_t pnp[] = {0x01,0x02,0xe5,0xab,0xcd,0x01,0x10}; + hid->pnp()->setValue((uint8_t*)pnp, sizeof(pnp)); + + /* + * Set hid informations + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml + */ + const uint8_t val1[] = {0x01,0x11,0x00,0x03}; + hid->hidInfo()->setValue((uint8_t*)val1, 4); + + /* + * Mouse + */ + const uint8_t reportMap2[] = { + USAGE_PAGE(1), 0x01, + USAGE(1), 0x02, + COLLECTION(1), 0x01, + USAGE(1), 0x01, + COLLECTION(1), 0x00, + USAGE_PAGE(1), 0x09, + USAGE_MINIMUM(1), 0x1, + USAGE_MAXIMUM(1), 0x3, + LOGICAL_MINIMUM(1), 0x0, + LOGICAL_MAXIMUM(1), 0x1, + REPORT_COUNT(1), 0x3, + REPORT_SIZE(1), 0x1, + INPUT(1), 0x2, // (Data, Variable, Absolute), ;3 button bits + REPORT_COUNT(1), 0x1, + REPORT_SIZE(1), 0x5, + INPUT(1), 0x1, //(Constant), ;5 bit padding + USAGE_PAGE(1), 0x1, //(Generic Desktop), + USAGE(1), 0x30, + USAGE(1), 0x31, + LOGICAL_MINIMUM(1), 0x81, + LOGICAL_MAXIMUM(1), 0x7f, + REPORT_SIZE(1), 0x8, + REPORT_COUNT(1), 0x2, + INPUT(1), 0x6, //(Data, Variable, Relative), ;2 position bytes (X & Y) + END_COLLECTION(0), + END_COLLECTION(0) + }; + /* + * Keyboard + */ + const uint8_t reportMap[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, // 1 byte (Modifier) + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x01, // 1 byte (Reserved) + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, // Num Lock + USAGE_MAXIMUM(1), 0x05, // Kana + OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x01, // 3 bits (Padding) + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x06, // 6 bytes (Keys) + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, // 101 keys + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + END_COLLECTION(0) + }; + /* + * Set report map (here is initialized device driver on client side) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.report_map.xml + */ + hid->setReportMap((uint8_t*)reportMap, sizeof(reportMap)); + + /* + * We are prepared to start hid device services. Before this point we can change all values and/or set parameters we need. + * Also before we start, if we want to provide battery info, we need to prepare battery service. + * We can setup characteristics authorization + */ + hid->startServices(); + + /* + * Its good to setup advertising by providing appearance and advertised service. This will let clients find our device by type + */ + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->setAppearance(HID_KEYBOARD); + pAdvertising->addServiceUUID(hid->hidService()->getUUID()); + pAdvertising->start(); + + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + pSecurity->setCapability(ESP_IO_CAP_NONE); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleHID(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main diff --git a/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp new file mode 100644 index 00000000..29bff125 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleHIDKeyboard.cpp @@ -0,0 +1,193 @@ +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#include "HIDKeyboardTypes.h" +#include +#include +#include + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleHIDDevice"; + +static BLEHIDDevice* hid; +BLECharacteristic* input; +BLECharacteristic* output; + +/* + * This callback is connect with output report. In keyboard output report report special keys changes, like CAPSLOCK, NUMLOCK + * We can add digital pins with LED to show status + * bit 1 - NUM LOCK + * bit 2 - CAPS LOCK + * bit 3 - SCROLL LOCK + */ +class MyOutputCallbacks : public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* me){ + uint8_t* value = (uint8_t*)(me->getValue().c_str()); + ESP_LOGI(LOG_TAG, "special keys: %d", *value); + } +}; + +class MyTask : public Task { + void run(void*){ + vTaskDelay(5000/portTICK_PERIOD_MS); // wait 5 seconds before send first message + while(1){ + const char* hello = "Hello world from esp32 hid keyboard!!!\n"; + while(*hello){ + KEYMAP map = keymap[(uint8_t)*hello]; + /* + * simulate keydown, we can send up to 6 keys + */ + uint8_t a[] = {map.modifier, 0x0, map.usage, 0x0,0x0,0x0,0x0,0x0}; + input->setValue(a,sizeof(a)); + input->notify(); + + /* + * simulate keyup + */ + uint8_t v[] = {0x0, 0x0, 0x0, 0x0,0x0,0x0,0x0,0x0}; + input->setValue(v, sizeof(v)); + input->notify(); + hello++; + + vTaskDelay(10/portTICK_PERIOD_MS); + } + vTaskDelay(2000/portTICK_PERIOD_MS); // simulate write message every 2 seconds + } + } +}; + +MyTask *task; +class MyCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer){ + task->start(); + } + + void onDisconnect(BLEServer* pServer){ + task->stop(); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + + task = new MyTask(); + BLEDevice::init("ESP32"); + BLEServer *pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyCallbacks()); + + /* + * Instantiate hid device + */ + hid = new BLEHIDDevice(pServer); + + + input = hid->inputReport(1); // <-- input REPORTID from report map + output = hid->outputReport(1); // <-- output REPORTID from report map + + output->setCallbacks(new MyOutputCallbacks()); + + /* + * Set manufacturer name (OPTIONAL) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.manufacturer_name_string.xml + */ + std::string name = "esp-community"; + hid->manufacturer()->setValue(name); + + /* + * Set pnp parameters (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.pnp_id.xml + */ + + hid->pnp(0x02, 0xe502, 0xa111, 0x0210); + + /* + * Set hid informations (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml + */ + hid->hidInfo(0x00,0x01); + + + /* + * Keyboard + */ + const uint8_t reportMap[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + REPORT_ID(1), 0x01, // REPORTID + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, // 1 byte (Modifier) + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x01, // 1 byte (Reserved) + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, // Num Lock + USAGE_MAXIMUM(1), 0x05, // Kana + OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x01, // 3 bits (Padding) + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x06, // 6 bytes (Keys) + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, // 101 keys + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + END_COLLECTION(0) + }; + /* + * Set report map (here is initialized device driver on client side) (MANDATORY) + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.report_map.xml + */ + hid->reportMap((uint8_t*)reportMap, sizeof(reportMap)); + + /* + * We are prepared to start hid device services. Before this point we can change all values and/or set parameters we need. + * Also before we start, if we want to provide battery info, we need to prepare battery service. + * We can setup characteristics authorization + */ + hid->startServices(); + + /* + * Its good to setup advertising by providing appearance and advertised service. This will let clients find our device by type + */ + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->setAppearance(HID_KEYBOARD); + pAdvertising->addServiceUUID(hid->hidService()->getUUID()); + pAdvertising->start(); + + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(1000000); + } +}; + + +void SampleHID(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main diff --git a/cpp_utils/tests/BLETests/SampleKeyboardTypes.h b/cpp_utils/tests/BLETests/SampleKeyboardTypes.h new file mode 100644 index 00000000..ef48a526 --- /dev/null +++ b/cpp_utils/tests/BLETests/SampleKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/cpp_utils/tests/BLETests/SampleWrite.cpp b/cpp_utils/tests/BLETests/SampleWrite.cpp index 2832f9ec..de8f251a 100644 --- a/cpp_utils/tests/BLETests/SampleWrite.cpp +++ b/cpp_utils/tests/BLETests/SampleWrite.cpp @@ -11,6 +11,7 @@ #include #include #include "BLEDevice.h" +#include "GeneralUtils.h" #include "sdkconfig.h" @@ -35,6 +36,7 @@ class MyCallbacks: public BLECharacteristicCallbacks { static void run() { + GeneralUtils::dumpInfo(); BLEDevice::init("MYDEVICE"); BLEServer *pServer = BLEDevice::createServer(); diff --git a/cpp_utils/tests/BLETests/main.cpp b/cpp_utils/tests/BLETests/main.cpp index 732ae16d..77c3567b 100644 --- a/cpp_utils/tests/BLETests/main.cpp +++ b/cpp_utils/tests/BLETests/main.cpp @@ -9,6 +9,7 @@ extern "C" { // The list of sample entry points. void Sample_MLE_15(void); void Sample1(void); +void SampleAsyncScan(void); void SampleClient(void); void SampleClient_Notify(void); void SampleClientAndServer(void); @@ -27,6 +28,7 @@ void SampleWrite(void); void app_main(void) { //Sample_MLE_15(); //Sample1(); + //SampleAsyncScan(); //SampleClient(); //SampleClient_Notify(); //SampleClientAndServer(); diff --git a/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp b/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp new file mode 100644 index 00000000..2ab4c909 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_Encryption.cpp @@ -0,0 +1,122 @@ +/* + * SampleClient_Encryption.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * 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) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClient_Encryption(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + + diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp new file mode 100644 index 00000000..2791f3d1 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_numeric_confirmation.cpp @@ -0,0 +1,153 @@ +/* + * SampleClient_authentication_numeric_confirmation.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * 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) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleClient_authentication_numeric_confirmation(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + diff --git a/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp new file mode 100644 index 00000000..4848385d --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleClient_authentication_passkey.cpp @@ -0,0 +1,152 @@ +/* + * SampleClient_authentication_passkey.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +#include +#include +#include +#include +#include "BLEDevice.h" + +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "Task.h" + +#include "sdkconfig.h" + +static const char* LOG_TAG = "SampleClient"; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +// The remote service we wish to connect to. +static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("0d563a58-196a-48ce-ace2-dfec78acc814"); + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGE(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return false; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "Security Request"); + return true; + } + void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl){ + if(auth_cmpl.success){ + ESP_LOGI(LOG_TAG, "remote BD_ADDR:"); + esp_log_buffer_hex(LOG_TAG, auth_cmpl.bd_addr, sizeof(auth_cmpl.bd_addr)); + ESP_LOGI(LOG_TAG, "address type = %d", auth_cmpl.addr_type); + } + ESP_LOGI(LOG_TAG, "pair status = %s", auth_cmpl.success ? "success" : "fail"); + } +}; + + +/** + * Become a BLE client to a remote BLE server. We are passed in the address of the BLE server + * as the input parameter when the task is created. + */ +class MyClient: public Task { + void run(void* data) { + + BLEAddress* pAddress = (BLEAddress*)data; + BLEClient* pClient = BLEDevice::createClient(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + // Connect to the remove BLE Server. + pClient->connect(*pAddress); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + return; + } + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", charUUID.toString().c_str()); + return; + } + + // Read the value of the characteristic. + std::string value = pRemoteCharacteristic->readValue(); + ESP_LOGD(LOG_TAG, "The characteristic value was: %s", value.c_str()); + + while(1) { + // Set a new value of the characteristic + ESP_LOGD(LOG_TAG, "Setting the new value"); + std::ostringstream stringStream; + struct timeval tv; + gettimeofday(&tv, nullptr); + stringStream << "Time since boot: " << tv.tv_sec; + pRemoteCharacteristic->writeValue(stringStream.str()); + + FreeRTOS::sleep(1000); + } + + pClient->disconnect(); + + ESP_LOGD(LOG_TAG, "%s", pClient->toString().c_str()); + ESP_LOGD(LOG_TAG, "-- End of task"); + } // run +}; // MyClient + + +/** + * 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) { + ESP_LOGD(LOG_TAG, "Advertised Device: %s", advertisedDevice.toString().c_str()); + + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + advertisedDevice.getScan()->stop(); + + ESP_LOGD(LOG_TAG, "Found our device! address: %s", advertisedDevice.getAddress().toString().c_str()); + MyClient* pMyClient = new MyClient(); + pMyClient->setStackSize(18000); + pMyClient->start(new BLEAddress(*advertisedDevice.getAddress().getNative())); + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +/** + * Perform the work of a sample BLE client. + */ +void SampleSecureClient(void) { + ESP_LOGD(LOG_TAG, "Scanning sample starting"); + BLEDevice::init(""); + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->start(5); +} // SampleClient + + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp b/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp new file mode 100644 index 00000000..10c16218 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_Encryption.cpp @@ -0,0 +1,85 @@ +/* + * SampleServer_Encryption.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MyCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + /* + * Here we have implemented simplest security. This kind security does not provide authentication + */ + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->setCallbacks(new MyCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_Encryption(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp new file mode 100644 index 00000000..3231b37d --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_numeric_confirmation.cpp @@ -0,0 +1,129 @@ +/* + * SampleServer_authentication_numeric_confirmation.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "The passkey YES/NO number:%d", pass_key); + vTaskDelay(5000); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class bleCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new bleCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setKeySize(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_IO); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + //pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_authentication_numeric_confirmation(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp new file mode 100644 index 00000000..24d45868 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authentication_passkey.cpp @@ -0,0 +1,124 @@ +/* + * SampleServer_authentication_passkey.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class MyCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; + +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); + BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new MyCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_OUT); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleSecureServer(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + + diff --git a/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp new file mode 100644 index 00000000..02e47e4d --- /dev/null +++ b/cpp_utils/tests/BLETests/security/SampleServer_authorization.cpp @@ -0,0 +1,131 @@ +/* + * SampleServer_authorization.cpp + * + * Created on: Dec 23, 2017 + * Author: chegewara + */ + + +/** + * Create a new BLE server. + */ +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include +#include +#include + + +#include "sdkconfig.h" + +static char LOG_TAG[] = "SampleServer"; + +class MySecurity : public BLESecurityCallbacks { + + uint32_t onPassKeyRequest(){ + ESP_LOGI(LOG_TAG, "PassKeyRequest"); + return 123456; + } + void onPassKeyNotify(uint32_t pass_key){ + ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key); + } + bool onConfirmPIN(uint32_t){ + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(LOG_TAG, "On Security Request"); + return true; + } + + void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){ + ESP_LOGI(LOG_TAG, "Starting BLE work!"); + if(cmpl.success){ + uint16_t length; + esp_ble_gap_get_whitelist_size(&length); + ESP_LOGD(LOG_TAG, "size: %d", length); + } + } +}; + +class bleCharacteristicCallback: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s", msg.c_str()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } + + void onRead(BLECharacteristic* pCharacteristic) { + std::string msg = pCharacteristic->getValue(); + ESP_LOGI(LOG_TAG, "BLE received: %s, %i", msg.c_str(), msg.length()); + esp_log_buffer_char(LOG_TAG, msg.c_str(), msg.length()); + esp_log_buffer_hex(LOG_TAG, msg.c_str(), msg.length()); + } +}; +class MainBLEServer: public Task { + void run(void *data) { + ESP_LOGD(LOG_TAG, "Starting BLE work!"); + esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG)); + BLEDevice::init("ESP32"); + BLEServer* pServer = BLEDevice::createServer(); +// BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + /* + * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation + */ + BLEDevice::setSecurityCallbacks(new MySecurity()); + + BLEService* pService = pServer->createService("91bad492-b950-4226-aa2b-4ede9fa42f59"); + + BLECharacteristic* pCharacteristic = pService->createCharacteristic( + BLEUUID("0d563a58-196a-48ce-ace2-dfec78acc814"), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_INDICATE + ); + /* + * Authorized permission to read/write characteristic. Require also protecting descriptor 2902 to protect notify/indicate requests + */ + pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + pCharacteristic->setCallbacks(new bleCharacteristicCallback()); + pCharacteristic->setValue("Hello World!"); + + BLE2902* p2902Descriptor = new BLE2902(); + p2902Descriptor->setNotifications(true); + pCharacteristic->addDescriptor(p2902Descriptor); + /* + * Authorized permission to read/write descriptor to protect notify/indicate requests + */ + p2902Descriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + pService->start(); + + BLEAdvertising* pAdvertising = pServer->getAdvertising(); + pAdvertising->addServiceUUID(BLEUUID(pService->getUUID())); + + BLESecurity *pSecurity = new BLESecurity(); + pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + pSecurity->setCapability(ESP_IO_CAP_NONE); + pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); + + pAdvertising->start(); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + delay(portMAX_DELAY); + } +}; + + +void SampleServer_Authorization(void) +{ + //esp_log_level_set("*", ESP_LOG_DEBUG); + MainBLEServer* pMainBleServer = new MainBLEServer(); + pMainBleServer->setStackSize(20000); + pMainBleServer->start(); + +} // app_main + + diff --git a/cpp_utils/tests/BLETests/security/app_main_security.cpp b/cpp_utils/tests/BLETests/security/app_main_security.cpp new file mode 100644 index 00000000..7b1bbca7 --- /dev/null +++ b/cpp_utils/tests/BLETests/security/app_main_security.cpp @@ -0,0 +1,33 @@ +/** + * Main file for running the BLE samples. + */ +extern "C" { + void app_main(void); +} + + +// The list of sample entry points. + +void SampleClient_Encryption(void); +void SampleServer_Encryption(void); +void SampleClient_authentication_passkey(void); +void SampleServer_authentication_passkey(void); +void SampleClient_authentication_numeric_confirmation(void); +void SampleServer_authentication_numeric_confirmation(void); + +void SampleServer_Authorization(void); + +// +// Un-comment ONE of the following +// --- +void app_main(void) { + + SampleClient_Encryption(); +// SampleServer_Encryption(); +// SampleClient_authentication_passkey(); +// SampleServer_authentication_passkey(); +// SampleClient_authentication_numeric_confirmation(); +// SampleServer_authentication_numeric_confirmation(); +// +// SampleServer_Authorization(); +} // app_main diff --git a/cpp_utils/tests/task_i2c_scanner.cpp b/cpp_utils/tests/task_i2c_scanner.cpp index b1bde2e0..061c3b08 100644 --- a/cpp_utils/tests/task_i2c_scanner.cpp +++ b/cpp_utils/tests/task_i2c_scanner.cpp @@ -6,6 +6,7 @@ #include "sdkconfig.h" +#define DEVICE_ADDRESS 0 #define SDA_PIN 25 #define SCL_PIN 26 @@ -14,7 +15,7 @@ class I2CScanner: public Task { void run(void *data) override { I2C i2c; - i2c.init((gpio_num_t)SDA_PIN, (gpio_num_t)SCL_PIN); + i2c.init((uint8_t)DEVICE_ADDRESS, (gpio_num_t)SDA_PIN, (gpio_num_t)SCL_PIN); i2c.scan(); } // End run }; diff --git a/eclipse/c_includes.xml b/eclipse/c_includes.xml index ee8feb85..60400ad5 100644 --- a/eclipse/c_includes.xml +++ b/eclipse/c_includes.xml @@ -50,7 +50,6 @@ ${IDF_PATH}/components/spi_flash/include ${IDF_PATH}/components/mbedtls/include ${IDF_PATH}/components/mdns/include -${IDF_PATH}/components/json/include ${IDF_PATH}/components/bt/include ${IDF_PATH}/components/bt/bluedroid/bta/include ${IDF_PATH}/components/bt/bluedroid/bta/sys/include @@ -74,6 +73,7 @@ ${IDF_PATH}/components/aws_iot/aws-iot-device-sdk-embedded-C/include ${IDF_PATH}/components/fatfs/src ${IDF_PATH}/components/wear_levelling/include +${IDF_PATH}/components/console @@ -127,18 +127,36 @@ ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + ESP_PLATFORM1 + +PLATFORM_ID11 + + +DEBUG_BUILD1 + diff --git a/error handling/fragments/espToError.c b/error handling/fragments/espToError.c index 66e90eb9..72127583 100644 --- a/error handling/fragments/espToError.c +++ b/error handling/fragments/espToError.c @@ -13,7 +13,7 @@ char *espToString(esp_err_t value) { case ESP_ERR_INVALID_ARG: return "Invalid argument"; case ESP_ERR_INVALID_SIZE: - return "Invalid state"; + return "Invalid size"; case ESP_ERR_INVALID_STATE: return "Invalid state"; case ESP_ERR_NOT_FOUND: diff --git a/hardware/displays/U8G2/u8g2_esp32_hal.c b/hardware/displays/U8G2/u8g2_esp32_hal.c index 05a2c994..caaa78d9 100644 --- a/hardware/displays/U8G2/u8g2_esp32_hal.c +++ b/hardware/displays/U8G2/u8g2_esp32_hal.c @@ -47,6 +47,7 @@ uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void } spi_bus_config_t bus_config; + memset(&bus_config, 0, sizeof(spi_bus_config_t)); bus_config.sclk_io_num = u8g2_esp32_hal.clk; // CLK bus_config.mosi_io_num = u8g2_esp32_hal.mosi; // MOSI bus_config.miso_io_num = -1; // MISO @@ -176,13 +177,13 @@ uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, case U8X8_MSG_GPIO_AND_DELAY_INIT: { uint64_t bitmask = 0; if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { - bitmask = bitmask | (1< +#include +#include +#include +#include +#include +#include +#include +#include "errorhandle_func.h" + +#include "sdkconfig.h" + +#define SDA_PIN 23 +#define SCL_PIN 22 +#define RTC_ADDRESS 0x68 // most I2C rtcs have their address on 0x68. any doubt check with i2c scanner snippet + +static char tag[] = "RTC"; + +static uint8_t intToBCD(uint8_t num) { + return ((num / 10) << 4) | (num%10); +} + +static uint8_t bcdToInt(uint8_t bcd) { + // 0x10 + return ((bcd >> 4) * 10) + (bcd & 0x0f);; +} + + +void initI2C() { + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = SDA_PIN; + conf.scl_io_num = SCL_PIN; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = 100000; + ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); + ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); +} + +/* + * PCF8523 slightly changed its 7 bytes encoded in BCD: + * 03h - Seconds - 00-59 + * 04h - Minutes - 00-59 + * 05h - Hours - 00-23 + * 06h - monthday - 01-31 + * 07h - weekday - 00-06 + * 08h - Month - 01-12 + * 09h - Year - 00-99 + * + */ +time_t rtc_readValue() { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_WRITE, true /* expect ack */)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, 0x03, 1)); // start address + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_READ, true /* expect ack */)); + uint8_t data[7]; + ESP_ERROR_CHECK(i2c_master_read(cmd, data, 7, false)); + ESP_ERROR_CHECK(i2c_master_stop(cmd)); + COMMANDCHECKOKERR(i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000/portTICK_PERIOD_MS),"RTC COMMAND"); + i2c_cmd_link_delete(cmd); + + int i; + for (i=0; i<7; i++) { + ESP_LOGD(tag, "%d: 0x%.2x", i, data[i]); + } + + struct tm tm; + tm.tm_sec = bcdToInt(data[0]); + tm.tm_min = bcdToInt(data[1]); + tm.tm_hour = bcdToInt(data[2]); + tm.tm_mday = bcdToInt(data[3]); + tm.tm_mon = bcdToInt(data[5]) - 1; // 0-11 - Note: The month on the PCF8523 is 1-12. + tm.tm_year = bcdToInt(data[6]) + 100; // Years since 1900 + time_t readTime = mktime(&tm); + return readTime; +} + +void rtc_writeValue(time_t newTime) { + ESP_LOGD(tag, ">> writeValue: %ld", newTime); + struct tm tm; + gmtime_r(&newTime, &tm); + char buf[30]; + ESP_LOGD(tag, " - %s", asctime_r(&tm, buf)); + + esp_err_t errRc; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + ESP_ERROR_CHECK(i2c_master_start(cmd)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, (RTC_ADDRESS << 1) | I2C_MASTER_WRITE, 1 /* expect ack */)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, 0x03, 1)); + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_sec), 1)); // seconds + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_min), 1 )); // minutes + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_hour), 1 )); // hours + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_mday), 1)); // date of month + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_wday+1), 1 )); // week day + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_mon+1), 1)); // month + ESP_ERROR_CHECK(i2c_master_write_byte(cmd, intToBCD(tm.tm_year-100), 1)); // year + ESP_ERROR_CHECK(i2c_master_stop(cmd)); + errRc = i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000/portTICK_PERIOD_MS); + if (errRc != 0) { + ESP_LOGE(tag, "i2c_master_cmd_begin: %d", errRc); + } + i2c_cmd_link_delete(cmd); +} + + +/* + implement in your time function + @Kolban created a nice one, here is my contribution. + + esp_err_t sntp_update(){ + + static const char *tag = "TIME_SETUP"; + esp_err_t ret; + char buffer[20]; + EventBits_t bitreturn; + TickType_t waittime = 10000/portTICK_PERIOD_MS; + + + bitreturn = xEventGroupWaitBits(event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, waittime); + + if((bitreturn & BIT0) != 0){ + + printf("going online\n"); + + } + else printf("going offline\n"); + + + // initialize the SNTP service + sntp_setoperatingmode(SNTP_OPMODE_POLL); + // to create SNTP server variable make a #define statement i.e. "pool.ntp.org" + sntp_setservername(0, CONFIG_SNTP_SERVER); + sntp_init(); + + initI2C(); + + time_t t; + struct tm timertc; + t = rtc_readValue(); + localtime_r(&t, &timertc); + + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timertc); + ESP_LOGI(tag,"Current time in rtc (GTM) is: %s\n\n", buffer); + + // wait for the service to set the time + time_t now; + struct tm timeinfo; + time(&now); + localtime_r(&now, &timeinfo); + int counter = 0; + + // try for a minute to get time from network + while((timeinfo.tm_year < (2018 - 1900)) && (counter < 12)) + { + + ESP_LOGW(tag,"Time outdated, waiting...\n"); + vTaskDelay(5000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + counter ++; + } + + if((timeinfo.tm_year < (2018 - 1900)) && (counter == 12)){ + ESP_LOGE(tag, "TIMEOUT"); + + + // stick to rtc time + now = rtc_readValue(); + + } + else{ + // update rtc time + rtc_writeValue(now); + } + + + // to create timezone variable make a #define statement i.e. "COT+5" + setenv("TZ",CONFIG_TIMEZONE_TZ, 1); + tzset(); + + // print the actual time in location + localtime_r(&now, &timeinfo); + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timeinfo); + ESP_LOGI(tag,"Current time in your Location: %s\n\n", buffer); + + // check time set in rtc in case it was updated from network + t = rtc_readValue(); + localtime_r(&t, &timertc); + strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &timertc); + ESP_LOGI(tag,"Current time in rtc (GTM) is: %s\n\n", buffer); + + ret = ESP_OK; + + return ret; +} + + */ diff --git a/memory/dma/DMA.cpp b/memory/dma/DMA.cpp new file mode 100644 index 00000000..a72a92e5 --- /dev/null +++ b/memory/dma/DMA.cpp @@ -0,0 +1,411 @@ +/* + * DMA.cpp + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#include "DMA.h" +#include +#include +#include +#include + +#include // Inclusions for WRITE_PERI_xxx and family. +#include // Inclusions for I2S registers. +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define D0 GPIO_NUM_4 +#define D1 GPIO_NUM_5 +#define D2 GPIO_NUM_18 +#define D3 GPIO_NUM_19 +#define D4 GPIO_NUM_36 +#define D5 GPIO_NUM_39 +#define D6 GPIO_NUM_34 +#define D7 GPIO_NUM_35 +#define VSYNC GPIO_NUM_25 +#define HREF GPIO_NUM_23 +#define PCLK GPIO_NUM_22 + +lldesc_t ll1; + +QueueHandle_t queueHandle; +intr_handle_t intHandle; + +class LogInterruptTask : public Task { + void run(void *data) { + printf("Starting interrupt log\n"); + uint32_t queueItem; + while(1) { + xQueueReceive(queueHandle, &queueItem, portMAX_DELAY); + printf("New interrupt value: 0x%x\n", queueItem); + } // End while 1 + } // LogInterruptTask#run +}; // LogInterruptTask + +static void IRAM_ATTR dmaInt(void *arg) { + uint32_t value = 0x99; + xQueueSendToBackFromISR(queueHandle, &value, nullptr); + I2S0.int_clr.val = I2S0.int_raw.val; +} + + +DMA::DMA() { + +} + +DMA::~DMA() { +} + + +void DMA::clearInterupts() { + + // Set 1 bits in the flags for I2S_INT_CLR_REG to clear interrupts. + I2S0.int_clr.val = 0xFFFFFFFF; // 32 bits of 1 to clear all flags. + +} // DMA#clearInterupts + + +void DMA::reset() { + + clearInterupts(); + ll1.eof = 1; + ll1.length = 0; // Number of valid bytes. + ll1.size = 1024; // Size of buffer. + ll1.offset = 0; + ll1.sosf = 0; + ll1.empty = 0; // No next link + ll1.owner = 1; // 1 = DMA, 2 = software + memset((void *)ll1.buf, 0xff, ll1.size); + + + + const uint32_t lc_conf_reset_flags = I2S_IN_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M; + I2S0.lc_conf.val |= lc_conf_reset_flags; + I2S0.lc_conf.val &= ~lc_conf_reset_flags; + + + // See TRM 12.4.2. + const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M; + I2S0.conf.val |= conf_reset_flags; + I2S0.conf.val &= ~conf_reset_flags; + + while (I2S0.state.rx_fifo_reset_back) { // Bit is 1 while not ready + ; + } + + I2S0.in_link.addr = (uint32_t)&ll1; +} // DMA#reset + + +void DMA::dumpBuffer() { + printf(">> dumpBuffer\n"); + GeneralUtils::hexDump((uint8_t*)ll1.buf, 64); + printf("<< dumpBuffer\n"); +} + +void DMA::setCameraMode() { + printf(">> setCameraMode\n"); + + gpio_pad_select_gpio(D0); + gpio_pad_select_gpio(D1); + gpio_pad_select_gpio(D2); + gpio_pad_select_gpio(D3); + gpio_pad_select_gpio(D4); + gpio_pad_select_gpio(D5); + gpio_pad_select_gpio(D6); + gpio_pad_select_gpio(D7); + gpio_pad_select_gpio(VSYNC); + gpio_pad_select_gpio(HREF); + gpio_pad_select_gpio(PCLK); + + gpio_set_direction(D0, GPIO_MODE_INPUT); + gpio_set_direction(D1, GPIO_MODE_INPUT); + gpio_set_direction(D2, GPIO_MODE_INPUT); + gpio_set_direction(D3, GPIO_MODE_INPUT); + gpio_set_direction(D4, GPIO_MODE_INPUT); + gpio_set_direction(D5, GPIO_MODE_INPUT); + gpio_set_direction(D6, GPIO_MODE_INPUT); + gpio_set_direction(D7, GPIO_MODE_INPUT); + gpio_set_direction(VSYNC, GPIO_MODE_INPUT); + gpio_set_direction(HREF, GPIO_MODE_INPUT); + gpio_set_direction(PCLK, GPIO_MODE_INPUT); + + // Map sources / sinks of data to their logical counter parts. + gpio_matrix_in(D0, I2S0I_DATA_IN0_IDX, false); + gpio_matrix_in(D1, I2S0I_DATA_IN1_IDX, false); + gpio_matrix_in(D2, I2S0I_DATA_IN2_IDX, false); + gpio_matrix_in(D3, I2S0I_DATA_IN3_IDX, false); + gpio_matrix_in(D4, I2S0I_DATA_IN4_IDX, false); + gpio_matrix_in(D5, I2S0I_DATA_IN5_IDX, false); + gpio_matrix_in(D6, I2S0I_DATA_IN6_IDX, false); + gpio_matrix_in(D7, I2S0I_DATA_IN7_IDX, false); + + // Set constants for the bits [15:8] alternating 1 and 0. This results in 0xAA. + gpio_matrix_in(0x30, I2S0I_DATA_IN8_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN9_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN10_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN11_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN12_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN13_IDX, false); + gpio_matrix_in(0x30, I2S0I_DATA_IN14_IDX, false); + gpio_matrix_in(0x38, I2S0I_DATA_IN15_IDX, false); + + gpio_matrix_in(0x38 /*VSYNC*/, I2S0I_V_SYNC_IDX, false); + gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false); // Constant high + gpio_matrix_in(0x38 /* HREF */, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(PCLK, I2S0I_WS_IN_IDX, false); + + //SET_PERI_REG_BITS(I2SCONF2_REG, I2S_CAMERA_EN, 1, I2S_CAMERA_EN_S); + + + // We must enable I2S0 peripheral + periph_module_enable(PERIPH_I2S0_MODULE); + + reset(); + + + /* + rx_msb_right | result | +---------------+--------------------------------------------------------------------------------+ + 0 | 00 00 v1low v1high 00 00 v2low v2high 00 00 v3low v3high 00 00 v4low v4high | + 1 | 00 00 v1low v1high 00 00 v2low ?? 00 00 v2low ?? 00 00 v3 low ?? | + */ + + // We enable I2S_CONF2_REG:I2S_CAMERA_EN + I2S0.conf2.camera_en = 1; // 1 for camera mode (TRM). + + // We enable I2S_CONF_REG:I2S_LCD_EN + I2S0.conf2.lcd_en = 1; // 1 for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_SLAVE_MOD + I2S0.conf.rx_slave_mod = 1; // 1 for slave receiving mode. Required for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_MSB_RIGHT + I2S0.conf.rx_msb_right = 0; // 0 required for camera mode (TRM). + + // I2S_CONF_REG:I2S_RX_RIGHT_FIRST + I2S0.conf.rx_right_first = 0; // 0 required for camera mode (TRM). + + // I2S_CONF_CHANG_REG:I2S_RX_CHAN_MOD + I2S0.conf_chan.rx_chan_mod = 1; // 1 required for camera mode (TRM). + + // I2S_FIFO_CONF_REG:I2S_RX_FIFO_MOD + I2S0.fifo_conf.rx_fifo_mod = 1; // 1 = 16bit single channel data. Required for camera mode (TRM). + + // Configure clock divider + I2S0.clkm_conf.clkm_div_a = 1; + I2S0.clkm_conf.clkm_div_b = 0; + I2S0.clkm_conf.clkm_div_num = 2; + + // FIFO will sink data to DMA + I2S0.fifo_conf.dscr_en = 1; + + // FIFO configuration + I2S0.fifo_conf.rx_fifo_mod = 2; + I2S0.fifo_conf.rx_fifo_mod_force_en = 1; + I2S0.conf_chan.rx_chan_mod = 1; + + // Clear flags which are used in I2S serial mode + I2S0.sample_rate_conf.rx_bits_mod = 0; + I2S0.conf.rx_right_first = 0; + I2S0.conf.rx_msb_right = 0; + I2S0.conf.rx_msb_shift = 0; + I2S0.conf.rx_mono = 0; + I2S0.conf.rx_short_sync = 0; + I2S0.timing.val = 0; + + + esp_err_t errRc; + errRc = esp_intr_alloc( + ETS_I2S0_INTR_SOURCE, + ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, + &dmaInt, + NULL, + &intHandle); + if (errRc != ESP_OK) { + printf("esp_intr_alloc: %d\n", errRc); + } + + errRc = esp_intr_enable(intHandle); + if (errRc != ESP_OK) { + printf("esp_intr_enable: %d\n", errRc); + } + + I2S0.rx_eof_num = 8; + + + ll1.eof = 1; + ll1.length = 0; // Number of valid bytes. + ll1.size = 1024; // Size of buffer. + ll1.offset = 0; + ll1.sosf = 0; + ll1.empty = 0; // No next link + ll1.owner = 1; // 1 = DMA, 0 = software + + memset((void *)ll1.buf, 0xff, ll1.size); + + // The address of the first in-link descriptor. + I2S0.in_link.addr = (uint32_t)&ll1; + + // Set to 1 to start in-link descriptor. + //I2S0.in_link.start = 1; + + startRX(); + + clearInterupts(); + + printf("<< setCameraMode\n"); +} // DMA#setCameraMode + + +void DMA::stopRX() { + // Set to 0 to stop receiving data. + I2S0.conf.rx_start = 0; + I2S0.conf.rx_reset = 1; +} // DMA#stopRX + + +void DMA::start() { + ll1.buf = (uint8_t*)malloc(1024); // Allocate storage for the linked list buffer. + queueHandle = xQueueCreate(50, sizeof(uint32_t)); + + LogInterruptTask* pLogInterruptTask = new LogInterruptTask(); + pLogInterruptTask->start(); +} // DMA#start + + +void DMA::startRX() { + // Set to 1 to start receiving data. + + printf(">> DMA::startRX\n"); + + I2S0.int_clr.val = I2S0.int_raw.val; + + I2S0.rx_eof_num = 8; + + I2S0.in_link.addr = (uint32_t)&ll1; + //I2S0.in_link.stop = 1; + I2S0.in_link.start = 1; + //I2S0.in_link.restart = 1; + I2S0.conf.rx_start = 1; + + // Enable interrupt generation + I2S0.int_ena.in_suc_eof = true; + //I2S0.int_ena.in_done = true; + /* + I2S0.int_ena.in_dscr_empty = true; + I2S0.int_ena.in_dscr_err = true; + I2S0.int_ena.in_err_eof = true; + + I2S0.int_ena.rx_hung = true; + I2S0.int_ena.rx_rempty = true; + I2S0.int_ena.rx_take_data = true; + I2S0.int_ena.rx_wfull = true; + */ + //I2S0.int_ena.rx_take_data = true; + + + printf("<< DMA::startRX\n"); +} // DMA#startRX + + +void DMA::dumpStatus() { + printf("conf\n"); + printf("%-20s: %d\n", "I2S_RX_SLAVE_MOD", I2S0.conf.rx_slave_mod); + printf("%-20s: %d\n", "I2S_RX_MSB_RIGHT", I2S0.conf.rx_msb_right); + printf("%-20s: %d\n", "I2S_RX_RIGHT_FIRST", I2S0.conf.rx_right_first); + printf("%-20s: %d\n", "I2S_RX_START", I2S0.conf.rx_start); + printf("%-20s: %d\n", "I2S_RX_RESET", I2S0.conf.rx_reset); + printf("-----\n"); + + printf("conf_chan\n"); + printf("%-20s: %d\n", "I2S_RX_CHAN_MOD", I2S0.conf_chan.rx_chan_mod); + printf("-----\n"); + + printf("fifo_conf (I2S_FIFO_CONF_REG)\n"); + printf("%-26s: %d\n", "I2S_RX_FIFO_MOD_FORCE_EN", I2S0.fifo_conf.rx_fifo_mod_force_en); + printf("%-26s: %d\n", "I2S_RX_FIFO_MOD", I2S0.fifo_conf.rx_fifo_mod); + printf("%-26s: %d\n", "I2S_RX_DSCR_EN", I2S0.fifo_conf.dscr_en); + printf("%-26s: %d\n", "I2S_RX_DATA_NUM", I2S0.fifo_conf.rx_data_num); + printf("-----\n"); + + printf("conf2\n"); + printf("%-20s: %d\n", "I2S_CAMERA_EN", I2S0.conf2.camera_en); + printf("%-20s: %d\n", "I2S_LCD_EN", I2S0.conf2.lcd_en); + printf("-----\n"); + + + printf("state\n"); + printf("%-20s: %d\n", "I2S_RX_FIFO_RESET_BACK", I2S0.state.rx_fifo_reset_back); + printf("-----\n"); + + printf("rx_eof_num (I2S_RXEOF_NUM_REG)\n"); + printf("%-20s: %d\n", "I2S_RXEOF_NUM_REG", I2S0.rx_eof_num); + printf("-----\n"); + + printf("in_eof_des_addr (I2S_IN_EOF_DES_ADDR_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_IN_EOF_DES_ADDR_REG", I2S0.in_eof_des_addr); + printf("-----\n"); + + printf("in_link_dscr (I2S_INLINK_DSCR_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_REG", I2S0.in_link_dscr); + printf("-----\n"); + + printf("in_link_dscr_bf0 (I2S_INLINK_DSCR_BF0_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_BF0_REG", I2S0.in_link_dscr_bf0); + printf("-----\n"); + + printf("in_link_dscr_bf1 (I2S_INLINK_DSCR_BF1_REG)\n"); + printf("%-20s: 0x%x\n", "I2S_INLINK_DSCR_BF1_REG", I2S0.in_link_dscr_bf1); + printf("-----\n"); + + printf("int_raw\n"); + printf("%-25s: %d\n", "I2S_IN_DSCR_EMPTY_INT_RAW", I2S0.int_raw.in_dscr_empty); + printf("%-25s: %d\n", "I2S_IN_DSCR_ERR_INT_RAW", I2S0.int_raw.in_dscr_err); + printf("%-25s: %d\n", "I2S_IN_SUC_EOF_INT_RAW", I2S0.int_raw.in_suc_eof); + printf("%-25s: %d\n", "I2S_IN_ERR_EOF_INT_RAW", I2S0.int_raw.in_err_eof); + printf("%-25s: %d\n", "I2S_IN_DONE_INT_RAW", I2S0.int_raw.in_done); + printf("%-25s: %d\n", "I2S_RX_HUNG_INT_RAW", I2S0.int_raw.rx_hung); + printf("%-25s: %d\n", "I2S_RX_REMPTY_INT_RAW", I2S0.int_raw.rx_rempty); + printf("%-25s: %d\n", "I2S_RX_WFULL_INT_RAW", I2S0.int_raw.rx_wfull); + printf("%-25s: %d\n", "I2S_RX_TAKE_DATA_INT_RAW", I2S0.int_raw.rx_take_data); + printf("-----\n"); + + printf("int_ena\n"); + printf("%-25s: %d\n", "I2S_IN_DSCR_EMPTY_INT_RAW", I2S0.int_ena.in_dscr_empty); + printf("%-25s: %d\n", "I2S_IN_DSCR_ERR_INT_RAW", I2S0.int_ena.in_dscr_err); + printf("%-25s: %d\n", "I2S_IN_SUC_EOF_INT_RAW", I2S0.int_ena.in_suc_eof); + printf("%-25s: %d\n", "I2S_IN_ERR_EOF_INT_RAW", I2S0.int_ena.in_err_eof); + printf("%-25s: %d\n", "I2S_IN_DONE_INT_RAW", I2S0.int_ena.in_done); + printf("%-25s: %d\n", "I2S_RX_HUNG_INT_RAW", I2S0.int_ena.rx_hung); + printf("%-25s: %d\n", "I2S_RX_REMPTY_INT_RAW", I2S0.int_ena.rx_rempty); + printf("%-25s: %d\n", "I2S_RX_WFULL_INT_RAW", I2S0.int_ena.rx_wfull); + printf("%-25s: %d\n", "I2S_RX_TAKE_DATA_INT_RAW", I2S0.int_ena.rx_take_data); + printf("-----\n"); + + printf("in_link\n"); + printf("%-20s: %d\n", "I2S_INLINK_PARK", I2S0.in_link.park); + printf("%-20s: %d\n", "I2S_INLINK_RESTART", I2S0.in_link.restart); + printf("%-20s: %d\n", "I2S_INLINK_START", I2S0.in_link.start); + printf("%-20s: %d\n", "I2S_INLINK_STOP", I2S0.in_link.stop); + printf("%-20s: 0x%x\n", "I2S_INLINK_ADDR", (I2S0.in_link.addr & 0xFFFFF)); + printf("-----\n"); + + printf("lldesc 1: 0x%x\n", (uint32_t)&ll1); + printf("%-20s: %d\n", "length", ll1.length); + printf("%-20s: %d\n", "size", ll1.size); + printf("%-20s: %d\n", "owner", ll1.owner); + printf("%-20s: %d\n", "empty", ll1.empty); + printf("%-20s: 0x%x\n", "&buf", (uint32_t)ll1.buf); +} // DMA#dumpStatus diff --git a/memory/dma/DMA.h b/memory/dma/DMA.h new file mode 100644 index 00000000..2be79458 --- /dev/null +++ b/memory/dma/DMA.h @@ -0,0 +1,26 @@ +/* + * DMA.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_DMA_H_ +#define MAIN_DMA_H_ + +class DMA { +public: + DMA(); + virtual ~DMA(); + void clearInterupts(); + void dumpBuffer(); + void dumpStatus(); + void reset(); + void setCameraMode(); + void start(); + void startRX(); + void stopRX(); + +}; + +#endif /* MAIN_DMA_H_ */ diff --git a/memory/dma/DMA_GPIO.h b/memory/dma/DMA_GPIO.h new file mode 100644 index 00000000..124adc28 --- /dev/null +++ b/memory/dma/DMA_GPIO.h @@ -0,0 +1,13 @@ +/* + * DMA_GPIO.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_DMA_GPIO_H_ +#define MAIN_DMA_GPIO_H_ + + + +#endif /* MAIN_DMA_GPIO_H_ */ diff --git a/memory/dma/GPIODriver.cpp b/memory/dma/GPIODriver.cpp new file mode 100644 index 00000000..78021c0d --- /dev/null +++ b/memory/dma/GPIODriver.cpp @@ -0,0 +1,88 @@ +/* + * GPIODriver.cpp + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#include "GPIODriver.h" +#include "DMA_GPIO.h" +#include +#include +#include + +#define D0 GPIO_NUM_4 +#define D1 GPIO_NUM_5 +#define D2 GPIO_NUM_18 +#define D3 GPIO_NUM_19 +#define D4 GPIO_NUM_13 // 36 +#define D5 GPIO_NUM_14 // 39 +#define D6 GPIO_NUM_15 // 34 +#define D7 GPIO_NUM_16 // 35 +#define VSYNC GPIO_NUM_25 +#define HREF GPIO_NUM_23 +#define PCLK GPIO_NUM_22 + +GPIODriver::GPIODriver() { +} + +GPIODriver::~GPIODriver() { +} + +void GPIODriver::run() { + printf(">> GPIODriver::run\n"); + // Setup the GPIO pins as output. + gpio_pad_select_gpio(D0); + gpio_pad_select_gpio(D1); + gpio_pad_select_gpio(D2); + gpio_pad_select_gpio(D3); + gpio_pad_select_gpio(D4); + gpio_pad_select_gpio(D5); + gpio_pad_select_gpio(D6); + gpio_pad_select_gpio(D7); + gpio_pad_select_gpio(VSYNC); + gpio_pad_select_gpio(HREF); + gpio_pad_select_gpio(PCLK); + + gpio_set_direction(D0, GPIO_MODE_OUTPUT); + gpio_set_direction(D1, GPIO_MODE_OUTPUT); + gpio_set_direction(D2, GPIO_MODE_OUTPUT); + gpio_set_direction(D3, GPIO_MODE_OUTPUT); + gpio_set_direction(D4, GPIO_MODE_OUTPUT); + gpio_set_direction(D5, GPIO_MODE_OUTPUT); + gpio_set_direction(D6, GPIO_MODE_OUTPUT); + gpio_set_direction(D7, GPIO_MODE_OUTPUT); + gpio_set_direction(VSYNC, GPIO_MODE_OUTPUT); + gpio_set_direction(HREF, GPIO_MODE_OUTPUT); + gpio_set_direction(PCLK, GPIO_MODE_OUTPUT); + + gpio_set_level(VSYNC, 1); + gpio_set_level(HREF, 1); + // Initialize the counter. + uint8_t counter = 0; + // Loop + + gpio_set_level(PCLK, 0); + while(1) { + + // Write the counter value to the GPIOs + gpio_set_level(D0, (counter & 0b00000001) != 0); + gpio_set_level(D1, (counter & 0b00000010) != 0); + gpio_set_level(D2, (counter & 0b00000100) != 0); + gpio_set_level(D3, (counter & 0b00001000) != 0); + gpio_set_level(D4, (counter & 0b00010000) != 0); + gpio_set_level(D5, (counter & 0b00100000) != 0); + gpio_set_level(D6, (counter & 0b01000000) != 0); + gpio_set_level(D7, (counter & 0b10000000) != 0); + + + // Pulse the clock + gpio_set_level(PCLK, 1); + vTaskDelay(10/portTICK_PERIOD_MS); + gpio_set_level(PCLK, 0); + + // Delay + vTaskDelay(990/portTICK_PERIOD_MS); + counter++; + } // End while 1 +} // GPIODriver#run diff --git a/memory/dma/GPIODriver.h b/memory/dma/GPIODriver.h new file mode 100644 index 00000000..03f8d7ec --- /dev/null +++ b/memory/dma/GPIODriver.h @@ -0,0 +1,18 @@ +/* + * GPIODriver.h + * + * Created on: May 27, 2018 + * Author: kolban + */ + +#ifndef MAIN_GPIODRIVER_H_ +#define MAIN_GPIODRIVER_H_ + +class GPIODriver { +public: + GPIODriver(); + virtual ~GPIODriver(); + void run(); +}; + +#endif /* MAIN_GPIODRIVER_H_ */ diff --git a/memory/dma/dma_console.cpp b/memory/dma/dma_console.cpp new file mode 100644 index 00000000..bc3bd05b --- /dev/null +++ b/memory/dma/dma_console.cpp @@ -0,0 +1,215 @@ +/** + * DMA Driver + * + * @author Neil Kolban + * @date 2018-05-26 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "DMA.h" +#include "GPIODriver.h" +#include + +DMA dma; + + +struct arg_end* reset_arg_end = arg_end(10); +void *reset_argtable[] = { + reset_arg_end +}; + +int runReset(int argc, char *argv[]) { + printf("Reset\n"); + dma.reset(); + return 0; +} + + +struct arg_end* start_arg_end = arg_end(10); +void *start_argtable[] = { + start_arg_end +}; + +int runStart(int argc, char *argv[]) { + printf("Started\n"); + return 0; +} + + +struct arg_end* startRX_arg_end = arg_end(10); +void *startRX_argtable[] = { + startRX_arg_end +}; + +int runStartRX(int argc, char *argv[]) { + printf("StartRX\n"); + return 0; +} + + +struct arg_end* stopRX_arg_end = arg_end(10); +void *stopRX_argtable[] = { + stopRX_arg_end +}; + +int runStopRX(int argc, char *argv[]) { + printf("StopRX\n"); + return 0; +} + + +struct arg_end* hexdump_arg_end = arg_end(10); +void *hexdump_argtable[] = { + hexdump_arg_end +}; + +int runHexdump(int argc, char *argv[]) { + printf("Hexdump\n"); + dma.dumpBuffer(); + return 0; +} + +struct arg_end* clientStart_arg_end = arg_end(10); +void *clientStart_arg_end_argtable[] = { + clientStart_arg_end +}; + +int runClientStart(int argc, char *argv[]) { + printf("runClientStart\n"); + GPIODriver gpioDriver; + gpioDriver.run(); + return 0; +} + + +struct arg_end* camera_arg_end = arg_end(10); +void *camera_argtable[] = { + camera_arg_end +}; + +int runCamera(int argc, char *argv[]) { + printf("camera\n"); + dma.setCameraMode(); + return 0; +} + +struct arg_end* stop_arg_end = arg_end(10); +void *stop_argtable[] = { + stop_arg_end +}; + +int runStop(int argc, char *argv[]) { + printf("Stopped\n"); + return 0; +} + + +struct arg_end* status_arg_end = arg_end(10); +void *status_argtable[] = { + stop_arg_end +}; + +int runStatus(int argc, char *argv[]) { + printf("Status\n"); + dma.dumpStatus(); + return 0; +} + + +void test_console(void) +{ + dma.start(); + // 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 = "start"; + consoleCmd.func = runStart; + consoleCmd.help = "Start processing"; + consoleCmd.argtable = start_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "stop"; + consoleCmd.func = runStop; + consoleCmd.help = "Stop processing"; + consoleCmd.argtable = stop_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "status"; + consoleCmd.func = runStatus; + consoleCmd.help = "Status processing"; + consoleCmd.argtable = status_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "camera"; + consoleCmd.func = runCamera; + consoleCmd.help = "camera processing"; + consoleCmd.argtable = camera_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "clientStart"; + consoleCmd.func = runClientStart; + consoleCmd.help = "clientStart processing"; + consoleCmd.argtable = clientStart_arg_end_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "hexdump"; + consoleCmd.func = runHexdump; + consoleCmd.help = "hexdump processing"; + consoleCmd.argtable = hexdump_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "startRX"; + consoleCmd.func = runStartRX; + consoleCmd.help = "startRX processing"; + consoleCmd.argtable = startRX_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "stopRX"; + consoleCmd.func = runStopRX; + consoleCmd.help = "stopRX processing"; + consoleCmd.argtable = stopRX_argtable; + esp_console_cmd_register(&consoleCmd); + + consoleCmd.command = "reset"; + consoleCmd.func = runReset; + consoleCmd.help = "reset processing"; + consoleCmd.argtable = reset_argtable; + esp_console_cmd_register(&consoleCmd); + + + //linenoiseClearScreen(); + linenoiseSetMultiLine(0); + + + while(1) { + char* line = linenoise("Enter command > "); + if (line != NULL) { + int ret; + esp_console_run(line, &ret); + linenoiseHistoryAdd(line); + linenoiseFree(line); + } // Line is not null + printf("--------------\n"); + } // End while loop +} diff --git a/memory/dma/main.cpp b/memory/dma/main.cpp new file mode 100644 index 00000000..f5715f5d --- /dev/null +++ b/memory/dma/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/networking/FTPServer/FTPCallbacks.cpp b/networking/FTPServer/FTPCallbacks.cpp new file mode 100644 index 00000000..99a6e111 --- /dev/null +++ b/networking/FTPServer/FTPCallbacks.cpp @@ -0,0 +1,154 @@ +#include "FTPServer.h" +#include +#include +#include + + +/** + * Called at the start of a STOR request. The file name is the name of the file the client would like to + * save. + */ +void FTPFileCallbacks::onStoreStart(std::string fileName) { + printf(">> FTPFileCallbacks::onStoreStart: fileName=%s\n", fileName.c_str()); + m_storeFile.open(fileName, std::ios::binary); // Open the file for writing. + if (m_storeFile.fail()) { + throw FTPServer::FileException(); + } + printf("<< FTPFileCallbacks::onStoreStart\n"); +} // FTPFileCallbacks#onStoreStart + + +/** + * Called when the client presents a new chunk of data to be saved. + */ +size_t FTPFileCallbacks::onStoreData(uint8_t* data, size_t size) { + printf(">> FTPFileCallbacks::onStoreData: size=%ld\n", size); + m_storeFile.write((char *)data, size); // Store data received. + printf("<< FTPFileCallbacks::onStoreData: size=%ld\n", size); + return size; +} // FTPFileCallbacks#onStoreData + + +/** + * Called at the end of a STOR request. This indicates that the client has completed its transmission of the + * file. + */ +void FTPFileCallbacks::onStoreEnd() { + printf(">> FTPFileCallbacks::onStoreEnd\n"); + m_storeFile.close(); // Close the open file. + printf("<< FTPFileCallbacks::onStoreEnd\n"); +} // FTPFileCallbacks#onStoreEnd + + +/** + * Called when the client requests retrieval of a file. + */ +void FTPFileCallbacks::onRetrieveStart(std::string fileName) { + printf(">> FTPFileCallbacks::onRetrieveStart: fileName=%s\n", fileName.c_str()); + m_byteCount = 0; + m_retrieveFile.open(fileName, std::ios::binary); + if (m_retrieveFile.fail()) { + printf("<< FTPFileCallbacks::onRetrieveStart: ***FileException***\n"); + throw FTPServer::FileException(); + } + printf("<< FTPFileCallbacks::onRetrieveStart\n"); +} // FTPFileCallbacks#onRetrieveStart + + +/** + * Called when the client is ready to receive the next piece of the file. To indicate that there + * is no more data to send, return a size of 0. + * @param data The data buffer that we can fill to return data back to the client. + * @param size The maximum size of the data buffer that we can populate. + * @return The size of data being returned. Return 0 to indicate that there is no more data to return. + */ +size_t FTPFileCallbacks::onRetrieveData(uint8_t* data, size_t size) { + printf(">> FTPFileCallbacks::onRetrieveData\n"); + m_retrieveFile.read((char *)data, size); + size_t readSize = m_retrieveFile.gcount(); + m_byteCount += readSize; + printf("<< FTPFileCallbacks::onRetrieveData: sizeRead=%ld\n", readSize); + return m_retrieveFile.gcount(); // Return the number of bytes read. +} // FTPFileCallbacks#onRetrieveData + + +/** + * Called when the retrieval has been completed. + */ +void FTPFileCallbacks::onRetrieveEnd() { + printf(">> FTPFileCallbacks::onRetrieveEnd\n"); + m_retrieveFile.close(); + printf("<< FTPFileCallbacks::onRetrieveEnd: bytesTransmitted=%d\n", m_byteCount); +} // FTPFileCallbacks#onRetrieveEnd + + +/** + * Return a list of files in the file system. + * @return a list of files in the file system. + */ +std::string FTPFileCallbacks::onDir() { + + DIR* dir = opendir(FTPServer::getCurrentDirectory().c_str()); + std::stringstream ss; + while(1) { + struct dirent* pDirentry = readdir(dir); + if (pDirentry == nullptr) { + break; + } + ss << pDirentry->d_name << "\r\n"; + } + closedir(dir); + return ss.str(); +} // FTPFileCallbacks#onDir + + +/// ---- END OF FTPFileCallbacks + + +void FTPCallbacks::onStoreStart(std::string fileName) { + printf(">> FTPCallbacks::onStoreStart: fileName=%s\n", fileName.c_str()); + printf("<< FTPCallbacks::onStoreStart\n"); +} // FTPCallbacks#onStoreStart + + +size_t FTPCallbacks::onStoreData(uint8_t* data, size_t size) { + printf(">> FTPCallbacks::onStoreData: size=%ld\n", size); + printf("<< FTPCallbacks::onStoreData\n"); + return 0; +} // FTPCallbacks#onStoreData + + +void FTPCallbacks::onStoreEnd() { + printf(">> FTPCallbacks::onStoreEnd\n"); + printf("<< FTPCallbacks::onStoreEnd\n"); +} // FTPCallbacks#onStoreEnd + + +void FTPCallbacks::onRetrieveStart(std::string fileName) { + printf(">> FTPCallbacks::onRetrieveStart\n"); + printf("<< FTPCallbacks::onRetrieveStart\n"); +} // FTPCallbacks#onRetrieveStart + + +size_t FTPCallbacks::onRetrieveData(uint8_t *data, size_t size) { + printf(">> FTPCallbacks::onRetrieveData\n"); + printf("<< FTPCallbacks::onRetrieveData: 0\n"); + return 0; +} // FTPCallbacks#onRetrieveData + + +void FTPCallbacks::onRetrieveEnd() { +printf(">> FTPCallbacks::onRetrieveEnd\n"); +printf("<< FTPCallbacks::onRetrieveEnd\n"); +} // FTPCallbacks#onRetrieveEnd + + +std::string FTPCallbacks::onDir() { + printf(">> FTPCallbacks::onDir\n"); + printf("<< FTPCallbacks::onDir\n"); + return ""; +} // FTPCallbacks#onDir + +FTPCallbacks::~FTPCallbacks() { + +} // FTPCallbacks#~FTPCallbacks diff --git a/networking/FTPServer/FTPServer.cpp b/networking/FTPServer/FTPServer.cpp new file mode 100644 index 00000000..87899c3b --- /dev/null +++ b/networking/FTPServer/FTPServer.cpp @@ -0,0 +1,819 @@ +/* + * FTPServer.cpp + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#include "FTPServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// trim from start (in place) +static void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} // ltrim + + +// trim from end (in place) +static void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} // rtrim + + +// trim from both ends (in place) +static void trim(std::string &s) { + ltrim(s); + rtrim(s); +} // trim + + +FTPServer::FTPServer() { + printf(">> FTPServer()\n"); + + m_dataSocket = -1; + m_clientSocket = -1; + m_dataPort = -1; + m_dataIp = -1; + m_passiveSocket = -1; + m_serverSocket = -1; + + m_callbacks = nullptr; + m_isPassive = false; + m_isImage = true; + m_chunkSize = 4096; + m_port = 21; // The default Server-PI port + m_loginRequired = false; + m_isAuthenticated = false; + m_userid = ""; + m_password = ""; + + printf("<< FTPServer()\n"); +} // FTPServer#FTPServer + + +FTPServer::~FTPServer() { + // Nothing to do here by default. +} // FTPServer#~FTPServer + + +/** + * Close the connection to the FTP client. + */ +void FTPServer::closeConnection() { + printf(">> closeConnection\n"); + close(m_clientSocket); + printf("<< closeConnection\n"); +} // FTPServer#closeConnection + + +/** + * Close a previously opened data connection. + */ +void FTPServer::closeData() { + printf(">> closeData\n"); + close(m_dataSocket); + m_dataSocket = -1; + printf("<< closeData\n"); +} // FTPServer#closeData + + +/** + * Close the passive listening socket that was opened by listenPassive. + */ +void FTPServer::closePassive() { + printf(">> closePassive\n"); + close(m_passiveSocket); + m_passiveSocket = -1; + printf("<< closePassive\n"); +} // FTPServer#closePassive + + +/** + * Retrieve the current directory. + */ +/* STATIC */ std::string FTPServer::getCurrentDirectory() { + char maxDirectory[256]; + std::string currentDirectory = getcwd(maxDirectory, sizeof(maxDirectory)); + return currentDirectory; +} // FTPServer#getCurrentDirectory + + +/** + * Create a listening socket for the new passive connection. + * @return a String for the passive parameters. + */ +std::string FTPServer::listenPassive() { + printf(">> listenPassive\n"); + + m_passiveSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_passiveSocket == -1) { + printf("socket: %s", strerror(errno)); + } + + struct sockaddr_in clientAddrInfo; + unsigned int addrInfoSize = sizeof(clientAddrInfo); + getsockname(m_clientSocket, (struct sockaddr*)&clientAddrInfo, &addrInfoSize); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(0); + int rc = bind(m_passiveSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + printf("bind: %s\n", strerror(errno)); + } + + rc = listen(m_passiveSocket, 5); + if (rc == -1) { + printf("listen: %s", strerror(errno)); + } + + unsigned int addrLen = sizeof(serverAddress); + rc = getsockname(m_passiveSocket, (struct sockaddr*)&serverAddress, &addrLen); + if (rc == -1) { + printf("getsockname: %s\n", strerror(errno)); + } + + + std::stringstream ss; + ss << ((clientAddrInfo.sin_addr.s_addr >> 0) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 8) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 16) & 0xff) << + "," << ((clientAddrInfo.sin_addr.s_addr >> 24) & 0xff) << + "," << ((serverAddress.sin_port >> 0) & 0xff) << + "," << ((serverAddress.sin_port >> 8) & 0xff); + std::string retStr = ss.str(); + + printf("<< listenPassive: %s\n", retStr.c_str()); + return retStr; +} // FTPServer#listenPassive + + +/** + * Handle the AUTH command. + */ +void FTPServer::onAuth(std::istringstream& ss) { + std::string param; + ss >> param; + printf(">> onAuth: %s\n", param.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + printf("<< onAuth\n"); +} // FTPServer#onAuth + + +/** + * Change the current working directory. + * @param ss A string stream where the first parameter is the directory to change to. + */ +void FTPServer::onCwd(std::istringstream& ss) { + std::string path; + ss >> path; + printf(">> onCwd: path=%s\n", path.c_str()); + chdir(path.c_str()); + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); + printf("<< onCwd\n"); +} // FTPServer#onCwd + + +/** + * Process the client transmitted LIST request. + */ +void FTPServer::onList(std::istringstream& ss) { + std::string directory; + ss >> directory; + printf(">> onList: directory=%s\n", directory.c_str()); + + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + if (m_callbacks != nullptr) { + std::string dirString = m_callbacks->onDir(); + sendData((uint8_t *)dirString.data(), dirString.length()); + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + printf("<< onList\n"); +} // FTPServer#onList + + +void FTPServer::onMkd(std::istringstream &ss) { + std::string path; + ss >> path; + printf(">> onMkd: path=%s\n", path.c_str()); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onMkd\n"); +} // FTPServer#onMkd + + +/** + * Process a NOOP operation. + */ +void FTPServer::onNoop(std::istringstream& ss) { + printf(">> onNoop\n"); + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + printf("<< onNoop\n"); +} // FTPServer#onNoop + + +/** + * Process PORT request. The information provided is encoded in the parameter as + * h1,h2,h3,h4,p1,p2 where h1,h2,h3,h4 is the IP address we should connect to + * and p1,p2 is the port number. The data is MSB + * + * Our logic does not form any connection but remembers the ip address and port number + * to be used for a subsequence data connection. + * + * Possible responses: + * 200 + * 500, 501, 421, 530 + */ +void FTPServer::onPort(std::istringstream& ss) { + printf(">> onPort\n"); + char c; + uint16_t h1, h2, h3, h4, p1, p2; + ss >> h1 >> c >> h2 >> c >> h3 >> c >> h4 >> c >> p1 >> c >> p2; + m_dataPort = p1*256 + p2; + printf("%d.%d.%d.%d %d\n", h1, h2, h3, h4, m_dataPort); + m_dataIp = h1<<24 | h2<<16 | h3<<8 | h4; + sendResponse(RESPONSE_200_COMMAND_OK); // Command okay. + m_isPassive = false; + + printf("<< onPort\n"); +} // FTPServer#onPort + + +/** + * Process the PASS command. + * Possible responses: + * 230 + * 202 + * 530 + * 500, 501, 503, 421 + * 332 + */ +void FTPServer::onPass(std::istringstream& ss) { + std::string password; + ss >> password; + printf(">> onPass: password=%s\n", password.c_str()); + + // If the immediate last command wasn't USER then don't try and process PASS. + if (m_lastCommand != "USER") { + sendResponse(RESPONSE_503_BAD_SEQUENCE); + printf("<< onPass\n"); + return; + } + + // Compare the supplied userid and passwords. + if (m_userid == m_suppliedUserid && password == m_password) { + sendResponse(RESPONSE_230_USER_LOGGED_IN); + m_isAuthenticated = true; + } else { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + closeConnection(); + m_isAuthenticated = false; + } + + + printf("<< onPass\n"); +} // FTPServer#onPass + + +/** + * Process the PASV command. + * Possible responses: + * 227 + * 500, 501, 502, 421, 530 + */ +void FTPServer::onPasv(std::istringstream& ss) { + printf(">> onPasv\n"); + std::string ipInfo = listenPassive(); + std::ostringstream responseTextSS; + responseTextSS << "Entering Passive Mode (" << ipInfo << ")."; + std::string responseText; + responseText = responseTextSS.str(); + sendResponse(RESPONSE_227_ENTERING_PASSIVE_MODE, responseText.c_str()); + m_isPassive = true; + + printf("<< onPasv\n"); +} // FTPServer#onPasv + + +/** + * Process the PWD command to determine our current working directory. + * Possible responses: + * 257 + * 500, 501, 502, 421, 550 + */ +void FTPServer::onPWD(std::istringstream& ss) { + printf(">> onPWD\n"); + sendResponse(257, "\"" + getCurrentDirectory() + "\""); + printf("<< onPWD: %s\n", getCurrentDirectory().c_str()); +} // FTPServer#onPWD + + +/** + * Possible responses: + * 221 + * 500 + */ +void FTPServer::onQuit(std::istringstream& ss) { + printf(">> onQuit\n"); + sendResponse(FTPServer::RESPONSE_221_CLOSING_CONTROL_CONNECTION); // Service closing control connection. + closeConnection(); // Close the connection to the client. + printf("<< onQuit\n"); +} // FTPServer#onQuit + + +/** + * Process a RETR command. The client sends this command to retrieve the content of a file. + * The name of the file is the first parameter in the input stream. + * + * Possible responses: + * 125, 150 + * (110) + * 226, 250 + * 425, 426, 451 + * 450, 550 + * 500, 501, 421, 530 + * @param ss The parameter stream. + */ +void FTPServer::onRetr(std::istringstream& ss) { + + // We open a data connection back to the client. We then invoke the callback to indicate that we have + // started a retrieve operation. We call the retrieve callback to request the next chunk of data and + // transmit this down the data connection. We repeat this until there is no more data to send at which + // point we close the data connection and we are done. + printf(">> onRetr\n"); + std::string fileName; + + ss >> fileName; + uint8_t data[m_chunkSize]; + + + if (m_callbacks != nullptr) { + try { + m_callbacks->onRetrieveStart(fileName); + } catch(FTPServer::FileException& e) { + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + printf("<< onRetr: Returned 550 to client.\n"); + return; + } + } + + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + openData(); + if (m_callbacks != nullptr) { + int readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + while(readSize > 0) { + sendData(data, readSize); + readSize = m_callbacks->onRetrieveData(data, m_chunkSize); + } + } + closeData(); + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + printf("<< onRetr\n"); +} // FTPServer#onRetr + + +void FTPServer::onRmd(std::istringstream &ss) { + printf(">> onRmd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onRmd\n"); +} // FTPServer#onRmd + + +/** + * Called to process a STOR request. This means that the client wishes to store a file + * on the server. The name of the file is found in the parameter. + */ +void FTPServer::onStor(std::istringstream& ss) { + printf(">> onStor\n"); + std::string fileName; + ss >> fileName; + + receiveFile(fileName); + printf("<< onStor\n"); +} // FTPServer#onStor + + +void FTPServer::onSyst(std::istringstream& ss) { + printf(">> onSyst\n"); + sendResponse(215, "UNIX Type: L8"); + printf("<< onSyst\n"); +} // FTPServer#onSyst + + +/** + * Process a TYPE request. The parameter that follows is the type of transfer we wish + * to process. Types include: + * I and A. + * + * Possible responses: + * 200 + * 500, 501, 504, 421, 530 + */ +void FTPServer::onType(std::istringstream& ss) { + printf(">> onType\n"); + std::string type; + ss >> type; + if (type.compare("I") == 0) { + m_isImage = true; + } else { + m_isImage = false; + } + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + printf("<< onType: isImage=%d\n", m_isImage); +} // FTPServer#onType + + +/** + * Process a USER request. The parameter that follows is the identity of the user. + * + * Possible responses: + * 230 + * 530 + * 500, 501, 421 + * 331, 332 + * + */ +void FTPServer::onUser(std::istringstream& ss) { + // When we receive a user command, we next want to know if we should ask for a password. If the m_loginRequired + // flag is set then we do indeed want a password and will send the response that we wish one. + + std::string userName; + ss >> userName; + printf(">> onUser: userName=%s\n", userName.c_str()); + if (m_loginRequired) { + sendResponse(FTPServer::RESPONSE_331_PASSWORD_REQUIRED); + } else { + sendResponse(FTPServer::RESPONSE_200_COMMAND_OK); // Command okay. + } + m_suppliedUserid = userName; // Save the username that was supplied. + printf("<< onUser\n"); +} // FTPServer#onUser + + +void FTPServer::onXmkd(std::istringstream &ss) { + printf(">> onXmkd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onXmkd\n"); +} // FTPServer#onXmkd + + +void FTPServer::onXrmd(std::istringstream &ss) { + printf(">> onXrmd\n"); + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); + printf("<< onXrmd\n"); +} // FTPServer#onXrmd + + +/** + * Open a data connection with the client. + * We will use closeData() to close the connection. + * @return True if the data connection succeeded. + */ +bool FTPServer::openData() { + if (m_isPassive) { + // Handle a passive connection ... here we receive a connection from the client from the passive socket. + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_dataSocket = accept(m_passiveSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + if (m_dataSocket == -1) { + printf("FTPServer::openData: accept(): %s\n", strerror(errno)); + closePassive(); + return false; + } + closePassive(); + } else { + // Handle an active connection ... here we connect to the client. + m_dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(m_dataIp); + serverAddress.sin_port = htons(m_dataPort); + + int rc = connect(m_dataSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + if (rc == -1) { + printf("FTPServer::openData: connect(): %s\n", strerror(errno)); + return false; + } + } + return true; +} // FTPServer#openData + + +/** + * Process commands received from the client. + */ +void FTPServer::processCommand() { + sendResponse(FTPServer::RESPONSE_220_SERVICE_READY); // Service ready. + printf(">> FTPServer::processCommand\n"); + m_lastCommand = ""; + while(1) { + std::string line = ""; + char currentChar; + char lastChar = '\0'; + int rc = recv(m_clientSocket, ¤tChar, 1, 0); + while(rc != -1 && rc!=0) { + line += currentChar; + if (lastChar == '\r' && currentChar == '\n') { + break; + } + printf("%c\n", currentChar); + lastChar = currentChar; + rc = recv(m_clientSocket, ¤tChar, 1, 0); + } // End while we are waiting for a line. + + if (rc == 0 || rc == -1) { // If we didn't get a line or an error, then we have finished processing commands. + break; + } + + std::string command; + std::istringstream ss(line); + getline(ss, command, ' '); + trim(command); + + // We now have a command to process. + + printf("Command: \"%s\"\n", command.c_str()); + if (command.compare("USER")==0) { + onUser(ss); + } + else if (command.compare("PASS")==0) { + onPass(ss); + } + else if (m_loginRequired && !m_isAuthenticated) { + sendResponse(RESPONSE_530_NOT_LOGGED_IN); + } + else if (command.compare("PASV")==0) { + onPasv(ss); + } + else if (command.compare("SYST")==0) { + onSyst(ss); + } + else if (command.compare("PORT")==0) { + onPort(ss); + } + else if (command.compare("LIST")==0) { + onList(ss); + } + else if (command.compare("TYPE")==0) { + onType(ss); + } + else if (command.compare("RETR")==0) { + onRetr(ss); + } + else if (command.compare("QUIT")==0) { + onQuit(ss); + } + else if (command.compare("AUTH")==0) { + onAuth(ss); + } + else if (command.compare("STOR")==0) { + onStor(ss); + } + else if (command.compare("PWD")==0) { + onPWD(ss); + } + else if (command.compare("MKD")==0) { + onMkd(ss); + } + else if (command.compare("XMKD")==0) { + onXmkd(ss); + } + else if (command.compare("RMD")==0) { + onRmd(ss); + } + else if (command.compare("XRMD")==0) { + onXrmd(ss); + } + else if (command.compare("CWD")==0) { + onCwd(ss); + } + else { + sendResponse(FTPServer::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. + } + m_lastCommand = command; + } // End loop processing commands. + + close(m_clientSocket); // We won't be processing any further commands from this client. + printf("<< FTPServer::processCommand\n"); +} // FTPServer::processCommand + + +/** + * Receive a file from the FTP client (STOR). The name of the file to be created is passed as a + * parameter. + */ +void FTPServer::receiveFile(std::string fileName) { + printf(">> receiveFile: %s\n", fileName.c_str()); + if (m_callbacks != nullptr) { + try { + m_callbacks->onStoreStart(fileName); + } catch(FTPServer::FileException& e) { + printf("Caught a file exception!\n"); + sendResponse(FTPServer::RESPONSE_550_ACTION_NOT_TAKEN); // Requested action not taken. + return; + } + } + openData(); + sendResponse(FTPServer::RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION); // File status okay; about to open data connection. + uint8_t buf[m_chunkSize]; + uint32_t totalSizeRead = 0; + while(1) { + int rc = recv(m_dataSocket, &buf, m_chunkSize, 0); + if (rc <= 0) { + break; + } + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveData(buf, rc); + } + totalSizeRead += rc; + } + sendResponse(FTPServer::RESPONSE_226_CLOSING_DATA_CONNECTION); // Closing data connection. + closeData(); + if (m_callbacks != nullptr) { + m_callbacks->onRetrieveEnd(); + } + printf("<< receiveFile: totalSizeRead=%d\n", totalSizeRead); +} // FTPServer#receiveFile + + +/** + * Send data to the client over the data connection previously opened with a call to openData(). + * @param pData A pointer to the data to send. + * @param size The number of bytes to send. + */ +void FTPServer::sendData(uint8_t* pData, uint32_t size) { + printf(">> FTPServer::sendData: size=%d\n", size); + int rc = send(m_dataSocket, pData, size, 0); + if (rc == -1) { + printf("FTPServer::sendData: send(): %s\n", strerror(errno)); + } + printf("<< FTPServer::sendData\n"); +} // FTPServer#sendData + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. + */ +void FTPServer::sendResponse(int code, std::string text) { + printf(">> sendResponse: (%d) %s\n", code, text.c_str()); + std::ostringstream ss; + ss << code << " " << text << "\r\n"; + int rc = send(m_clientSocket, ss.str().data(), ss.str().length(), 0); + printf("<< sendResponse\n"); +} // FTPServer#sendResponse + + +/** + * Send a response to the client. A response is composed of two parts. The first is a code as architected in the + * FTP specification. The second is a piece of text. In this function, a standard piece of text is used based on + * the code. + */ +void FTPServer::sendResponse(int code) { + std::string text = "unknown"; + + switch(code) { // Map the code to a text string. + case RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION: + text = "File status okay; about to open data connection."; + break; + case RESPONSE_200_COMMAND_OK: + text = "Command okay."; + break; + case RESPONSE_220_SERVICE_READY: + text = "Service ready."; + break; + case RESPONSE_221_CLOSING_CONTROL_CONNECTION: + text = "Service closing control connection."; + break; + case RESPONSE_226_CLOSING_DATA_CONNECTION: + text = "Closing data connection."; + break; + case RESPONSE_230_USER_LOGGED_IN: + text = "User logged in, proceed."; + break; + case RESPONSE_331_PASSWORD_REQUIRED: + text = "Password required."; + break; + case RESPONSE_500_COMMAND_UNRECOGNIZED: + text = "Syntax error, command unrecognized."; + break; + case RESPONSE_502_COMMAND_NOT_IMPLEMENTED: + text = "Command not implemented."; + break; + case RESPONSE_503_BAD_SEQUENCE: + text = "Bad sequence of commands."; + break; + case RESPONSE_530_NOT_LOGGED_IN: + text = "Not logged in."; + break; + case RESPONSE_550_ACTION_NOT_TAKEN: + text = "Requested action not taken."; + break; + default: + break; + } + sendResponse(code, text); // Send the code AND the text to the FTP client. +} // FTPServer#sendResponse + + +/** + * Set the callbacks that are to be invoked to perform work. + * @param pCallbacks An instance of an FTPCallbacks based class. + */ +void FTPServer::setCallbacks(FTPCallbacks* pCallbacks) { + m_callbacks = pCallbacks; +} // FTPServer#setCallbacks + + +void FTPServer::setCredentials(std::string userid, std::string password) { + printf(">> setCredentials: userid=%s\n", userid.c_str()); + m_loginRequired = true; + m_userid = userid; + m_password = password; + printf("<< setCredentials\n"); +} // FTPServer#setCredentials + + +/** + * Set the TCP port we should listen on for FTP client requests. + */ +void FTPServer::setPort(uint16_t port) { + m_port = port; +} // FTPServer#setPort + + +/** + * Start being an FTP Server. + */ +void FTPServer::start() { + m_serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_serverSocket == -1) { + printf("socket: %s", strerror(errno)); + } + + int enable = 1; + setsockopt(m_serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_port); + int rc = bind(m_serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); + if (rc == -1) { + printf("bind: %s\n", strerror(errno)); + } + rc = listen(m_serverSocket, 5); + if (rc == -1) { + printf("listen: %s", strerror(errno)); + } + while(1) { + waitForFTPClient(); + processCommand(); + } +} // FTPServer#start + + +/** + * Wait for a new client to connect. + */ +int FTPServer::waitForFTPClient() { + printf(">> FTPServer::waitForFTPClient\n"); + + struct sockaddr_in clientAddress; + socklen_t clientAddressLength = sizeof(clientAddress); + m_clientSocket = accept(m_serverSocket, (struct sockaddr *)&clientAddress, &clientAddressLength); + + char ipAddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddr, sizeof(ipAddr)); + printf("Received connection from %s [%d]\n", ipAddr, clientAddress.sin_port); + + struct sockaddr_in socketAddressInfo; + unsigned int socketAddressInfoSize = sizeof(socketAddressInfo); + getsockname(m_clientSocket, (struct sockaddr*)&socketAddressInfo, &socketAddressInfoSize); + + inet_ntop(AF_INET, &socketAddressInfo.sin_addr, ipAddr, sizeof(ipAddr)); + printf("Connected at %s [%d]\n", ipAddr, socketAddressInfo.sin_port); + printf("<< FTPServer::waitForFTPClient: fd=%d\n", m_clientSocket); + + return m_clientSocket; +} // FTPServer::waitForFTPClient + diff --git a/networking/FTPServer/FTPServer.h b/networking/FTPServer/FTPServer.h new file mode 100644 index 00000000..7dc56f13 --- /dev/null +++ b/networking/FTPServer/FTPServer.h @@ -0,0 +1,137 @@ +/* + * FTPServer.h + * + * Created on: May 6, 2018 + * Author: kolban + */ + +#ifndef NETWORKING_FTPSERVER_FTPSERVER_H_ +#define NETWORKING_FTPSERVER_FTPSERVER_H_ +#include +#include +#include +#include +#include + + +class FTPCallbacks { +public: + virtual void onStoreStart(std::string fileName); + virtual size_t onStoreData(uint8_t* data, size_t size); + virtual void onStoreEnd(); + virtual void onRetrieveStart(std::string fileName); + virtual size_t onRetrieveData(uint8_t *data, size_t size); + virtual void onRetrieveEnd(); + virtual std::string onDir(); + virtual ~FTPCallbacks(); +}; + +/** + * An implementation of FTPCallbacks that uses Posix File I/O to perform file access. + */ +class FTPFileCallbacks : public FTPCallbacks { +private: + std::ofstream m_storeFile; // File used to store data from the client. + std::ifstream m_retrieveFile; // File used to retrieve data for the client. + uint32_t m_byteCount; // Count of bytes sent over wire. +public: + void onStoreStart(std::string fileName) override; // Called for a STOR request. + size_t onStoreData(uint8_t* data, size_t size) override; // Called when a chunk of STOR data becomes available. + void onStoreEnd() override; // Called at the end of a STOR request. + void onRetrieveStart(std::string fileName) override; // Called at the start of a RETR request. + size_t onRetrieveData(uint8_t* data, size_t size) override; // Called to retrieve a chunk of RETR data. + void onRetrieveEnd() override; // Called when we have retrieved all the data. + std::string onDir() override; // Called to retrieve all the directory entries. +}; + + +class FTPServer { +private: + int m_serverSocket; // The socket the FTP server is listening on. + int m_clientSocket; // The current client socket. + int m_dataSocket; // The data socket. + int m_passiveSocket; // The socket on which the server is listening for passive FTP connections. + uint16_t m_port; // The port the FTP server will use. + uint16_t m_dataPort; // The port for data connections. + uint32_t m_dataIp; // The ip address for data connections. + bool m_isPassive; // Are we in passive mode? If not, then we are in active mode. + bool m_isImage; // Are we in image mode? + size_t m_chunkSize; // The maximum chunk size. + std::string m_userid; // The required userid. + std::string m_password; // The required password. + std::string m_suppliedUserid; // The userid supplied from the USER command. + bool m_loginRequired; // Do we required a login? + bool m_isAuthenticated; // Have we authenticated? + std::string m_lastCommand; // The last command that was processed. + + FTPCallbacks* m_callbacks; // The callbacks for processing. + + void closeConnection(); + void closeData(); + void closePassive(); + void onAuth(std::istringstream& ss); + void onCwd(std::istringstream& ss); + void onList(std::istringstream& ss); + void onMkd(std::istringstream& ss); + void onNoop(std::istringstream& ss); + void onPass(std::istringstream& ss); + void onPasv(std::istringstream& ss); + void onPort(std::istringstream& ss); + void onPWD(std::istringstream& ss); + void onQuit(std::istringstream& ss); + void onRetr(std::istringstream& ss); + void onRmd(std::istringstream& ss); + void onStor(std::istringstream& ss); + void onSyst(std::istringstream& ss); + void onType(std::istringstream& ss); + void onUser(std::istringstream& ss); + void onXmkd(std::istringstream& ss); + void onXrmd(std::istringstream& ss); + + bool openData(); + + void receiveFile(std::string fileName); + void sendResponse(int code); + void sendResponse(int code, std::string text); + void sendData(uint8_t* pData, uint32_t size); + std::string listenPassive(); + int waitForFTPClient(); + void processCommand(); + +public: + FTPServer(); + virtual ~FTPServer(); + void setCredentials(std::string userid, std::string password); + void start(); + void setPort(uint16_t port); + void setCallbacks(FTPCallbacks* pFTPCallbacks); + static std::string getCurrentDirectory(); + class FileException: public std::exception { + + }; + + // Response codes. + static const int RESPONSE_150_ABOUT_TO_OPEN_DATA_CONNECTION = 150; + static const int RESPONSE_200_COMMAND_OK = 200; + static const int RESPONSE_202_COMMAND_NOT_IMPLEMENTED = 202; + static const int RESPONSE_212_DIRECTORY_STATUS = 212; + static const int RESPONSE_213_FILE_STATUS = 213; + static const int RESPONSE_214_HELP_MESSAGE = 214; + static const int RESPONSE_220_SERVICE_READY = 220; + static const int RESPONSE_221_CLOSING_CONTROL_CONNECTION = 221; + static const int RESPONSE_230_USER_LOGGED_IN = 230; + static const int RESPONSE_226_CLOSING_DATA_CONNECTION = 226; + static const int RESPONSE_227_ENTERING_PASSIVE_MODE = 227; + static const int RESPONSE_331_PASSWORD_REQUIRED = 331; + static const int RESPONSE_332_NEED_ACCOUNT = 332; + static const int RESPONSE_500_COMMAND_UNRECOGNIZED = 500; + static const int RESPONSE_502_COMMAND_NOT_IMPLEMENTED = 502; + static const int RESPONSE_503_BAD_SEQUENCE = 503; + static const int RESPONSE_530_NOT_LOGGED_IN = 530; + static const int RESPONSE_550_ACTION_NOT_TAKEN = 550; + static const int RESPONSE_553_FILE_NAME_NOT_ALLOWED = 553; +}; + + + +#endif /* NETWORKING_FTPSERVER_FTPSERVER_H_ */ diff --git a/networking/FTPServer/README.md b/networking/FTPServer/README.md new file mode 100644 index 00000000..d5d570c3 --- /dev/null +++ b/networking/FTPServer/README.md @@ -0,0 +1,76 @@ +# ESP32 FTP Server +The world is awash in excellent FTP servers both as stand-alone applications as well as libraries that can be linked with to build ones own integrated FTP server. This project is yet another. + +The design intent is that it will be available to ESP32 based applications to server files from a Posix file system. + +## FTP Protocol +We will be concentrating exclsuively on the FTP Server protocol. This will allow an ESP32 to serve up files to FTP clients and allow FTP clients to deposit new files. + +The FTP Protocol is described in [RFC 959](https://tools.ietf.org/html/rfc959). + +Our server engine will implement the Server-PI (Server Protocol Interpreter) and the Server-DTP (Server Data Transfer Process). + +## Commands + +### PASV +The client has requested that the server enter passive mode. The response must be: + +``` +227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). +``` + +### PORT +Client requests that the server form a data connection to the given host and port. This is used in FTP *active* mode. +``` +PORT {h1,h2,h3,h4,p1,p2} +``` +port number is p1*256 + p2 + +### RETR +This command represents a request *from the client* to retrieve a file held by the server. + +### STOR +This command represents a request *from the client* to stor a file on the server. + +### SYST +Determine the System type of the FTP server. +``` +SYST +``` +The recommended response is: +``` +215 UNIX Type: L8 +``` + +### TYPE +Define the type of data to be transmitted. Codes include: + +* `A` - ASCII +* `E` - EBCDIC +* `I` - Image (binary) + +Example: +``` +TYPE I +``` + +### USER +Supply the user name that the client wishes to present to the FTP server. +``` +USER {name} +``` + +A response of `331` will request a password and should then be followed by the `PASS` command. + +## Active vs Passive +The FTP server can communicate data to the client in one of two modes called *active* and *passive*. In active mode, the FTP client is responsible for setting up a listening socket. When the FTP client is ready to receive data, it sends a `PORT` command informing the server of the IP address and port against which the FTP server should form the connection. +In passive mode, the FTP client sends a `PASV` command to the FTP server. The FTP server then responds with a host and port pair which will be listened upon to receive a connection request *from* the client. Once received, the data can then flow over this connection. + +See also: + +* [Active FTP vs. Passive FTP, a Definitive Explanation](http://slacksite.com/other/ftp.html) + +# References + +* [FTP: File Transfer Protocol](https://cr.yp.to/ftp.html) +* [Wikipedia: File Transfer Protocol](https://en.wikipedia.org/wiki/File_Transfer_Protocol) \ No newline at end of file diff --git a/networking/FTPServer/main.cpp b/networking/FTPServer/main.cpp new file mode 100644 index 00000000..45725051 --- /dev/null +++ b/networking/FTPServer/main.cpp @@ -0,0 +1,12 @@ +#include "FTPServer.h" +#include + + +int main(int argc, char* argv[]) { + printf("FTPServer starting!\n"); + FTPServer *ftpServer = new FTPServer(); + ftpServer->setCallbacks(new FTPFileCallbacks()); + //ftpServer->setCredentials("user", "pass"); + ftpServer->setPort(9876); + ftpServer->start(); +} diff --git a/networking/bootwifi/BootWiFi.cpp b/networking/bootwifi/BootWiFi.cpp index f56016b8..cc07c898 100644 --- a/networking/bootwifi/BootWiFi.cpp +++ b/networking/bootwifi/BootWiFi.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "BootWiFi.h" #include "sdkconfig.h" @@ -201,7 +202,7 @@ static void processForm(HttpRequest* pRequest, HttpResponse* pResponse) { //pResponse->sendData(std::string((char*)selectAP_html, selectAP_html_len)); pResponse->close(); FreeRTOS::sleep(500); - GeneralUtils::restart(); + System::restart(); ESP_LOGD(LOG_TAG, "<< processForm"); } // processForm @@ -242,6 +243,7 @@ class BootWifiEventHandler: public WiFiEventHandler { esp_err_t staGotIp(system_event_sta_got_ip_t event_sta_got_ip) { ESP_LOGD("BootWifiEventHandler", ">> staGotIP"); + m_pBootWiFi->m_apConnectionStatus = ESP_OK; // Set the status to ESP_OK m_pBootWiFi->m_completeSemaphore.give(); // If we got an IP address, then we can end the boot process. ESP_LOGD("BootWifiEventHandler", "<< staGotIP"); return ESP_OK; @@ -252,8 +254,17 @@ class BootWifiEventHandler: public WiFiEventHandler { }; +/** + * Boot WiFi + * + * @brief Get connected to WiFi + * + * @detailed If SSID & Password were previously saved, connect to the AP. Otherwise become an AP and start an HTTP server so that the user can set SSID & Password - then save it. + * + */ void BootWiFi::bootWiFi2() { ESP_LOGD(LOG_TAG, ">> bootWiFi2"); + // Check for a GPIO override which occurs when a physical Pin is high // during the test. This can force the ability to check for new configuration // even if the existing configured access point is available. @@ -280,7 +291,9 @@ void BootWiFi::bootWiFi2() { connectionInfo.ipInfo.gw.addr, connectionInfo.ipInfo.netmask.addr ); - m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password); // Connect to the access point. + + m_apConnectionStatus = m_wifi.connectAP(connectionInfo.ssid, connectionInfo.password); // Try to connect to the access point. + m_completeSemaphore.give(); // end the boot process so we don't hang... } else { // We do NOT have connection information. Let us now become an access @@ -305,19 +318,30 @@ void BootWiFi::setAccessPointCredentials(std::string ssid, std::string password) } // setAccessPointCredentials -void BootWiFi::boot() { + +/** + * @brief Main entry point into booting WiFi - see BootWiFi2 for more detail. + * + * The event handler will be called back with the outcome of the connection. + * + * @returns ESP_OK if successfully connected to an access point. Otherwise returns wifi_err_reason_t - to print use GeneralUtils::wifiErrorToString + */ +uint8_t BootWiFi::boot() { ESP_LOGD(LOG_TAG, ">> boot"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " | BootWiFi |"); ESP_LOGD(LOG_TAG, " +----------+"); ESP_LOGD(LOG_TAG, " Access point credentials: %s/%s", m_ssid.c_str(), m_password.c_str()); - m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. + m_completeSemaphore.take("boot"); // Take the semaphore which will be unlocked when we complete booting. bootWiFi2(); - m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. + m_completeSemaphore.wait("boot"); // Wait for the semaphore that indicated we have completed booting. + m_wifi.setWifiEventHandler(nullptr); // Remove the WiFi boot handler when we have completed booting. ESP_LOGD(LOG_TAG, "<< boot"); -} + return m_apConnectionStatus; +} // boot BootWiFi::BootWiFi() { m_httpServerStarted = false; + m_apConnectionStatus = UINT8_MAX; setAccessPointCredentials("esp32", "password"); // Default access point credentials } diff --git a/networking/bootwifi/BootWiFi.h b/networking/bootwifi/BootWiFi.h index 3d37dccd..9c4a17b6 100644 --- a/networking/bootwifi/BootWiFi.h +++ b/networking/bootwifi/BootWiFi.h @@ -24,12 +24,13 @@ class BootWiFi { bool m_httpServerStarted; std::string m_ssid; std::string m_password; + uint8_t m_apConnectionStatus; // receives the connection status. ESP_OK = received SYSTEM_EVENT_STA_GOT_IP event. FreeRTOS::Semaphore m_completeSemaphore = FreeRTOS::Semaphore("completeSemaphore"); public: BootWiFi(); void setAccessPointCredentials(std::string ssid, std::string password); - void boot(); + uint8_t boot(); }; #endif /* MAIN_BOOTWIFI_H_ */ diff --git a/networking/bootwifi/README.md b/networking/bootwifi/README.md index 3d93f557..2f2a7c6c 100644 --- a/networking/bootwifi/README.md +++ b/networking/bootwifi/README.md @@ -13,6 +13,27 @@ This code is supplied in the form of an ESP-IDF module. In addition, BootWiFi h The logic uses C++ exception handling and hence C++ exception handling must be enabled in `make menuconfig`. +## Example +Below is a sample of a minimal main.cpp. +* If you run it on an esp32, it should create a new open wifi network with SSID: "ESP32". +* Connect to that network. +* Then open a browser and go to http://192.168.4.1. +```C++ +#include "BootWiFi.h" +#include "sdkconfig.h" + +extern "C" { + void app_main(void); +} + +BootWiFi *boot; + +void app_main(void) { + boot = new BootWiFi(); + boot->boot(); +} +``` + ## GPIO boot override To enable the ability to specify a GPIO pin to override known station information, compile the code with `-DBOOTWIFI_OVERRIDE_GPIO=` when `` is a GPIO pin number. If the pin is high at startup, then it will override. The pin is configured as pull-down low so it need not be artificially held low. The default is no override pin. diff --git a/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c b/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c index f46a0fde..38de27c9 100644 --- a/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c +++ b/networking/mqtt/paho_mqtt_embedded_c/MQTTClient-C/src/linux/MQTTLinux.c @@ -215,7 +215,9 @@ static int NetworkConnectSSL(Network *n, char *addr, int port) { mbedtls_entropy_init(&mqtt_ssl_context->entropy); mbedtls_ssl_conf_dbg(&mqtt_ssl_context->conf, my_debug, stdout); +#ifdef CONFIG_MBEDTLS_DEBUG mbedtls_debug_set_threshold(4); // Log at verbose only +#endif // CONFIG_MBEDTLS_DEBUG int ret = mbedtls_ctr_drbg_seed( &mqtt_ssl_context->ctr_drbg, diff --git a/tasks/watchdogs/README.md b/tasks/watchdogs/README.md new file mode 100644 index 00000000..052bc868 --- /dev/null +++ b/tasks/watchdogs/README.md @@ -0,0 +1,6 @@ +# Watchdogs +This is a sample application that illustrates the capabilities of watchdogs and watchdog processing. + +A related YouTube video is available here: + +https://www.youtube.com/watch?v=C2xF3O6qkbg \ No newline at end of file diff --git a/tasks/watchdogs/main.cpp b/tasks/watchdogs/main.cpp new file mode 100644 index 00000000..de4484c5 --- /dev/null +++ b/tasks/watchdogs/main.cpp @@ -0,0 +1,155 @@ +#include "freertos/FreeRTOS.h" +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "nvs_flash.h" +#include "driver/gpio.h" +#include + +esp_err_t event_handler(void *ctx, system_event_t *event) +{ + return ESP_OK; +} + +extern "C" +{ + void app_main(void); +} + +void highPriorityTask(void *myData) +{ + printf("High priority task started and now looping for 10 seconds. Our priority is %d.\n", uxTaskPriorityGet(nullptr)); + TickType_t startTicks = xTaskGetTickCount(); + while (xTaskGetTickCount() - startTicks < (10 * 1000 / portTICK_PERIOD_MS)) + { + // Do nothing but loop + } + printf("High priority task ended\n"); + vTaskDelete(nullptr); +} + +void hardLoopTask(void *myData) +{ + printf("Hard loop task started ...\n"); + while (1) + { + // do nothing but burn CPU + } +} + +void hardLoopTaskNoInterrupts(void *myData) +{ + printf("Hard loop task disabling interrupts started ...\n"); + taskDISABLE_INTERRUPTS(); + while (1) + { + // do nothing but burn CPU + } +} + +void myTask(void *myData) +{ + printf("# Running in myTask\n"); + printf("# Registering our new task with the task watchdog.\n"); + esp_task_wdt_add(nullptr); + + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + printf("# Removing our watchdog registration so we can do something expensive.\n"); + esp_task_wdt_delete(nullptr); + + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Re-registering our task with the task watchdog.\n"); + esp_task_wdt_add(nullptr); + printf("# Looping 5 times with a delay of 1 second and not feeding the watchdog.\n"); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + } + + printf("# Our current task priority is %d.\n", uxTaskPriorityGet(nullptr)); + printf("# Spwaning a higher priority task\n"); + xTaskCreate(highPriorityTask, // Task code + "Priority task", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 5, // Priority + nullptr // task handle + ); + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + printf("Spawning a hard-loop function!\n"); + xTaskCreate(hardLoopTaskNoInterrupts, // Task code + "Hard Loop", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 5, // Priority + nullptr // task handle + ); + + printf("# Looping 5 times with a delay of 1 second and positively feeding the watchdog.\n"); + esp_task_wdt_reset(); + for (int i = 0; i < 5; i++) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Tick\n"); + esp_task_wdt_reset(); + } + + + printf("# Removing our watchdog registration before we end the task.\n"); + esp_task_wdt_delete(nullptr); + + printf("# Ending myTask\n"); + vTaskDelete(nullptr); +} // myTask + +void app_main(void) +{ + xTaskHandle handle; + printf("App starting\n"); + printf("Initializing the task watchdog subsystem with an interval of 2 seconds.\n"); + esp_task_wdt_init(2, false); + + printf("Creatign a new task.\n"); + // Now let us create a new task. + xTaskCreate(myTask, // Task code + "My Task", // Name of task + 16 * 1024, // Stack size + nullptr, // Task data + 0, // Priority + &handle // task handle + ); + + //printf("App Ended!\n"); +} // app_main diff --git a/tools/MMUTool/MMUTool.html b/tools/MMUTool/MMUTool.html new file mode 100644 index 00000000..b50337ab --- /dev/null +++ b/tools/MMUTool/MMUTool.html @@ -0,0 +1,134 @@ + + + + +MMUTool + + + + + +

ESP32 MMU Tool

+

Storage address:

+

MMU entry:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
0x3F40 00000x3F40 FFFF0Data
0x3F41 00000x3F41 FFFF1Data
.........Data
0x3F7F 00000x3F7F FFFF63Data
0x4000 00000x4000 FFFF64Instruction
0x4001 00000x4001 FFFF65Instruction
.........Instruction
0x403F 00000x403F FFFF127Instruction
0x4040 00000x4040 FFFF128Instruction
.........Instruction
0x407F 00000x407F FFFF255Instruction
+ + + \ No newline at end of file diff --git a/tools/bootloaderExamine/.gitignore b/tools/bootloaderExamine/.gitignore new file mode 100644 index 00000000..5db6b6fc --- /dev/null +++ b/tools/bootloaderExamine/.gitignore @@ -0,0 +1,2 @@ +/a.out +/app-template.bin diff --git a/tools/bootloaderExamine/main.cpp b/tools/bootloaderExamine/main.cpp new file mode 100644 index 00000000..60472e30 --- /dev/null +++ b/tools/bootloaderExamine/main.cpp @@ -0,0 +1,153 @@ +#include +#include +#include + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t segment_count; + /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_mode; + /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_speed: 4; + /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint8_t spi_size: 4; + uint32_t entry_addr; + /* WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP + * pin and sets this field to 0xEE=disabled) */ + uint8_t wp_pin; + /* Drive settings for the SPI flash pins (read by ROM bootloader) */ + uint8_t spi_pin_drv[3]; + /* Reserved bytes in ESP32 additional header space, currently unused */ + uint8_t reserved[11]; + /* If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest + * is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature + * is appended after this (and the simple hash is included in the signed data). */ + uint8_t hash_appended; +} __attribute__((packed)) esp_image_header_t; + +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_segment_header_t; + + +/** + * @brief Return a memory area name given an address. + * @param addr The address to decode. + * @return A string description of the memory area. + */ +static const char* area(uint32_t addr) { + if (addr >= 0x3FF80000 && addr <= 0x3FF81FFF) { + return "RTS Fast (8K)"; + } + if (addr >= 0x3FF82000 && addr <= 0x3FF8FFFF) { + return "Reserved"; + } + if (addr >= 0x3FF90000 && addr <= 0x3FF9FFFF) { + return "Internal ROM 1 (64K)"; + } + if (addr >= 0x3FFA0000 && addr <= 0x3FFADFFF) { + return "Reserved"; + } + if (addr >= 0x3FFAE000 && addr <= 0x3FFDFFFF) { + return "Internal SRAM 2 (200K)"; + } + if (addr >= 0x3FFE0000 && addr <= 0x3FFFFFFF) { + return "Internal SRAM 1 (128K)"; + } + if (addr >= 0x40000000 && addr <= 0x40007FFF) { + return "Internal ROM 0 0 (32K)"; + } + if (addr >= 0x40008000 && addr <= 0x4005FFFF) { + return "Internal ROM 0 (352K)"; + } + if (addr >= 0x40060000 && addr <= 0x4006FFFF) { + return "Reserved"; + } + if (addr >= 0x40070000 && addr <= 0x4007FFFF) { + return "Internal SRAM 0 (64K)"; + } + if (addr >= 0x40080000 && addr <= 0x4009FFFF) { + return "Internal SRAM 0 (128K)"; + } + if (addr >= 0x400A0000 && addr <= 0x400AFFFF) { + return "Internal SRAM 1 (64K)"; + } + if (addr >= 0x400B0000 && addr <= 0x400B7FFF) { + return "Internal SRAM 1 (32K)"; + } + if (addr >= 0x400B8000 && addr <= 0x400BFFFF) { + return "Internal SRAM 1 (32K)"; + } + if (addr >= 0x400C0000 && addr <= 0x400C1FFF) { + return "RTC FAST Memory (8K)"; + } + if (addr >= 0x50000000 && addr <= 0x50001FFF) { + return "RTC SLOW Memory (8K)"; + } + if (addr >= 0x3F400000 && addr <= 0x3F7FFFFF) { + return "External Memory (Data)"; + } + if (addr >= 0x3F800000 && addr <= 0x3FBFFFFF) { + return "External Memory (Data)"; + } + if (addr >= 0x400C2000 && addr <= 0x40BFFFFF) { + return "External Memory (Instruction)"; + } + return "Un-described"; +} //area + + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ESP32_BIN_FILE.bin\n", argv[0]); + return 0; + } + + const char* fileName = argv[1]; + esp_image_header_t header; + esp_image_segment_header_t imageSegmentHeader; + + FILE* file = fopen(fileName, "rb"); + if (file == nullptr) { + printf("Failed to open file %s\n", fileName); + return 0; + } + + size_t count = fread(&header, sizeof(esp_image_header_t), 1, file); + if (count != 1) { + printf("Failed to read esp_image_header_t\n"); + return 0; + } + + if (header.magic != 0xE9) { + printf("Failed to find magic number (0xE9) in BIN file header.\n"); + return 0; + } + + printf("Dump of ESP32 binary file: %s\n", fileName); + printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s, hash_appended: %d\n", + header.magic, header.segment_count, header.entry_addr, area(header.entry_addr), header.hash_appended); + + printf("\n"); + printf("Seg | Start | End | Length | Area\n"); + printf("----+------------+------------+-------------------+-------------------------------\n"); + for (int i=0; i { + resolveMoreData = resolveA; // The promise is resolved when append is called to provide more data. + }); + } // moreData + + + /** + * Get the next SLIP data packet. + * Assume that the serial data received is a sequence of packets. This function returns a promise that + * is resolved when a packet is available for processing. + */ + async function getNextDataPacket() { + var p = new Promise(async (resolve, reject) => { + console.log("getNextDataPacket>>"); + while(true) { // Keep looping until we have a data packet. + if (DataPacket().containsSLIPPacket(serialDataBuffer)) { // Does the buffer now contain a SLIP packet? + var newDataPacket = DataPacket(); // Create a new data packet + serialDataBuffer = newDataPacket.setSLIPPacket(serialDataBuffer); // Load the data packet with the raw serial data. + console.log("Got a new data packet: %s\n%s", + newDataPacket.toString(), hexy.hexy(newDataPacket.getData())); + resolve(newDataPacket); + return; + } + await moreData(); // We didn't find a data packet in our data, so wait for more data. + } // We will loop forever waiting for packets. + }); + return p; // Return the promise that is resolved when we have a data packet. + } // getNextDataPacket + + + empty(); // Initialize / empty the serial data. + return { + "append": append, + "empty": empty, + "getNextDataPacket": getNextDataPacket + } +} // SerialData + + +/** + * A data packet encapsulates a command or response. + */ +function DataPacket() { + var dataPacket = Buffer.allocUnsafe(0); + + /** + * Examine the data buffer looking for a slip trailer (0xc0). If found, we return the index + * at which it was found. If not found, we return -1. + * @param {Buffer} data The data in which we are looking for the slip trailer. + * @returns The original of the slip trailer or -1 if not found. + */ + function getSLIPTrailer(data) { + for (i=1; i 0; + } // containsSLIPPacket + + + /** + * Process the data passed in that is SLIP encoded and build a parsed data packet. + * @param {Buffer} data The data to build a packet from. This will be raw data received from UART. + * @returns The unprocessed data. + */ + function setSLIPPacket(data) { + console.log("setSLIPPacket:\n%s", hexy.hexy(data)); + assert(data); // Assert if we have been presented no data. + assert(data.length > 0); // Assert that the data length is not 0. + assert(data[0] == 0xc0); // Assert if the first bytes isn't a SLIP marker. + + var end = getSLIPTrailer(data); // Get the end marker for the SLIP packet. + if (end == -1) { + throw "No SLIP trailer in data we are to process."; + } + assert(end > 0); + + dataPacket = Buffer.allocUnsafe(data.length); // Our data will be no MORE (but likely less) than data.length bytes in size. + for (var i=1, j=0; i 0xc0 + dataPacket[j] = 0xc0; + i++ + } else if (data[i] == 0xdb && data[i+1] == 0xdd) { // 0xdb 0xdd -> 0xdb + dataPacket[j] = 0xdb; + i++; + } else { + dataPacket[j] = data[i] + } + } // End loop over data. + + // We now have a buffer (dataPacket) that contains our decoded data but will be too large. However, j contains the number of bytes we want. + var temp = Buffer.allocUnsafe(j); + dataPacket.copy(temp, 0, 0, j); // Copy dataPacket[0..j-1] -> temp + dataPacket = temp; // Set datapacket to the shrunk buffer. + + // We have now processed what we wanted from the incoming data and now we must return the unused data. + i++; + var unprocessedLength = data.length - i; + temp = Buffer.allocUnsafe(unprocessedLength); + data.copy(temp, 0, i); // copy data[i..] -> temp + + console.log("setSLIPPacket: New data has length: %d, returning unused buffer of size: %d", dataPacket.length, temp.length); + if (temp.length > 0) { + console.log("Unused:\n%s", hexy.hexy(temp)); + } + return temp; + } // setSLIPPacket + + + /** + * Internally we have a dataPacket. We want to return a SLIP encoded buffer from that packet. + * @returns A SLIP encoded representation of our data Packet. + */ + function getSLIPPacket() { + var temp = Buffer.allocUnsafe(dataPacket.length * 2); + var i = 0; // The variable called "i" will be the current byte being written into the slip encoded output. + temp[i] = 0xc0; + i++; + for (j=0; j temp2 + temp2[i] = 0xc0; // Terminate with a slip trailder. + return temp2; + } // getSLIPPacket + + /** + * Set the data. + * @param {*} data + */ + function setData(data) { + dataPacket = data; + } + + function getData() { + return dataPacket; + } + + function toString() { + var result; + if (dataPacket[0] == 0) { + result = "Request: "; + result += "command: " + commandToString(dataPacket[1]); + result += ", size: " + dataPacket.readUInt16LE(2); + result += ", calculatedSize: " + (dataPacket.length - 8); + } else if (dataPacket[0] == 1) { + result = "Response: "; + result += "command: " + commandToString(dataPacket[1]); + result += ", size: " + dataPacket.readUInt16LE(2); + result += ", calculatedSize: " + (dataPacket.length - 8); + result += ", value: " + dataPacket.readUInt32LE(4).toString(16); + // For the ESP32 + // Size - 4 = status + // Size - 3 = error + result += ", status: " + dataPacket[dataPacket.length-2]; + result += ", errorCode: " + dataPacket[dataPacket.length-1]; + } else { + result = "Unknown command: " + dataPacket[0]; + } + return result; + } // toString + + return { + "containsSLIPPacket": containsSLIPPacket, // Does the data passed in define contain a SLIP packet? + "getSLIPPacket": getSLIPPacket, + "setSLIPPacket": setSLIPPacket, + "getData": getData, + "setData": setData, + "toString": toString + } // End of return +} // DataPacket + + +/** + * Convert a command code to a string representation. + * @param {String} command The command to be converted. + * @returns A string representation of the command. + */ +function commandToString(command) { + switch(command) { + case COMMAND_FLASH_BEGIN: + return "COMMAND_FLASH_BEGIN"; + case COMMAND_FLASH_DATA: + return "COMMAND_FLASH_DATA"; + case COMMAND_FLASH_END: + return "COMMAND_FLASH_END"; + case COMMAND_MEM_BEGIN: + return "COMMAND_MEM_BEGIN"; + case COMMAND_MEM_END: + return "COMMAND_MEM_END"; + case COMMAND_MEM_DATA: + return "COMMAND_MEM_DATA"; + case COMMAND_SYNC: + return "COMMAND_SYNC"; + case COMMAND_WRITE_REG: + return "COMMAND_WRITE_REG"; + case COMMAND_READ_REG: + return "COMMAND_READ_REG"; + case COMMAND_SPI_SET_PARAMS: + return "COMMAND_SPI_SET_PARAMS"; + case COMMAND_SPI_FLASH_MD5: + return "COMMAND_SPI_FLASH_MD5"; + } + return "Unknown command: " + command; +} // commandToString + + +function setFlags(options) { + return new Promise((resolve, reject) => { + port.set(options, () => { + resolve(); + }); + }); +} // setFlags + + +function delay(msecs) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, msecs); + }); +} // delay + + +/** + * Write the command header + * @param command // The ESP32 flash loader command + * @param data // The data to send. + * @param checksum // [Optional] The checksum value + * @returns + */ +async function buildAndSendRequest(command, data, checksum = 0) { + console.log("buildAndSendRequest: %s, checksum: 0x%s, dataLength: %d", commandToString(command), checksum.toString(16), data.length); + var buf = Buffer.allocUnsafe(8 + data.length); // Create a buffer used to hold the command. + buf.writeUInt8(0, 0); // Flag as a request [0] + buf.writeUInt8(command, 1); // Write the command [1] + buf.writeUInt16LE(data.length, 2); // Write the size [2-3] + buf.writeUInt32LE(checksum, 4); // Write the checksum [4-7] + data.copy(buf, 8); // Copy data[0..] to buf[8..] + var dataPacket = DataPacket(); // Create a new data packet object + dataPacket.setData(buf); // Populate the data packet + await portWrite(dataPacket.getSLIPPacket()); // Send the data packet to the ESP32. +} // buildAndSendRequest + + +/** + * Promise to flush all pending transmitting data before resolution. + * @returns A promise that is resolved when the transmission data has all been sent. + */ +async function drain() { + return new Promise((resolve, reject) => { + port.drain(() => {resolve()}); + }); +} // drain + + +/** + * Promise to discard all un-transmitted output data and discard all unread input data. + * @returns A promise that is resolved when input/output data is discarded. + */ +function flush() { + return; + console.log("Flushing..."); + return new Promise((resolve, reject) => { + port.flush(()=>{resolve()}); + }); +} // flush + +// We send a UART command request. The thinking is that we will now start to receive a UART command response. +// The response won't arrive as a unit but will instead be dribbled in over time asynchronously. As such, we need to maintain +// state to process it. A response is composed of a response header and response payload. The response header (as seen by +// UART) will contain: +// 0xC0 - SLIP header +// 0x01 - Direction. Should be 0x01 for a response. +// 0x?? - Command +// 0x?? 0x?? - Size of payload data. +// 0x?? 0x?? 0x?? 0x?? - Value. Used by some commands (NOT ALL)!! +// 0x?? ... - Data as indicated by the size command. +// 0x?? 0x?? 0x?? 0x?? - Outcome +// 0xC0 - SLIP trailer +function ProcessResponse() { + var command; + var data; + var dataSize; + var value; + var status; + var errorCode; + var resolve; + var reject = null; + var promise = null; + + var timeoutId; + + + async function getResponse() { + console.log(">> ProcessResponse.getResponse()") + + promise = new Promise(async (resolveA, rejectA) => { + resolve = resolveA; + reject = rejectA; + timeoutId = setTimeout(()=>{ + console.log("getResponse timeout!"); + reject(); + }, 1000); + var dp = await serialData.getNextDataPacket(); + console.log(" .. getResponse ... we think we have a new datapacket!"); + processPacket(dp.getData()); + resolve(); + }); + return promise; + } + + function processPacket(inputData) { + assert(inputData); + assert(inputData.length > 0); + console.log("Received a packet, length %d:\n%s", inputData.length, hexy.hexy(inputData)); + // 0 - direction + // 1 - command + // [2-3] - size + // [4-7] - value + // [8-(length-3)] - data + // [length-2] - status + // [length-1] - error code + if (inputData.readUInt8(0) != 0x01) { + throw "Expected direction to be 0x01 for a response."; + } + command = inputData.readUInt8(1); + dataSize = inputData.readUInt16LE(2); + value = inputData.readUInt32LE(4); + data = Buffer.allocUnsafe(inputData.length - 8 - 2); + inputData.copy(data, 0, 8, inputData.length - 2); + status = inputData.readUInt8(inputData.length - 2); + erorCode = inputData.readUInt8(inputData.length - 1); + clearTimeout(timeoutId); + } // processPacket + + return { + getResponse: getResponse, + getData: function() { + return data; + } + }; +} // ProcessResponse + + +/** + * Send a SYNC command and wait for the response. + */ +async function doSync() { + console.log("Sending sync"); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(36); // The sync message is 0x07 0x07 0x12 0x20 0x55 (repeated 32 times) + buf.writeUInt8(0x07, 0); + buf.writeUInt8(0x07, 1); + buf.writeUInt8(0x12, 2); + buf.writeUInt8(0x20, 3); + for (var i=4; i<36; i++) { + buf.writeUInt8(0x55, i); + } + await buildAndSendRequest(COMMAND_SYNC, buf); + console.log("response: " + response); + try { + await response.getResponse(); + resolve(); + } + catch(e) { + console.log("Failed to get SYNC command response: " + e); + reject(); + } + }); + return p; +} // doSync + + +/** + * Calculate the checksum of the data. + * The current algorithm is the seed 0xEF XORed with each of the bytes of data. + * @param {Buffer} data The data against which the checksum is to be calculated. + * @returns The checksum value. + */ +function calculateChecksum(data) { + var checksum = 0xef; + for (var i=0; i> memEnd: executeFlag: %d, entrypointAddress: 0x%s", executeFlag, entrypointAddress.toString(16)); + var p = new Promise(async function (resolve, reject) { + await flush(); // Discard any pending input or output. + var buf = Buffer.allocUnsafe(2*4); // Allocate the payload which is 2 * 32bit words + buf.writeUInt32LE(executeFlag, 0); // Execute flag. + buf.writeUInt32LE(entrypointAddress, 4); // Entry point address. + await buildAndSendRequest(COMMAND_MEM_END, buf); + await response.getResponse(); // Wait for a response. + resolve(); // Resolve the promise. + }); + return p; + } // memEnd + + /** + * Determine if we have sent all the data for the given MEM_BEGIN transaction. + * @returns True if we have sent all the data we need to send. + */ + function allDataSent() { + // We maintain state in a number of variables. The variable called sizeWritten is the number of bytes that we have written to + // the ESP32. The variable called totalSize is the number of bytes that we expect to write. The notion of all data sent + // will occur when the sizeWritten is equal to totalSize. + return sizeWritten >= totalSize; + } // allDataSent + + + /** + * Write a MEM_DATA command. + * This command should follow a MEM_BEGIN command. If transmits a unit of data that is written directly into + * the ESP32 RAM. The address in RAM is supplied by the MEM_BEGIN command and previous MEM_DATA calls. + * @param {Buffer} data + */ + async function memData(data) { + console.log(">> memData: sequenceId: %d", sequenceNumber); + var sizeRemaining = totalSize - sizeWritten; // Calculate the amount of data still expected. + var offsetIntoData = sequenceNumber * MEM_PACKET_SIZE; // Calculate the offset into the data from which to send. + var sizeToWrite = sizeRemaining > MEM_PACKET_SIZE?MEM_PACKET_SIZE:sizeRemaining; // Calculate how large this unit should be. + var buf2 = Buffer.allocUnsafe(4*4 + data.length); // Allocate storage for the command preamble. + buf2.writeUInt32LE(sizeToWrite, 0); // Size of data to be sent. + buf2.writeUInt32LE(sequenceNumber, 4); // Current sequence number. + buf2.writeUInt32LE(0, 8); // Zero. + buf2.writeUInt32LE(0, 12); // Zero. + data.copy(buf2, 16); + //var buf = Buffer.concat([buf2, data]); + //console.log("Here is the data we are sending:\n%s", hexy.hexy(buf)); + + var p = new Promise(async function(resolve, reject) { + console.log("+----------+"); + console.log("| MEM_DATA |"); + console.log("+----------+"); + await flush(); // Discard any untransmitted and unreceived data. // End the SLIP communication. + await buildAndSendRequest(COMMAND_MEM_DATA, buf2, calculateChecksum(data)); + sequenceNumber ++; // Increment the sequence number. + sizeWritten += sizeToWrite; // Updae the size of the data that we have now written. // Ensure that all the transmitted data is now outbound. + await response.getResponse(); // Await a response. + resolve(); // Indicate that the command has been completed. + }); + return p; + }; // memData + + + /** + * Transmit a buffer of data to a given address within ESP32 RAM. + * @param {Buffer} data The data to be written into ESP32 RAM. + * @param {integer} address The address that data will be written to. + */ + async function send(data, address) { + // If the data is not 32bit aligned, expand to 32 bits. + if (data.length % 4 != 0) { + let buf = Buffer.allocUnsafe((data.length + 4) & 0xFFFFFFFC).fill(0xff); + data.copy(buf); + data = buf; + } + console.log(">> send data of size: %d, to RAM address 0x%s", data.length, address.toString(16)); + await memBegin(data.length, address); + while(!allDataSent()) { + await memData(data); + } + }; // send + + + return { + "send": send, + "end": memEnd + }; +} // MemCommands + + +function portWrite(data) { + console.log("Writing data down port, length: %d", data.length) + return new Promise((resolve, reject) => { + port.write(data, async ()=> { + await drain; + resolve() + }); + }); +} // portWrite + + +/** + * Process the handling of flash commands. + */ +function FlashCommands() { + var sequenceNumber; // The current sequence number. + var totalSize; // The total number of bytes we expect to send. + var sizeWritten; // The number of bytes we have written so far. + + const FLASH_PACKET_SIZE = 0x4000; // 16K = 16384 bytes + + + async function flashBegin(size, address) { + console.log("Flash Begin: size: %d, address: 0x%s", size, address.toString(16)); + var p = new Promise(async function (resolve, reject) { + console.log("+-------------+"); + console.log("| FLASH_BEGIN |"); + console.log("+-------------+"); + await flush(); + var buf = Buffer.allocUnsafe(4*4); + buf.writeUInt32LE(size, 0); // Size of data to be sent/erased. + buf.writeUInt32LE(Math.ceil(size/FLASH_PACKET_SIZE), 4); // Number of data packets to be sent. + buf.writeUInt32LE(FLASH_PACKET_SIZE, 8); // Size of each data packet. + buf.writeUInt32LE(address, 12); // Offset in memory to start writing. + await buildAndSendRequest(COMMAND_FLASH_BEGIN, buf); + await response.getResponse(); + resolve(); + }); + return p; + } // flashBegin + + + async function flashData(data, sequenceNumber) { + console.log(">> flashData: sequenceId: %d, size: %d", sequenceNumber, data.length); + if (data.length < FLASH_PACKET_SIZE) { + var temp = Buffer.alloc(FLASH_PACKET_SIZE, 0xff); + data.copy(temp); + data = temp; + } + var buf2 = Buffer.alloc(4*4 + data.length); // Allocate storage for the command preamble. + buf2.writeUInt32LE(data.length, 0); // Size of data to be sent. + buf2.writeUInt32LE(sequenceNumber, 4); // Current sequence number. + buf2.writeUInt32LE(0, 8); + buf2.writeUInt32LE(0, 12); + data.copy(buf2, 16); + //var buf = Buffer.concat([buf2, data]); + //console.log("Here is the data we are sending:\n%s", hexy.hexy(buf)); + + var p = new Promise(async function(resolve, reject) { + console.log("+------------+"); + console.log("| FLASH_DATA |"); + console.log("+------------+"); + await flush(); // Discard any untransmitted and unreceived data. + await buildAndSendRequest(COMMAND_FLASH_DATA, buf2, calculateChecksum(data)); + await response.getResponse(); // Await a response. + resolve(); // Indicate that the command has been completed. + }); + return p; + }; // flashData + + + /** + * Send the FLASH_END command. + * @param {Integer} command + * @returns A promise that is resolved when the FLASH_END command completes. + */ + function flashEnd(command) { + console.log("+-----------+"); + console.log("| FLASH_END |"); + console.log("+-----------+"); + console.log(">> memEnd: command: %d", command); + var p = new Promise(async function (resolve, reject) { + await flush(); // Discard any pending input or output. + var buf = Buffer.allocUnsafe(4); // Allocate the payload which is one 32bit word. + buf.writeUInt32LE(command, 0); // Execute flag. + await buildAndSendRequest(COMMAND_FLASH_END, buf); + await response.getResponse(); // Wait for a response. + resolve(); // Resolve the promise. + }); + return p; + } // flashEnd + + /** + * Transmit a buffer of data to a given address within ESP32 FLASH. + * @param {Buffer} data The data to be written into ESP32 FLASH. + * @param {integer} address The address that data will be written to. + */ + async function send(data, address) { + return new Promise(async (resolve, reject) => { + // If the data is not 32bit aligned, expand to 32 bits. + if (data.length % 4 != 0) { + let buf = Buffer.allocUnsafe((data.length + 4) & 0xFFFFFFFC).fill(0xff); + data.copy(buf); + data = buf; + } + console.log(">> send flash data of size: %d, to flash address 0x%s", data.length, address.toString(16)); + await flashBegin(data.length, address); + + sequenceNumber = 0; // The sequence number of the next FLASH_DATA transmission. + totalSize = data.length; // The size of the data we are going to be sending. + sizeWritten = 0; // The size of the data we have written so far. + + while(sizeWritten < totalSize) { + var sizeRemaining = totalSize - sizeWritten; // Calculate the amount of data still expected. + var offsetIntoData = sequenceNumber * FLASH_PACKET_SIZE; // Calculate the offset into the data from which to send. + var sizeToWrite = sizeRemaining > FLASH_PACKET_SIZE?FLASH_PACKET_SIZE:sizeRemaining; // Calculate how large this unit should be. + var tempBuf = Buffer.allocUnsafe(sizeToWrite); + data.copy(tempBuf, 0, offsetIntoData, offsetIntoData+sizeToWrite); + await flashData(tempBuf, sequenceNumber); + sequenceNumber++; + sizeWritten += sizeToWrite; + } + await flashEnd(1); + resolve(); + }); + }; // send + + return { + "send": send, + "end": flashEnd + }; + +} // FlashCommands + + +/** + * Upload the flasher application. + * @returns A Promise that is fulfilled when the flasher has been uploaded. + */ +async function uploadFlasher() { + // We read a JSON file that has been prepared to contain the flasher application. This JSON contains: + // { + // textAddress: - A string representation (hex) of the memory address into which the text data should be loaded. + // dataAddress: - A string representation (hex) of the memory address into which the data data should be loaded. + // textData: - A base64 encoded representation of the text data. + // dataData: - A base64 encoded representation of the data data + // entryPoint: - A string representation of the entry point (hex) for execution. + // } + // + return new Promise(async (resolve, reject)=>{ + console.log(">> uploadFlasher"); + // Load the flasher from the local file. + var flasherDataRaw = fs.readFileSync("esptool/flasher_stub/build/flasher_stub.json", "utf8"); + var flasherData = JSON.parse(flasherDataRaw); + + flasherData.textData = Buffer.from(flasherData.textData, "base64"); + flasherData.dataData = Buffer.from(flasherData.dataData, "base64"); + console.log("Text data length: %d, Data data length: %d", flasherData.textData.length, flasherData.dataData.length); + + await MemCommands().send(flasherData.textData, parseInt(flasherData.textAddress, 16)); + await MemCommands().send(flasherData.dataData, parseInt(flasherData.dataAddress, 16)); + await MemCommands().end(0, parseInt(flasherData.entryPoint, 16)); + console.log("<< uploadFlasher complete"); + resolve(); + }); // End of promise +} // uploadFlasher + + +/** + * Write a value to an ESP32 register defined. + * @param {Integer} address The address of the register. + * @param {Integer} value The value to be written to the register. + * @returns A promise that is fulfilled when the register has been written. + */ +function writeReg(address, value) { + console.log("+-----------+"); + console.log("| WRITE_REG |"); + console.log("+-----------+"); + console.log(">> Writing register: Command: 0x%s, Address: 0x%s, value: 0x%s", + COMMAND_WRITE_REG.toString(16), address.toString(16), value.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4*4); + buf.writeUInt32LE(address, 0); + buf.writeUInt32LE(value, 4); + buf.writeUInt32LE(0xffffffff, 8); // Mask + buf.writeUInt32LE(0, 12); // Delay + await buildAndSendRequest(COMMAND_WRITE_REG, buf); + await response.getResponse(); + resolve(); + }); + return p; +} // writeReg + + +/** + * Set the SPI configuration parameters. + * @param {Integer} size The size of SPI flash. + * @returns A promise that is fulfilled when the SPI parameters have been set. + */ +function spiSetParams(size) { + console.log("+----------------+"); + console.log("| SPI_SET_PARAMS |"); + console.log("+----------------+"); + console.log(">> SPI Set Params: Command: 0x%s, size:%d", + COMMAND_SPI_SET_PARAMS.toString(16), size); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(6*4); + buf.writeUInt32LE(0, 0); // id + buf.writeUInt32LE(size, 4); // Total size + buf.writeUInt32LE(64*1024, 8); // block size + buf.writeUInt32LE(4*1024, 12); // sector size + buf.writeUInt32LE(256, 16); // page size + buf.writeUInt32LE(0xffff, 20); // status mask + await buildAndSendRequest(COMMAND_SPI_SET_PARAMS, buf); + await response.getResponse(); + resolve(); + }); + return p; +} // spiSetParams + + +/** + * Erase the flash memory. + * @returns A promise that is fulfilled when the flash memory has been erased. + */ +function eraseFlash() { + console.log("+-------------+"); + console.log("| ERASE_FLASH |"); + console.log("+-------------+"); + console.log(">> Erase Flash: Command: 0x%s", COMMAND_ERASE_FLASH.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(0); + await buildAndSendRequest(COMMAND_ERASE_FLASH, buf); + await response.getResponse(); + resolve(); + }); + return p; +} // spiSetParams + + +/** + * Read an ESP32 register. + * @param {Integer} address The address of the register to be read. + * @returns The result of reading the register. + */ +function readReg(address) { + console.log(">> Reading register: Command: 0x%s, Address: 0x%s", COMMAND_READ_REG.toString(16), address.toString(16)); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4); + buf.writeUInt32LE(address, 0); + await buildAndSendRequest(COMMAND_READ_REG, buf); + var result = await response.getResponse(); + console.log("readReg: %O", result); + resolve(result); + }); + return p; +} // readReg + + + +/** + * Get the MD5 hash of an area of flash memory. + * @param {Integer} address The address of the flash memory. + * @param {Integer} size The size of the flasg memory. + * @returns The MD5 hash value. + */ +function spiFlashMD5(address, size) { + console.log("+---------------+"); + console.log("| SPI_FLASH_MD5 |"); + console.log("+---------------+"); + console.log(">> spiFlashMD5: Command: 0x%s, Address: 0x%s, Size: %d", COMMAND_SPI_FLASH_MD5.toString(16), address.toString(16), size); + var p = new Promise(async function(resolve, reject) { + await flush(); + var buf = Buffer.allocUnsafe(4 * 4); + buf.writeUInt32LE(address, 0); + buf.writeUInt32LE(size, 4); + buf.writeUInt32LE(0, 8); + buf.writeUInt32LE(0, 12); + await buildAndSendRequest(COMMAND_SPI_FLASH_MD5, buf); + await response.getResponse(); + console.log("spiFlashMD5 result:\n%s", hexy.hexy(response.getData())); + resolve(response.getData()); + }); + return p; +} // spiFlashMD5 + + +/** + * Read the value of an eFuse. + * @param {Integer} fuseNumber + * @returns The value of the eFuse. + */ +function readEfuse(fuseNumber) { + console.log(">> readEFuse: %d", fuseNumber); + return readReg(EFUSE_REG_BASE + (4 * fuseNumber)); +} // readEfuse + + +/** + * The ESP32 needs to be in a "download" mode in order to process commands. For many boards, a sequence of + * DTR/RTS UART twiddles can be sends to achieve that goal. This function performs that task. + * + * @returns A Promise that is fulfilled when the ESP32 is in download mode. + */ +function enterDownloadMode() { + console.log(">> enterDownloadMode: Enter download mode, esp32r0Delay = " + esp32r0Delay); + return new Promise((resolve, reject) => { + setFlags({ dtr: false, rts: true }).then(()=>{ + return delay(100); + }).then(() => { + if (esp32r0Delay) { + return delay(1200); + } + return Promise.resolve(); + }).then(()=> { + return setFlags({ dtr: true, rts: false }); + }).then(() => { + if (esp32r0Delay) { + return delay(400); + } + return Promise.resolve(); + }).then(()=> { + return delay(50); + }).then(()=> { + return setFlags({ dtr: false, rts: false }); + }).then(() => { + resolve(); + }); + }); +} // enterDownloadMode + + +/** + * Flash the content of the file supplied by filename into ESP32 flash specified by the address. + * @param {Integer} address The address in flash to write the file. + * @param {String} fileName The name of the file to read from. + */ +async function flashFile(address, fileName) { + console.log("Flashing file %s to address 0x%s", fileName, address.toString(16)); + var fileData = fs.readFileSync(fileName); + console.log("Read file ... size is %d, md5: %s", fileData.length, md5(fileData)); + await FlashCommands().send(fileData, address); + console.log("Flash File complete"); + return fileData.length; +} // flashFile + + +async function sleep(interval) { + return new Promise((resolve) => { + setTimeout(() => {resolve()}, interval); + }); +} + + +console.log("Start!"); +if (argv._.length%2 != 0) { + console.log("Wrong number of args"); + return; +} +response = ProcessResponse(); +console.log("Response created: " + response); +port.open(async (err)=>{ + if (err) { + console.log("Error opening: " + err); + return; + } + console.log("Open!"); + await enterDownloadMode(); + //var p = Promise.resolve(); + console.log("We should now be in ESP32 download mode!"); + serialData.empty(); + while(1) { + try { + await doSync(); + break; + } + catch(e) { + console.log(e); + } + } + await sleep(1000); + serialData.empty(); + console.log("+----------------+") + console.log("| Sync Complete! |"); + console.log("+----------------+") + + await readEfuse(3); + console.log("Read fuse completed!"); + await uploadFlasher(); + serialData.empty(); + console.log("Flasher upload completed and running!"); + /* + await writeReg(0x6000202c, 0x00000017); + await writeReg(0x6000201c, 0x90000000); + await writeReg(0x60002024, 0x9f000070); + await writeReg(0x60002080, 0x00000000); + await writeReg(0x60002000, 0x00040000); + await writeReg(0x6000201c, 0x80000040); + await writeReg(0x60002024, 0x00000000); + */ + await spiSetParams(0x400000); + + // Walk through each of the files presented and upload them. The input on the command line should be of the form: + // address file [address file]* + + for (var i=0; i { + serialData.append(data); // Append the new data into the serial buffer. +}); + +console.log("Init done");