Merge branch 'master' into patch-1

This commit is contained in:
Angel Nunez Mencias 2025-07-24 00:42:53 +02:00 committed by GitHub
commit d55646344d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
249 changed files with 11394 additions and 3999 deletions

View file

@ -43,6 +43,7 @@ body:
- latest stable Release (if not listed below) - latest stable Release (if not listed below)
- latest development Release Candidate (RC-X) - latest development Release Candidate (RC-X)
- latest master (checkout manually) - latest master (checkout manually)
- v3.3.0
- v3.2.1 - v3.2.1
- v3.2.0 - v3.2.0
- v3.1.3 - v3.1.3

View file

@ -90,6 +90,7 @@ if [ "$BUILD_LOG" -eq 1 ]; then
fi fi
#build sketches for different targets #build sketches for different targets
build "esp32c5" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}"
build "esp32p4" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" build "esp32p4" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}"
build "esp32s3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" build "esp32s3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}"
build "esp32s2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" build "esp32s2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}"

View file

@ -156,6 +156,7 @@ function build_sketch { # build_sketch <ide_path> <user_path> <path-to-ino> [ext
esp32c6_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') esp32c6_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g')
esp32h2_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') esp32h2_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g')
esp32p4_opts=$(echo "PSRAM=enabled,USBMode=default,$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') esp32p4_opts=$(echo "PSRAM=enabled,USBMode=default,$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g')
esp32c5_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g')
# Select the common part of the FQBN based on the target. The rest will be # Select the common part of the FQBN based on the target. The rest will be
# appended depending on the passed options. # appended depending on the passed options.
@ -191,6 +192,10 @@ function build_sketch { # build_sketch <ide_path> <user_path> <path-to-ino> [ext
[ -n "${options:-$esp32p4_opts}" ] && opt=":${options:-$esp32p4_opts}" [ -n "${options:-$esp32p4_opts}" ] && opt=":${options:-$esp32p4_opts}"
fqbn="espressif:esp32:esp32p4$opt" fqbn="espressif:esp32:esp32p4$opt"
;; ;;
"esp32c5")
[ -n "${options:-$esp32c5_opts}" ] && opt=":${options:-$esp32c5_opts}"
fqbn="espressif:esp32:esp32c5$opt"
;;
*) *)
echo "ERROR: Invalid chip: $target" echo "ERROR: Invalid chip: $target"
exit 1 exit 1

View file

@ -47,6 +47,7 @@ on:
- "!.github/scripts/upload_*" - "!.github/scripts/upload_*"
- "variants/esp32/**/*" - "variants/esp32/**/*"
- "variants/esp32c3/**/*" - "variants/esp32c3/**/*"
- "variants/esp32c5/**/*"
- "variants/esp32c6/**/*" - "variants/esp32c6/**/*"
- "variants/esp32h2/**/*" - "variants/esp32h2/**/*"
- "variants/esp32p4/**/*" - "variants/esp32p4/**/*"
@ -247,7 +248,7 @@ jobs:
# See https://hub.docker.com/r/espressif/idf/tags and # See https://hub.docker.com/r/espressif/idf/tags and
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
# for details. # for details.
idf_ver: ["release-v5.4"] idf_ver: ["release-v5.3","release-v5.4","release-v5.5"]
idf_target: idf_target:
[ [
"esp32", "esp32",
@ -282,7 +283,7 @@ jobs:
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always() if: always()
with: with:
name: sdkconfig-${{ matrix.idf_target }} name: sdkconfig-${{ matrix.idf_ver }}-${{ matrix.idf_target }}
path: ./components/arduino-esp32/idf_component_examples/**/sdkconfig path: ./components/arduino-esp32/idf_component_examples/**/sdkconfig
# Save artifacts to gh-pages # Save artifacts to gh-pages

View file

@ -9,8 +9,8 @@ stages:
- result - result
variables: variables:
ESP_IDF_VERSION: "5.4" ESP_IDF_VERSION: "5.5"
ESP_ARDUINO_VERSION: "3.2.1" ESP_ARDUINO_VERSION: "3.3.0"
############# #############
# `default` # # `default` #

View file

@ -6,7 +6,7 @@
# idf.py build # idf.py build
set(min_supported_idf_version "5.3.0") set(min_supported_idf_version "5.3.0")
set(max_supported_idf_version "5.4.99") set(max_supported_idf_version "5.5.99")
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}")
if ("${idf_version}" AND NOT "$ENV{ARDUINO_SKIP_IDF_VERSION_CHECK}") if ("${idf_version}" AND NOT "$ENV{ARDUINO_SKIP_IDF_VERSION_CHECK}")
@ -184,7 +184,8 @@ set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
libraries/Matter/src/Matter.cpp) libraries/Matter/src/Matter.cpp
libraries/Matter/src/MatterEndPoint.cpp)
set(ARDUINO_LIBRARY_PPP_SRCS set(ARDUINO_LIBRARY_PPP_SRCS
libraries/PPP/src/PPP.cpp libraries/PPP/src/PPP.cpp
@ -361,13 +362,13 @@ endforeach()
set(includedirs variants/${CONFIG_ARDUINO_VARIANT}/ cores/esp32/ ${ARDUINO_LIBRARIES_INCLUDEDIRS}) set(includedirs variants/${CONFIG_ARDUINO_VARIANT}/ cores/esp32/ ${ARDUINO_LIBRARIES_INCLUDEDIRS})
set(srcs ${CORE_SRCS} ${ARDUINO_LIBRARIES_SRCS}) set(srcs ${CORE_SRCS} ${ARDUINO_LIBRARIES_SRCS})
set(priv_includes cores/esp32/libb64) set(priv_includes cores/esp32/libb64)
set(requires spi_flash esp_partition mbedtls wpa_supplicant esp_adc esp_eth http_parser esp_ringbuf esp_driver_gptimer esp_driver_usb_serial_jtag driver) set(requires spi_flash esp_partition mbedtls wpa_supplicant esp_adc esp_eth http_parser esp_ringbuf esp_driver_gptimer esp_driver_usb_serial_jtag driver esp_http_client esp_https_ota)
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support bt esp_hid usb esp_psram ${ARDUINO_LIBRARIES_REQUIRES}) set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support bt esp_hid usb esp_psram ${ARDUINO_LIBRARIES_REQUIRES})
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThread) if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThread)
#if(CONFIG_SOC_IEEE802154_SUPPORTED) # Does not work! #if(CONFIG_SOC_IEEE802154_SUPPORTED) # Does not work!
#if(CONFIG_OPENTHREAD_ENABLED) # Does not work! #if(CONFIG_OPENTHREAD_ENABLED) # Does not work!
if(IDF_TARGET STREQUAL "esp32c6" OR IDF_TARGET STREQUAL "esp32h2") # Sadly only this works if(IDF_TARGET STREQUAL "esp32c6" OR IDF_TARGET STREQUAL "esp32h2" OR IDF_TARGET STREQUAL "esp32c5") # Sadly only this works
list(APPEND requires openthread) list(APPEND requires openthread)
endif() endif()
endif() endif()

View file

@ -161,6 +161,217 @@ esp32c2.menu.EraseFlash.all.upload.erase_cmd=-e
############################################################## ##############################################################
esp32c5.name=ESP32C5 Dev Module
esp32c5.bootloader.tool=esptool_py
esp32c5.bootloader.tool.default=esptool_py
esp32c5.upload.tool=esptool_py
esp32c5.upload.tool.default=esptool_py
esp32c5.upload.tool.network=esp_ota
esp32c5.upload.maximum_size=1310720
esp32c5.upload.maximum_data_size=327680
esp32c5.upload.flags=
esp32c5.upload.extra_flags=
esp32c5.upload.use_1200bps_touch=false
esp32c5.upload.wait_for_upload_port=false
esp32c5.serial.disableDTR=false
esp32c5.serial.disableRTS=false
esp32c5.build.tarch=riscv32
esp32c5.build.target=esp
esp32c5.build.mcu=esp32c5
esp32c5.build.core=esp32
esp32c5.build.variant=esp32c5
esp32c5.build.board=ESP32C5_DEV
esp32c5.build.bootloader_addr=0x2000
esp32c5.build.cdc_on_boot=0
esp32c5.build.f_cpu=240000000L
esp32c5.build.flash_size=4MB
esp32c5.build.flash_freq=80m
esp32c5.build.flash_mode=qio
esp32c5.build.boot=qio
esp32c5.build.partitions=default
esp32c5.build.defines=
## IDE 2.0 Seems to not update the value
esp32c5.menu.JTAGAdapter.default=Disabled
esp32c5.menu.JTAGAdapter.default.build.copy_jtag_files=0
esp32c5.menu.JTAGAdapter.builtin=Integrated USB JTAG
esp32c5.menu.JTAGAdapter.builtin.build.openocdscript=esp32c5-builtin.cfg
esp32c5.menu.JTAGAdapter.builtin.build.copy_jtag_files=1
esp32c5.menu.JTAGAdapter.external=FTDI Adapter
esp32c5.menu.JTAGAdapter.external.build.openocdscript=esp32c5-ftdi.cfg
esp32c5.menu.JTAGAdapter.external.build.copy_jtag_files=1
esp32c5.menu.JTAGAdapter.bridge=ESP USB Bridge
esp32c5.menu.JTAGAdapter.bridge.build.openocdscript=esp32c5-bridge.cfg
esp32c5.menu.JTAGAdapter.bridge.build.copy_jtag_files=1
esp32c5.menu.PSRAM.disabled=Disabled
esp32c5.menu.PSRAM.disabled.build.defines=
esp32c5.menu.PSRAM.enabled=Enabled
esp32c5.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
esp32c5.menu.CDCOnBoot.default=Disabled
esp32c5.menu.CDCOnBoot.default.build.cdc_on_boot=0
esp32c5.menu.CDCOnBoot.cdc=Enabled
esp32c5.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
esp32c5.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
esp32c5.menu.PartitionScheme.default.build.partitions=default
esp32c5.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS)
esp32c5.menu.PartitionScheme.defaultffat.build.partitions=default_ffat
esp32c5.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS)
esp32c5.menu.PartitionScheme.default_8MB.build.partitions=default_8MB
esp32c5.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336
esp32c5.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS)
esp32c5.menu.PartitionScheme.minimal.build.partitions=minimal
esp32c5.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2)
esp32c5.menu.PartitionScheme.no_fs.build.partitions=no_fs
esp32c5.menu.PartitionScheme.no_fs.upload.maximum_size=2031616
esp32c5.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS)
esp32c5.menu.PartitionScheme.no_ota.build.partitions=no_ota
esp32c5.menu.PartitionScheme.no_ota.upload.maximum_size=2097152
esp32c5.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS)
esp32c5.menu.PartitionScheme.noota_3g.build.partitions=noota_3g
esp32c5.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576
esp32c5.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS)
esp32c5.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat
esp32c5.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152
esp32c5.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS)
esp32c5.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat
esp32c5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576
esp32c5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS)
esp32c5.menu.PartitionScheme.huge_app.build.partitions=huge_app
esp32c5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728
esp32c5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
esp32c5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs
esp32c5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080
esp32c5.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS)
esp32c5.menu.PartitionScheme.fatflash.build.partitions=ffat
esp32c5.menu.PartitionScheme.fatflash.upload.maximum_size=2097152
esp32c5.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS)
esp32c5.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB
esp32c5.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728
esp32c5.menu.PartitionScheme.rainmaker=RainMaker 4MB
esp32c5.menu.PartitionScheme.rainmaker.build.partitions=rainmaker
esp32c5.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080
esp32c5.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA
esp32c5.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota
esp32c5.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656
esp32c5.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB
esp32c5.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB
esp32c5.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480
esp32c5.menu.PartitionScheme.zigbee_2MB=Zigbee 2MB with spiffs
esp32c5.menu.PartitionScheme.zigbee_2MB.build.partitions=zigbee_2MB
esp32c5.menu.PartitionScheme.zigbee_2MB.upload.maximum_size=1310720
esp32c5.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs
esp32c5.menu.PartitionScheme.zigbee.build.partitions=zigbee
esp32c5.menu.PartitionScheme.zigbee.upload.maximum_size=1310720
esp32c5.menu.PartitionScheme.zigbee_8MB=Zigbee 8MB with spiffs
esp32c5.menu.PartitionScheme.zigbee_8MB.build.partitions=zigbee_8MB
esp32c5.menu.PartitionScheme.zigbee_8MB.upload.maximum_size=3407872
esp32c5.menu.PartitionScheme.zigbee_zczr_2MB=Zigbee ZCZR 2MB with spiffs
esp32c5.menu.PartitionScheme.zigbee_zczr_2MB.build.partitions=zigbee_zczr_2MB
esp32c5.menu.PartitionScheme.zigbee_zczr_2MB.upload.maximum_size=1310720
esp32c5.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs
esp32c5.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr
esp32c5.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720
esp32c5.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs
esp32c5.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB
esp32c5.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872
esp32c5.menu.PartitionScheme.custom=Custom
esp32c5.menu.PartitionScheme.custom.build.partitions=
esp32c5.menu.PartitionScheme.custom.upload.maximum_size=16777216
esp32c5.menu.CPUFreq.240=240MHz (WiFi)
esp32c5.menu.CPUFreq.240.build.f_cpu=240000000L
esp32c5.menu.CPUFreq.120=120MHz (WiFi)
esp32c5.menu.CPUFreq.120.build.f_cpu=120000000L
esp32c5.menu.CPUFreq.80=80MHz (WiFi)
esp32c5.menu.CPUFreq.80.build.f_cpu=80000000L
esp32c5.menu.CPUFreq.40=40MHz
esp32c5.menu.CPUFreq.40.build.f_cpu=40000000L
esp32c5.menu.CPUFreq.20=20MHz
esp32c5.menu.CPUFreq.20.build.f_cpu=20000000L
esp32c5.menu.CPUFreq.10=10MHz
esp32c5.menu.CPUFreq.10.build.f_cpu=10000000L
esp32c5.menu.FlashMode.qio=QIO
esp32c5.menu.FlashMode.qio.build.flash_mode=dio
esp32c5.menu.FlashMode.qio.build.boot=qio
esp32c5.menu.FlashMode.dio=DIO
esp32c5.menu.FlashMode.dio.build.flash_mode=dio
esp32c5.menu.FlashMode.dio.build.boot=dio
esp32c5.menu.FlashFreq.80=80MHz
esp32c5.menu.FlashFreq.80.build.flash_freq=80m
esp32c5.menu.FlashFreq.40=40MHz
esp32c5.menu.FlashFreq.40.build.flash_freq=40m
esp32c5.menu.FlashSize.4M=4MB (32Mb)
esp32c5.menu.FlashSize.4M.build.flash_size=4MB
esp32c5.menu.FlashSize.8M=8MB (64Mb)
esp32c5.menu.FlashSize.8M.build.flash_size=8MB
esp32c5.menu.FlashSize.2M=2MB (16Mb)
esp32c5.menu.FlashSize.2M.build.flash_size=2MB
esp32c5.menu.FlashSize.16M=16MB (128Mb)
esp32c5.menu.FlashSize.16M.build.flash_size=16MB
esp32c5.menu.UploadSpeed.921600=921600
esp32c5.menu.UploadSpeed.921600.upload.speed=921600
esp32c5.menu.UploadSpeed.115200=115200
esp32c5.menu.UploadSpeed.115200.upload.speed=115200
esp32c5.menu.UploadSpeed.256000.windows=256000
esp32c5.menu.UploadSpeed.256000.upload.speed=256000
esp32c5.menu.UploadSpeed.230400.windows.upload.speed=256000
esp32c5.menu.UploadSpeed.230400=230400
esp32c5.menu.UploadSpeed.230400.upload.speed=230400
esp32c5.menu.UploadSpeed.460800.linux=460800
esp32c5.menu.UploadSpeed.460800.macosx=460800
esp32c5.menu.UploadSpeed.460800.upload.speed=460800
esp32c5.menu.UploadSpeed.512000.windows=512000
esp32c5.menu.UploadSpeed.512000.upload.speed=512000
esp32c5.menu.DebugLevel.none=None
esp32c5.menu.DebugLevel.none.build.code_debug=0
esp32c5.menu.DebugLevel.error=Error
esp32c5.menu.DebugLevel.error.build.code_debug=1
esp32c5.menu.DebugLevel.warn=Warn
esp32c5.menu.DebugLevel.warn.build.code_debug=2
esp32c5.menu.DebugLevel.info=Info
esp32c5.menu.DebugLevel.info.build.code_debug=3
esp32c5.menu.DebugLevel.debug=Debug
esp32c5.menu.DebugLevel.debug.build.code_debug=4
esp32c5.menu.DebugLevel.verbose=Verbose
esp32c5.menu.DebugLevel.verbose.build.code_debug=5
esp32c5.menu.EraseFlash.none=Disabled
esp32c5.menu.EraseFlash.none.upload.erase_cmd=
esp32c5.menu.EraseFlash.all=Enabled
esp32c5.menu.EraseFlash.all.upload.erase_cmd=-e
esp32c5.menu.ZigbeeMode.default=Disabled
esp32c5.menu.ZigbeeMode.default.build.zigbee_mode=
esp32c5.menu.ZigbeeMode.default.build.zigbee_libs=
esp32c5.menu.ZigbeeMode.ed=Zigbee ED (end device)
esp32c5.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED
esp32c5.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native
esp32c5.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router)
esp32c5.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR
esp32c5.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native
esp32c5.menu.ZigbeeMode.ed_debug=Zigbee ED (end device) - Debug
esp32c5.menu.ZigbeeMode.ed_debug.build.zigbee_mode=-DZIGBEE_MODE_ED
esp32c5.menu.ZigbeeMode.ed_debug.build.zigbee_libs=-lesp_zb_api.ed.debug -lzboss_stack.ed.debug -lzboss_port.native.debug
esp32c5.menu.ZigbeeMode.zczr_debug=Zigbee ZCZR (coordinator/router) - Debug
esp32c5.menu.ZigbeeMode.zczr_debug.build.zigbee_mode=-DZIGBEE_MODE_ZCZR
esp32c5.menu.ZigbeeMode.zczr_debug.build.zigbee_libs=-lesp_zb_api.zczr.debug -lzboss_stack.zczr.debug -lzboss_port.native.debug
##############################################################
esp32p4.name=ESP32P4 Dev Module esp32p4.name=ESP32P4 Dev Module
esp32p4.bootloader.tool=esptool_py esp32p4.bootloader.tool=esptool_py

View file

@ -21,6 +21,7 @@
#include "Esp.h" #include "Esp.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "spi_flash_mmap.h" #include "spi_flash_mmap.h"
#include "esp_idf_version.h"
#include <memory> #include <memory>
#include <soc/soc.h> #include <soc/soc.h>
#include <esp_partition.h> #include <esp_partition.h>
@ -63,6 +64,9 @@ extern "C" {
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/spi_flash.h" #include "esp32p4/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32p4 is located at 0x2000 #define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32p4 is located at 0x2000
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32c5 is located at 0x2000
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
#endif #endif
@ -301,7 +305,12 @@ const char *EspClass::getChipModel(void) {
case CHIP_ESP32C6: return "ESP32-C6"; case CHIP_ESP32C6: return "ESP32-C6";
case CHIP_ESP32H2: return "ESP32-H2"; case CHIP_ESP32H2: return "ESP32-H2";
case CHIP_ESP32P4: return "ESP32-P4"; case CHIP_ESP32P4: return "ESP32-P4";
default: return "UNKNOWN"; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
case CHIP_ESP32C5: return "ESP32-C5";
case CHIP_ESP32C61: return "ESP32-C61";
case CHIP_ESP32H21: return "ESP32-H21";
#endif
default: return "UNKNOWN";
} }
#endif #endif
} }

View file

@ -162,6 +162,8 @@ typedef enum {
#define SOC_RX0 (gpio_num_t)23 #define SOC_RX0 (gpio_num_t)23
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#define SOC_RX0 (gpio_num_t)38 #define SOC_RX0 (gpio_num_t)38
#elif CONFIG_IDF_TARGET_ESP32C5
#define SOC_RX0 (gpio_num_t)12
#endif #endif
#endif #endif
@ -180,6 +182,8 @@ typedef enum {
#define SOC_TX0 (gpio_num_t)24 #define SOC_TX0 (gpio_num_t)24
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#define SOC_TX0 (gpio_num_t)37 #define SOC_TX0 (gpio_num_t)37
#elif CONFIG_IDF_TARGET_ESP32C5
#define SOC_TX0 (gpio_num_t)11
#endif #endif
#endif #endif
@ -203,6 +207,8 @@ typedef enum {
#define RX1 (gpio_num_t)0 #define RX1 (gpio_num_t)0
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#define RX1 (gpio_num_t)11 #define RX1 (gpio_num_t)11
#elif CONFIG_IDF_TARGET_ESP32C5
#define RX1 (gpio_num_t)4
#endif #endif
#endif #endif
@ -223,6 +229,8 @@ typedef enum {
#define TX1 (gpio_num_t)1 #define TX1 (gpio_num_t)1
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#define TX1 (gpio_num_t)10 #define TX1 (gpio_num_t)10
#elif CONFIG_IDF_TARGET_ESP32C5
#define TX1 (gpio_num_t)5
#endif #endif
#endif #endif
#endif /* SOC_UART_HP_NUM > 1 */ #endif /* SOC_UART_HP_NUM > 1 */

View file

@ -67,6 +67,10 @@ static void printPkgVersion(void) {
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_2_REG, EFUSE_PKG_VERSION); uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_2_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver); chip_report_printf("%lu", pkg_ver);
#elif CONFIG_IDF_TARGET_ESP32C5
// ToDo: Update this line when EFUSE_PKG_VERSION is available again for ESP32-C5
uint32_t pkg_ver = 0; //REG_GET_FIELD(EFUSE_RD_MAC_SYS2_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#else #else
chip_report_printf("Unknown"); chip_report_printf("Unknown");
#endif #endif
@ -88,7 +92,12 @@ static void printChipInfo(void) {
case CHIP_ESP32C6: chip_report_printf("ESP32-C6\n"); break; case CHIP_ESP32C6: chip_report_printf("ESP32-C6\n"); break;
case CHIP_ESP32H2: chip_report_printf("ESP32-H2\n"); break; case CHIP_ESP32H2: chip_report_printf("ESP32-H2\n"); break;
case CHIP_ESP32P4: chip_report_printf("ESP32-P4\n"); break; case CHIP_ESP32P4: chip_report_printf("ESP32-P4\n"); break;
default: chip_report_printf("Unknown %d\n", info.model); break; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
case CHIP_ESP32C5: chip_report_printf("ESP32-C5\n"); break;
case CHIP_ESP32C61: chip_report_printf("ESP32-C61\n"); break;
case CHIP_ESP32H21: chip_report_printf("ESP32-H21\n"); break;
#endif
default: chip_report_printf("Unknown %d\n", info.model); break;
} }
printPkgVersion(); printPkgVersion();
chip_report_printf(" Revision : %.2f\n", (float)(info.revision) / 100.0); chip_report_printf(" Revision : %.2f\n", (float)(info.revision) / 100.0);

View file

@ -15,7 +15,7 @@
#include "esp32-hal-bt.h" #include "esp32-hal-bt.h"
#if SOC_BT_SUPPORTED #if SOC_BT_SUPPORTED
#if defined(CONFIG_BT_BLUEDROID_ENABLED) && __has_include("esp_bt.h") #if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && __has_include("esp_bt.h")
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
bool btInUse() { bool btInUse() {
@ -116,7 +116,7 @@ bool btStop() {
return false; return false;
} }
#else // CONFIG_BT_ENABLED #else // !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED))
bool btStarted() { bool btStarted() {
return false; return false;
} }
@ -129,6 +129,6 @@ bool btStop() {
return false; return false;
} }
#endif /* CONFIG_BT_ENABLED */ #endif /* !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) */
#endif /* SOC_BT_SUPPORTED */ #endif /* SOC_BT_SUPPORTED */

View file

@ -19,7 +19,8 @@
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_log.h" #include "esp_log.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) \
&& !defined(CONFIG_IDF_TARGET_ESP32C5)
#include "soc/rtc_cntl_reg.h" #include "soc/rtc_cntl_reg.h"
#include "soc/syscon_reg.h" #include "soc/syscon_reg.h"
#endif #endif
@ -50,6 +51,8 @@
#include "esp32h2/rom/rtc.h" #include "esp32h2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/rtc.h" #include "esp32p4/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/rtc.h"
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
#endif #endif
@ -183,7 +186,7 @@ bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) {
rtc_cpu_freq_config_t conf, cconf; rtc_cpu_freq_config_t conf, cconf;
uint32_t capb, apb; uint32_t capb, apb;
//Get XTAL Frequency and calculate min CPU MHz //Get XTAL Frequency and calculate min CPU MHz
#if (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) #if (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32C5))
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get(); rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get();
#endif #endif
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
@ -199,7 +202,7 @@ bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) {
} }
} }
#endif #endif
#if (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) #if (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32C5))
if (cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 120 && cpu_freq_mhz != 80) { if (cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 120 && cpu_freq_mhz != 80) {
if (xtal >= RTC_XTAL_FREQ_40M) { if (xtal >= RTC_XTAL_FREQ_40M) {
log_e("Bad frequency: %u MHz! Options are: 240, 160, 120, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2, xtal / 4); log_e("Bad frequency: %u MHz! Options are: 240, 160, 120, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2, xtal / 4);
@ -275,6 +278,12 @@ bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) {
(conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_APLL) ? "APLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "8M")), (conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_APLL) ? "APLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "8M")),
conf.source_freq_mhz, conf.div, conf.freq_mhz, apb conf.source_freq_mhz, conf.div, conf.freq_mhz, apb
); );
#elif defined(CONFIG_IDF_TARGET_ESP32C5)
log_d(
"%s: %u / %u = %u Mhz, APB: %u Hz",
(conf.source == SOC_CPU_CLK_SRC_PLL_F240M || conf.source == SOC_CPU_CLK_SRC_PLL_F160M) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "8M"),
conf.source_freq_mhz, conf.div, conf.freq_mhz, apb
);
#else #else
log_d( log_d(
"%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "17.5M"), "%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "17.5M"),

View file

@ -43,7 +43,10 @@
#include "soc/i2c_struct.h" #include "soc/i2c_struct.h"
#include "soc/periph_defs.h" #include "soc/periph_defs.h"
#include "hal/i2c_ll.h" #include "hal/i2c_ll.h"
#include "hal/i2c_types.h"
#ifndef CONFIG_IDF_TARGET_ESP32C5
#include "hal/clk_gate_ll.h" #include "hal/clk_gate_ll.h"
#endif
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#include "esp32-hal-i2c-slave.h" #include "esp32-hal-i2c-slave.h"
#include "esp32-hal-periman.h" #include "esp32-hal-periman.h"
@ -325,7 +328,7 @@ esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t
frequency = 100000L; frequency = 100000L;
} }
frequency = (frequency * 5) / 4; frequency = (frequency * 5) / 4;
#if !defined(CONFIG_IDF_TARGET_ESP32P4) #if !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32C5)
if (i2c->num == 0) { if (i2c->num == 0) {
periph_ll_enable_clk_clear_rst(PERIPH_I2C0_MODULE); periph_ll_enable_clk_clear_rst(PERIPH_I2C0_MODULE);
#if SOC_HP_I2C_NUM > 1 #if SOC_HP_I2C_NUM > 1
@ -335,10 +338,15 @@ esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t
} }
#endif // !defined(CONFIG_IDF_TARGET_ESP32P4) #endif // !defined(CONFIG_IDF_TARGET_ESP32P4)
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)) \
|| (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 3) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0))
i2c_ll_set_mode(i2c->dev, I2C_BUS_MODE_SLAVE); i2c_ll_set_mode(i2c->dev, I2C_BUS_MODE_SLAVE);
i2c_ll_enable_pins_open_drain(i2c->dev, true); i2c_ll_enable_pins_open_drain(i2c->dev, true);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_ll_enable_fifo_mode(i2c->dev, true); i2c_ll_enable_fifo_mode(i2c->dev, true);
#else
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
#endif
#else #else
i2c_ll_slave_init(i2c->dev); i2c_ll_slave_init(i2c->dev);
i2c_ll_slave_set_fifo_mode(i2c->dev, true); i2c_ll_slave_set_fifo_mode(i2c->dev, true);
@ -363,7 +371,7 @@ esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK); i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK); i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_ll_enable_fifo_mode(i2c->dev, true); i2c_ll_enable_fifo_mode(i2c->dev, true);
#else #else
i2c_ll_slave_set_fifo_mode(i2c->dev, true); i2c_ll_slave_set_fifo_mode(i2c->dev, true);
@ -558,6 +566,9 @@ static bool i2c_slave_set_frequency(i2c_slave_struct_t *i2c, uint32_t clk_speed)
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_APB); /*!< I2C source clock from APB, 80M*/ i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_APB); /*!< I2C source clock from APB, 80M*/
} }
#elif SOC_I2C_SUPPORT_XTAL #elif SOC_I2C_SUPPORT_XTAL
#ifndef XTAL_CLK_FREQ
#define XTAL_CLK_FREQ APB_CLK_FREQ
#endif
i2c_ll_master_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal); i2c_ll_master_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal);
I2C_CLOCK_SRC_ATOMIC() { I2C_CLOCK_SRC_ATOMIC() {
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_XTAL); /*!< I2C source clock from XTAL, 40M */ i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_XTAL); /*!< I2C source clock from XTAL, 40M */

View file

@ -88,6 +88,9 @@ static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) {
} }
} }
#ifndef SOC_LEDC_TIMER_NUM
#define SOC_LEDC_TIMER_NUM 4
#endif
// Find first unused timer // Find first unused timer
for (uint8_t i = 0; i < SOC_LEDC_TIMER_NUM; i++) { for (uint8_t i = 0; i < SOC_LEDC_TIMER_NUM; i++) {
if (!(used_timers & (1 << i))) { if (!(used_timers & (1 << i))) {

View file

@ -34,6 +34,8 @@
#include "esp32h2/rom/gpio.h" #include "esp32h2/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/gpio.h" #include "esp32p4/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/gpio.h"
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
#endif #endif

View file

@ -25,12 +25,13 @@
#include "esp_ota_ops.h" #include "esp_ota_ops.h"
#endif //CONFIG_APP_ROLLBACK_ENABLE #endif //CONFIG_APP_ROLLBACK_ENABLE
#include "esp_private/startup_internal.h" #include "esp_private/startup_internal.h"
#if defined(CONFIG_BT_BLUEDROID_ENABLED) && SOC_BT_SUPPORTED && __has_include("esp_bt.h") #if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
#include "esp_bt.h" #include "esp_bt.h"
#endif //CONFIG_BT_BLUEDROID_ENABLED #endif
#include <sys/time.h> #include <sys/time.h>
#include "soc/rtc.h" #include "soc/rtc.h"
#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) \
&& !defined(CONFIG_IDF_TARGET_ESP32C5)
#include "soc/rtc_cntl_reg.h" #include "soc/rtc_cntl_reg.h"
#include "soc/syscon_reg.h" #include "soc/syscon_reg.h"
#endif #endif
@ -56,6 +57,8 @@
#include "esp32h2/rom/rtc.h" #include "esp32h2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/rtc.h" #include "esp32p4/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/rtc.h"
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
@ -243,7 +246,7 @@ bool verifyRollbackLater() {
} }
#endif #endif
#ifdef CONFIG_BT_BLUEDROID_ENABLED #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
//overwritten in esp32-hal-bt.c //overwritten in esp32-hal-bt.c
bool btInUse() __attribute__((weak)); bool btInUse() __attribute__((weak));
@ -305,7 +308,7 @@ void initArduino() {
if (err) { if (err) {
log_e("Failed to initialize NVS! Error: %u", err); log_e("Failed to initialize NVS! Error: %u", err);
} }
#if defined(CONFIG_BT_BLUEDROID_ENABLED) && SOC_BT_SUPPORTED #if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED
if (!btInUse()) { if (!btInUse()) {
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
} }

View file

@ -29,6 +29,8 @@
#include "esp32s3/rom/cache.h" #include "esp32s3/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/cache.h" #include "esp32p4/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/cache.h"
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
#endif #endif

View file

@ -26,7 +26,9 @@
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#ifndef CONFIG_IDF_TARGET_ESP32C5
#include "hal/clk_gate_ll.h" #include "hal/clk_gate_ll.h"
#endif
#include "esp32-hal-periman.h" #include "esp32-hal-periman.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
@ -61,6 +63,9 @@
#include "esp32p4/rom/ets_sys.h" #include "esp32p4/rom/ets_sys.h"
#include "esp32p4/rom/gpio.h" #include "esp32p4/rom/gpio.h"
#include "hal/spi_ll.h" #include "hal/spi_ll.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/ets_sys.h"
#include "esp32c5/rom/gpio.h"
#else #else
#error Target CONFIG_IDF_TARGET is not supported #error Target CONFIG_IDF_TARGET is not supported
#endif #endif
@ -120,7 +125,7 @@ struct spi_struct_t {
#define SPI_SS_IDX(p, n) ((p == 0) ? SPI_FSPI_SS_IDX(n) : ((p == 1) ? SPI_HSPI_SS_IDX(n) : 0)) #define SPI_SS_IDX(p, n) ((p == 0) ? SPI_FSPI_SS_IDX(n) : ((p == 1) ? SPI_HSPI_SS_IDX(n) : 0))
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 #elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5
// ESP32C3 // ESP32C3
#define SPI_COUNT (1) #define SPI_COUNT (1)
@ -158,7 +163,7 @@ static spi_t _spi_bus_array[] = {
{(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false}
#elif CONFIG_IDF_TARGET_ESP32C3 #elif CONFIG_IDF_TARGET_ESP32C3
{(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false}
#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 #elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5
{(spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} {(spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false}
#else #else
{(volatile spi_dev_t *)(DR_REG_SPI0_BASE), 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), 0, -1, -1, -1, -1, false},
@ -181,7 +186,7 @@ static spi_t _spi_bus_array[] = {
{(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}
#elif CONFIG_IDF_TARGET_ESP32C3 #elif CONFIG_IDF_TARGET_ESP32C3
{(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}
#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 #elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5
{(spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} {(spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}
#else #else
{(volatile spi_dev_t *)(DR_REG_SPI0_BASE), NULL, 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), NULL, 0, -1, -1, -1, -1, false},
@ -707,7 +712,7 @@ spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t
spi->dev->user.doutdin = 1; spi->dev->user.doutdin = 1;
int i; int i;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = 0x00000000; spi->dev->data_buf[i].val = 0x00000000;
#else #else
spi->dev->data_buf[i] = 0x00000000; spi->dev->data_buf[i] = 0x00000000;
@ -755,7 +760,7 @@ void spiWrite(spi_t *spi, const uint32_t *data, uint8_t len) {
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i]; spi->dev->data_buf[i].val = data[i];
#else #else
spi->dev->data_buf[i] = data[i]; spi->dev->data_buf[i] = data[i];
@ -782,7 +787,7 @@ void spiTransfer(spi_t *spi, uint32_t *data, uint8_t len) {
spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1;
spi->dev->miso_dlen.usr_miso_dbitlen = (len * 32) - 1; spi->dev->miso_dlen.usr_miso_dbitlen = (len * 32) - 1;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i]; spi->dev->data_buf[i].val = data[i];
#else #else
spi->dev->data_buf[i] = data[i]; spi->dev->data_buf[i] = data[i];
@ -795,7 +800,7 @@ void spiTransfer(spi_t *spi, uint32_t *data, uint8_t len) {
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data[i] = spi->dev->data_buf[i].val; data[i] = spi->dev->data_buf[i].val;
#else #else
data[i] = spi->dev->data_buf[i]; data[i] = spi->dev->data_buf[i];
@ -813,7 +818,7 @@ void spiWriteByte(spi_t *spi, uint8_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -835,7 +840,7 @@ uint8_t spiTransferByte(spi_t *spi, uint8_t data) {
SPI_MUTEX_LOCK(); SPI_MUTEX_LOCK();
spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; spi->dev->mosi_dlen.usr_mosi_dbitlen = 7;
spi->dev->miso_dlen.usr_miso_dbitlen = 7; spi->dev->miso_dlen.usr_miso_dbitlen = 7;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -846,7 +851,7 @@ uint8_t spiTransferByte(spi_t *spi, uint8_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val & 0xFF; data = spi->dev->data_buf[0].val & 0xFF;
#else #else
data = spi->dev->data_buf[0] & 0xFF; data = spi->dev->data_buf[0] & 0xFF;
@ -876,7 +881,7 @@ void spiWriteWord(spi_t *spi, uint16_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -900,7 +905,7 @@ uint16_t spiTransferWord(spi_t *spi, uint16_t data) {
SPI_MUTEX_LOCK(); SPI_MUTEX_LOCK();
spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->mosi_dlen.usr_mosi_dbitlen = 15;
spi->dev->miso_dlen.usr_miso_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 15;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -911,7 +916,7 @@ uint16_t spiTransferWord(spi_t *spi, uint16_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val; data = spi->dev->data_buf[0].val;
#else #else
data = spi->dev->data_buf[0]; data = spi->dev->data_buf[0];
@ -935,7 +940,7 @@ void spiWriteLong(spi_t *spi, uint32_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -959,7 +964,7 @@ uint32_t spiTransferLong(spi_t *spi, uint32_t data) {
SPI_MUTEX_LOCK(); SPI_MUTEX_LOCK();
spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->mosi_dlen.usr_mosi_dbitlen = 31;
spi->dev->miso_dlen.usr_miso_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 31;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -970,7 +975,7 @@ uint32_t spiTransferLong(spi_t *spi, uint32_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val; data = spi->dev->data_buf[0].val;
#else #else
data = spi->dev->data_buf[0]; data = spi->dev->data_buf[0];
@ -1009,7 +1014,7 @@ static void __spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, ui
spi->dev->miso_dlen.usr_miso_dbitlen = ((bytes * 8) - 1); spi->dev->miso_dlen.usr_miso_dbitlen = ((bytes * 8) - 1);
for (i = 0; i < words; i++) { for (i = 0; i < words; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = wordsBuf[i]; //copy buffer to spi fifo spi->dev->data_buf[i].val = wordsBuf[i]; //copy buffer to spi fifo
#else #else
spi->dev->data_buf[i] = wordsBuf[i]; //copy buffer to spi fifo spi->dev->data_buf[i] = wordsBuf[i]; //copy buffer to spi fifo
@ -1026,7 +1031,7 @@ static void __spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, ui
if (out) { if (out) {
for (i = 0; i < words; i++) { for (i = 0; i < words; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
wordsBuf[i] = spi->dev->data_buf[i].val; //copy spi fifo to buffer wordsBuf[i] = spi->dev->data_buf[i].val; //copy spi fifo to buffer
#else #else
wordsBuf[i] = spi->dev->data_buf[i]; //copy spi fifo to buffer wordsBuf[i] = spi->dev->data_buf[i]; //copy spi fifo to buffer
@ -1167,7 +1172,7 @@ void ARDUINO_ISR_ATTR spiWriteByteNL(spi_t *spi, uint8_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1186,7 +1191,7 @@ uint8_t spiTransferByteNL(spi_t *spi, uint8_t data) {
} }
spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; spi->dev->mosi_dlen.usr_mosi_dbitlen = 7;
spi->dev->miso_dlen.usr_miso_dbitlen = 7; spi->dev->miso_dlen.usr_miso_dbitlen = 7;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1197,7 +1202,7 @@ uint8_t spiTransferByteNL(spi_t *spi, uint8_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val & 0xFF; data = spi->dev->data_buf[0].val & 0xFF;
#else #else
data = spi->dev->data_buf[0] & 0xFF; data = spi->dev->data_buf[0] & 0xFF;
@ -1216,7 +1221,7 @@ void ARDUINO_ISR_ATTR spiWriteShortNL(spi_t *spi, uint16_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1238,7 +1243,7 @@ uint16_t spiTransferShortNL(spi_t *spi, uint16_t data) {
} }
spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->mosi_dlen.usr_mosi_dbitlen = 15;
spi->dev->miso_dlen.usr_miso_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 15;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1249,7 +1254,7 @@ uint16_t spiTransferShortNL(spi_t *spi, uint16_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val & 0xFFFF; data = spi->dev->data_buf[0].val & 0xFFFF;
#else #else
data = spi->dev->data_buf[0] & 0xFFFF; data = spi->dev->data_buf[0] & 0xFFFF;
@ -1271,7 +1276,7 @@ void ARDUINO_ISR_ATTR spiWriteLongNL(spi_t *spi, uint32_t data) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1293,7 +1298,7 @@ uint32_t spiTransferLongNL(spi_t *spi, uint32_t data) {
} }
spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->mosi_dlen.usr_mosi_dbitlen = 31;
spi->dev->miso_dlen.usr_miso_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 31;
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1304,7 +1309,7 @@ uint32_t spiTransferLongNL(spi_t *spi, uint32_t data) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val; data = spi->dev->data_buf[0].val;
#else #else
data = spi->dev->data_buf[0]; data = spi->dev->data_buf[0];
@ -1335,7 +1340,7 @@ void spiWriteNL(spi_t *spi, const void *data_in, uint32_t len) {
spi->dev->miso_dlen.usr_miso_dbitlen = 0; spi->dev->miso_dlen.usr_miso_dbitlen = 0;
#endif #endif
for (size_t i = 0; i < c_longs; i++) { for (size_t i = 0; i < c_longs; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i]; spi->dev->data_buf[i].val = data[i];
#else #else
spi->dev->data_buf[i] = data[i]; spi->dev->data_buf[i] = data[i];
@ -1374,7 +1379,7 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint
spi->dev->miso_dlen.usr_miso_dbitlen = (c_len * 8) - 1; spi->dev->miso_dlen.usr_miso_dbitlen = (c_len * 8) - 1;
if (data) { if (data) {
for (size_t i = 0; i < c_longs; i++) { for (size_t i = 0; i < c_longs; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i]; spi->dev->data_buf[i].val = data[i];
#else #else
spi->dev->data_buf[i] = data[i]; spi->dev->data_buf[i] = data[i];
@ -1382,7 +1387,7 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint
} }
} else { } else {
for (size_t i = 0; i < c_longs; i++) { for (size_t i = 0; i < c_longs; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = 0xFFFFFFFF; spi->dev->data_buf[i].val = 0xFFFFFFFF;
#else #else
spi->dev->data_buf[i] = 0xFFFFFFFF; spi->dev->data_buf[i] = 0xFFFFFFFF;
@ -1398,13 +1403,13 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint
if (result) { if (result) {
if (c_len & 3) { if (c_len & 3) {
for (size_t i = 0; i < (c_longs - 1); i++) { for (size_t i = 0; i < (c_longs - 1); i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
result[i] = spi->dev->data_buf[i].val; result[i] = spi->dev->data_buf[i].val;
#else #else
result[i] = spi->dev->data_buf[i]; result[i] = spi->dev->data_buf[i];
#endif #endif
} }
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
uint32_t last_data = spi->dev->data_buf[c_longs - 1].val; uint32_t last_data = spi->dev->data_buf[c_longs - 1].val;
#else #else
uint32_t last_data = spi->dev->data_buf[c_longs - 1]; uint32_t last_data = spi->dev->data_buf[c_longs - 1];
@ -1416,7 +1421,7 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint
} }
} else { } else {
for (size_t i = 0; i < c_longs; i++) { for (size_t i = 0; i < c_longs; i++) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
result[i] = spi->dev->data_buf[i].val; result[i] = spi->dev->data_buf[i].val;
#else #else
result[i] = spi->dev->data_buf[i]; result[i] = spi->dev->data_buf[i];
@ -1458,7 +1463,7 @@ void spiTransferBitsNL(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits) {
spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1);
spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[0].val = data; spi->dev->data_buf[0].val = data;
#else #else
spi->dev->data_buf[0] = data; spi->dev->data_buf[0] = data;
@ -1469,7 +1474,7 @@ void spiTransferBitsNL(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits) {
#endif #endif
spi->dev->cmd.usr = 1; spi->dev->cmd.usr = 1;
while (spi->dev->cmd.usr); while (spi->dev->cmd.usr);
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
data = spi->dev->data_buf[0].val; data = spi->dev->data_buf[0].val;
#else #else
data = spi->dev->data_buf[0]; data = spi->dev->data_buf[0];
@ -1510,27 +1515,27 @@ void ARDUINO_ISR_ATTR spiWritePixelsNL(spi_t *spi, const void *data_in, uint32_t
if (msb) { if (msb) {
if (l_bytes && i == (c_longs - 1)) { if (l_bytes && i == (c_longs - 1)) {
if (l_bytes == 2) { if (l_bytes == 2) {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
MSB_16_SET(spi->dev->data_buf[i].val, data[i]); MSB_16_SET(spi->dev->data_buf[i].val, data[i]);
#else #else
MSB_16_SET(spi->dev->data_buf[i], data[i]); MSB_16_SET(spi->dev->data_buf[i], data[i]);
#endif #endif
} else { } else {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i] & 0xFF; spi->dev->data_buf[i].val = data[i] & 0xFF;
#else #else
spi->dev->data_buf[i] = data[i] & 0xFF; spi->dev->data_buf[i] = data[i] & 0xFF;
#endif #endif
} }
} else { } else {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
MSB_PIX_SET(spi->dev->data_buf[i].val, data[i]); MSB_PIX_SET(spi->dev->data_buf[i].val, data[i]);
#else #else
MSB_PIX_SET(spi->dev->data_buf[i], data[i]); MSB_PIX_SET(spi->dev->data_buf[i], data[i]);
#endif #endif
} }
} else { } else {
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
spi->dev->data_buf[i].val = data[i]; spi->dev->data_buf[i].val = data[i];
#else #else
spi->dev->data_buf[i] = data[i]; spi->dev->data_buf[i] = data[i];

View file

@ -208,6 +208,14 @@ static bool lpuartCheckPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rt
} }
#endif // SOC_UART_LP_NUM >= 1 #endif // SOC_UART_LP_NUM >= 1
#ifndef GPIO_FUNC_IN_LOW
#define GPIO_FUNC_IN_LOW GPIO_MATRIX_CONST_ZERO_INPUT
#endif
#ifndef GPIO_FUNC_IN_HIGH
#define GPIO_FUNC_IN_HIGH GPIO_MATRIX_CONST_ONE_INPUT
#endif
// Negative Pin Number will keep it unmodified, thus this function can detach individual pins // Negative Pin Number will keep it unmodified, thus this function can detach individual pins
// This function will also unset the pins in the Peripheral Manager and set the pin to -1 after detaching // This function will also unset the pins in the Peripheral Manager and set the pin to -1 after detaching
static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) {
@ -223,7 +231,8 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
// detaches HP and LP pins and sets Peripheral Manager and UART information // detaches HP and LP pins and sets Peripheral Manager and UART information
if (rxPin >= 0 && uart->_rxPin == rxPin && perimanGetPinBusType(rxPin) == ESP32_BUS_TYPE_UART_RX) { if (rxPin >= 0 && uart->_rxPin == rxPin && perimanGetPinBusType(rxPin) == ESP32_BUS_TYPE_UART_RX) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO); //gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO);
esp_rom_gpio_pad_select_gpio(rxPin);
// avoids causing BREAK in the UART line // avoids causing BREAK in the UART line
if (uart->_inverted) { if (uart->_inverted) {
esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false);
@ -237,7 +246,8 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
} }
} }
if (txPin >= 0 && uart->_txPin == txPin && perimanGetPinBusType(txPin) == ESP32_BUS_TYPE_UART_TX) { if (txPin >= 0 && uart->_txPin == txPin && perimanGetPinBusType(txPin) == ESP32_BUS_TYPE_UART_TX) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[txPin], PIN_FUNC_GPIO); //gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[txPin], PIN_FUNC_GPIO);
esp_rom_gpio_pad_select_gpio(txPin);
esp_rom_gpio_connect_out_signal(txPin, SIG_GPIO_OUT_IDX, false, false); esp_rom_gpio_connect_out_signal(txPin, SIG_GPIO_OUT_IDX, false, false);
uart->_txPin = -1; // -1 means unassigned/detached uart->_txPin = -1; // -1 means unassigned/detached
if (!perimanClearPinBus(txPin)) { if (!perimanClearPinBus(txPin)) {
@ -246,7 +256,8 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
} }
} }
if (ctsPin >= 0 && uart->_ctsPin == ctsPin && perimanGetPinBusType(ctsPin) == ESP32_BUS_TYPE_UART_CTS) { if (ctsPin >= 0 && uart->_ctsPin == ctsPin && perimanGetPinBusType(ctsPin) == ESP32_BUS_TYPE_UART_CTS) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[ctsPin], PIN_FUNC_GPIO); //gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[ctsPin], PIN_FUNC_GPIO);
esp_rom_gpio_pad_select_gpio(ctsPin);
esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false); esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false);
uart->_ctsPin = -1; // -1 means unassigned/detached uart->_ctsPin = -1; // -1 means unassigned/detached
if (!perimanClearPinBus(ctsPin)) { if (!perimanClearPinBus(ctsPin)) {
@ -255,7 +266,8 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
} }
} }
if (rtsPin >= 0 && uart->_rtsPin == rtsPin && perimanGetPinBusType(rtsPin) == ESP32_BUS_TYPE_UART_RTS) { if (rtsPin >= 0 && uart->_rtsPin == rtsPin && perimanGetPinBusType(rtsPin) == ESP32_BUS_TYPE_UART_RTS) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rtsPin], PIN_FUNC_GPIO); //gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rtsPin], PIN_FUNC_GPIO);
esp_rom_gpio_pad_select_gpio(rtsPin);
esp_rom_gpio_connect_out_signal(rtsPin, SIG_GPIO_OUT_IDX, false, false); esp_rom_gpio_connect_out_signal(rtsPin, SIG_GPIO_OUT_IDX, false, false);
uart->_rtsPin = -1; // -1 means unassigned/detached uart->_rtsPin = -1; // -1 means unassigned/detached
if (!perimanClearPinBus(rtsPin)) { if (!perimanClearPinBus(rtsPin)) {
@ -310,11 +322,19 @@ static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx)
return false; return false;
} }
if (uart_num < SOC_UART_HP_NUM) { if (uart_num < SOC_UART_HP_NUM) {
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
if (upin->input) {
gpio_iomux_input(io_num, upin->iomux_func, upin->signal);
} else {
gpio_iomux_output(io_num, upin->iomux_func);
}
#else
gpio_iomux_out(io_num, upin->iomux_func, false); gpio_iomux_out(io_num, upin->iomux_func, false);
// If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix. // If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix.
if (upin->input) { if (upin->input) {
gpio_iomux_in(io_num, upin->signal); gpio_iomux_in(io_num, upin->signal);
} }
#endif
} }
#if (SOC_UART_LP_NUM >= 1) && (SOC_RTCIO_PIN_COUNT >= 1) #if (SOC_UART_LP_NUM >= 1) && (SOC_RTCIO_PIN_COUNT >= 1)
else { else {
@ -369,7 +389,12 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx
#endif #endif
if (tx_rx_same_io || !_uartTrySetIomuxPin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) { if (tx_rx_same_io || !_uartTrySetIomuxPin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) {
if (uart_num < SOC_UART_HP_NUM) { if (uart_num < SOC_UART_HP_NUM) {
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
gpio_input_enable(rx_io_num); gpio_input_enable(rx_io_num);
#else
gpio_func_sel(rx_io_num, PIN_FUNC_GPIO);
gpio_ll_input_enable(&GPIO, rx_io_num);
#endif
esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0); esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
} }
#if SOC_LP_GPIO_MATRIX_SUPPORTED #if SOC_LP_GPIO_MATRIX_SUPPORTED
@ -402,8 +427,14 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx
if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) {
if (uart_num < SOC_UART_HP_NUM) { if (uart_num < SOC_UART_HP_NUM) {
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
gpio_pullup_en(cts_io_num); gpio_pullup_en(cts_io_num);
gpio_input_enable(cts_io_num); gpio_input_enable(cts_io_num);
#else
gpio_func_sel(cts_io_num, PIN_FUNC_GPIO);
gpio_set_pull_mode(cts_io_num, GPIO_PULLUP_ONLY);
gpio_set_direction(cts_io_num, GPIO_MODE_INPUT);
#endif
esp_rom_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); esp_rom_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0);
} }
#if SOC_LP_GPIO_MATRIX_SUPPORTED #if SOC_LP_GPIO_MATRIX_SUPPORTED
@ -950,7 +981,7 @@ void uartSetRxInvert(uart_t *uart, bool invert) {
if (uart == NULL) { if (uart == NULL) {
return; return;
} }
#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5
// POTENTIAL ISSUE :: original code only set/reset rxd_inv bit // POTENTIAL ISSUE :: original code only set/reset rxd_inv bit
// IDF or LL set/reset the whole inv_mask! // IDF or LL set/reset the whole inv_mask!
// if (invert) // if (invert)
@ -1267,8 +1298,12 @@ bool uartSetClockSource(uint8_t uartNum, uart_sclk_t clkSrc) {
#if SOC_UART_LP_NUM >= 1 #if SOC_UART_LP_NUM >= 1
if (uart->num >= SOC_UART_HP_NUM) { if (uart->num >= SOC_UART_HP_NUM) {
switch (clkSrc) { switch (clkSrc) {
case UART_SCLK_XTAL: uart->_uart_clock_source = LP_UART_SCLK_XTAL_D2; break; case UART_SCLK_XTAL: uart->_uart_clock_source = LP_UART_SCLK_XTAL_D2; break;
case UART_SCLK_RTC: uart->_uart_clock_source = LP_UART_SCLK_LP_FAST; break; #if CONFIG_IDF_TARGET_ESP32C5
case UART_SCLK_RTC: uart->_uart_clock_source = LP_UART_SCLK_RC_FAST; break;
#else
case UART_SCLK_RTC: uart->_uart_clock_source = LP_UART_SCLK_LP_FAST; break;
#endif
case UART_SCLK_DEFAULT: case UART_SCLK_DEFAULT:
default: uart->_uart_clock_source = LP_UART_SCLK_DEFAULT; default: uart->_uart_clock_source = LP_UART_SCLK_DEFAULT;
} }

View file

@ -21,9 +21,9 @@ extern "C" {
/** Major version number (X.x.x) */ /** Major version number (X.x.x) */
#define ESP_ARDUINO_VERSION_MAJOR 3 #define ESP_ARDUINO_VERSION_MAJOR 3
/** Minor version number (x.X.x) */ /** Minor version number (x.X.x) */
#define ESP_ARDUINO_VERSION_MINOR 2 #define ESP_ARDUINO_VERSION_MINOR 3
/** Patch version number (x.x.X) */ /** Patch version number (x.x.X) */
#define ESP_ARDUINO_VERSION_PATCH 1 #define ESP_ARDUINO_VERSION_PATCH 0
/** /**
* Macro to convert ARDUINO version number into an integer * Macro to convert ARDUINO version number into an integer

View file

@ -4,8 +4,8 @@ from esp_docs.conf_docs import * # noqa: F403,F401
# Used for substituting variables in the documentation # Used for substituting variables in the documentation
rst_prolog = """ rst_prolog = """
.. |version| replace:: 3.2.1 .. |version| replace:: 3.3.0
.. |idf_version| replace:: 5.4 .. |idf_version| replace:: 5.5
""" """
languages = ["en"] languages = ["en"]

View file

@ -10,6 +10,7 @@ targets:
- esp32c6 - esp32c6
- esp32h2 - esp32h2
- esp32p4 - esp32p4
- esp32c5
tags: tags:
- arduino - arduino
files: files:
@ -22,6 +23,7 @@ files:
- "variants/esp32c6/**/*" - "variants/esp32c6/**/*"
- "variants/esp32h2/**/*" - "variants/esp32h2/**/*"
- "variants/esp32p4/**/*" - "variants/esp32p4/**/*"
- "variants/esp32c5/**/*"
exclude: exclude:
- "docs/" - "docs/"
- "docs/**/*" - "docs/**/*"
@ -44,7 +46,7 @@ files:
- "platform.txt" - "platform.txt"
- "programmers.txt" - "programmers.txt"
dependencies: dependencies:
idf: ">=5.3,<5.5" idf: ">=5.3,<5.6"
# mdns 1.2.1 is necessary to build H2 with no WiFi # mdns 1.2.1 is necessary to build H2 with no WiFi
espressif/mdns: espressif/mdns:
version: "^1.2.3" version: "^1.2.3"
@ -52,12 +54,12 @@ dependencies:
espressif/esp_modem: espressif/esp_modem:
version: "^1.1.0" version: "^1.1.0"
espressif/esp-zboss-lib: espressif/esp-zboss-lib:
version: "==1.6.3" version: "==1.6.4" # compatible with esp-zigbee-lib 1.6.5
require: public require: public
rules: rules:
- if: "target not in [esp32c2, esp32p4]" - if: "target not in [esp32c2, esp32p4]"
espressif/esp-zigbee-lib: espressif/esp-zigbee-lib:
version: "==1.6.3" version: "==1.6.5"
require: public require: public
rules: rules:
- if: "target not in [esp32c2, esp32p4]" - if: "target not in [esp32c2, esp32p4]"

View file

@ -1,5 +1,5 @@
name=ArduinoOTA name=ArduinoOTA
version=3.2.1 version=3.3.0
author=Ivan Grokhotkov and Hristo Gochkov author=Ivan Grokhotkov and Hristo Gochkov
maintainer=Hristo Gochkov <hristo@espressif.com> maintainer=Hristo Gochkov <hristo@espressif.com>
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download.

View file

@ -1,5 +1,5 @@
name=ESP32 Async UDP name=ESP32 Async UDP
version=3.2.1 version=3.3.0
author=Me-No-Dev author=Me-No-Dev
maintainer=Me-No-Dev maintainer=Me-No-Dev
sentence=Async UDP Library for ESP32 sentence=Async UDP Library for ESP32

View file

@ -1,8 +1,12 @@
# ESP32 BLE for Arduino # ESP32 BLE for Arduino
The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino. The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino.
The original source of the project, **which is not maintained anymore**, can be found here: https://github.com/nkolban/esp32-snippets The original source of the Bluedroid project, **which is not maintained anymore**, can be found here: https://github.com/nkolban/esp32-snippets
Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues <br> (please don't use https://github.com/nkolban/esp32-snippets/issues!) Some parts of the NimBLE implementation are based on the work of h2zero, which can be found here: https://github.com/h2zero/NimBLE-Arduino
Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues <br> (please don't use https://github.com/nkolban/esp32-snippets/issues or https://github.com/h2zero/NimBLE-Arduino/issues!)
Documentation for using the library can be found here: https://github.com/nkolban/esp32-snippets/tree/master/Documentation Documentation for using the library can be found here: https://github.com/nkolban/esp32-snippets/tree/master/Documentation
For a more customizable and feature-rich implementation of the NimBLE stack, you can use the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library.

View file

@ -7,8 +7,10 @@
author: chegewara author: chegewara
*/ */
#ifndef SOC_BLE_50_SUPPORTED #ifndef CONFIG_BLUEDROID_ENABLED
#warning "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3" #error "NimBLE does not support extended scan yet. Try using Bluedroid."
#elif !defined(SOC_BLE_50_SUPPORTED)
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
#else #else
#include <BLEDevice.h> #include <BLEDevice.h>

View file

@ -1,6 +1,7 @@
{ {
"fqbn_append": "PartitionScheme=huge_app", "fqbn_append": "PartitionScheme=huge_app",
"requires": [ "requires": [
"CONFIG_SOC_BLE_50_SUPPORTED=y" "CONFIG_SOC_BLE_50_SUPPORTED=y",
"CONFIG_BLUEDROID_ENABLED=y"
] ]
} }

View file

@ -6,7 +6,9 @@
author: chegewara author: chegewara
*/ */
#ifndef CONFIG_BT_BLE_50_FEATURES_SUPPORTED #ifndef CONFIG_BLUEDROID_ENABLED
#error "NimBLE does not support multi advertising yet. Try using Bluedroid."
#elif !defined(CONFIG_BT_BLE_50_FEATURES_SUPPORTED)
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3" #error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
#else #else

View file

@ -1,6 +1,7 @@
{ {
"fqbn_append": "PartitionScheme=huge_app", "fqbn_append": "PartitionScheme=huge_app",
"requires": [ "requires": [
"CONFIG_SOC_BLE_50_SUPPORTED=y" "CONFIG_SOC_BLE_50_SUPPORTED=y",
"CONFIG_BLUEDROID_ENABLED=y"
] ]
} }

View file

@ -5,7 +5,9 @@
author: chegewara author: chegewara
*/ */
#ifndef CONFIG_BT_BLE_50_FEATURES_SUPPORTED #ifndef CONFIG_BLUEDROID_ENABLED
#error "NimBLE does not support periodic advertising yet. Try using Bluedroid."
#elif !defined(CONFIG_BT_BLE_50_FEATURES_SUPPORTED)
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3" #error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
#else #else
#include <BLEDevice.h> #include <BLEDevice.h>

View file

@ -1,6 +1,7 @@
{ {
"fqbn_append": "PartitionScheme=huge_app", "fqbn_append": "PartitionScheme=huge_app",
"requires": [ "requires": [
"CONFIG_SOC_BLE_50_SUPPORTED=y" "CONFIG_SOC_BLE_50_SUPPORTED=y",
"CONFIG_BLUEDROID_ENABLED=y"
] ]
} }

View file

@ -7,8 +7,10 @@
author: chegewara author: chegewara
*/ */
#ifndef SOC_BLE_50_SUPPORTED #ifndef CONFIG_BLUEDROID_ENABLED
#warning "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3" #error "NimBLE does not support periodic sync yet. Try using Bluedroid."
#elif !defined(SOC_BLE_50_SUPPORTED)
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
#else #else
#include <BLEDevice.h> #include <BLEDevice.h>
#include <BLEUtils.h> #include <BLEUtils.h>

View file

@ -1,6 +1,7 @@
{ {
"fqbn_append": "PartitionScheme=huge_app", "fqbn_append": "PartitionScheme=huge_app",
"requires": [ "requires": [
"CONFIG_SOC_BLE_50_SUPPORTED=y" "CONFIG_SOC_BLE_50_SUPPORTED=y",
"CONFIG_BLUEDROID_ENABLED=y"
] ]
} }

View file

@ -36,25 +36,33 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
if (advertisedDevice.haveManufacturerData() == true) { if (advertisedDevice.haveManufacturerData() == true) {
String strManufacturerData = advertisedDevice.getManufacturerData(); String strManufacturerData = advertisedDevice.getManufacturerData();
uint8_t cManufacturerData[100]; // Buffer to store manufacturer data (BLE max is 255 bytes)
memcpy(cManufacturerData, strManufacturerData.c_str(), strManufacturerData.length()); uint8_t cManufacturerData[255];
size_t dataLength = strManufacturerData.length();
if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) { // Bounds checking to prevent buffer overflow
Serial.println("Found an iBeacon!"); if (dataLength <= sizeof(cManufacturerData)) {
BLEBeacon oBeacon = BLEBeacon(); memcpy(cManufacturerData, strManufacturerData.c_str(), dataLength);
oBeacon.setData(strManufacturerData);
Serial.printf("iBeacon Frame\n"); if (dataLength == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) {
Serial.printf( Serial.println("Found an iBeacon!");
"ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), BLEBeacon oBeacon = BLEBeacon();
ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower() oBeacon.setData(strManufacturerData);
); Serial.printf("iBeacon Frame\n");
} else { Serial.printf(
Serial.println("Found another manufacturers beacon!"); "ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()),
Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()
for (int i = 0; i < strManufacturerData.length(); i++) { );
Serial.printf("[%X]", cManufacturerData[i]); } else {
Serial.println("Found another manufacturers beacon!");
Serial.printf("strManufacturerData: %d ", dataLength);
for (int i = 0; i < dataLength; i++) {
Serial.printf("[%X]", cManufacturerData[i]);
}
Serial.printf("\n");
} }
Serial.printf("\n"); } else {
Serial.printf("Manufacturer data too large (%d bytes), skipping\n", dataLength);
} }
} }

View file

@ -19,6 +19,7 @@ static boolean doScan = false;
static BLERemoteCharacteristic *pRemoteCharacteristic; static BLERemoteCharacteristic *pRemoteCharacteristic;
static BLEAdvertisedDevice *myDevice; static BLEAdvertisedDevice *myDevice;
// Callback function to handle notifications
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
Serial.print("Notify callback for characteristic "); Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
@ -80,6 +81,7 @@ bool connectToServer() {
} }
if (pRemoteCharacteristic->canNotify()) { if (pRemoteCharacteristic->canNotify()) {
// Register/Subscribe for notifications
pRemoteCharacteristic->registerForNotify(notifyCallback); pRemoteCharacteristic->registerForNotify(notifyCallback);
} }

View file

@ -69,6 +69,7 @@ void setup() {
); );
// Creates BLE Descriptor 0x2902: Client Characteristic Configuration Descriptor (CCCD) // Creates BLE Descriptor 0x2902: Client Characteristic Configuration Descriptor (CCCD)
// Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
pCharacteristic->addDescriptor(new BLE2902()); pCharacteristic->addDescriptor(new BLE2902());
// Adds also the Characteristic User Description - 0x2901 descriptor // Adds also the Characteristic User Description - 0x2901 descriptor
descriptor_2901 = new BLE2901(); descriptor_2901 = new BLE2901();

View file

@ -5,6 +5,9 @@
updated by chegewara updated by chegewara
Create a BLE server that, once we receive a connection, will send periodic notifications. Create a BLE server that, once we receive a connection, will send periodic notifications.
The server will continue advertising for more connections after the first one and will notify
the value of a counter to all connected clients.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
@ -26,8 +29,8 @@
BLEServer *pServer = NULL; BLEServer *pServer = NULL;
BLECharacteristic *pCharacteristic = NULL; BLECharacteristic *pCharacteristic = NULL;
int connectedClients = 0;
bool deviceConnected = false; bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0; uint32_t value = 0;
// See the following for generating UUIDs: // See the following for generating UUIDs:
@ -38,12 +41,17 @@ uint32_t value = 0;
class MyServerCallbacks : public BLEServerCallbacks { class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) { void onConnect(BLEServer *pServer) {
deviceConnected = true; connectedClients++;
Serial.print("Client connected. Total clients: ");
Serial.println(connectedClients);
// Continue advertising for more connections
BLEDevice::startAdvertising(); BLEDevice::startAdvertising();
}; };
void onDisconnect(BLEServer *pServer) { void onDisconnect(BLEServer *pServer) {
deviceConnected = false; connectedClients--;
Serial.print("Client disconnected. Total clients: ");
Serial.println(connectedClients);
} }
}; };
@ -66,8 +74,7 @@ void setup() {
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE
); );
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902()); pCharacteristic->addDescriptor(new BLE2902());
// Start the service // Start the service
@ -79,27 +86,38 @@ void setup() {
pAdvertising->setScanResponse(false); pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising(); BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify..."); Serial.println("Waiting for client connections to notify...");
} }
void loop() { void loop() {
// notify changed value // Notify changed value to all connected clients
if (deviceConnected) { if (connectedClients > 0) {
Serial.print("Notifying value: ");
Serial.print(value);
Serial.print(" to ");
Serial.print(connectedClients);
Serial.println(" client(s)");
pCharacteristic->setValue((uint8_t *)&value, 4); pCharacteristic->setValue((uint8_t *)&value, 4);
pCharacteristic->notify(); pCharacteristic->notify();
value++; value++;
delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms // Bluetooth stack will go into congestion, if too many packets are sent.
// In 6 hours of testing, I was able to go as low as 3ms.
// When using core debug level "debug" or "verbose", the delay can be increased in
// order to reduce the number of debug messages in the serial monitor.
delay(100);
} }
// disconnecting
if (!deviceConnected && oldDeviceConnected) { // Disconnecting - restart advertising when no clients are connected
if (connectedClients == 0 && deviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising pServer->startAdvertising(); // restart advertising
Serial.println("start advertising"); Serial.println("No clients connected, restarting advertising");
oldDeviceConnected = deviceConnected; deviceConnected = false;
} }
// connecting
if (deviceConnected && !oldDeviceConnected) { // Connecting - update state when first client connects
// do stuff here on connecting if (connectedClients > 0 && !deviceConnected) {
oldDeviceConnected = deviceConnected; // do stuff here on first connecting
deviceConnected = true;
} }
} }

View file

@ -40,10 +40,12 @@ uint8_t txValue = 0;
class MyServerCallbacks : public BLEServerCallbacks { class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) { void onConnect(BLEServer *pServer) {
deviceConnected = true; deviceConnected = true;
Serial.println("Device connected");
}; };
void onDisconnect(BLEServer *pServer) { void onDisconnect(BLEServer *pServer) {
deviceConnected = false; deviceConnected = false;
Serial.println("Device disconnected");
} }
}; };
@ -80,6 +82,7 @@ void setup() {
// Create a BLE Characteristic // Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY); pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
// Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
pTxCharacteristic->addDescriptor(new BLE2902()); pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE); BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
@ -97,22 +100,24 @@ void setup() {
void loop() { void loop() {
if (deviceConnected) { if (deviceConnected) {
Serial.print("Notifying Value: ");
Serial.println(txValue);
pTxCharacteristic->setValue(&txValue, 1); pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify(); pTxCharacteristic->notify();
txValue++; txValue++;
delay(10); // bluetooth stack will go into congestion, if too many packets are sent delay(1000); // Notifying every 1 second
} }
// disconnecting // disconnecting
if (!deviceConnected && oldDeviceConnected) { if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising pServer->startAdvertising(); // restart advertising
Serial.println("start advertising"); Serial.println("Started advertising again...");
oldDeviceConnected = deviceConnected; oldDeviceConnected = false;
} }
// connecting // connecting
if (deviceConnected && !oldDeviceConnected) { if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting // do stuff here on connecting
oldDeviceConnected = deviceConnected; oldDeviceConnected = true;
} }
} }

View file

@ -1,7 +1,7 @@
name=BLE name=BLE
version=3.2.1 version=3.3.0
author=Neil Kolban <kolban1@kolban.com> author=Neil Kolban <kolban1@kolban.com>
maintainer=Dariusz Krempa <esp32@esp32.eu.org> maintainer=lucasssvaz
sentence=BLE functions for ESP32 sentence=BLE functions for ESP32
paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform. paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform.
category=Communication category=Communication

View file

@ -1,29 +1,48 @@
/* /*
BLE2901.h BLE2901.h
GATT Descriptor 0x2901 Characteristic User Description GATT Descriptor 0x2901 Characteristic User Description
The value of this description is a user-readable string The value of this description is a user-readable string
describing the characteristic. describing the characteristic.
The Characteristic User Description descriptor The Characteristic User Description descriptor
provides a textual user description for a characteristic provides a textual user description for a characteristic
value. value.
If the Writable Auxiliary bit of the Characteristics If the Writable Auxiliary bit of the Characteristics
Properties is set then this descriptor is written. Only one Properties is set then this descriptor is written. Only one
User Description descriptor exists in a characteristic User Description descriptor exists in a characteristic
definition. definition.
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes and definitions *
***************************************************************************/
#include "BLE2901.h" #include "BLE2901.h"
BLE2901::BLE2901() : BLEDescriptor(BLEUUID((uint16_t)0x2901)) {} // BLE2901 #define BLE2901_UUID 0x2901
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_att.h>
#define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN
#endif
/***************************************************************************
* Common functions *
***************************************************************************/
BLE2901::BLE2901() : BLEDescriptor(BLEUUID((uint16_t)BLE2901_UUID)) {}
/** /**
* @brief Set the Characteristic User Description * @brief Set the Characteristic User Description
@ -36,5 +55,5 @@ void BLE2901::setDescription(String userDesc) {
setValue(userDesc); setValue(userDesc);
} }
#endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -1,37 +1,48 @@
/* /*
BLE2901.h BLE2901.h
GATT Descriptor 0x2901 Characteristic User Description GATT Descriptor 0x2901 Characteristic User Description
The value of this description is a user-readable string The value of this description is a user-readable string
describing the characteristic. describing the characteristic.
The Characteristic User Description descriptor
provides a textual user description for a characteristic
value.
If the Writable Auxiliary bit of the Characteristics
Properties is set then this descriptor is written. Only one
User Description descriptor exists in a characteristic
definition.
The Characteristic User Description descriptor
provides a textual user description for a characteristic
value.
If the Writable Auxiliary bit of the Characteristics
Properties is set then this descriptor is written. Only one
User Description descriptor exists in a characteristic
definition.
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLE2901_H_ #ifndef COMPONENTS_CPP_UTILS_BLE2901_H_
#define COMPONENTS_CPP_UTILS_BLE2901_H_ #define COMPONENTS_CPP_UTILS_BLE2901_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
/**
* @brief GATT Descriptor 0x2901 Characteristic User Description
*/
class BLE2901 : public BLEDescriptor { class BLE2901 : public BLEDescriptor {
public: public:
/***************************************************************************
* Common public functions *
***************************************************************************/
BLE2901(); BLE2901();
void setDescription(String desc); void setDescription(String desc);
}; // BLE2901 }; // BLE2901
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLE2901_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLE2901_H_ */

View file

@ -3,46 +3,86 @@
* *
* Created on: Jun 25, 2017 * Created on: Jun 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
/* /*
* See also: * See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes and definitions *
***************************************************************************/
#include "BLE2902.h" #include "BLE2902.h"
BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t)0x2902)) { #define BLE2902_UUID 0x2902
/***************************************************************************
* Common functions *
***************************************************************************/
BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t)BLE2902_UUID)) {
#if defined(CONFIG_BLUEDROID_ENABLED)
uint8_t data[2] = {0, 0}; uint8_t data[2] = {0, 0};
setValue(data, 2); setValue(data, 2);
} // BLE2902 #endif
}
/** /**
* @brief Get the notifications value. * @brief Get the notifications value.
* @return The notifications value. True if notifications are enabled and false if not. * @return The notifications value. True if notifications are enabled and false if not.
*/ */
bool BLE2902::getNotifications() { bool BLE2902::getNotifications() {
#if defined(CONFIG_BLUEDROID_ENABLED)
return (getValue()[0] & (1 << 0)) != 0; return (getValue()[0] & (1 << 0)) != 0;
} // getNotifications #endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_pCharacteristic != nullptr) {
return (m_pCharacteristic->getProperties() & BLECharacteristic::PROPERTY_NOTIFY) != 0;
} else {
log_w("BLE2902::getNotifications() called on an uninitialized descriptor");
return false;
}
#endif
}
/** /**
* @brief Get the indications value. * @brief Get the indications value.
* @return The indications value. True if indications are enabled and false if not. * @return The indications value. True if indications are enabled and false if not.
*/ */
bool BLE2902::getIndications() { bool BLE2902::getIndications() {
#if defined(CONFIG_BLUEDROID_ENABLED)
return (getValue()[0] & (1 << 1)) != 0; return (getValue()[0] & (1 << 1)) != 0;
} // getIndications #endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_pCharacteristic != nullptr) {
return (m_pCharacteristic->getProperties() & BLECharacteristic::PROPERTY_INDICATE) != 0;
} else {
log_w("BLE2902::getIndications() called on an uninitialized descriptor");
return false;
}
#endif
}
/** /**
* @brief Set the indications flag. * @brief Set the indications flag.
* @param [in] flag The indications flag. * @param [in] flag The indications flag.
*/ */
void BLE2902::setIndications(bool flag) { void BLE2902::setIndications(bool flag) {
#if defined(CONFIG_BLUEDROID_ENABLED)
uint8_t *pValue = getValue(); uint8_t *pValue = getValue();
if (flag) { if (flag) {
pValue[0] |= 1 << 1; pValue[0] |= 1 << 1;
@ -50,13 +90,23 @@ void BLE2902::setIndications(bool flag) {
pValue[0] &= ~(1 << 1); pValue[0] &= ~(1 << 1);
} }
setValue(pValue, 2); setValue(pValue, 2);
} // setIndications #endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_pCharacteristic != nullptr) {
m_pCharacteristic->setIndicateProperty(flag);
} else {
log_w("BLE2902::setIndications() called on an uninitialized descriptor");
}
#endif
}
/** /**
* @brief Set the notifications flag. * @brief Set the notifications flag.
* @param [in] flag The notifications flag. * @param [in] flag The notifications flag.
*/ */
void BLE2902::setNotifications(bool flag) { void BLE2902::setNotifications(bool flag) {
#if defined(CONFIG_BLUEDROID_ENABLED)
uint8_t *pValue = getValue(); uint8_t *pValue = getValue();
if (flag) { if (flag) {
pValue[0] |= 1 << 0; pValue[0] |= 1 << 0;
@ -64,7 +114,16 @@ void BLE2902::setNotifications(bool flag) {
pValue[0] &= ~(1 << 0); pValue[0] &= ~(1 << 0);
} }
setValue(pValue, 2); setValue(pValue, 2);
} // setNotifications
#endif #endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_pCharacteristic != nullptr) {
m_pCharacteristic->setNotifyProperty(flag);
} else {
log_w("BLE2902::setNotifications() called on an uninitialized descriptor");
}
#endif
}
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,15 +3,24 @@
* *
* Created on: Jun 25, 2017 * Created on: Jun 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 28, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_ #ifndef COMPONENTS_CPP_UTILS_BLE2902_H_
#define COMPONENTS_CPP_UTILS_BLE2902_H_ #define COMPONENTS_CPP_UTILS_BLE2902_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
@ -23,16 +32,35 @@
* See also: * See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/ */
// Class declaration for Bluedroid
#if defined(CONFIG_BLUEDROID_ENABLED)
class BLE2902 : public BLEDescriptor { class BLE2902 : public BLEDescriptor {
public: #endif
BLE2902();
bool getNotifications();
bool getIndications();
void setNotifications(bool flag);
void setIndications(bool flag);
}; // BLE2902 // Class declaration for NimBLE (deprecated)
#if defined(CONFIG_NIMBLE_ENABLED)
class [[deprecated("NimBLE does not support manually adding 2902 descriptors as they \
are automatically added when the characteristic has notifications or indications enabled. \
Get/Set the notifications/indications properties of the characteristic instead. \
This class will be removed in a future version.")]] BLE2902 : public BLEDescriptor {
#endif
#endif /* CONFIG_BLUEDROID_ENABLED */ public:
/***************************************************************************
* Common public functions *
***************************************************************************/
BLE2902();
bool getNotifications();
bool getIndications();
void setNotifications(bool flag);
void setIndications(bool flag);
private:
friend class BLECharacteristic;
}; // BLE2902
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */

View file

@ -3,6 +3,10 @@
* *
* Created on: Dec 23, 2017 * Created on: Dec 23, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
/* /*
@ -13,18 +17,30 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes and definitions *
***************************************************************************/
#include "BLE2904.h" #include "BLE2904.h"
BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t)0x2904)) { #define BLE2904_UUID 0x2904
#define BLE2904_DEFAULT_NAMESPACE 1 // 1 = Bluetooth SIG Assigned Numbers
#define BLE2904_DEFAULT_UNIT 0x2700 // 0x2700 = Unitless
/***************************************************************************
* Common functions *
***************************************************************************/
BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t)BLE2904_UUID)) {
m_data.m_format = 0; m_data.m_format = 0;
m_data.m_exponent = 0; m_data.m_exponent = 0;
m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers m_data.m_namespace = BLE2904_DEFAULT_NAMESPACE;
m_data.m_unit = 0; m_data.m_unit = BLE2904_DEFAULT_UNIT;
m_data.m_description = 0; m_data.m_description = 0;
setValue((uint8_t *)&m_data, sizeof(m_data)); setValue((uint8_t *)&m_data, sizeof(m_data));
} // BLE2902 }
/** /**
* @brief Set the description. * @brief Set the description.
@ -40,7 +56,7 @@ void BLE2904::setDescription(uint16_t description) {
void BLE2904::setExponent(int8_t exponent) { void BLE2904::setExponent(int8_t exponent) {
m_data.m_exponent = exponent; m_data.m_exponent = exponent;
setValue((uint8_t *)&m_data, sizeof(m_data)); setValue((uint8_t *)&m_data, sizeof(m_data));
} // setExponent }
/** /**
* @brief Set the format. * @brief Set the format.
@ -48,7 +64,7 @@ void BLE2904::setExponent(int8_t exponent) {
void BLE2904::setFormat(uint8_t format) { void BLE2904::setFormat(uint8_t format) {
m_data.m_format = format; m_data.m_format = format;
setValue((uint8_t *)&m_data, sizeof(m_data)); setValue((uint8_t *)&m_data, sizeof(m_data));
} // setFormat }
/** /**
* @brief Set the namespace. * @brief Set the namespace.
@ -56,7 +72,7 @@ void BLE2904::setFormat(uint8_t format) {
void BLE2904::setNamespace(uint8_t namespace_value) { void BLE2904::setNamespace(uint8_t namespace_value) {
m_data.m_namespace = namespace_value; m_data.m_namespace = namespace_value;
setValue((uint8_t *)&m_data, sizeof(m_data)); setValue((uint8_t *)&m_data, sizeof(m_data));
} // setNamespace }
/** /**
* @brief Set the units for this value. It should be one of the encoded values defined here: * @brief Set the units for this value. It should be one of the encoded values defined here:
@ -66,7 +82,7 @@ void BLE2904::setNamespace(uint8_t namespace_value) {
void BLE2904::setUnit(uint16_t unit) { void BLE2904::setUnit(uint16_t unit) {
m_data.m_unit = unit; m_data.m_unit = unit;
setValue((uint8_t *)&m_data, sizeof(m_data)); setValue((uint8_t *)&m_data, sizeof(m_data));
} // setUnit }
#endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,27 +3,43 @@
* *
* Created on: Dec 23, 2017 * Created on: Dec 23, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_ #ifndef COMPONENTS_CPP_UTILS_BLE2904_H_
#define COMPONENTS_CPP_UTILS_BLE2904_H_ #define COMPONENTS_CPP_UTILS_BLE2904_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
/***************************************************************************
* Common types *
***************************************************************************/
struct BLE2904_Data { struct BLE2904_Data {
uint8_t m_format; uint8_t m_format;
int8_t m_exponent; int8_t m_exponent;
uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace; uint8_t m_namespace;
uint16_t m_description; uint16_t m_description;
} __attribute__((packed)); } __attribute__((packed));
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief Descriptor for Characteristic Presentation Format. * @brief Descriptor for Characteristic Presentation Format.
* *
@ -34,7 +50,10 @@ struct BLE2904_Data {
*/ */
class BLE2904 : public BLEDescriptor { class BLE2904 : public BLEDescriptor {
public: public:
BLE2904(); /***************************************************************************
* Common public constants *
***************************************************************************/
static const uint8_t FORMAT_BOOLEAN = 1; static const uint8_t FORMAT_BOOLEAN = 1;
static const uint8_t FORMAT_UINT2 = 2; static const uint8_t FORMAT_UINT2 = 2;
static const uint8_t FORMAT_UINT4 = 3; static const uint8_t FORMAT_UINT4 = 3;
@ -62,7 +81,13 @@ public:
static const uint8_t FORMAT_UTF8 = 25; static const uint8_t FORMAT_UTF8 = 25;
static const uint8_t FORMAT_UTF16 = 26; static const uint8_t FORMAT_UTF16 = 26;
static const uint8_t FORMAT_OPAQUE = 27; static const uint8_t FORMAT_OPAQUE = 27;
static const uint8_t FORMAT_MEDASN1 = 28;
/***************************************************************************
* Common public functions *
***************************************************************************/
BLE2904();
void setDescription(uint16_t); void setDescription(uint16_t);
void setExponent(int8_t exponent); void setExponent(int8_t exponent);
void setFormat(uint8_t format); void setFormat(uint8_t format);
@ -70,9 +95,15 @@ public:
void setUnit(uint16_t unit); void setUnit(uint16_t unit);
private: private:
friend class BLECharacteristic;
/***************************************************************************
* Common private properties *
***************************************************************************/
BLE2904_Data m_data; BLE2904_Data m_data;
}; // BLE2904 }; // BLE2904
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */

View file

@ -3,12 +3,21 @@
* *
* Created on: Jul 2, 2017 * Created on: Jul 2, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEAddress.h" #include "BLEAddress.h"
#include <string> #include <string>
@ -17,43 +26,33 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <malloc.h> #include <malloc.h>
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#endif #endif
/** /***************************************************************************
* @brief Create an address from the native ESP32 representation. * NimBLE includes *
* @param [in] address The native representation. ***************************************************************************/
*/
BLEAddress::BLEAddress(esp_bd_addr_t address) {
memcpy(m_address, address, ESP_BD_ADDR_LEN);
} // BLEAddress
/** #if defined(CONFIG_NIMBLE_ENABLED)
* @brief Create an address from a hex string /*************************************************
* * NOTE: NimBLE address bytes are in INVERSE ORDER!
* A hex string is of the format: * We will accommodate that fact in these methods.
* ``` *************************************************/
* 00:00:00:00:00:00 #include <algorithm>
* ``` #endif
* which is 17 characters in length.
*
* @param [in] stringAddress The hex representation of the address.
*/
BLEAddress::BLEAddress(String stringAddress) {
if (stringAddress.length() != 17) {
return;
}
int data[6]; /***************************************************************************
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]); * Common functions *
m_address[0] = (uint8_t)data[0]; ***************************************************************************/
m_address[1] = (uint8_t)data[1];
m_address[2] = (uint8_t)data[2]; BLEAddress::BLEAddress() {
m_address[3] = (uint8_t)data[3]; memset(m_address, 0, ESP_BD_ADDR_LEN);
m_address[4] = (uint8_t)data[4]; #if defined(CONFIG_NIMBLE_ENABLED)
m_address[5] = (uint8_t)data[5]; m_addrType = 0;
} // BLEAddress #endif
}
/** /**
* @brief Determine if this address equals another. * @brief Determine if this address equals another.
@ -61,10 +60,20 @@ BLEAddress::BLEAddress(String stringAddress) {
* @return True if the addresses are equal. * @return True if the addresses are equal.
*/ */
bool BLEAddress::equals(BLEAddress otherAddress) { bool BLEAddress::equals(BLEAddress otherAddress) {
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_addrType != otherAddress.m_addrType) {
return false;
}
#endif
return memcmp(otherAddress.getNative(), m_address, ESP_BD_ADDR_LEN) == 0; return memcmp(otherAddress.getNative(), m_address, ESP_BD_ADDR_LEN) == 0;
} // equals }
bool BLEAddress::operator==(const BLEAddress &otherAddress) const { bool BLEAddress::operator==(const BLEAddress &otherAddress) const {
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_addrType != otherAddress.m_addrType) {
return false;
}
#endif
return memcmp(otherAddress.m_address, m_address, ESP_BD_ADDR_LEN) == 0; return memcmp(otherAddress.m_address, m_address, ESP_BD_ADDR_LEN) == 0;
} }
@ -92,9 +101,9 @@ bool BLEAddress::operator>(const BLEAddress &otherAddress) const {
* @brief Return the native representation of the address. * @brief Return the native representation of the address.
* @return The native representation of the address. * @return The native representation of the address.
*/ */
esp_bd_addr_t *BLEAddress::getNative() { uint8_t *BLEAddress::getNative() {
return &m_address; return m_address;
} // getNative }
/** /**
* @brief Convert a BLE address to a string. * @brief Convert a BLE address to a string.
@ -110,11 +119,112 @@ esp_bd_addr_t *BLEAddress::getNative() {
String BLEAddress::toString() { String BLEAddress::toString() {
auto size = 18; auto size = 18;
char *res = (char *)malloc(size); char *res = (char *)malloc(size);
#if defined(CONFIG_BLUEDROID_ENABLED)
snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]); snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]);
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[5], m_address[4], m_address[3], m_address[2], m_address[1], m_address[0]);
#endif
String ret(res); String ret(res);
free(res); free(res);
return ret; return ret;
} // toString }
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/**
* @brief Create an address from the native ESP32 representation.
* @param [in] address The native representation.
*/
BLEAddress::BLEAddress(esp_bd_addr_t address) {
memcpy(m_address, address, ESP_BD_ADDR_LEN);
}
/**
* @brief Create an address from a hex string
*
* A hex string is of the format:
* ```
* 00:00:00:00:00:00
* ```
* which is 17 characters in length.
*
* @param [in] stringAddress The hex representation of the address.
*/
BLEAddress::BLEAddress(String stringAddress) {
if (stringAddress.length() != 17) {
return;
}
int data[6];
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
for (size_t index = 0; index < sizeof(m_address); index++) {
m_address[index] = (uint8_t)data[index];
}
}
#endif #endif
/***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
/*************************************************
* NOTE: NimBLE address bytes are in INVERSE ORDER!
* We will accommodate that fact in these methods.
*************************************************/
BLEAddress::BLEAddress(uint8_t address[ESP_BD_ADDR_LEN], uint8_t type) {
std::reverse_copy(address, address + sizeof(m_address), m_address);
m_addrType = type;
}
BLEAddress::BLEAddress(ble_addr_t address) {
memcpy(m_address, address.val, ESP_BD_ADDR_LEN);
m_addrType = address.type;
}
uint8_t BLEAddress::getType() {
return m_addrType;
}
/**
* @brief Create an address from a hex string
*
* A hex string is of the format:
* ```
* 00:00:00:00:00:00
* ```
* which is 17 characters in length.
*
* @param [in] stringAddress The hex representation of the address.
* @param [in] type The address type.
*/
BLEAddress::BLEAddress(String stringAddress, uint8_t type) {
if (stringAddress.length() != 17) {
return;
}
int data[6];
m_addrType = type;
// NimBLE addresses are in INVERSE ORDER!
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]);
for (size_t index = 0; index < sizeof(m_address); index++) {
m_address[index] = (uint8_t)data[index];
}
}
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,19 +3,53 @@
* *
* Created on: Jul 2, 2017 * Created on: Jul 2, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_ #ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_
#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_ #define COMPONENTS_CPP_UTILS_BLEADDRESS_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "WString.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gap_ble_api.h> // ESP32 BLE
/***************************************************************************
* Common includes *
***************************************************************************/
#include "WString.h"
#include <esp_bt.h>
#include <string> #include <string>
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_ble_api.h>
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <nimble/ble.h>
#define ESP_BD_ADDR_LEN BLE_DEV_ADDR_LEN
#endif
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN];
#endif
/** /**
* @brief A %BLE device address. * @brief A %BLE device address.
* *
@ -23,8 +57,11 @@
*/ */
class BLEAddress { class BLEAddress {
public: public:
BLEAddress(esp_bd_addr_t address); /***************************************************************************
BLEAddress(String stringAddress); * Common public declarations *
***************************************************************************/
BLEAddress();
bool equals(BLEAddress otherAddress); bool equals(BLEAddress otherAddress);
bool operator==(const BLEAddress &otherAddress) const; bool operator==(const BLEAddress &otherAddress) const;
bool operator!=(const BLEAddress &otherAddress) const; bool operator!=(const BLEAddress &otherAddress) const;
@ -32,13 +69,45 @@ public:
bool operator<=(const BLEAddress &otherAddress) const; bool operator<=(const BLEAddress &otherAddress) const;
bool operator>(const BLEAddress &otherAddress) const; bool operator>(const BLEAddress &otherAddress) const;
bool operator>=(const BLEAddress &otherAddress) const; bool operator>=(const BLEAddress &otherAddress) const;
esp_bd_addr_t *getNative(); uint8_t *getNative();
String toString(); String toString();
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLEAddress(esp_bd_addr_t address);
BLEAddress(String stringAddress);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLEAddress(ble_addr_t address);
BLEAddress(String stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
BLEAddress(uint8_t address[ESP_BD_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC);
uint8_t getType();
#endif
private: private:
esp_bd_addr_t m_address; /***************************************************************************
* Common private properties *
***************************************************************************/
uint8_t m_address[ESP_BD_ADDR_LEN];
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint8_t m_addrType;
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */

View file

@ -4,22 +4,35 @@
* During the scanning procedure, we will be finding advertised BLE devices. This class * During the scanning procedure, we will be finding advertised BLE devices. This class
* models a found device. * models a found device.
* *
*
* See also: * See also:
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
* *
* Created on: Jul 3, 2017 * Created on: Jul 3, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include "BLEAdvertisedDevice.h" #include "BLEAdvertisedDevice.h"
#include "BLEUtils.h" #include "BLEUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/***************************************************************************
* Common functions *
***************************************************************************/
BLEAdvertisedDevice::BLEAdvertisedDevice() { BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_adFlag = 0; m_adFlag = 0;
m_appearance = 0; m_appearance = 0;
@ -32,15 +45,47 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_serviceDataUUIDs = {}; m_serviceDataUUIDs = {};
m_txPower = 0; m_txPower = 0;
m_pScan = nullptr; m_pScan = nullptr;
m_advType = 0;
#if defined(CONFIG_NIMBLE_ENABLED)
m_callbackSent = false;
#endif
m_haveAppearance = false; m_haveAppearance = false;
m_haveManufacturerData = false; m_haveManufacturerData = false;
m_haveName = false; m_haveName = false;
m_haveRSSI = false; m_haveRSSI = false;
m_haveTXPower = false; m_haveTXPower = false;
m_isLegacyAdv = true;
} // BLEAdvertisedDevice } // BLEAdvertisedDevice
bool BLEAdvertisedDevice::isLegacyAdvertisement() {
return m_isLegacyAdv;
}
bool BLEAdvertisedDevice::isScannable() {
#if defined(CONFIG_BLUEDROID_ENABLED)
return isLegacyAdvertisement() && (m_advType == ESP_BLE_EVT_CONN_ADV || m_advType == ESP_BLE_EVT_DISC_ADV);
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
return isLegacyAdvertisement() && (m_advType == BLE_HCI_ADV_TYPE_ADV_IND || m_advType == BLE_HCI_ADV_TYPE_ADV_SCAN_IND);
#endif
}
bool BLEAdvertisedDevice::isConnectable() {
#if defined(CONFIG_BLUEDROID_ENABLED)
return m_advType == ESP_BLE_EVT_CONN_ADV || m_advType == ESP_BLE_EVT_CONN_DIR_ADV;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_isLegacyAdv) {
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
}
return (m_advType & BLE_HCI_ADV_CONN_MASK) || (m_advType & BLE_HCI_ADV_DIRECT_MASK);
#endif
}
/** /**
* @brief Get the address. * @brief Get the address.
* *
@ -277,75 +322,75 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len)
length--; length--;
char *pHex = BLEUtils::buildHexData(nullptr, payload, length); char *pHex = BLEUtils::buildHexData(nullptr, payload, length);
log_d("Type: 0x%.2x (%s), length: %d, data: %s", ad_type, BLEUtils::advTypeToString(ad_type), length, pHex); log_d("Type: 0x%.2x (%s), length: %d, data: %s", ad_type, BLEUtils::advDataTypeToString(ad_type), length, pHex);
free(pHex); free(pHex);
switch (ad_type) { switch (ad_type) {
case ESP_BLE_AD_TYPE_NAME_CMPL: case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09
{ // Adv Data Type: 0x09 { // Adv Data Type: ESP_BLE_AD_TYPE_NAME_CMPL
setName(String(reinterpret_cast<char *>(payload), length)); setName(String(reinterpret_cast<char *>(payload), length));
break; break;
} // ESP_BLE_AD_TYPE_NAME_CMPL } // 0x09
case ESP_BLE_AD_TYPE_TX_PWR: case ESP_BLE_AD_TYPE_TX_PWR: // 0x0A
{ // Adv Data Type: 0x0A { // Adv Data Type: ESP_BLE_AD_TYPE_TX_PWR
setTXPower(*payload); setTXPower(*payload);
break; break;
} // ESP_BLE_AD_TYPE_TX_PWR } // 0x0A
case ESP_BLE_AD_TYPE_APPEARANCE: case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19
{ // Adv Data Type: 0x19 { // Adv Data Type: ESP_BLE_AD_TYPE_APPEARANCE
setAppearance(*reinterpret_cast<uint16_t *>(payload)); setAppearance(*reinterpret_cast<uint16_t *>(payload));
break; break;
} // ESP_BLE_AD_TYPE_APPEARANCE } // 0x19
case ESP_BLE_AD_TYPE_FLAG: case ESP_BLE_AD_TYPE_FLAG: // 0x01
{ // Adv Data Type: 0x01 { // Adv Data Type: ESP_BLE_AD_TYPE_FLAG
setAdFlag(*payload); setAdFlag(*payload);
break; break;
} // ESP_BLE_AD_TYPE_FLAG } // 0x01
case ESP_BLE_AD_TYPE_16SRV_CMPL: case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02
case ESP_BLE_AD_TYPE_16SRV_PART: case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03
{ // Adv Data Type: 0x02 { // Adv Data Type: ESP_BLE_AD_TYPE_16SRV_PART/CMPL
for (int var = 0; var < length / 2; ++var) { for (int var = 0; var < length / 2; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t *>(payload + var * 2))); setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t *>(payload + var * 2)));
} }
break; break;
} // ESP_BLE_AD_TYPE_16SRV_PART } // 0x02, 0x03
case ESP_BLE_AD_TYPE_32SRV_CMPL: case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04
case ESP_BLE_AD_TYPE_32SRV_PART: case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05
{ // Adv Data Type: 0x04 { // Adv Data Type: ESP_BLE_AD_TYPE_32SRV_PART/CMPL
for (int var = 0; var < length / 4; ++var) { for (int var = 0; var < length / 4; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t *>(payload + var * 4))); setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t *>(payload + var * 4)));
} }
break; break;
} // ESP_BLE_AD_TYPE_32SRV_PART } // 0x04, 0x05
case ESP_BLE_AD_TYPE_128SRV_CMPL: case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07
{ // Adv Data Type: 0x07 { // Adv Data Type: ESP_BLE_AD_TYPE_128SRV_CMPL
setServiceUUID(BLEUUID(payload, 16, false)); setServiceUUID(BLEUUID(payload, 16, false));
break; break;
} // ESP_BLE_AD_TYPE_128SRV_CMPL } // 0x07
case ESP_BLE_AD_TYPE_128SRV_PART: case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06
{ // Adv Data Type: 0x06 { // Adv Data Type: ESP_BLE_AD_TYPE_128SRV_PART
setServiceUUID(BLEUUID(payload, 16, false)); setServiceUUID(BLEUUID(payload, 16, false));
break; break;
} // ESP_BLE_AD_TYPE_128SRV_PART } // 0x06
// See CSS Part A 1.4 Manufacturer Specific Data // See CSS Part A 1.4 Manufacturer Specific Data
case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xFF
{ {
setManufacturerData(String(reinterpret_cast<char *>(payload), length)); setManufacturerData(String(reinterpret_cast<char *>(payload), length));
break; break;
} // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE } // 0xFF
case ESP_BLE_AD_TYPE_SERVICE_DATA: case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16
{ // Adv Data Type: 0x16 (Service Data) - 2 byte UUID { // Adv Data Type: ESP_BLE_AD_TYPE_SERVICE_DATA - 2 byte UUID
if (length < 2) { if (length < 2) {
log_e("Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); log_e("Length too small for SERVICE_DATA");
break; break;
} }
uint16_t uuid = *(uint16_t *)payload; uint16_t uuid = *(uint16_t *)payload;
@ -354,12 +399,12 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len)
setServiceData(String(reinterpret_cast<char *>(payload + 2), length - 2)); setServiceData(String(reinterpret_cast<char *>(payload + 2), length - 2));
} }
break; break;
} //ESP_BLE_AD_TYPE_SERVICE_DATA } // 0x16
case ESP_BLE_AD_TYPE_32SERVICE_DATA: case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20
{ // Adv Data Type: 0x20 (Service Data) - 4 byte UUID { // Adv Data Type: ESP_BLE_AD_TYPE_32SERVICE_DATA - 4 byte UUID
if (length < 4) { if (length < 4) {
log_e("Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); log_e("Length too small for 32SERVICE_DATA");
break; break;
} }
uint32_t uuid = *(uint32_t *)payload; uint32_t uuid = *(uint32_t *)payload;
@ -368,12 +413,12 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len)
setServiceData(String(reinterpret_cast<char *>(payload + 4), length - 4)); setServiceData(String(reinterpret_cast<char *>(payload + 4), length - 4));
} }
break; break;
} //ESP_BLE_AD_TYPE_32SERVICE_DATA } // 0x20
case ESP_BLE_AD_TYPE_128SERVICE_DATA: case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21
{ // Adv Data Type: 0x21 (Service Data) - 16 byte UUID { // Adv Data Type: ESP_BLE_AD_TYPE_128SERVICE_DATA - 16 byte UUID
if (length < 16) { if (length < 16) {
log_e("Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); log_e("Length too small for 128SERVICE_DATA");
break; break;
} }
@ -382,13 +427,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len)
setServiceData(String(reinterpret_cast<char *>(payload + 16), length - 16)); setServiceData(String(reinterpret_cast<char *>(payload + 16), length - 16));
} }
break; break;
} //ESP_BLE_AD_TYPE_32SERVICE_DATA } // 0x21
default: default:
{ {
log_d("Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type); log_d("Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type);
break; break;
} } // default
} // switch } // switch
payload += length; payload += length;
} // Length <> 0 } // Length <> 0
@ -405,9 +450,19 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len)
* @param [in] payload The payload of the advertised device. * @param [in] payload The payload of the advertised device.
* @param [in] total_len The length of payload * @param [in] total_len The length of payload
*/ */
void BLEAdvertisedDevice::setPayload(uint8_t *payload, size_t total_len) { void BLEAdvertisedDevice::setPayload(uint8_t *payload, size_t total_len, bool append) {
m_payload = payload; if (m_payload == nullptr || m_payloadLength == 0) {
m_payloadLength = total_len; return;
}
if (append) {
m_payload = (uint8_t *)realloc(m_payload, m_payloadLength + total_len);
memcpy(m_payload + m_payloadLength, payload, total_len);
m_payloadLength += total_len;
} else {
m_payload = payload;
m_payloadLength = total_len;
}
} // setPayload } // setPayload
/** /**
@ -567,7 +622,7 @@ uint8_t *BLEAdvertisedDevice::getPayload() {
return m_payload; return m_payload;
} }
esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() { uint8_t BLEAdvertisedDevice::getAddressType() {
return m_addressType; return m_addressType;
} }
@ -587,7 +642,7 @@ ble_frame_type_t BLEAdvertisedDevice::getFrameType() {
return BLE_UNKNOWN_FRAME; return BLE_UNKNOWN_FRAME;
} }
void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) { void BLEAdvertisedDevice::setAddressType(uint8_t type) {
m_addressType = type; m_addressType = type;
} }
@ -595,5 +650,13 @@ size_t BLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength; return m_payloadLength;
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ void BLEAdvertisedDevice::setAdvType(uint8_t type) {
m_advType = type;
}
uint8_t BLEAdvertisedDevice::getAdvType() {
return m_advType;
}
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jul 3, 2017 * Created on: Jul 3, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
@ -11,15 +15,38 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gattc_api.h>
/***************************************************************************
* Common includes *
***************************************************************************/
#include <map> #include <map>
#include "BLEAddress.h" #include "BLEAddress.h"
#include "BLEScan.h" #include "BLEScan.h"
#include "BLEUUID.h" #include "BLEUUID.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gattc_api.h>
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <nimble/ble.h>
#include <host/ble_gap.h>
#endif
/***************************************************************************
* Common types *
***************************************************************************/
typedef enum { typedef enum {
BLE_UNKNOWN_FRAME, BLE_UNKNOWN_FRAME,
BLE_EDDYSTONE_UUID_FRAME, BLE_EDDYSTONE_UUID_FRAME,
@ -28,7 +55,12 @@ typedef enum {
BLE_FRAME_MAX BLE_FRAME_MAX
} ble_frame_type_t; } ble_frame_type_t;
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEScan; class BLEScan;
/** /**
* @brief A representation of a %BLE advertised device found by a scan. * @brief A representation of a %BLE advertised device found by a scan.
* *
@ -37,8 +69,11 @@ class BLEScan;
*/ */
class BLEAdvertisedDevice { class BLEAdvertisedDevice {
public: public:
BLEAdvertisedDevice(); /***************************************************************************
* Common public declarations *
***************************************************************************/
BLEAdvertisedDevice();
BLEAddress getAddress(); BLEAddress getAddress();
uint16_t getAppearance(); uint16_t getAppearance();
String getManufacturerData(); String getManufacturerData();
@ -57,10 +92,15 @@ public:
int8_t getTXPower(); int8_t getTXPower();
uint8_t *getPayload(); uint8_t *getPayload();
size_t getPayloadLength(); size_t getPayloadLength();
esp_ble_addr_type_t getAddressType(); uint8_t getAddressType();
ble_frame_type_t getFrameType(); ble_frame_type_t getFrameType();
void setAddressType(esp_ble_addr_type_t type); void setAddressType(uint8_t type);
void setAdvType(uint8_t type);
uint8_t getAdvType();
bool isLegacyAdvertisement();
bool isScannable();
bool isConnectable();
bool isAdvertisingService(BLEUUID uuid); bool isAdvertisingService(BLEUUID uuid);
bool haveAppearance(); bool haveAppearance();
bool haveManufacturerData(); bool haveManufacturerData();
@ -75,28 +115,15 @@ public:
private: private:
friend class BLEScan; friend class BLEScan;
void parseAdvertisement(uint8_t *payload, size_t total_len = 62); /***************************************************************************
void setPayload(uint8_t *payload, size_t total_len = 62); * Common private properties *
void setAddress(BLEAddress address); ***************************************************************************/
void setAdFlag(uint8_t adFlag);
void setAdvertizementResult(uint8_t *payload);
void setAppearance(uint16_t appearance);
void setManufacturerData(String manufacturerData);
void setName(String name);
void setRSSI(int rssi);
void setScan(BLEScan *pScan);
void setServiceData(String data);
void setServiceDataUUID(BLEUUID uuid);
void setServiceUUID(const char *serviceUUID);
void setServiceUUID(BLEUUID serviceUUID);
void setTXPower(int8_t txPower);
bool m_haveAppearance; bool m_haveAppearance;
bool m_haveManufacturerData; bool m_haveManufacturerData;
bool m_haveName; bool m_haveName;
bool m_haveRSSI; bool m_haveRSSI;
bool m_haveTXPower; bool m_haveTXPower;
BLEAddress m_address = BLEAddress((uint8_t *)"\0\0\0\0\0\0"); BLEAddress m_address = BLEAddress((uint8_t *)"\0\0\0\0\0\0");
uint8_t m_adFlag; uint8_t m_adFlag;
uint16_t m_appearance; uint16_t m_appearance;
@ -111,7 +138,37 @@ private:
std::vector<BLEUUID> m_serviceDataUUIDs; std::vector<BLEUUID> m_serviceDataUUIDs;
uint8_t *m_payload; uint8_t *m_payload;
size_t m_payloadLength = 0; size_t m_payloadLength = 0;
esp_ble_addr_type_t m_addressType; uint8_t m_addressType;
uint8_t m_advType;
bool m_isLegacyAdv;
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
bool m_callbackSent;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
void parseAdvertisement(uint8_t *payload, size_t total_len = 62);
void setPayload(uint8_t *payload, size_t total_len = 62, bool append = false);
void setAddress(BLEAddress address);
void setAdFlag(uint8_t adFlag);
void setAdvertizementResult(uint8_t *payload);
void setAppearance(uint16_t appearance);
void setManufacturerData(String manufacturerData);
void setName(String name);
void setRSSI(int rssi);
void setScan(BLEScan *pScan);
void setServiceData(String data);
void setServiceDataUUID(BLEUUID uuid);
void setServiceUUID(const char *serviceUUID);
void setServiceUUID(BLEUUID serviceUUID);
void setTXPower(int8_t txPower);
}; };
/** /**
@ -123,30 +180,55 @@ private:
*/ */
class BLEAdvertisedDeviceCallbacks { class BLEAdvertisedDeviceCallbacks {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLEAdvertisedDeviceCallbacks() {} virtual ~BLEAdvertisedDeviceCallbacks() {}
/** /**
* @brief Called when a new scan result is detected. * @brief Called when a new scan result is detected.
* *
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the * 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. * 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) = 0;
}; };
#ifdef SOC_BLE_50_SUPPORTED #if defined(SOC_BLE_50_SUPPORTED) && defined(CONFIG_BLUEDROID_ENABLED)
class BLEExtAdvertisingCallbacks { class BLEExtAdvertisingCallbacks {
public: public:
virtual ~BLEExtAdvertisingCallbacks() {} /***************************************************************************
/** * Common public declarations *
* @brief Called when a new scan result is detected. ***************************************************************************/
*
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
virtual void onResult(esp_ble_gap_ext_adv_report_t report) = 0;
};
#endif // SOC_BLE_50_SUPPORTED
#endif /* CONFIG_BLUEDROID_ENABLED */ virtual ~BLEExtAdvertisingCallbacks() {}
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
/**
* @brief Called when a new scan result is detected.
*
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
#if defined(CONFIG_BLUEDROID_ENABLED)
virtual void onResult(esp_ble_gap_ext_adv_report_t report) = 0;
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
// Extended advertising for NimBLE is not supported yet.
#if defined(CONFIG_NIMBLE_ENABLED)
virtual void onResult(struct ble_gap_ext_disc_desc report) = 0;
#endif
};
#endif // SOC_BLE_50_SUPPORTED && CONFIG_BLUEDROID_ENABLED
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,10 @@
* *
* Created on: Jun 21, 2017 * Created on: Jun 21, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
@ -11,20 +15,97 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gap_ble_api.h>
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEUUID.h" #include "BLEUUID.h"
#include <vector> #include <vector>
#include "RTOS.h" #include "RTOS.h"
#include "BLEUtils.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_ble_api.h>
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#include <services/gap/ble_svc_gap.h>
#include <host/ble_hs_adv.h>
#define ESP_BLE_ADV_DATA_LEN_MAX BLE_HS_ADV_MAX_SZ
#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0)
#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1)
#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2)
#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3)
#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4)
#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00)
#endif /* CONFIG_NIMBLE_ENABLED */
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef enum {
ESP_BLE_AD_TYPE_FLAG = 0x01,
ESP_BLE_AD_TYPE_16SRV_PART = 0x02,
ESP_BLE_AD_TYPE_16SRV_CMPL = 0x03,
ESP_BLE_AD_TYPE_32SRV_PART = 0x04,
ESP_BLE_AD_TYPE_32SRV_CMPL = 0x05,
ESP_BLE_AD_TYPE_128SRV_PART = 0x06,
ESP_BLE_AD_TYPE_128SRV_CMPL = 0x07,
ESP_BLE_AD_TYPE_NAME_SHORT = 0x08,
ESP_BLE_AD_TYPE_NAME_CMPL = 0x09,
ESP_BLE_AD_TYPE_TX_PWR = 0x0A,
ESP_BLE_AD_TYPE_DEV_CLASS = 0x0D,
ESP_BLE_AD_TYPE_SM_TK = 0x10,
ESP_BLE_AD_TYPE_SM_OOB_FLAG = 0x11,
ESP_BLE_AD_TYPE_INT_RANGE = 0x12,
ESP_BLE_AD_TYPE_SOL_SRV_UUID = 0x14,
ESP_BLE_AD_TYPE_128SOL_SRV_UUID = 0x15,
ESP_BLE_AD_TYPE_SERVICE_DATA = 0x16,
ESP_BLE_AD_TYPE_PUBLIC_TARGET = 0x17,
ESP_BLE_AD_TYPE_RANDOM_TARGET = 0x18,
ESP_BLE_AD_TYPE_APPEARANCE = 0x19,
ESP_BLE_AD_TYPE_ADV_INT = 0x1A,
ESP_BLE_AD_TYPE_LE_DEV_ADDR = 0x1b,
ESP_BLE_AD_TYPE_LE_ROLE = 0x1c,
ESP_BLE_AD_TYPE_SPAIR_C256 = 0x1d,
ESP_BLE_AD_TYPE_SPAIR_R256 = 0x1e,
ESP_BLE_AD_TYPE_32SOL_SRV_UUID = 0x1f,
ESP_BLE_AD_TYPE_32SERVICE_DATA = 0x20,
ESP_BLE_AD_TYPE_128SERVICE_DATA = 0x21,
ESP_BLE_AD_TYPE_LE_SECURE_CONFIRM = 0x22,
ESP_BLE_AD_TYPE_LE_SECURE_RANDOM = 0x23,
ESP_BLE_AD_TYPE_URI = 0x24,
ESP_BLE_AD_TYPE_INDOOR_POSITION = 0x25,
ESP_BLE_AD_TYPE_TRANS_DISC_DATA = 0x26,
ESP_BLE_AD_TYPE_LE_SUPPORT_FEATURE = 0x27,
ESP_BLE_AD_TYPE_CHAN_MAP_UPDATE = 0x28,
ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF,
} esp_ble_adv_data_type;
#endif
/** /**
* @brief Advertisement data set by the programmer to be published by the %BLE server. * @brief Advertisement data set by the programmer to be published by the %BLE server.
*/ */
class BLEAdvertisementData { class BLEAdvertisementData {
// Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will
// be exposed on demand/request or as time permits.
//
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setCompleteServices(BLEUUID uuid); void setCompleteServices(BLEUUID uuid);
void setFlags(uint8_t); void setFlags(uint8_t);
@ -33,85 +114,144 @@ public:
void setPartialServices(BLEUUID uuid); void setPartialServices(BLEUUID uuid);
void setServiceData(BLEUUID uuid, String data); void setServiceData(BLEUUID uuid, String data);
void setShortName(String name); void setShortName(String name);
void addData(String data); // Add data to the payload. void setPreferredParams(uint16_t min, uint16_t max);
String getPayload(); // Retrieve the current advert payload. void addTxPower();
void addData(String data);
void addData(char *data, size_t length);
String getPayload();
private: private:
friend class BLEAdvertising; friend class BLEAdvertising;
String m_payload; // The payload of the advertisement.
}; // BLEAdvertisementData /***************************************************************************
* Common private declarations *
***************************************************************************/
String m_payload;
};
/** /**
* @brief Perform and manage %BLE advertising. * @brief Perform and manage %BLE advertising.
*
* A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
*/ */
class BLEAdvertising { class BLEAdvertising {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEAdvertising(); BLEAdvertising();
void addServiceUUID(BLEUUID serviceUUID); void addServiceUUID(BLEUUID serviceUUID);
void addServiceUUID(const char *serviceUUID); void addServiceUUID(const char *serviceUUID);
bool removeServiceUUID(int index); bool removeServiceUUID(int index);
bool removeServiceUUID(BLEUUID serviceUUID); bool removeServiceUUID(BLEUUID serviceUUID);
bool removeServiceUUID(const char *serviceUUID); bool removeServiceUUID(const char *serviceUUID);
bool start();
bool stop(); bool stop();
void reset();
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setAdvertisementType(esp_ble_adv_type_t adv_type); void setAdvertisementType(uint8_t adv_type);
void setAdvertisementChannelMap(esp_ble_adv_channel_t channel_map);
void setMaxInterval(uint16_t maxinterval); void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval); void setMinInterval(uint16_t mininterval);
bool setAdvertisementData(BLEAdvertisementData &advertisementData); bool setAdvertisementData(BLEAdvertisementData &advertisementData);
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
bool setScanResponseData(BLEAdvertisementData &advertisementData); bool setScanResponseData(BLEAdvertisementData &advertisementData);
void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
bool setDeviceAddress(esp_bd_addr_t addr, 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 setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t); void setMaxPreferred(uint16_t);
void setScanResponse(bool); void setScanResponse(bool);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
bool setDeviceAddress(esp_bd_addr_t addr, esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
void setAdvertisementChannelMap(esp_ble_adv_channel_t channel_map);
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
bool start();
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void setName(String name);
void addTxPower();
void advCompleteCB();
bool isAdvertising();
void onHostSync();
bool start(uint32_t duration = 0, void (*advCompleteCB)(BLEAdvertising *pAdv) = nullptr);
static int handleGAPEvent(ble_gap_event *event, void *arg);
#endif
private: private:
esp_ble_adv_data_t m_advData; /***************************************************************************
esp_ble_adv_data_t m_scanRespData; // Used for configuration of scan response data when m_scanResp is true * Common private properties *
esp_ble_adv_params_t m_advParams; ***************************************************************************/
std::vector<BLEUUID> m_serviceUUIDs; std::vector<BLEUUID> m_serviceUUIDs;
bool m_customAdvData = false; // Are we using custom advertising data? bool m_customAdvData = false;
bool m_customScanResponseData = false; // Are we using custom scan response data? bool m_customScanResponseData = false;
FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert");
bool m_scanResp = true; bool m_scanResp = true;
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_adv_data_t m_advData;
esp_ble_adv_data_t m_scanRespData;
esp_ble_adv_params_t m_advParams;
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
ble_hs_adv_fields m_advData;
ble_hs_adv_fields m_scanData;
ble_gap_adv_params m_advParams;
bool m_advDataSet;
void (*m_advCompCB)(BLEAdvertising *pAdv);
uint8_t m_slaveItvl[4];
uint32_t m_duration;
String m_name;
#endif
}; };
#ifdef SOC_BLE_50_SUPPORTED /***************************************************************************
* Bluedroid 5.0 specific classes *
***************************************************************************/
#if defined(SOC_BLE_50_SUPPORTED) && defined(CONFIG_BLUEDROID_ENABLED)
class BLEMultiAdvertising { class BLEMultiAdvertising {
private:
esp_ble_gap_ext_adv_params_t *params_arrays;
esp_ble_gap_ext_adv_t *ext_adv;
uint8_t count;
public: public:
BLEMultiAdvertising(uint8_t num = 1); BLEMultiAdvertising(uint8_t num = 1);
~BLEMultiAdvertising() {} ~BLEMultiAdvertising() {}
bool setAdvertisingParams(uint8_t instance, const esp_ble_gap_ext_adv_params_t *params);
bool setAdvertisingData(uint8_t instance, uint16_t length, const uint8_t *data); bool setAdvertisingData(uint8_t instance, uint16_t length, const uint8_t *data);
bool setScanRspData(uint8_t instance, uint16_t length, const uint8_t *data); bool setScanRspData(uint8_t instance, uint16_t length, const uint8_t *data);
bool start(); bool start();
bool start(uint8_t num, uint8_t from); bool start(uint8_t num, uint8_t from);
void setDuration(uint8_t instance, int duration = 0, int max_events = 0); void setDuration(uint8_t instance, int duration = 0, int max_events = 0);
bool setInstanceAddress(uint8_t instance, esp_bd_addr_t rand_addr); bool setInstanceAddress(uint8_t instance, uint8_t *rand_addr);
bool stop(uint8_t num_adv, const uint8_t *ext_adv_inst); bool stop(uint8_t num_adv, const uint8_t *ext_adv_inst);
bool remove(uint8_t instance); bool remove(uint8_t instance);
bool clear(); bool clear();
bool setPeriodicAdvertisingParams(uint8_t instance, const esp_ble_gap_periodic_adv_params_t *params);
bool setPeriodicAdvertisingData(uint8_t instance, uint16_t length, const uint8_t *data); bool setPeriodicAdvertisingData(uint8_t instance, uint16_t length, const uint8_t *data);
bool startPeriodicAdvertising(uint8_t instance); bool startPeriodicAdvertising(uint8_t instance);
bool setAdvertisingParams(uint8_t instance, const esp_ble_gap_ext_adv_params_t *params);
bool setPeriodicAdvertisingParams(uint8_t instance, const esp_ble_gap_periodic_adv_params_t *params);
private:
esp_ble_gap_ext_adv_params_t *params_arrays;
esp_ble_gap_ext_adv_t *ext_adv;
uint8_t count;
}; };
#endif
#endif // SOC_BLE_50_SUPPORTED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* CONFIG_BLUEDROID_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */

View file

@ -3,17 +3,31 @@
* *
* Created on: Jan 4, 2018 * Created on: Jan 4, 2018
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes and definitions *
***************************************************************************/
#include "BLEBeacon.h" #include "BLEBeacon.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#define ENDIAN_CHANGE_U16(x) ((((x) & 0xFF00) >> 8) + (((x) & 0xFF) << 8)) #define ENDIAN_CHANGE_U16(x) ((((x) & 0xFF00) >> 8) + (((x) & 0xFF) << 8))
/***************************************************************************
* Common functions *
***************************************************************************/
BLEBeacon::BLEBeacon() { BLEBeacon::BLEBeacon() {
m_beaconData.manufacturerId = 0x4c00; m_beaconData.manufacturerId = 0x4c00;
m_beaconData.subType = 0x02; m_beaconData.subType = 0x02;
@ -22,11 +36,11 @@ BLEBeacon::BLEBeacon() {
m_beaconData.minor = 0; m_beaconData.minor = 0;
m_beaconData.signalPower = 0; m_beaconData.signalPower = 0;
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
} // BLEBeacon }
String BLEBeacon::getData() { String BLEBeacon::getData() {
return String((char *)&m_beaconData, sizeof(m_beaconData)); return String((char *)&m_beaconData, sizeof(m_beaconData));
} // getData }
uint16_t BLEBeacon::getMajor() { uint16_t BLEBeacon::getMajor() {
return m_beaconData.major; return m_beaconData.major;
@ -48,37 +62,38 @@ int8_t BLEBeacon::getSignalPower() {
return m_beaconData.signalPower; return m_beaconData.signalPower;
} }
/**
* Set the raw data for the beacon record.
*/
void BLEBeacon::setData(String data) { void BLEBeacon::setData(String data) {
if (data.length() != sizeof(m_beaconData)) { if (data.length() != sizeof(m_beaconData)) {
log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData));
return; return;
} }
memcpy(&m_beaconData, data.c_str(), sizeof(m_beaconData)); memcpy(&m_beaconData, data.c_str(), sizeof(m_beaconData));
} // setData }
void BLEBeacon::setMajor(uint16_t major) { void BLEBeacon::setMajor(uint16_t major) {
m_beaconData.major = ENDIAN_CHANGE_U16(major); m_beaconData.major = ENDIAN_CHANGE_U16(major);
} // setMajor }
void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { void BLEBeacon::setManufacturerId(uint16_t manufacturerId) {
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
} // setManufacturerId }
void BLEBeacon::setMinor(uint16_t minor) { void BLEBeacon::setMinor(uint16_t minor) {
m_beaconData.minor = ENDIAN_CHANGE_U16(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) { void BLEBeacon::setSignalPower(int8_t signalPower) {
m_beaconData.signalPower = signalPower; m_beaconData.signalPower = signalPower;
} // setSignalPower }
void BLEBeacon::setProximityUUID(BLEUUID uuid) {
uuid = uuid.to128();
#if defined(CONFIG_BLUEDROID_ENABLED)
memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16);
#elif defined(CONFIG_NIMBLE_ENABLED)
memcpy(m_beaconData.proximityUUID, uuid.getNative()->u128.value, 16);
#endif #endif
}
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jan 4, 2018 * Created on: Jan 4, 2018
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ #ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_
@ -10,6 +14,13 @@
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEUUID.h" #include "BLEUUID.h"
/** /**
* @brief Representation of a beacon. * @brief Representation of a beacon.
@ -18,6 +29,10 @@
*/ */
class BLEBeacon { class BLEBeacon {
private: private:
/***************************************************************************
* Common types *
***************************************************************************/
struct { struct {
uint16_t manufacturerId; uint16_t manufacturerId;
uint8_t subType; uint8_t subType;
@ -29,6 +44,10 @@ private:
} __attribute__((packed)) m_beaconData; } __attribute__((packed)) m_beaconData;
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEBeacon(); BLEBeacon();
String getData(); String getData();
uint16_t getMajor(); uint16_t getMajor();
@ -44,5 +63,6 @@ public:
void setSignalPower(int8_t signalPower); void setSignalPower(int8_t signalPower);
}; // BLEBeacon }; // BLEBeacon
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */

View file

@ -3,12 +3,22 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include <string.h> #include <string.h>
#include <iomanip> #include <iomanip>
@ -23,10 +33,31 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/***************************************************************************
* Common definitions *
***************************************************************************/
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
/***************************************************************************
* NimBLE definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#define NIMBLE_SUB_NOTIFY 0x0001
#define NIMBLE_SUB_INDICATE 0x0002
#endif
/***************************************************************************
* Common global variables *
***************************************************************************/
static BLECharacteristicCallbacks defaultCallback; //null-object-pattern static BLECharacteristicCallbacks defaultCallback; //null-object-pattern
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] uuid - UUID (const char*) for the characteristic.
@ -42,15 +73,23 @@ BLECharacteristic::BLECharacteristic(const char *uuid, uint32_t properties) : BL
BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
m_bleUUID = uuid; m_bleUUID = uuid;
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_properties = (esp_gatt_char_prop_t)0;
m_pCallbacks = &defaultCallback; m_pCallbacks = &defaultCallback;
setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); #ifdef CONFIG_BLUEDROID_ENABLED
setReadProperty((properties & PROPERTY_READ) != 0); m_properties = 0;
setWriteProperty((properties & PROPERTY_WRITE) != 0); setBroadcastProperty((properties & BLECharacteristic::PROPERTY_BROADCAST) != 0);
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); setReadProperty((properties & BLECharacteristic::PROPERTY_READ) != 0);
setIndicateProperty((properties & PROPERTY_INDICATE) != 0); setWriteProperty((properties & BLECharacteristic::PROPERTY_WRITE) != 0);
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); setNotifyProperty((properties & BLECharacteristic::PROPERTY_NOTIFY) != 0);
setIndicateProperty((properties & BLECharacteristic::PROPERTY_INDICATE) != 0);
setWriteNoResponseProperty((properties & BLECharacteristic::PROPERTY_WRITE_NR) != 0);
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_readMux = portMUX_INITIALIZER_UNLOCKED;
m_removed = 0;
m_properties = properties;
#endif
} // BLECharacteristic } // BLECharacteristic
/** /**
@ -66,51 +105,20 @@ BLECharacteristic::~BLECharacteristic() {
* @return N/A. * @return N/A.
*/ */
void BLECharacteristic::addDescriptor(BLEDescriptor *pDescriptor) { void BLECharacteristic::addDescriptor(BLEDescriptor *pDescriptor) {
#ifdef CONFIG_NIMBLE_ENABLED
if (pDescriptor->getUUID() == BLEUUID(uint16_t(0x2902))) {
log_i("NimBLE automatically creates the 0x2902 descriptor if a characteristic has a notification or indication property assigned to it.\n"
"You should check the characteristic properties for notification or indication rather than adding the descriptor manually.\n"
"This will be removed in a future version of the library.");
pDescriptor->executeCreate(this);
return;
}
#endif
log_v(">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); log_v(">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
log_v("<< addDescriptor()"); log_v("<< addDescriptor()");
} // addDescriptor } // addDescriptor
/**
* @brief Register a new characteristic with the ESP runtime.
* @param [in] pService The service with which to associate this characteristic.
*/
void BLECharacteristic::executeCreate(BLEService *pService) {
log_v(">> executeCreate()");
if (m_handle != NULL_HANDLE) {
log_e("Characteristic already has a handle.");
return;
}
m_pService = pService; // Save the service to which this characteristic belongs.
log_d("Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", getUUID().toString().c_str(), m_pService->toString().c_str());
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP;
m_semaphoreCreateEvt.take("executeCreate");
esp_err_t errRc = ::esp_ble_gatts_add_char(
m_pService->getHandle(), getUUID().getNative(), static_cast<esp_gatt_perm_t>(m_permissions), getProperties(), nullptr,
&control
); // Whether to auto respond or not.
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreCreateEvt.wait("executeCreate");
BLEDescriptor *pDescriptor = m_descriptorMap.getFirst();
while (pDescriptor != nullptr) {
pDescriptor->executeCreate(this);
pDescriptor = m_descriptorMap.getNext();
} // End while
log_v("<< executeCreate");
} // executeCreate
/** /**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
@ -137,8 +145,10 @@ uint16_t BLECharacteristic::getHandle() {
return m_handle; return m_handle;
} // getHandle } // getHandle
void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) { void BLECharacteristic::setAccessPermissions(uint8_t perm) {
#ifdef CONFIG_BLUEDROID_ENABLED
m_permissions = perm; m_permissions = perm;
#endif
} }
esp_gatt_char_prop_t BLECharacteristic::getProperties() { esp_gatt_char_prop_t BLECharacteristic::getProperties() {
@ -184,6 +194,308 @@ size_t BLECharacteristic::getLength() {
return m_value.getLength(); return m_value.getLength();
} // getLength } // getLength
/**
* @brief Register a new characteristic with the ESP runtime.
* @param [in] pService The service with which to associate this characteristic.
*/
void BLECharacteristic::executeCreate(BLEService *pService) {
log_v(">> executeCreate()");
if (m_handle != NULL_HANDLE) {
log_e("Characteristic already has a handle.");
return;
}
m_pService = pService; // Save the service to which this characteristic belongs.
#ifdef CONFIG_BLUEDROID_ENABLED
log_d("Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", getUUID().toString().c_str(), m_pService->toString().c_str());
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP;
m_semaphoreCreateEvt.take("executeCreate");
esp_err_t errRc = ::esp_ble_gatts_add_char(
m_pService->getHandle(), getUUID().getNative(), static_cast<esp_gatt_perm_t>(m_permissions), getProperties(), nullptr,
&control
); // Whether to auto respond or not.
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreCreateEvt.wait("executeCreate");
#endif
BLEDescriptor *pDescriptor = m_descriptorMap.getFirst();
while (pDescriptor != nullptr) {
pDescriptor->executeCreate(this);
pDescriptor = m_descriptorMap.getNext();
} // End while
log_v("<< executeCreate");
} // executeCreate
/**
* @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
* will block waiting a positive confirmation from the client.
* @return N/A
*/
void BLECharacteristic::indicate() {
log_v(">> indicate: length: %d", m_value.getValue().length());
notify(false);
log_v("<< indicate");
} // indicate
/**
* @brief Set the permission to broadcast.
* A characteristics has properties associated with it which define what it is capable of doing.
* One of these is the broadcast flag.
* @param [in] value The flag value of the property.
* @return N/A
*/
void BLECharacteristic::setBroadcastProperty(bool value) {
//log_d("setBroadcastProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
}
} // setBroadcastProperty
/**
* @brief Set the callback handlers for this characteristic.
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
*/
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks *pCallbacks) {
log_v(">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallback;
}
log_v("<< setCallbacks");
} // setCallbacks
/**
* @brief Set the BLE handle associated with this characteristic.
* A user program will request that a characteristic be created against a service. When the characteristic has been
* registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
* server/service but it is told to the service, not the characteristic associated with the service. This internally
* exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
* to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
* that will be propagated down to it which contain a handle value and now know that the event is destined for it.
* @param [in] handle The handle associated with this characteristic.
*/
void BLECharacteristic::setHandle(uint16_t handle) {
#if defined(CONFIG_BLUEDROID_ENABLED)
log_v(">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
m_handle = handle;
log_v("<< setHandle");
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
log_w("NimBLE does not support manually setting the handle of a characteristic. Ignoring request.");
#endif
} // setHandle
/**
* @brief Set the Indicate property value.
* @param [in] value Set to true if we are to allow indicate messages.
*/
void BLECharacteristic::setIndicateProperty(bool value) {
//log_d("setIndicateProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
}
} // setIndicateProperty
/**
* @brief Set the Notify property value.
* @param [in] value Set to true if we are to allow notification messages.
*/
void BLECharacteristic::setNotifyProperty(bool value) {
//log_d("setNotifyProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
}
} // setNotifyProperty
/**
* @brief Set the Read property value.
* @param [in] value Set to true if we are to allow reads.
*/
void BLECharacteristic::setReadProperty(bool value) {
//log_d("setReadProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
}
} // setReadProperty
/**
* @brief Set the value of the characteristic.
* @param [in] data The data to set for the characteristic.
* @param [in] length The length of the data in bytes.
*/
void BLECharacteristic::setValue(uint8_t *data, size_t length) {
// The call to BLEUtils::buildHexData() doesn't output anything if the log level is not
// "VERBOSE". As it is quite CPU intensive, it is much better to not call it if not needed.
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
char *pHex = BLEUtils::buildHexData(nullptr, data, length);
log_v(">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);
#endif
if (length > ESP_GATT_MAX_ATTR_LEN) {
log_e("Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
return;
}
m_semaphoreSetValue.take();
m_value.setValue(data, length);
m_semaphoreSetValue.give();
log_v("<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from string data.
* We set the value of the characteristic from the bytes contained in the
* string.
* @param [in] Set the value of the characteristic.
* @return N/A.
*/
void BLECharacteristic::setValue(String value) {
setValue((uint8_t *)(value.c_str()), 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) {
float temp = data32;
setValue((uint8_t *)&temp, 4);
} // setValue
void BLECharacteristic::setValue(double &data64) {
double temp = data64;
setValue((uint8_t *)&temp, 8);
} // setValue
/**
* @brief Set the Write No Response property value.
* @param [in] value Set to true if we are to allow writes with no response.
*/
void BLECharacteristic::setWriteNoResponseProperty(bool value) {
//log_d("setWriteNoResponseProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
}
} // setWriteNoResponseProperty
/**
* @brief Set the Write property value.
* @param [in] value Set to true if we are to allow writes.
*/
void BLECharacteristic::setWriteProperty(bool value) {
//log_d("setWriteProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
}
} // setWriteProperty
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
String BLECharacteristic::toString() {
String res = "UUID: " + m_bleUUID.toString() + ", handle : 0x";
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
res += hex;
res += " ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_READ) {
res += "Read ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) {
res += "Write ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) {
res += "WriteNoResponse ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) {
res += "Broadcast ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) {
res += "Notify ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) {
res += "Indicate ";
}
return res;
} // toString
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
// Common callbacks
void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) {
log_d(">> onRead: default");
log_d("<< onRead");
} // onRead
void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) {
log_d(">> onWrite: default");
log_d("<< onWrite");
} // onWrite
void BLECharacteristicCallbacks::onNotify(BLECharacteristic *pCharacteristic) {
log_d(">> onNotify: default");
log_d("<< onNotify");
} // onNotify
void BLECharacteristicCallbacks::onStatus(BLECharacteristic *pCharacteristic, Status s, uint32_t code) {
log_d(">> onStatus: default");
log_d("<< onStatus");
} // onStatus
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/** /**
* Handle a GATT server event. * Handle a GATT server event.
*/ */
@ -452,19 +764,6 @@ void BLECharacteristic::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_ga
log_v("<< handleGATTServerEvent"); log_v("<< handleGATTServerEvent");
} // handleGATTServerEvent } // handleGATTServerEvent
/**
* @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
* will block waiting a positive confirmation from the client.
* @return N/A
*/
void BLECharacteristic::indicate() {
log_v(">> indicate: length: %d", m_value.getValue().length());
notify(false);
log_v("<< indicate");
} // indicate
/** /**
* @brief Send a notify. * @brief Send a notify.
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
@ -554,246 +853,287 @@ void BLECharacteristic::notify(bool is_notification) {
log_v("<< notify"); log_v("<< notify");
} // Notify } // Notify
/**
* @brief Set the permission to broadcast.
* A characteristics has properties associated with it which define what it is capable of doing.
* One of these is the broadcast flag.
* @param [in] value The flag value of the property.
* @return N/A
*/
void BLECharacteristic::setBroadcastProperty(bool value) {
//log_d("setBroadcastProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
}
} // setBroadcastProperty
/**
* @brief Set the callback handlers for this characteristic.
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
*/
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks *pCallbacks) {
log_v(">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
if (pCallbacks != nullptr) {
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallback;
}
log_v("<< setCallbacks");
} // setCallbacks
/**
* @brief Set the BLE handle associated with this characteristic.
* A user program will request that a characteristic be created against a service. When the characteristic has been
* registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
* server/service but it is told to the service, not the characteristic associated with the service. This internally
* exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
* to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
* that will be propagated down to it which contain a handle value and now know that the event is destined for it.
* @param [in] handle The handle associated with this characteristic.
*/
void BLECharacteristic::setHandle(uint16_t handle) {
log_v(">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
m_handle = handle;
log_v("<< setHandle");
} // setHandle
/**
* @brief Set the Indicate property value.
* @param [in] value Set to true if we are to allow indicate messages.
*/
void BLECharacteristic::setIndicateProperty(bool value) {
//log_d("setIndicateProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
}
} // setIndicateProperty
/**
* @brief Set the Notify property value.
* @param [in] value Set to true if we are to allow notification messages.
*/
void BLECharacteristic::setNotifyProperty(bool value) {
//log_d("setNotifyProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
}
} // setNotifyProperty
/**
* @brief Set the Read property value.
* @param [in] value Set to true if we are to allow reads.
*/
void BLECharacteristic::setReadProperty(bool value) {
//log_d("setReadProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
}
} // setReadProperty
/**
* @brief Set the value of the characteristic.
* @param [in] data The data to set for the characteristic.
* @param [in] length The length of the data in bytes.
*/
void BLECharacteristic::setValue(uint8_t *data, size_t length) {
// The call to BLEUtils::buildHexData() doesn't output anything if the log level is not
// "VERBOSE". As it is quite CPU intensive, it is much better to not call it if not needed.
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
char *pHex = BLEUtils::buildHexData(nullptr, data, length);
log_v(">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);
#endif
if (length > ESP_GATT_MAX_ATTR_LEN) {
log_e("Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
return;
}
m_semaphoreSetValue.take();
m_value.setValue(data, length);
m_semaphoreSetValue.give();
log_v("<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from string data.
* We set the value of the characteristic from the bytes contained in the
* string.
* @param [in] Set the value of the characteristic.
* @return N/A.
*/
void BLECharacteristic::setValue(String value) {
setValue((uint8_t *)(value.c_str()), 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) {
float temp = data32;
setValue((uint8_t *)&temp, 4);
} // setValue
void BLECharacteristic::setValue(double &data64) {
double temp = data64;
setValue((uint8_t *)&temp, 8);
} // setValue
/**
* @brief Set the Write No Response property value.
* @param [in] value Set to true if we are to allow writes with no response.
*/
void BLECharacteristic::setWriteNoResponseProperty(bool value) {
//log_d("setWriteNoResponseProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
}
} // setWriteNoResponseProperty
/**
* @brief Set the Write property value.
* @param [in] value Set to true if we are to allow writes.
*/
void BLECharacteristic::setWriteProperty(bool value) {
//log_d("setWriteProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
}
} // setWriteProperty
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
String BLECharacteristic::toString() {
String res = "UUID: " + m_bleUUID.toString() + ", handle : 0x";
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
res += hex;
res += " ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_READ) {
res += "Read ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) {
res += "Write ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) {
res += "WriteNoResponse ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) {
res += "Broadcast ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) {
res += "Notify ";
}
if (m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) {
res += "Indicate ";
}
return res;
} // toString
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param) { void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param) {
onRead(pCharacteristic); onRead(pCharacteristic);
} // onRead } // onRead
void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) {
log_d(">> onRead: default");
log_d("<< onRead");
} // onRead
void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param) { void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param) {
onWrite(pCharacteristic); onWrite(pCharacteristic);
} // onWrite } // onWrite
void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) { #endif /* CONFIG_BLUEDROID_ENABLED */
log_d(">> onWrite: default");
log_d("<< onWrite"); /***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
const ble_uuid_t *uuid;
int rc;
struct ble_gap_conn_desc desc;
BLECharacteristic *pCharacteristic = (BLECharacteristic *)arg;
log_d("Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(), ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");
uuid = ctxt->chr->uuid;
if (ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0) {
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
{
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if (ctxt->om->om_pkthdr_len > 8) {
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
}
portENTER_CRITICAL(&pCharacteristic->m_readMux);
rc = os_mbuf_append(ctxt->om, (uint8_t *)pCharacteristic->m_value.getValue().c_str(), pCharacteristic->m_value.getValue().length());
portEXIT_CRITICAL(&pCharacteristic->m_readMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR:
{
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data, len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while (next != NULL) {
if ((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
return 0;
}
default: break;
}
}
return BLE_ATT_ERR_UNLIKELY;
//m_descriptorMap.handleGATTServerEvent(conn_handle, attr_handle, ctxt, arg);
}
/**
* @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status.
*/
void BLECharacteristic::setSubscribe(struct ble_gap_event *event) {
ble_gap_conn_desc desc;
if (ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
return;
}
uint16_t subVal = 0;
if (event->subscribe.cur_notify > 0 && (m_properties & BLECharacteristic::PROPERTY_NOTIFY)) {
subVal |= NIMBLE_SUB_NOTIFY;
}
if (event->subscribe.cur_indicate && (m_properties & BLECharacteristic::PROPERTY_INDICATE)) {
subVal |= NIMBLE_SUB_INDICATE;
}
log_i("New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal);
if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
BLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle);
}
auto it = m_subscribedVec.begin();
for (; it != m_subscribedVec.end(); ++it) {
if ((*it).first == event->subscribe.conn_handle) {
break;
}
}
if (subVal > 0) {
if (it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
} else {
(*it).second = subVal;
}
} else if (it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
}
m_pCallbacks->onSubscribe(this, &desc, subVal);
}
/**
* @brief Send a notify.
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
* will not block; it is a fire and forget.
* @return N/A.
*/
void BLECharacteristic::notify(bool is_notification) {
log_v(">> notify: length: %d", m_value.getValue().length());
assert(getService() != nullptr);
assert(getService()->getServer() != nullptr);
int rc = 0;
m_pCallbacks->onNotify(this); // Invoke the notify callback.
// GeneralUtils::hexDump() doesn't output anything if the log level is not
// "VERBOSE". Additionally, it is very CPU intensive, even when it doesn't
// output anything! So it is much better to *not* call it at all if not needed.
// In a simple program which calls BLECharacteristic::notify() every 50 ms,
// the performance gain of this little optimization is 37% in release mode
// (-O3) and 57% in debug mode.
// Of course, the "#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE" guard
// could also be put inside the GeneralUtils::hexDump() function itself. But
// it's better to put it here also, as it is clearer (indicating a verbose log
// thing) and it allows to remove the "m_value.getValue().c_str()" call, which
// is, in itself, quite CPU intensive.
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
GeneralUtils::hexDump((uint8_t *)m_value.getValue().c_str(), m_value.getValue().length());
#endif
if (getService()->getServer()->getConnectedCount() == 0) {
log_v("<< notify: No connected clients.");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_CLIENT, 0);
return;
}
if (m_subscribedVec.size() == 0) {
log_v("<< notify: No clients subscribed.");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_SUBSCRIBER, 0);
return;
}
if (is_notification) {
if (!(m_properties & BLECharacteristic::PROPERTY_NOTIFY)) {
log_v("<< notifications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NOTIFY_DISABLED, 0); // Invoke the notify callback.
return;
}
} else {
if (!(m_properties & BLECharacteristic::PROPERTY_INDICATE)) {
log_v("<< indications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED, 0); // Invoke the notify callback.
return;
}
}
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || (m_properties & BLE_GATT_CHR_F_READ_AUTHOR) || (m_properties & BLE_GATT_CHR_F_READ_ENC);
for (auto &myPair : m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(myPair.first);
// check if connected and subscribed
if (_mtu == 0 || myPair.second == 0) {
continue;
}
if (reqSec) {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(myPair.first, &desc);
if (rc != 0 || !desc.sec_state.encrypted) {
continue;
}
}
String value = getValue();
size_t length = value.length();
if (length > _mtu - 3) {
log_w("- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
if (is_notification && (!(myPair.second & NIMBLE_SUB_NOTIFY))) {
log_w("Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
if (!is_notification && (!(myPair.second & NIMBLE_SUB_INDICATE))) {
log_w("Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
}
if (!is_notification) { // is indication
m_semaphoreConfEvt.take("indicate");
}
// don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call.
os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t *)value.c_str(), length);
if (!is_notification && (m_properties & BLECharacteristic::PROPERTY_INDICATE)) {
if (!BLEDevice::getServer()->setIndicateWait(myPair.first)) {
log_e("prior Indication in progress");
os_mbuf_free_chain(om);
return;
}
rc = ble_gatts_indicate_custom(myPair.first, m_handle, om);
if (rc != 0) {
BLEDevice::getServer()->clearIndicateWait(myPair.first);
}
} else {
rc = ble_gatts_notify_custom(myPair.first, m_handle, om);
}
if (rc != 0) {
log_e("<< ble_gatts_%s_custom: rc=%d %s", is_notification ? "notify" : "indicate", rc, GeneralUtils::errorToString(rc));
m_semaphoreConfEvt.give();
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_GATT, rc); // Invoke the notify callback.
return;
}
if (!is_notification) { // is indication
if (!m_semaphoreConfEvt.timedWait("indicate", indicationTimeout)) {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); // Invoke the notify callback.
} else {
auto code = m_semaphoreConfEvt.value();
if (code == ESP_OK) {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_INDICATE, code); // Invoke the notify callback.
} else {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, code);
}
}
} else {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); // Invoke the notify callback.
}
}
log_v("<< notify");
} // Notify
void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc) {
onRead(pCharacteristic);
} // onRead
void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc) {
onWrite(pCharacteristic);
} // onWrite } // onWrite
void BLECharacteristicCallbacks::onNotify(BLECharacteristic *pCharacteristic) { void BLECharacteristicCallbacks::onSubscribe(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) {
log_d(">> onNotify: default"); log_d(">> onSubscribe: default");
log_d("<< onNotify"); log_d("<< onSubscribe");
} // onNotify } // onSubscribe
void BLECharacteristicCallbacks::onStatus(BLECharacteristic *pCharacteristic, Status s, uint32_t code) { #endif /* CONFIG_NIMBLE_ENABLED */
log_d(">> onStatus: default");
log_d("<< onStatus");
} // onStatus
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ #ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
@ -11,15 +15,59 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <string> #include <string>
#include <map> #include <map>
#include "BLEUUID.h" #include "BLEUUID.h"
#include <esp_gatts_api.h>
#include <esp_gap_ble_api.h>
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
#include "BLEValue.h" #include "BLEValue.h"
#include "RTOS.h" #include "RTOS.h"
#include "BLEUtils.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h>
#include <esp_gap_ble_api.h>
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <vector>
#include <host/ble_gatt.h>
#include <host/ble_att.h>
#include "BLEConnInfo.h"
#define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN
#define ESP_GATT_CHAR_PROP_BIT_READ BLE_GATT_CHR_PROP_READ
#define ESP_GATT_CHAR_PROP_BIT_WRITE BLE_GATT_CHR_PROP_WRITE
#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR BLE_GATT_CHR_PROP_WRITE_NO_RSP
#define ESP_GATT_CHAR_PROP_BIT_BROADCAST BLE_GATT_CHR_PROP_BROADCAST
#define ESP_GATT_CHAR_PROP_BIT_NOTIFY BLE_GATT_CHR_PROP_NOTIFY
#define ESP_GATT_CHAR_PROP_BIT_INDICATE BLE_GATT_CHR_PROP_INDICATE
#endif
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef uint16_t esp_gatt_char_prop_t;
typedef uint8_t esp_gatt_perm_t;
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEService; class BLEService;
class BLEDescriptor; class BLEDescriptor;
@ -30,6 +78,10 @@ class BLECharacteristicCallbacks;
*/ */
class BLEDescriptorMap { class BLEDescriptorMap {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
void setByUUID(const char *uuid, BLEDescriptor *pDescriptor); void setByUUID(const char *uuid, BLEDescriptor *pDescriptor);
void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor);
void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor);
@ -37,11 +89,32 @@ public:
BLEDescriptor *getByUUID(BLEUUID uuid); BLEDescriptor *getByUUID(BLEUUID uuid);
BLEDescriptor *getByHandle(uint16_t handle); BLEDescriptor *getByHandle(uint16_t handle);
String toString(); 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 *getFirst();
BLEDescriptor *getNext(); BLEDescriptor *getNext();
int getRegisteredDescriptorCount();
void removeDescriptor(BLEDescriptor *pDescriptor);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, ble_gatt_access_ctxt *ctxt, void *arg);
#endif
private: private:
/***************************************************************************
* Common private properties *
***************************************************************************/
std::map<BLEDescriptor *, String> m_uuidMap; std::map<BLEDescriptor *, String> m_uuidMap;
std::map<uint16_t, BLEDescriptor *> m_handleMap; std::map<uint16_t, BLEDescriptor *> m_handleMap;
std::map<BLEDescriptor *, String>::iterator m_iterator; std::map<BLEDescriptor *, String>::iterator m_iterator;
@ -55,6 +128,48 @@ private:
*/ */
class BLECharacteristic { class BLECharacteristic {
public: public:
/***************************************************************************
* Common properties *
***************************************************************************/
static const uint32_t indicationTimeout = 1000;
/***************************************************************************
* Bluedroid public properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static const uint32_t PROPERTY_READ = 1 << 0;
static const uint32_t PROPERTY_WRITE = 1 << 1;
static const uint32_t PROPERTY_NOTIFY = 1 << 2;
static const uint32_t PROPERTY_BROADCAST = 1 << 3;
static const uint32_t PROPERTY_INDICATE = 1 << 4;
static const uint32_t PROPERTY_WRITE_NR = 1 << 5;
#endif
/***************************************************************************
* NimBLE public properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static const uint32_t PROPERTY_READ = BLE_GATT_CHR_F_READ;
static const uint32_t PROPERTY_READ_ENC = BLE_GATT_CHR_F_READ_ENC;
static const uint32_t PROPERTY_READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN;
static const uint32_t PROPERTY_READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR;
static const uint32_t PROPERTY_WRITE = BLE_GATT_CHR_F_WRITE;
static const uint32_t PROPERTY_WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP;
static const uint32_t PROPERTY_WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC;
static const uint32_t PROPERTY_WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN;
static const uint32_t PROPERTY_WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR;
static const uint32_t PROPERTY_BROADCAST = BLE_GATT_CHR_F_BROADCAST;
static const uint32_t PROPERTY_NOTIFY = BLE_GATT_CHR_F_NOTIFY;
static const uint32_t PROPERTY_INDICATE = BLE_GATT_CHR_F_INDICATE;
#endif
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLECharacteristic(const char *uuid, uint32_t properties = 0); BLECharacteristic(const char *uuid, uint32_t properties = 0);
BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); BLECharacteristic(BLEUUID uuid, uint32_t properties = 0);
virtual ~BLECharacteristic(); virtual ~BLECharacteristic();
@ -66,14 +181,9 @@ public:
String getValue(); String getValue();
uint8_t *getData(); uint8_t *getData();
size_t getLength(); size_t getLength();
void indicate(); void indicate();
void notify(bool is_notification = true); void notify(bool is_notification = true);
void setBroadcastProperty(bool value);
void setCallbacks(BLECharacteristicCallbacks *pCallbacks); void setCallbacks(BLECharacteristicCallbacks *pCallbacks);
void setIndicateProperty(bool value);
void setNotifyProperty(bool value);
void setReadProperty(bool value);
void setValue(uint8_t *data, size_t size); void setValue(uint8_t *data, size_t size);
void setValue(String value); void setValue(String value);
void setValue(uint16_t &data16); void setValue(uint16_t &data16);
@ -81,20 +191,16 @@ public:
void setValue(int &data32); void setValue(int &data32);
void setValue(float &data32); void setValue(float &data32);
void setValue(double &data64); void setValue(double &data64);
void setWriteProperty(bool value);
void setWriteNoResponseProperty(bool value);
String toString(); String toString();
uint16_t getHandle(); uint16_t getHandle();
void setAccessPermissions(esp_gatt_perm_t perm); void setAccessPermissions(uint8_t perm);
esp_gatt_char_prop_t getProperties();
static const uint32_t PROPERTY_READ = 1 << 0; void setReadProperty(bool value);
static const uint32_t PROPERTY_WRITE = 1 << 1; void setWriteProperty(bool value);
static const uint32_t PROPERTY_NOTIFY = 1 << 2; void setNotifyProperty(bool value);
static const uint32_t PROPERTY_BROADCAST = 1 << 3; void setBroadcastProperty(bool value);
static const uint32_t PROPERTY_INDICATE = 1 << 4; void setIndicateProperty(bool value);
static const uint32_t PROPERTY_WRITE_NR = 1 << 5; void setWriteNoResponseProperty(bool value);
static const uint32_t indicationTimeout = 1000;
private: private:
friend class BLEServer; friend class BLEServer;
@ -102,6 +208,10 @@ private:
friend class BLEDescriptor; friend class BLEDescriptor;
friend class BLECharacteristicMap; friend class BLECharacteristicMap;
/***************************************************************************
* Common private properties *
***************************************************************************/
BLEUUID m_bleUUID; BLEUUID m_bleUUID;
BLEDescriptorMap m_descriptorMap; BLEDescriptorMap m_descriptorMap;
uint16_t m_handle; uint16_t m_handle;
@ -109,18 +219,53 @@ private:
BLECharacteristicCallbacks *m_pCallbacks; BLECharacteristicCallbacks *m_pCallbacks;
BLEService *m_pService; BLEService *m_pService;
BLEValue m_value; BLEValue m_value;
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; bool m_writeEvt = false;
bool m_writeEvt = false; // If we have started a long write, this tells the commit code that we were the target
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void executeCreate(BLEService *pService);
esp_gatt_char_prop_t getProperties();
BLEService *getService();
void setHandle(uint16_t handle);
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
FreeRTOS::Semaphore m_semaphoreSetValue = FreeRTOS::Semaphore("SetValue"); FreeRTOS::Semaphore m_semaphoreSetValue = FreeRTOS::Semaphore("SetValue");
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
portMUX_TYPE m_readMux;
uint8_t m_removed;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
void executeCreate(BLEService *pService);
BLEService *getService();
void setHandle(uint16_t handle);
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void setSubscribe(struct ble_gap_event *event);
static int handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
#endif
}; // BLECharacteristic }; // BLECharacteristic
/** /**
@ -132,6 +277,10 @@ private:
*/ */
class BLECharacteristicCallbacks { class BLECharacteristicCallbacks {
public: public:
/***************************************************************************
* Common public types *
***************************************************************************/
typedef enum { typedef enum {
SUCCESS_INDICATE, SUCCESS_INDICATE,
SUCCESS_NOTIFY, SUCCESS_NOTIFY,
@ -139,51 +288,41 @@ public:
ERROR_NOTIFY_DISABLED, ERROR_NOTIFY_DISABLED,
ERROR_GATT, ERROR_GATT,
ERROR_NO_CLIENT, ERROR_NO_CLIENT,
ERROR_NO_SUBSCRIBER,
ERROR_INDICATE_TIMEOUT, ERROR_INDICATE_TIMEOUT,
ERROR_INDICATE_FAILURE ERROR_INDICATE_FAILURE
} Status; } Status;
/***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLECharacteristicCallbacks(); virtual ~BLECharacteristicCallbacks();
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] param The BLE GATTS param. Use param->read.
*/
virtual void onRead(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param);
/**
* @brief DEPRECATED! Callback function to support a read request. Called only if onRead(,) is not overridden
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
virtual void onRead(BLECharacteristic *pCharacteristic); virtual void onRead(BLECharacteristic *pCharacteristic);
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] param The BLE GATTS param. Use param->write.
*/
virtual void onWrite(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param);
/**
* @brief DEPRECATED! Callback function to support a write request. Called only if onWrite(,) is not overridden.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
virtual void onWrite(BLECharacteristic *pCharacteristic); virtual void onWrite(BLECharacteristic *pCharacteristic);
/**
* @brief Callback function to support a Notify request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
virtual void onNotify(BLECharacteristic *pCharacteristic); virtual void onNotify(BLECharacteristic *pCharacteristic);
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] s Status of the notification/indication
* @param [in] code Additional code of underlying errors
*/
virtual void onStatus(BLECharacteristic *pCharacteristic, Status s, uint32_t code); virtual void onStatus(BLECharacteristic *pCharacteristic, Status s, uint32_t code);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
virtual void onRead(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param);
virtual void onWrite(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
virtual void onRead(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc);
virtual void onWrite(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc);
virtual void onSubscribe(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue);
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */

View file

@ -3,19 +3,34 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include "BLEService.h" #include "BLEService.h"
#include "BLEUtils.h"
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#endif #endif
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief Return the characteristic by handle. * @brief Return the characteristic by handle.
* @param [in] handle The handle to look up the characteristic. * @param [in] handle The handle to look up the characteristic.
@ -77,17 +92,22 @@ BLECharacteristic *BLECharacteristicMap::getNext() {
} // getNext } // getNext
/** /**
* @brief Pass the GATT server event onwards to each of the characteristics found in the mapping * @brief Get the number of registered characteristics.
* @param [in] event * @return The number of registered characteristics.
* @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) { int BLECharacteristicMap::getRegisteredCharacteristicCount() {
// Invoke the handler for every Service we have. return m_uuidMap.size();
for (auto &myPair : m_uuidMap) { } // getRegisteredCharacteristicCount
myPair.first->handleGATTServerEvent(event, gatts_if, param);
} /**
} // handleGATTServerEvent * @brief Removes characteristic from maps.
* @param [in] characteristic The characteristic to remove.
* @return N/A.
*/
void BLECharacteristicMap::removeCharacteristic(BLECharacteristic *characteristic) {
m_handleMap.erase(characteristic->getHandle());
m_uuidMap.erase(characteristic);
} // removeCharacteristic
/** /**
* @brief Set the characteristic by handle. * @brief Set the characteristic by handle.
@ -130,5 +150,37 @@ String BLECharacteristicMap::toString() {
return res; return res;
} // toString } // toString
#endif /* CONFIG_BLUEDROID_ENABLED */ /***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/**
* @brief Pass the GATT server event onwards to each of the characteristics found in the mapping
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
// Invoke the handler for every Service we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
} // handleGATTServerEvent
#endif // CONFIG_BLUEDROID_ENABLED
/***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void BLECharacteristicMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, ble_gatt_access_ctxt *ctxt, void *arg) {
// Invoke the handler for every Service we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(conn_handle, attr_handle, ctxt, arg);
}
}
#endif // CONFIG_NIMBLE_ENABLED
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

File diff suppressed because it is too large Load diff

View file

@ -1,66 +1,128 @@
/* /*
* BLEDevice.h * BLEClient.h
* *
* Created on: Mar 22, 2017 * Created on: Mar 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef MAIN_BLEDEVICE_H_ #ifndef MAIN_BLECLIENT_H_
#define MAIN_BLEDEVICE_H_ #define MAIN_BLECLIENT_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <esp_gattc_api.h>
#include <string.h> #include <string.h>
#include <map> #include <map>
#include <string> #include <string>
//#include "BLEExceptions.h"
#include "BLERemoteService.h" #include "BLERemoteService.h"
#include "BLEService.h" #include "BLEService.h"
#include "BLEAddress.h" #include "BLEAddress.h"
#include "BLEAdvertisedDevice.h" #include "BLEAdvertisedDevice.h"
#include "BLEUtils.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gattc_api.h>
#ifndef BLE_ERR_REM_USER_CONN_TERM
#define BLE_ERR_REM_USER_CONN_TERM 0x13
#endif
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <nimble/ble.h>
#include <host/ble_hs.h>
#include <nimble/nimble_port.h>
#define ESP_GATT_IF_NONE BLE_HS_CONN_HANDLE_NONE
#endif
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef uint16_t esp_gatt_if_t;
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLERemoteService; class BLERemoteService;
class BLEClientCallbacks; class BLEClientCallbacks;
class BLEAdvertisedDevice; class BLEAdvertisedDevice;
struct BLETaskData;
/** /**
* @brief A model of a %BLE client. * @brief A model of a %BLE client.
*/ */
class BLEClient { class BLEClient {
public: public:
/***************************************************************************
* Common public properties *
***************************************************************************/
uint16_t m_appId;
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEClient(); BLEClient();
~BLEClient(); ~BLEClient();
bool connect(BLEAdvertisedDevice *device); bool connect(BLEAdvertisedDevice *device);
bool connectTimeout(BLEAdvertisedDevice *device, uint32_t timeoutMS = portMAX_DELAY); bool connectTimeout(BLEAdvertisedDevice *device, uint32_t timeoutMS = portMAX_DELAY);
bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC, uint32_t timeoutMS = portMAX_DELAY); // Connect to the remote BLE Server bool connect(BLEAddress address, uint8_t type = 0, uint32_t timeoutMS = portMAX_DELAY);
void disconnect(); // Disconnect from the remote BLE Server bool secureConnection();
BLEAddress getPeerAddress(); // Get the address of the remote BLE Server int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
int getRssi(); // Get the RSSI of the remote BLE Server BLEAddress getPeerAddress();
std::map<std::string, BLERemoteService *> *getServices(); // Get a map of the services offered by the remote BLE Server int getRssi();
BLERemoteService *getService(const char *uuid); // Get a reference to a specified service offered by the remote BLE server. std::map<std::string, BLERemoteService *> *getServices();
BLERemoteService *getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server. BLERemoteService *getService(const char *uuid);
String getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service. BLERemoteService *getService(BLEUUID uuid);
String getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID);
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); bool isConnected();
bool isConnected(); // Return true if we are connected.
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, String value); // Set the value of a given characteristic at a given service. void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, String value);
String toString();
String toString(); // Return a string representation of this client.
uint16_t getConnId(); uint16_t getConnId();
esp_gatt_if_t getGattcIf(); esp_gatt_if_t getGattcIf();
uint16_t getMTU(); uint16_t getMTU();
bool setMTU(uint16_t mtu); bool setMTU(uint16_t mtu);
uint16_t m_appId; /***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static int handleGAPEvent(struct ble_gap_event *event, void *arg);
static int serviceDiscoveredCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg);
#endif
private: private:
friend class BLEDevice; friend class BLEDevice;
@ -68,15 +130,14 @@ private:
friend class BLERemoteCharacteristic; friend class BLERemoteCharacteristic;
friend class BLERemoteDescriptor; friend class BLERemoteDescriptor;
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); /***************************************************************************
* Common private properties *
***************************************************************************/
BLEAddress m_peerAddress = BLEAddress((uint8_t *)"\0\0\0\0\0\0"); // The BD address of the remote server. BLEAddress m_peerAddress = BLEAddress((uint8_t *)"\0\0\0\0\0\0");
uint16_t m_conn_id; uint16_t m_conn_id;
// int m_deviceType; bool m_haveServices = false;
esp_gatt_if_t m_gattc_if; bool m_isConnected = false;
bool m_haveServices = false; // Have we previously obtain the set of services from the remote server.
bool m_isConnected = false; // Are we currently connected.
BLEClientCallbacks *m_pClientCallbacks; BLEClientCallbacks *m_pClientCallbacks;
FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt");
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
@ -84,20 +145,76 @@ private:
FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
std::map<std::string, BLERemoteService *> m_servicesMap; std::map<std::string, BLERemoteService *> m_servicesMap;
std::map<BLERemoteService *, uint16_t> m_servicesMapByInstID; std::map<BLERemoteService *, uint16_t> m_servicesMapByInstID;
void clearServices(); // Clear any existing services.
uint16_t m_mtu = 23; uint16_t m_mtu = 23;
}; // class BLEDevice
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_gatt_if_t m_gattc_if;
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
int m_lastErr;
int32_t m_connectTimeout;
uint8_t m_terminateFailCount;
ble_gap_conn_params m_pConnParams;
mutable BLETaskData *m_pTaskData;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
void clearServices();
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static void dcTimerCb(ble_npl_event *event);
#endif
}; // class BLEClient
/** /**
* @brief Callbacks associated with a %BLE client. * @brief Callbacks associated with a %BLE client.
*/ */
class BLEClientCallbacks { class BLEClientCallbacks {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLEClientCallbacks(){}; virtual ~BLEClientCallbacks(){};
virtual void onConnect(BLEClient *pClient) = 0; virtual void onConnect(BLEClient *pClient);
virtual void onDisconnect(BLEClient *pClient) = 0; virtual void onDisconnect(BLEClient *pClient);
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
virtual bool onConnParamsUpdateRequest(BLEClient *pClient, const ble_gap_upd_params *params);
virtual uint32_t onPassKeyRequest();
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc);
virtual bool onConfirmPIN(uint32_t pin);
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* MAIN_BLEDEVICE_H_ */ #endif /* MAIN_BLECLIENT_H_ */

View file

@ -0,0 +1,110 @@
/*
* Copyright 2020-2025 Ryan Powell <ryan@nable-embedded.io> and
* esp-nimble-cpp, NimBLE-Arduino contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BLECONNINFO_H_
#define BLECONNINFO_H_
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#include "BLEAddress.h"
/**
* @brief Connection information.
*/
class BLEConnInfo {
public:
/** @brief Gets the over-the-air address of the connected peer */
BLEAddress getAddress() const {
return BLEAddress(m_desc.peer_ota_addr);
}
/** @brief Gets the ID address of the connected peer */
BLEAddress getIdAddress() const {
return BLEAddress(m_desc.peer_id_addr);
}
/** @brief Gets the connection handle (also known as the connection id) of the connected peer */
uint16_t getConnHandle() const {
return m_desc.conn_handle;
}
/** @brief Gets the connection interval for this connection (in 1.25ms units) */
uint16_t getConnInterval() const {
return m_desc.conn_itvl;
}
/** @brief Gets the supervision timeout for this connection (in 10ms units) */
uint16_t getConnTimeout() const {
return m_desc.supervision_timeout;
}
/** @brief Gets the allowable latency for this connection (unit = number of intervals) */
uint16_t getConnLatency() const {
return m_desc.conn_latency;
}
/** @brief Gets the maximum transmission unit size for this connection (in bytes) */
uint16_t getMTU() const {
return ble_att_mtu(m_desc.conn_handle);
}
/** @brief Check if we are in the master role in this connection */
bool isMaster() const {
return (m_desc.role == BLE_GAP_ROLE_MASTER);
}
/** @brief Check if we are in the slave role in this connection */
bool isSlave() const {
return (m_desc.role == BLE_GAP_ROLE_SLAVE);
}
/** @brief Check if we are connected to a bonded peer */
bool isBonded() const {
return (m_desc.sec_state.bonded == 1);
}
/** @brief Check if the connection in encrypted */
bool isEncrypted() const {
return (m_desc.sec_state.encrypted == 1);
}
/** @brief Check if the the connection has been authenticated */
bool isAuthenticated() const {
return (m_desc.sec_state.authenticated == 1);
}
/** @brief Gets the key size used to encrypt the connection */
uint8_t getSecKeySize() const {
return m_desc.sec_state.key_size;
}
private:
friend class BLEServer;
friend class BLEClient;
friend class BLECharacteristic;
friend class BLEDescriptor;
ble_gap_conn_desc m_desc{};
BLEConnInfo(){};
BLEConnInfo(ble_gap_conn_desc desc) {
m_desc = desc;
}
};
#endif
#endif // BLECONNINFO_H_

View file

@ -3,25 +3,50 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Apr 3, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include <string.h> #include <string.h>
#include <iomanip> #include <iomanip>
#include <stdlib.h> #include <stdlib.h>
#include "sdkconfig.h" #include "sdkconfig.h"
#include <esp_err.h> #include <esp_err.h>
#include "BLE2904.h"
#include "BLEService.h" #include "BLEService.h"
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/***************************************************************************
* Common definitions *
***************************************************************************/
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
/***************************************************************************
* Common global variables *
***************************************************************************/
static BLEDescriptorCallbacks defaultCallbacks;
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief BLEDescriptor constructor. * @brief BLEDescriptor constructor.
*/ */
@ -32,13 +57,15 @@ BLEDescriptor::BLEDescriptor(const char *uuid, uint16_t len) : BLEDescriptor(BLE
*/ */
BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) { BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) {
m_bleUUID = uuid; m_bleUUID = uuid;
m_value.attr_len = 0; // Initial length is 0. m_handle = NULL_HANDLE; // Handle is initially unknown.
m_value.attr_max_len = max_len; // Maximum length of the data. m_pCharacteristic = nullptr; // No initial characteristic.
m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCallback = nullptr; // No initial callback.
m_pCharacteristic = nullptr; // No initial characteristic. m_value.attr_len = 0; // Initial length is 0.
m_pCallback = nullptr; // No initial callback. m_value.attr_max_len = max_len; // Maximum length of the data.
m_value.attr_value = (uint8_t *)malloc(max_len); // Allocate storage for the value. m_value.attr_value = (uint8_t *)malloc(max_len); // Allocate storage for the value.
#if CONFIG_NIMBLE_ENABLED
m_removed = 0;
#endif
} // BLEDescriptor } // BLEDescriptor
/** /**
@ -62,6 +89,7 @@ void BLEDescriptor::executeCreate(BLECharacteristic *pCharacteristic) {
m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service.
#if CONFIG_BLUEDROID_ENABLED
esp_attr_control_t control; esp_attr_control_t control;
control.auto_rsp = ESP_GATT_AUTO_RSP; control.auto_rsp = ESP_GATT_AUTO_RSP;
m_semaphoreCreateEvt.take("executeCreate"); m_semaphoreCreateEvt.take("executeCreate");
@ -73,6 +101,7 @@ void BLEDescriptor::executeCreate(BLECharacteristic *pCharacteristic) {
} }
m_semaphoreCreateEvt.wait("executeCreate"); m_semaphoreCreateEvt.wait("executeCreate");
#endif
log_v("<< executeCreate"); log_v("<< executeCreate");
} // executeCreate } // executeCreate
@ -107,6 +136,118 @@ uint8_t *BLEDescriptor::getValue() {
return m_value.attr_value; return m_value.attr_value;
} // getValue } // getValue
/**
* @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to.
*/
BLECharacteristic *BLEDescriptor::getCharacteristic() {
return m_pCharacteristic;
} // getCharacteristic
/**
* @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
*/
void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks *pCallback) {
log_v(">> setCallbacks: 0x%x", (uint32_t)pCallback);
if (pCallback != nullptr) {
m_pCallback = pCallback;
} else {
m_pCallback = &defaultCallbacks;
}
log_v("<< setCallbacks");
} // setCallbacks
/**
* @brief Set the handle of this descriptor.
* Set the handle of this descriptor to be the supplied value.
* @param [in] handle The handle to be associated with this descriptor.
* @return N/A.
*/
void BLEDescriptor::setHandle(uint16_t handle) {
#if defined(CONFIG_BLUEDROID_ENABLED)
log_v(">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
m_handle = handle;
log_v("<< setHandle()");
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
log_w("NimBLE does not support manually setting the handle of a descriptor. Ignoring request.");
#endif
} // setHandle
/**
* @brief Set the value of the descriptor.
* @param [in] data The data to set for the descriptor.
* @param [in] length The length of the data in bytes.
*/
void BLEDescriptor::setValue(uint8_t *data, size_t length) {
if (length > m_value.attr_max_len) {
log_e("Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return;
}
m_semaphoreSetValue.take();
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
#if CONFIG_BLUEDROID_ENABLED
if (m_handle != NULL_HANDLE) {
esp_ble_gatts_set_attr_value(m_handle, length, (const uint8_t *)data);
log_d("Set the value in the GATTS database using handle 0x%x", m_handle);
}
#endif
m_semaphoreSetValue.give();
} // setValue
/**
* @brief Set the value of the descriptor.
* @param [in] value The value of the descriptor in string form.
*/
void BLEDescriptor::setValue(String value) {
setValue((uint8_t *)value.c_str(), value.length());
} // setValue
void BLEDescriptor::setAccessPermissions(uint8_t perm) {
m_permissions = perm;
}
/**
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.
*/
String BLEDescriptor::toString() {
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
String res = "UUID: " + m_bleUUID.toString() + ", handle: 0x" + hex;
return res;
} // toString
BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {}
/**
* @brief Callback function to support a read request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onRead(BLEDescriptor *pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onRead: default");
log_d("BLEDescriptorCallbacks", "<< onRead");
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onWrite(BLEDescriptor *pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onWrite: default");
log_d("BLEDescriptorCallbacks", "<< onWrite");
} // onWrite
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/** /**
* @brief Handle GATT server events for the descripttor. * @brief Handle GATT server events for the descripttor.
* @param [in] event * @param [in] event
@ -185,88 +326,84 @@ void BLEDescriptor::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_i
} // switch event } // switch event
} // handleGATTServerEvent } // handleGATTServerEvent
/** #endif
* @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. /***************************************************************************
*/ * NimBLE functions *
void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks *pCallback) { ***************************************************************************/
log_v(">> setCallbacks: 0x%x", (uint32_t)pCallback);
m_pCallback = pCallback; #if defined(CONFIG_NIMBLE_ENABLED)
log_v("<< setCallbacks");
} // setCallbacks
/** /**
* @brief Set the handle of this descriptor. * @brief Handle GATT server events for the descriptor.
* Set the handle of this descriptor to be the supplied value. * @param [in] conn_handle The connection handle.
* @param [in] handle The handle to be associated with this descriptor. * @param [in] attr_handle The attribute handle.
* @return N/A. * @param [in] ctxt The GATT access context.
* @param [in] arg The argument.
*/ */
void BLEDescriptor::setHandle(uint16_t handle) { int BLEDescriptor::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
log_v(">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); const ble_uuid_t *uuid;
m_handle = handle; int rc;
log_v("<< setHandle()"); struct ble_gap_conn_desc desc;
} // setHandle BLEDescriptor *pDescriptor = (BLEDescriptor *)arg;
/** log_d("Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write");
* @brief Set the value of the descriptor.
* @param [in] data The data to set for the descriptor. uuid = ctxt->chr->uuid;
* @param [in] length The length of the data in bytes. if (ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0) {
*/ switch (ctxt->op) {
void BLEDescriptor::setValue(uint8_t *data, size_t length) { case BLE_GATT_ACCESS_OP_READ_DSC:
if (length > ESP_GATT_MAX_ATTR_LEN) { {
log_e("Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); rc = ble_gap_conn_find(conn_handle, &desc);
return; assert(rc == 0);
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if (ctxt->om->om_pkthdr_len > 8 || pDescriptor->m_value.attr_len <= (ble_att_mtu(desc.conn_handle) - 3)) {
pDescriptor->m_pCallback->onRead(pDescriptor);
}
ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.attr_value, pDescriptor->m_value.attr_len);
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC:
{
uint16_t att_max_len = pDescriptor->m_value.attr_max_len;
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[att_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data, len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while (next != NULL) {
if ((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pDescriptor->setValue(buf, len);
pDescriptor->m_pCallback->onWrite(pDescriptor);
return 0;
}
default: break;
}
} }
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
if (m_handle != NULL_HANDLE) {
esp_ble_gatts_set_attr_value(m_handle, length, (const uint8_t *)data);
log_d("Set the value in the GATTS database using handle 0x%x", m_handle);
}
} // setValue
/** return BLE_ATT_ERR_UNLIKELY;
* @brief Set the value of the descriptor.
* @param [in] value The value of the descriptor in string form.
*/
void BLEDescriptor::setValue(String value) {
setValue((uint8_t *)value.c_str(), value.length());
} // setValue
void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) {
m_permissions = perm;
} }
/** #endif
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.
*/
String BLEDescriptor::toString() {
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
String res = "UUID: " + m_bleUUID.toString() + ", handle: 0x" + hex;
return res;
} // toString
BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
/**
* @brief Callback function to support a read request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onRead(BLEDescriptor *pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onRead: default");
log_d("BLEDescriptorCallbacks", "<< onRead");
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onWrite(BLEDescriptor *pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onWrite: default");
log_d("BLEDescriptorCallbacks", "<< onWrite");
} // onWrite
#endif /* CONFIG_BLUEDROID_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ #ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
@ -11,13 +15,60 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <string> #include <string>
#include "BLEUUID.h" #include "BLEUUID.h"
#include "BLECharacteristic.h" #include "BLECharacteristic.h"
#include <esp_gatts_api.h>
#include "RTOS.h" #include "RTOS.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h>
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_att.h>
#include "BLEConnInfo.h"
#define ESP_GATT_PERM_READ BLE_ATT_F_READ
#define ESP_GATT_PERM_WRITE BLE_ATT_F_WRITE
#define ESP_GATT_PERM_READ_ENCRYPTED BLE_ATT_F_READ_ENC
#define ESP_GATT_PERM_WRITE_ENCRYPTED BLE_ATT_F_WRITE_ENC
#define ESP_GATT_PERM_READ_AUTHORIZATION BLE_ATT_F_READ_AUTHOR
#define ESP_GATT_PERM_WRITE_AUTHORIZATION BLE_ATT_F_WRITE_AUTHOR
#define ESP_GATT_PERM_READ_ENC_MITM BLE_ATT_F_READ_AUTHEN
#define ESP_GATT_PERM_WRITE_ENC_MITM BLE_ATT_F_WRITE_AUTHEN
#endif
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef struct {
uint16_t attr_max_len; /*!< attribute max value length */
uint16_t attr_len; /*!< attribute current value length */
uint8_t *attr_value; /*!< the pointer to attribute value */
} esp_attr_value_t;
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEService; class BLEService;
class BLECharacteristic; class BLECharacteristic;
class BLEDescriptorCallbacks; class BLEDescriptorCallbacks;
@ -27,33 +78,83 @@ class BLEDescriptorCallbacks;
*/ */
class BLEDescriptor { class BLEDescriptor {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEDescriptor(const char *uuid, uint16_t max_len = 100); BLEDescriptor(const char *uuid, uint16_t max_len = 100);
BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100);
virtual ~BLEDescriptor(); virtual ~BLEDescriptor();
uint16_t getHandle(); // Get the handle of the descriptor. uint16_t getHandle(); // Get the handle of the descriptor.
size_t getLength(); // Get the length of the value of the descriptor. size_t getLength(); // Get the length of the value of the descriptor.
BLEUUID getUUID(); // Get the UUID of the descriptor. BLEUUID getUUID(); // Get the UUID of the descriptor.
uint8_t *getValue(); // Get a pointer to the value 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); BLECharacteristic *getCharacteristic(); // Get the characteristic that this descriptor belongs to.
void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor. void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor.
void setCallbacks(BLEDescriptorCallbacks *pCallbacks); // Set callbacks to be invoked for 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(uint8_t *data, size_t size); // Set the value of the descriptor as a pointer to data.
void setValue(String value); // Set the value of the descriptor as a data buffer. void setValue(String value); // Set the value of the descriptor as a data buffer.
String toString(); // Convert the descriptor to a string representation. String toString(); // Convert the descriptor to a string representation.
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static int handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
#endif
private: private:
friend class BLEDescriptorMap; friend class BLEDescriptorMap;
friend class BLECharacteristic; friend class BLECharacteristic;
friend class BLEService;
friend class BLE2901;
friend class BLE2902;
friend class BLE2904;
/***************************************************************************
* Common private properties *
***************************************************************************/
BLEUUID m_bleUUID; BLEUUID m_bleUUID;
uint16_t m_handle; uint16_t m_handle;
esp_attr_value_t m_value;
BLEDescriptorCallbacks *m_pCallback; BLEDescriptorCallbacks *m_pCallback;
BLECharacteristic *m_pCharacteristic; BLECharacteristic *m_pCharacteristic;
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; FreeRTOS::Semaphore m_semaphoreSetValue = FreeRTOS::Semaphore("SetValue");
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
esp_attr_value_t m_value; uint8_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint8_t m_permissions = HA_FLAG_PERM_RW;
uint8_t m_removed = 0;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
void executeCreate(BLECharacteristic *pCharacteristic); void executeCreate(BLECharacteristic *pCharacteristic);
void setHandle(uint16_t handle); void setHandle(uint16_t handle);
@ -68,11 +169,15 @@ private:
*/ */
class BLEDescriptorCallbacks { class BLEDescriptorCallbacks {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLEDescriptorCallbacks(); virtual ~BLEDescriptorCallbacks();
virtual void onRead(BLEDescriptor *pDescriptor); virtual void onRead(BLEDescriptor *pDescriptor);
virtual void onWrite(BLEDescriptor *pDescriptor); virtual void onWrite(BLEDescriptor *pDescriptor);
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */

View file

@ -3,21 +3,50 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include "BLECharacteristic.h" #include "BLECharacteristic.h"
#include "BLEDescriptor.h" #include "BLEDescriptor.h"
#include <esp_gatts_api.h> // ESP32 BLE
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#endif #endif
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h> // ESP32 BLE
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include "host/ble_gatt.h"
#endif
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief Return the descriptor by UUID. * @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor. * @param [in] UUID The UUID to look up the descriptor.
@ -81,6 +110,24 @@ void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor *pDescriptor)
m_handleMap.insert(std::pair<uint16_t, BLEDescriptor *>(handle, pDescriptor)); m_handleMap.insert(std::pair<uint16_t, BLEDescriptor *>(handle, pDescriptor));
} // setByHandle } // setByHandle
/**
* @brief Get the number of registered descriptors.
* @return The number of registered descriptors.
*/
int BLEDescriptorMap::getRegisteredDescriptorCount() {
return m_uuidMap.size();
}
/**
* @brief Remove a descriptor from the map.
* @param [in] pDescriptor The descriptor to remove.
* @return N/A.
*/
void BLEDescriptorMap::removeDescriptor(BLEDescriptor *pDescriptor) {
m_uuidMap.erase(pDescriptor);
m_handleMap.erase(pDescriptor->getHandle());
}
/** /**
* @brief Return a string representation of the descriptor map. * @brief Return a string representation of the descriptor map.
* @return A string representation of the descriptor map. * @return A string representation of the descriptor map.
@ -102,19 +149,6 @@ String BLEDescriptorMap::toString() {
return res; return res;
} // toString } // toString
/**
* @brief Pass the GATT server event onwards to each of the descriptors found in the mapping
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLEDescriptorMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
// Invoke the handler for every descriptor we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
} // handleGATTServerEvent
/** /**
* @brief Get the first descriptor in the map. * @brief Get the first descriptor in the map.
* @return The first descriptor in the map. * @return The first descriptor in the map.
@ -142,5 +176,41 @@ BLEDescriptor *BLEDescriptorMap::getNext() {
return pRet; return pRet;
} // getNext } // getNext
#endif /* CONFIG_BLUEDROID_ENABLED */ /***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/**
* @brief Pass the GATT server event onwards to each of the descriptors found in the mapping
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLEDescriptorMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
// Invoke the handler for every descriptor we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
} // handleGATTServerEvent
#endif
/***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void BLEDescriptorMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, ble_gatt_access_ctxt *ctxt, void *arg) {
// Invoke the handler for every descriptor we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(conn_handle, attr_handle, ctxt, arg);
}
}
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,10 @@
* *
* Created on: Mar 16, 2017 * Created on: Mar 16, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef MAIN_BLEDevice_H_ #ifndef MAIN_BLEDevice_H_
@ -11,88 +15,252 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <esp_gattc_api.h> // ESP32 BLE /***************************************************************************
#include <map> // Part of C++ STL * Common includes *
***************************************************************************/
#include <map>
#include <string> #include <string>
#include <esp_bt.h> #include <esp_bt.h>
#include "BLEServer.h" #include "BLEServer.h"
#include "BLEClient.h" #include "BLEClient.h"
#include "BLEUtils.h" #include "BLEUtils.h"
#include "BLEScan.h" #include "BLEScan.h"
#include "BLEAdvertising.h"
#include "BLESecurity.h"
#include "BLEAddress.h" #include "BLEAddress.h"
#include "BLEUtils.h"
/** /***************************************************************************
* @brief BLE functions. * Bluedroid includes *
*/ ***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#define ESP_GATT_IF_NONE BLE_HS_CONN_HANDLE_NONE
// NimBLE configuration compatibility macros
#if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_SCAN_DUPLICATE_BY_ADV_DATA
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
#endif
#if defined(CONFIG_BT_CTRL_SCAN_DUPL_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_BT_CTRL_SCAN_DUPL_TYPE
#endif
#if defined(CONFIG_BT_LE_SCAN_DUPL_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_BT_LE_SCAN_DUPL_TYPE
#endif
#if defined(CONFIG_DUPLICATE_SCAN_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
#endif
#if defined(CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE
#endif
#if defined(CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT
#endif
#if defined(CONFIG_NIMBLE_MAX_CONNECTIONS) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS)
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEAddress;
class BLEDeviceCallbacks;
class BLESecurityCallbacks;
class BLEServer;
class BLEScan;
class BLEAdvertising;
class BLEClient;
class BLESecurity;
/***************************************************************************
* Bluedroid type definitions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); 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 (*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); typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE type definitions and externals *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
extern "C" void ble_store_config_init(void);
typedef int (*gap_event_handler)(struct ble_gap_event *event, void *param);
#endif
class BLEDevice { class BLEDevice {
public: public:
static BLEClient *createClient(); // Create a new BLE client. /***************************************************************************
static BLEServer *createServer(); // Create a new BLE server. * Common public properties *
static BLEAddress getAddress(); // Retrieve our own local BD address. ***************************************************************************/
static BLEScan *getScan(); // Get the scan object
static String getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server. static uint16_t m_appId;
static void init(String deviceName); // Initialize the local BLE environment. static uint16_t m_localMTU;
static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType = ESP_BLE_PWR_TYPE_DEFAULT); // Set our power level. static gap_event_handler m_customGapHandler;
static void setValue(
BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, String value /***************************************************************************
); // Set the value of a characteristic on a service on a server. * Bluedroid public properties *
static 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. #if defined(CONFIG_BLUEDROID_ENABLED)
static void setEncryptionLevel(esp_ble_sec_act_t level); static esp_ble_sec_act_t m_securityLevel;
static gattc_event_handler m_customGattcHandler;
static gatts_event_handler m_customGattsHandler;
#endif
/***************************************************************************
* Common public declarations *
***************************************************************************/
static BLEClient *createClient();
static BLEServer *createServer();
static BLEAddress getAddress();
static BLEServer *getServer();
static BLEScan *getScan();
static String getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID);
static void init(String deviceName);
static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType = ESP_BLE_PWR_TYPE_DEFAULT);
static int getPower(esp_ble_power_type_t powerType = ESP_BLE_PWR_TYPE_DEFAULT);
static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, String value);
static String toString();
static void whiteListAdd(BLEAddress address);
static void whiteListRemove(BLEAddress address);
static void setSecurityCallbacks(BLESecurityCallbacks *pCallbacks); static void setSecurityCallbacks(BLESecurityCallbacks *pCallbacks);
static esp_err_t setMTU(uint16_t mtu); static esp_err_t setMTU(uint16_t mtu);
static uint16_t getMTU(); static uint16_t getMTU();
static bool getInitialized(); // Returns the state of the device, is it initialized or not? static bool getInitialized();
/* move advertising to BLEDevice for saving ram and flash in beacons */
static BLEAdvertising *getAdvertising(); static BLEAdvertising *getAdvertising();
static void startAdvertising(); static void startAdvertising();
static void stopAdvertising(); static void stopAdvertising();
static uint16_t m_appId;
/* multi connect */
static std::map<uint16_t, conn_status_t> getPeerDevices(bool client); static std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
static void addPeerDevice(void *peer, bool is_client, uint16_t conn_id); static void addPeerDevice(void *peer, bool is_client, uint16_t conn_id);
static void updatePeerDevice(void *peer, bool _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 void removePeerDevice(uint16_t conn_id, bool client);
static BLEClient *getClientByID(uint16_t conn_id);
static BLEClient *getClientByAddress(BLEAddress address);
static BLEClient *getClientByGattIf(uint16_t conn_id); static BLEClient *getClientByGattIf(uint16_t conn_id);
static void setCustomGapHandler(gap_event_handler handler); static void setCustomGapHandler(gap_event_handler handler);
static void deinit(bool release_memory = false);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static void setEncryptionLevel(esp_ble_sec_act_t level);
static void setCustomGattcHandler(gattc_event_handler handler); static void setCustomGattcHandler(gattc_event_handler handler);
static void setCustomGattsHandler(gatts_event_handler handler); static void setCustomGattsHandler(gatts_event_handler handler);
static void deinit(bool release_memory = false); #endif
static uint16_t m_localMTU;
static esp_ble_sec_act_t m_securityLevel; /***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static void onReset(int reason);
static void onSync(void);
static void host_task(void *param);
static bool setOwnAddrType(uint8_t type);
static bool setOwnAddr(BLEAddress &addr);
static bool setOwnAddr(uint8_t *addr);
static void setDeviceCallbacks(BLEDeviceCallbacks *cb);
static bool onWhiteList(BLEAddress &address);
#endif
private: private:
friend class BLEClient;
friend class BLEScan;
friend class BLEServer;
friend class BLECharacteristic;
friend class BLEAdvertising;
/***************************************************************************
* Common private properties *
***************************************************************************/
static BLEServer *m_pServer; static BLEServer *m_pServer;
static BLEScan *m_pScan; static BLEScan *m_pScan;
static BLEClient *m_pClient; static BLEClient *m_pClient;
static BLESecurityCallbacks *m_securityCallbacks; static BLESecurityCallbacks *m_securityCallbacks;
static BLEAdvertising *m_bleAdvertising; static BLEAdvertising *m_bleAdvertising;
static esp_gatt_if_t getGattcIF();
static std::map<uint16_t, conn_status_t> m_connectedClientsMap; static std::map<uint16_t, conn_status_t> m_connectedClientsMap;
static portMUX_TYPE mux; static portMUX_TYPE mux;
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static uint8_t m_ownAddrType;
static bool m_synced;
static std::vector<BLEAddress> m_whiteList;
static BLEDeviceCallbacks defaultDeviceCallbacks;
static BLEDeviceCallbacks *m_pDeviceCallbacks;
static ble_gap_event_listener m_listener;
#endif
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static esp_gatt_if_t getGattcIF();
static void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); static void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
static void gattServerEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); static void gattServerEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
static void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); static void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
#endif
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 }; // class BLE
#endif /* CONFIG_BLUEDROID_ENABLED */ /***************************************************************************
* NimBLE specific classes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
class BLEDeviceCallbacks {
public:
virtual ~BLEDeviceCallbacks(){};
virtual int onStoreStatus(struct ble_store_status_event *event, void *arg);
};
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* MAIN_BLEDevice_H_ */ #endif /* MAIN_BLEDevice_H_ */

View file

@ -8,12 +8,16 @@
* Fix time stamp (0.1 second resolution) * Fix time stamp (0.1 second resolution)
* Fixes based on EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md * Fixes based on EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
* *
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on pcbreflux's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
@ -176,5 +180,5 @@ void BLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil; m_eddystoneData.tmil = tmil;
} // setTime } // setTime
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -1,202 +0,0 @@
/*
* BLEEddystoneTLM.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
* Edited on: Mar 20, 2020 by beegee-tokyo
* Fix temperature value (8.8 fixed format)
* Fix time stamp (0.1 second resolution)
* Fixes based on EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
*
*/
#include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <string.h>
#include <stdio.h>
#include "esp32-hal-log.h"
#include "BLEEddystoneTLM.h"
static const char LOG_TAG[] = "BLEEddystoneTLM";
BLEEddystoneTLM::BLEEddystoneTLM() {
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)/256;
m_eddystoneData.advCount = 0;
m_eddystoneData.tmil = 0;
_initHeadder();
} // BLEEddystoneTLM
BLEEddystoneTLM::BLEEddystoneTLM(BLEAdvertisedDevice *advertisedDevice){
char* payload = (char*)advertisedDevice->getPayload();
for(int i = 0; i < advertisedDevice->getPayloadLength(); ++i){
if(payload[i] == 0x16 && advertisedDevice->getPayloadLength() >= i+2+sizeof(m_eddystoneData) && payload[i+1] == 0xAA && payload[i+2] == 0xFE && payload[i+3] == 0x20){
log_d("Eddystone TLM data frame starting at byte [%d]", i+3);
setData(std::string(payload+i+3, sizeof(m_eddystoneData)));
break;
}
}
_initHeadder();
}
String BLEEddystoneTLM::getData() {
return String((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
BLEUUID BLEEddystoneTLM::getUUID() {
return beaconUUID;
} // getUUID
uint8_t BLEEddystoneTLM::getVersion() {
return m_eddystoneData.version;
} // getVersion
uint16_t BLEEddystoneTLM::getVolt() {
return ENDIAN_CHANGE_U16(m_eddystoneData.volt);
} // getVolt
float BLEEddystoneTLM::getTemp() {
return EDDYSTONE_TEMP_U16_TO_FLOAT(m_eddystoneData.temp);
} // getTemp
uint16_t BLEEddystoneTLM::getRawTemp() {
return ENDIAN_CHANGE_U16(m_eddystoneData.temp);
} // getRawTemp
uint32_t BLEEddystoneTLM::getCount() {
return ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
} // getCount
uint32_t BLEEddystoneTLM::getTime() {
return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10;
} // getTime
String BLEEddystoneTLM::getFrame(){
String frame(BLEHeadder);
frame += String((char*) &m_eddystoneData, sizeof(m_eddystoneData));
log_d("Compiled frame of length %d Bytes", frame.length());
for(int i = 0; i < frame.length(); ++i){
log_d("[%d]=0x%02X",i, frame[i]);
}
return frame;
} // getServiceData
String BLEEddystoneTLM::toString() {
String out = "";
uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
char val[12];
out += "Version "; // + std::string(m_eddystoneData.version);
snprintf(val, sizeof(val), "%d", m_eddystoneData.version);
out += val;
out += "\n";
out += "Battery Voltage "; // + ENDIAN_CHANGE_U16(m_eddystoneData.volt);
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt));
out += val;
out += " mV\n";
out += "Temperature ";
snprintf(val, sizeof(val), "%.2f", ((int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp)) / 256.0f);
out += val;
out += " C\n";
out += "Adv. Count ";
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
out += val;
out += "\n";
out += "Time in seconds ";
snprintf(val, sizeof(val), "%d", rawsec/10);
out += val;
out += "\n";
out += "Time ";
snprintf(val, sizeof(val), "%04d", rawsec / 864000);
out += val;
out += ".";
snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60);
out += val;
out += "\n";
return out;
} // toString
/**
* Set the raw data for the beacon record.
* Example:
* uint8_t *payload = advertisedDevice.getPayload();
* eddystoneTLM.setData(std::string((char*)payload+22, advertisedDevice.getPayloadLength() - 22));
* Note: the offset 22 works for current implementation of example BLE_EddystoneTLM Beacon.ino, however
* the position is not static and it is programmers responsibility to align the data.
* Data frame:
* | Field || Len | Type | UUID | EddyStone TLM |
* | Offset || 0 | 1 | 2 | 4 |
* | Len || 1 B | 1 B | 2 B | 14 B |
* | Data || ?? | ?? | 0xAA | 0xFE | ??? |
*
* EddyStone TLM frame:
* | Field || Type | Version | Batt mV | Beacon temp | Cnt since boot | Time since boot |
* | Offset || 0 | 1 | 2 | 4 | 6 | 10 |
* | Len || 1 B | 1 B | 2 B | 2 B | 4 B | 4 B |
* | Data || 0x20 | ?? | ?? | ?? | ?? | ?? | | | | | | | | |
*/
void BLEEddystoneTLM::setData(std::string data) {
if (data.length() != sizeof(m_eddystoneData)) {
log_e("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;
} // setUUID
void BLEEddystoneTLM::setVersion(uint8_t version) {
m_eddystoneData.version = version;
} // setVersion
// Set voltage in ESP32 native Big endian and convert it to little endian used for BLE Frame
void BLEEddystoneTLM::setVolt(uint16_t volt) {
m_eddystoneData.volt = ENDIAN_CHANGE_U16(volt);
} // setVolt
void BLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = EDDYSTONE_TEMP_FLOAT_TO_U16(temp);
} // setTemp
void BLEEddystoneTLM::setCount(uint32_t advCount) {
m_eddystoneData.advCount = advCount;
} // setCount
void BLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil;
} // setTime
void BLEEddystoneTLM::_initHeadder(){
BLEHeadder[0] = 0x02; // Len
BLEHeadder[1] = 0x01; // Type Flags
BLEHeadder[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
BLEHeadder[3] = 0x03; // Len
BLEHeadder[4] = 0x03; // Type 16-Bit UUID
BLEHeadder[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
BLEHeadder[6] = 0xFE; // Eddystone UUID 1 MSB
BLEHeadder[7] = 0x11; // Length of TLM Beacon Data is constant 17 B (not counting the length field itself)
BLEHeadder[8] = 0x16; // Type Service Data
BLEHeadder[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
BLEHeadder[10] = 0xFE; // Eddystone UUID 1 MSB
BLEHeadder[11] = 0x20; // Eddystone Frame Type - TLM
}
#endif

View file

@ -1,8 +1,12 @@
/* /*
* BLEEddystoneTLM.cpp * BLEEddystoneTLM.h
* *
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on pcbreflux's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef _BLEEddystoneTLM_H_ #ifndef _BLEEddystoneTLM_H_
@ -10,6 +14,9 @@
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include "BLEUUID.h" #include "BLEUUID.h"
#include <BLEAdvertisedDevice.h> #include <BLEAdvertisedDevice.h>
@ -57,5 +64,6 @@ private:
} __attribute__((packed)) m_eddystoneData; } __attribute__((packed)) m_eddystoneData;
}; // BLEEddystoneTLM }; // BLEEddystoneTLM
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* _BLEEddystoneTLM_H_ */ #endif /* _BLEEddystoneTLM_H_ */

View file

@ -3,14 +3,20 @@
* *
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*
* Upgraded on: Feb 20, 2023 * Upgraded on: Feb 20, 2023
* By: Tomas Pilny * By: Tomas Pilny
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on pcbreflux's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <string.h> #include <string.h>
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#include "BLEEddystoneURL.h" #include "BLEEddystoneURL.h"
@ -158,7 +164,11 @@ void BLEEddystoneURL::setData(String data) {
} // setData } // setData
void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { void BLEEddystoneURL::setUUID(BLEUUID l_uuid) {
#if defined(CONFIG_BLUEDROID_ENABLED)
uint16_t beaconUUID = l_uuid.getNative()->uuid.uuid16; uint16_t beaconUUID = l_uuid.getNative()->uuid.uuid16;
#elif defined(CONFIG_NIMBLE_ENABLED)
uint16_t beaconUUID = l_uuid.getNative()->u16.value;
#endif
BLEHeadder[10] = beaconUUID >> 8; BLEHeadder[10] = beaconUUID >> 8;
BLEHeadder[9] = beaconUUID & 0x00FF; BLEHeadder[9] = beaconUUID & 0x00FF;
} // setUUID } // setUUID

View file

@ -3,9 +3,13 @@
* *
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*
* Upgraded on: Feb 20, 2023 * Upgraded on: Feb 20, 2023
* By: Tomas Pilny * By: Tomas Pilny
* *
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on pcbreflux's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef _BLEEddystoneURL_H_ #ifndef _BLEEddystoneURL_H_
@ -13,6 +17,9 @@
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include "BLEUUID.h" #include "BLEUUID.h"
#include <BLEAdvertisedDevice.h> #include <BLEAdvertisedDevice.h>
#include "esp_bt.h" #include "esp_bt.h"
@ -57,5 +64,6 @@ private:
char BLEHeadder[12]; char BLEHeadder[12];
}; // BLEEddystoneURL }; // BLEEddystoneURL
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* _BLEEddystoneURL_H_ */ #endif /* _BLEEddystoneURL_H_ */

View file

@ -1,66 +0,0 @@
/*
* BLEEddystoneURL.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
* Upgraded on: Feb 17, 2023
* By: Tomas Pilny
*
*/
#ifndef _BLEEddystoneURL_H_
#define _BLEEddystoneURL_H_
#include "BLEUUID.h"
#include <BLEAdvertisedDevice.h>
#include <Arduino.h>
#define EDDYSTONE_URL_FRAME_TYPE 0x10
extern String EDDYSTONE_URL_PREFIX[];
extern String EDDYSTONE_URL_SUFFIX[];
/**
* @brief Representation of a beacon.
* See:
* * https://github.com/google/eddystone
*/
class BLEEddystoneURL {
public:
BLEEddystoneURL();
BLEEddystoneURL(BLEAdvertisedDevice *advertisedDevice);
std::string getData();
String getFrame();
BLEUUID getUUID();
int8_t getPower();
std::string getURL();
String getPrefix();
String getSuffix();
std::string getDecodedURL();
void setData(std::string data);
void setUUID(BLEUUID l_uuid);
void setPower(int8_t advertisedTxPower);
void setURL(std::string url);
int setSmartURL(String url);
private:
<<<<<<< Updated upstream
uint16_t beaconUUID;
uint8_t lengthURL;
struct {
uint8_t frameType;
int8_t advertisedTxPower;
uint8_t url[18]; // 18 bytes: 1 byte for URL scheme + up to 17 bytes of URL
} __attribute__((packed)) m_eddystoneData;
=======
uint8_t lengthURL; // Describes length of URL part including prefix and suffix - max 18 B (excluding TX power, frame type and preceding header)
struct {
int8_t advertisedTxPower;
uint8_t url[18]; // Byte [0] is for prefix. Last byte **can** contain suffix
} __attribute__((packed)) m_eddystoneData;
void _initHeadder();
char BLEHeadder[12];
>>>>>>> Stashed changes
}; // BLEEddystoneURL
#endif /* _BLEEddystoneURL_H_ */

View file

@ -5,4 +5,9 @@
* Author: kolban * Author: kolban
*/ */
#include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED
//#include "BLEExceptions.h" //#include "BLEExceptions.h"
#endif /* SOC_BLE_SUPPORTED */

View file

@ -3,15 +3,37 @@
* *
* Created on: Jan 03, 2018 * Created on: Jan 03, 2018
* Author: chegewara * Author: chegewara
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLEHIDDevice.h" #include "BLEHIDDevice.h"
#include "BLE2904.h" #include "BLE2904.h"
#include "BLEDescriptor.h"
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#ifdef CONFIG_NIMBLE_ENABLED
#include <host/ble_att.h>
#endif
/***************************************************************************
* Common functions *
***************************************************************************/
BLEHIDDevice::BLEHIDDevice(BLEServer *server) { BLEHIDDevice::BLEHIDDevice(BLEServer *server) {
/* /*
@ -45,10 +67,12 @@ BLEHIDDevice::BLEHIDDevice(BLEServer *server) {
m_batteryLevelCharacteristic = m_batteryLevelCharacteristic =
m_batteryService->createCharacteristic((uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); m_batteryService->createCharacteristic((uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor);
#if CONFIG_BLUEDROID_ENABLED
BLE2902 *batLevelIndicator = new BLE2902(); BLE2902 *batLevelIndicator = new BLE2902();
// Battery Level Notification is ON by default, making it work always on BLE Pairing and Bonding // Battery Level Notification is ON by default, making it work always on BLE Pairing and Bonding
batLevelIndicator->setNotifications(true); batLevelIndicator->setNotifications(true);
m_batteryLevelCharacteristic->addDescriptor(batLevelIndicator); m_batteryLevelCharacteristic->addDescriptor(batLevelIndicator);
#endif
/* /*
* This value is setup here because its default value in most usage cases, its very rare to use boot mode * This value is setup here because its default value in most usage cases, its very rare to use boot mode
@ -117,16 +141,19 @@ BLECharacteristic *BLEHIDDevice::inputReport(uint8_t reportID) {
BLECharacteristic *inputReportCharacteristic = BLECharacteristic *inputReportCharacteristic =
m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908)); BLEDescriptor *inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908));
BLE2902 *p2902 = new BLE2902();
inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
inputReportDescriptor->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}; uint8_t desc1_val[] = {reportID, 0x01};
inputReportDescriptor->setValue((uint8_t *)desc1_val, 2); inputReportDescriptor->setValue((uint8_t *)desc1_val, 2);
inputReportCharacteristic->addDescriptor(p2902);
inputReportCharacteristic->addDescriptor(inputReportDescriptor); inputReportCharacteristic->addDescriptor(inputReportDescriptor);
#if CONFIG_BLUEDROID_ENABLED
BLE2902 *p2902 = new BLE2902();
p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
inputReportCharacteristic->addDescriptor(p2902);
#endif
return inputReportCharacteristic; return inputReportCharacteristic;
} }
@ -175,7 +202,9 @@ BLECharacteristic *BLEHIDDevice::featureReport(uint8_t reportID) {
*/ */
BLECharacteristic *BLEHIDDevice::bootInput() { BLECharacteristic *BLEHIDDevice::bootInput() {
BLECharacteristic *bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY); BLECharacteristic *bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY);
#if CONFIG_BLUEDROID_ENABLED
bootInputCharacteristic->addDescriptor(new BLE2902()); bootInputCharacteristic->addDescriptor(new BLE2902());
#endif
return bootInputCharacteristic; return bootInputCharacteristic;
} }
@ -252,5 +281,5 @@ BLEService *BLEHIDDevice::batteryService() {
return m_batteryService; return m_batteryService;
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jan 03, 2018 * Created on: Jan 03, 2018
* Author: chegewara * Author: chegewara
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef _BLEHIDDEVICE_H_ #ifndef _BLEHIDDEVICE_H_
@ -12,7 +16,7 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include "BLECharacteristic.h" #include "BLECharacteristic.h"
#include "BLEService.h" #include "BLEService.h"
@ -75,6 +79,6 @@ private:
BLECharacteristic *m_batteryLevelCharacteristic; //0x2a19 BLECharacteristic *m_batteryLevelCharacteristic; //0x2a19
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* _BLEHIDDEVICE_H_ */ #endif /* _BLEHIDDEVICE_H_ */

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,10 @@
* *
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
@ -11,27 +15,80 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <functional> #include <functional>
#include <esp_gattc_api.h>
#include "BLERemoteService.h" #include "BLERemoteService.h"
#include "BLERemoteDescriptor.h" #include "BLERemoteDescriptor.h"
#include "BLEUUID.h" #include "BLEUUID.h"
#include "RTOS.h" #include "RTOS.h"
#include "BLEUtils.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gattc_api.h>
#endif
/***************************************************************************
* NimBLE includes and definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_sm.h>
#include <host/ble_gatt.h>
#include <host/ble_att.h>
#define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN
#define ESP_GATT_CHAR_PROP_BIT_READ BLE_GATT_CHR_PROP_READ
#define ESP_GATT_CHAR_PROP_BIT_WRITE BLE_GATT_CHR_PROP_WRITE
#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR BLE_GATT_CHR_PROP_WRITE_NO_RSP
#define ESP_GATT_CHAR_PROP_BIT_BROADCAST BLE_GATT_CHR_PROP_BROADCAST
#define ESP_GATT_CHAR_PROP_BIT_NOTIFY BLE_GATT_CHR_PROP_NOTIFY
#define ESP_GATT_CHAR_PROP_BIT_INDICATE BLE_GATT_CHR_PROP_INDICATE
#endif
/***************************************************************************
* Common types *
***************************************************************************/
typedef std::function<void(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)> notify_callback;
/***************************************************************************
* NimBLE types *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
typedef struct {
const BLEUUID *uuid;
void *task_data;
} desc_filter_t;
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLERemoteService; class BLERemoteService;
class BLERemoteDescriptor; class BLERemoteDescriptor;
typedef std::function<void(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)> notify_callback;
/** /**
* @brief A model of a remote %BLE characteristic. * @brief A model of a remote %BLE characteristic.
*/ */
class BLERemoteCharacteristic { class BLERemoteCharacteristic {
public: public:
~BLERemoteCharacteristic(); /***************************************************************************
* Common public declarations *
***************************************************************************/
// Public member functions ~BLERemoteCharacteristic();
bool canBroadcast(); bool canBroadcast();
bool canIndicate(); bool canIndicate();
bool canNotify(); bool canNotify();
@ -49,29 +106,34 @@ public:
uint32_t readUInt32(); uint32_t readUInt32();
float readFloat(); float readFloat();
void registerForNotify(notify_callback _callback, bool notifications = true, bool descriptorRequiresRegistration = true); void registerForNotify(notify_callback _callback, bool notifications = true, bool descriptorRequiresRegistration = true);
void writeValue(uint8_t *data, size_t length, bool response = false); bool writeValue(uint8_t *data, size_t length, bool response = false);
void writeValue(String newValue, bool response = false); bool writeValue(String newValue, bool response = false);
void writeValue(uint8_t newValue, bool response = false); bool writeValue(uint8_t newValue, bool response = false);
String toString(); String toString();
uint8_t *readRawData(); uint8_t *readRawData();
void setAuth(esp_gatt_auth_req_t auth); void setAuth(uint8_t auth);
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
bool subscribe(bool notifications = true, notify_callback notifyCallback = nullptr, bool response = true);
bool unsubscribe(bool response = true);
#endif
private: private:
BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService *pRemoteService);
friend class BLEClient; friend class BLEClient;
friend class BLERemoteService; friend class BLERemoteService;
friend class BLERemoteDescriptor; 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); * Common private properties *
***************************************************************************/
void removeDescriptors();
void retrieveDescriptors();
// Private properties
BLEUUID m_uuid; BLEUUID m_uuid;
esp_gatt_char_prop_t m_charProp; uint8_t m_charProp;
esp_gatt_auth_req_t m_auth; uint8_t m_auth;
uint16_t m_handle; uint16_t m_handle;
BLERemoteService *m_pRemoteService; BLERemoteService *m_pRemoteService;
FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt");
@ -83,8 +145,46 @@ private:
// We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID.
std::map<std::string, BLERemoteDescriptor *> m_descriptorMap; std::map<std::string, BLERemoteDescriptor *> m_descriptorMap;
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint16_t m_defHandle;
uint16_t m_endHandle;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
void removeDescriptors();
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, uint8_t charProp, BLERemoteService *pRemoteService);
void retrieveDescriptors();
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam);
#endif
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLERemoteCharacteristic(BLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
bool retrieveDescriptors(const BLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, void *arg);
#endif
}; // BLERemoteCharacteristic }; // BLERemoteCharacteristic
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */

View file

@ -3,23 +3,48 @@
* *
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "WString.h"
#include <sstream> #include <sstream>
#include "BLERemoteDescriptor.h" #include "BLERemoteDescriptor.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
BLERemoteDescriptor::BLERemoteDescriptor(uint16_t handle, BLEUUID uuid, BLERemoteCharacteristic *pRemoteCharacteristic) { /***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <freertos/task.h>
#include <host/ble_gatt.h>
#include <host/ble_att.h>
#include <host/ble_hs.h>
#endif
/***************************************************************************
* Common functions *
***************************************************************************/
BLERemoteDescriptor::BLERemoteDescriptor(uint16_t handle, BLEUUID uuid, BLERemoteCharacteristic *pRemoteCharacteristic) {
m_handle = handle; m_handle = handle;
m_uuid = uuid; m_uuid = uuid;
m_pRemoteCharacteristic = pRemoteCharacteristic; m_pRemoteCharacteristic = pRemoteCharacteristic;
m_auth = ESP_GATT_AUTH_REQ_NONE; m_auth = 0;
} }
/** /**
@ -46,6 +71,75 @@ BLEUUID BLERemoteDescriptor::getUUID() {
return m_uuid; return m_uuid;
} // getUUID } // getUUID
uint8_t BLERemoteDescriptor::readUInt8() {
String value = readValue();
if (value.length() >= 1) {
return (uint8_t)value[0];
}
return 0;
} // readUInt8
uint16_t BLERemoteDescriptor::readUInt16() {
String value = readValue();
if (value.length() >= 2) {
return *(uint16_t *)value.c_str();
}
return 0;
} // readUInt16
uint32_t BLERemoteDescriptor::readUInt32() {
String value = readValue();
if (value.length() >= 4) {
return *(uint32_t *)value.c_str();
}
return 0;
} // readUInt32
/**
* @brief Return a string representation of this BLE Remote Descriptor.
* @return A string representation of this BLE Remote Descriptor.
*/
String BLERemoteDescriptor::toString() {
char val[6];
snprintf(val, sizeof(val), "%d", getHandle());
String res = "handle: ";
res += val;
res += ", uuid: " + getUUID().toString();
return res;
} // toString
/**
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor.
* @param [in] response True if we expect a response.
*/
bool BLERemoteDescriptor::writeValue(String newValue, bool response) {
return writeValue((uint8_t *)newValue.c_str(), newValue.length(), response);
} // writeValue
/**
* @brief Write a byte value to the Descriptor.
* @param [in] The single byte to write.
* @param [in] True if we expect a response.
*/
bool BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) {
return writeValue(&newValue, 1, response);
} // writeValue
/**
* @brief Set authentication request type for characteristic
* @param [in] auth Authentication request type.
*/
void BLERemoteDescriptor::setAuth(uint8_t auth) {
m_auth = auth;
}
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void BLERemoteDescriptor::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam) { void BLERemoteDescriptor::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam) {
switch (event) { switch (event) {
// ESP_GATTC_READ_DESCR_EVT // ESP_GATTC_READ_DESCR_EVT
@ -99,7 +193,7 @@ String BLERemoteDescriptor::readValue() {
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server
getHandle(), // The handle of this characteristic getHandle(), // The handle of this characteristic
m_auth (esp_gatt_auth_req_t)m_auth
); // Security ); // Security
if (errRc != ESP_OK) { if (errRc != ESP_OK) {
@ -115,55 +209,19 @@ String BLERemoteDescriptor::readValue() {
return m_value; return m_value;
} // readValue } // readValue
uint8_t BLERemoteDescriptor::readUInt8() {
String value = readValue();
if (value.length() >= 1) {
return (uint8_t)value[0];
}
return 0;
} // readUInt8
uint16_t BLERemoteDescriptor::readUInt16() {
String value = readValue();
if (value.length() >= 2) {
return *(uint16_t *)value.c_str();
}
return 0;
} // readUInt16
uint32_t BLERemoteDescriptor::readUInt32() {
String value = readValue();
if (value.length() >= 4) {
return *(uint32_t *)value.c_str();
}
return 0;
} // readUInt32
/**
* @brief Return a string representation of this BLE Remote Descriptor.
* @return A string representation of this BLE Remote Descriptor.
*/
String BLERemoteDescriptor::toString() {
char val[6];
snprintf(val, sizeof(val), "%d", getHandle());
String res = "handle: ";
res += val;
res += ", uuid: " + getUUID().toString();
return res;
} // toString
/** /**
* @brief Write data to the BLE Remote Descriptor. * @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor. * @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send. * @param [in] length The length of the data to send.
* @param [in] response True if we expect a response. * @param [in] response True if we expect a response.
* @return True if successful
*/ */
void BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response) { bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response) {
log_v(">> writeValue: %s", toString().c_str()); log_v(">> writeValue: %s", toString().c_str());
// Check to see that we are connected. // Check to see that we are connected.
if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected"); log_e("Disconnected");
return; return false;
} }
m_semaphoreWriteDescrEvt.take("writeValue"); m_semaphoreWriteDescrEvt.take("writeValue");
@ -172,7 +230,7 @@ void BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), getHandle(), m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), getHandle(),
length, // Data length length, // Data length
data, // Data data, // Data
response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, m_auth response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, (esp_gatt_auth_req_t)m_auth
); );
if (errRc != ESP_OK) { if (errRc != ESP_OK) {
log_e("esp_ble_gattc_write_char_descr: %d", errRc); log_e("esp_ble_gattc_write_char_descr: %d", errRc);
@ -180,33 +238,246 @@ void BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response
m_semaphoreWriteDescrEvt.wait("writeValue"); m_semaphoreWriteDescrEvt.wait("writeValue");
log_v("<< writeValue"); log_v("<< writeValue");
return (errRc == ESP_OK);
} // writeValue } // writeValue
/** #endif
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor. /***************************************************************************
* @param [in] response True if we expect a response. * NimBLE functions *
*/ ***************************************************************************/
void BLERemoteDescriptor::writeValue(String newValue, bool response) {
writeValue((uint8_t *)newValue.c_str(), newValue.length(), response); #if defined(CONFIG_NIMBLE_ENABLED)
} // writeValue
/** /**
* @brief Write a byte value to the Descriptor. * @brief Remote descriptor constructor.
* @param [in] The single byte to write. * @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
* @param [in] True if we expect a response. * @param [in] dsc A pointer to the struct that contains the descriptor information.
*/ */
void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { BLERemoteDescriptor::BLERemoteDescriptor(BLERemoteCharacteristic *pRemoteCharacteristic, const struct ble_gatt_dsc *dsc) {
writeValue(&newValue, 1, response); log_d(">> BLERemoteDescriptor()");
} // writeValue switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16: m_uuid = BLEUUID(dsc->uuid.u16.value); break;
case BLE_UUID_TYPE_32: m_uuid = BLEUUID(dsc->uuid.u32.value); break;
case BLE_UUID_TYPE_128: m_uuid = BLEUUID(const_cast<ble_uuid128_t *>(&dsc->uuid.u128)); break;
default: break;
}
/** m_handle = dsc->handle;
* @brief Set authentication request type for characteristic m_pRemoteCharacteristic = pRemoteCharacteristic;
* @param [in] auth Authentication request type. m_auth = 0;
*/
void BLERemoteDescriptor::setAuth(esp_gatt_auth_req_t auth) { log_d("<< BLERemoteDescriptor(): %s", m_uuid.toString().c_str());
m_auth = auth;
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ /**
* @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor.
*/
String BLERemoteDescriptor::readValue() {
log_d(">> Descriptor readValue: %s", toString().c_str());
BLEClient *pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
String value{};
if (!pClient->isConnected()) {
log_e("Disconnected");
return value;
}
int rc = 0;
int retryCount = 1;
BLETaskData taskData(const_cast<BLERemoteDescriptor *>(this), 0, &value);
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, BLERemoteDescriptor::onReadCB, &taskData);
if (rc != 0) {
goto exit;
}
BLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
switch (rc) {
case 0:
case BLE_HS_EDONE: rc = 0; break;
// Descriptor is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
log_i("Attribute not long");
rc = ble_gattc_read(pClient->getConnId(), m_handle, BLERemoteDescriptor::onReadCB, &taskData);
if (rc != 0) {
goto exit;
}
retryCount++;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection()) {
break;
}
/* Else falls through. */
default: goto exit;
}
} while (rc != 0 && retryCount--);
m_semaphoreReadDescrEvt.take("readValue");
m_value = value;
m_semaphoreReadDescrEvt.give();
exit:
if (rc != 0) {
log_e("<< readValue failed rc=%d, %s", rc, BLEUtils::returnCodeToString(rc));
} else {
log_d("<< Descriptor readValue(): length: %d rc=%d", value.length(), rc);
}
return value;
} // readValue
/**
* @brief Callback for Descriptor read operation.
* @return success == 0 or error code.
*/
int BLERemoteDescriptor::onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
BLETaskData *pTaskData = static_cast<BLETaskData *>(arg);
BLERemoteDescriptor *desc = static_cast<BLERemoteDescriptor *>(pTaskData->m_pInstance);
uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
if (error->status == BLE_HS_ENOTCONN) {
log_e("<< Descriptor Read; Not connected");
BLEUtils::taskRelease(*pTaskData, error->status);
return error->status;
}
if (conn_id != conn_handle) {
return 0;
}
log_d("Read complete; status=%d conn_handle=%d", error->status, conn_handle);
String *strBuf = static_cast<String *>(pTaskData->m_pBuf);
int rc = error->status;
if (rc == 0) {
if (attr) {
uint32_t data_len = OS_MBUF_PKTLEN(attr->om);
if (((*strBuf).length() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
log_d("Got %d bytes", data_len);
(*strBuf) += String((char *)attr->om->om_data, data_len);
return 0;
}
}
}
BLEUtils::taskRelease(*pTaskData, rc);
return rc;
}
/**
* @brief Callback for descriptor write operation.
* @return success == 0 or error code.
*/
int BLERemoteDescriptor::onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
BLETaskData *pTaskData = static_cast<BLETaskData *>(arg);
BLERemoteDescriptor *descriptor = static_cast<BLERemoteDescriptor *>(pTaskData->m_pInstance);
int rc = error->status;
if (rc == BLE_HS_ENOTCONN) {
log_e("<< Descriptor Write; Not connected");
BLEUtils::taskRelease(*pTaskData, rc);
return rc;
}
if (descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle) {
return 0;
}
log_i("Write complete; status=%d conn_handle=%d", rc, conn_handle);
BLEUtils::taskRelease(*pTaskData, rc);
return 0;
}
/**
* @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
* @param [in] response True if we expect a write response.
* @return True if successful
*/
bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response) {
log_d(">> Descriptor writeValue: %s", toString().c_str());
BLEClient *pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
// Check to see that we are connected.
if (!pClient->isConnected()) {
log_e("Disconnected");
return false;
}
int rc = 0;
int retryCount = 1;
uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
BLETaskData taskData(const_cast<BLERemoteDescriptor *>(this));
// Check if the data length is longer than we can write in 1 connection event.
// If so we must do a long write which requires a response.
if (length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
goto exit;
}
do {
if (length > mtu) {
log_i("long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, BLERemoteDescriptor::onWriteCB, &taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, data, length, BLERemoteDescriptor::onWriteCB, &taskData);
}
if (rc != 0) {
goto exit;
}
BLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
switch (rc) {
case 0:
case BLE_HS_EDONE: rc = 0; break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
log_e("Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection()) {
break;
}
/* Else falls through. */
default: goto exit;
}
} while (rc != 0 && retryCount--);
exit:
if (rc != 0) {
log_e("<< writeValue failed rc=%d, %s", rc, BLEUtils::returnCodeToString(rc));
} else {
log_d("<< writeValue success. length: %d rc=%d", length, rc);
}
return (rc == 0);
} // writeValue
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
@ -11,21 +15,41 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <string> #include <string>
#include <esp_gattc_api.h>
#include "BLERemoteCharacteristic.h" #include "BLERemoteCharacteristic.h"
#include "BLEUUID.h" #include "BLEUUID.h"
#include "RTOS.h" #include "RTOS.h"
#include "BLEUtils.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gattc_api.h>
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLERemoteCharacteristic; class BLERemoteCharacteristic;
/** /**
* @brief A model of remote %BLE descriptor. * @brief A model of remote %BLE descriptor.
*/ */
class BLERemoteDescriptor { class BLERemoteDescriptor {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
uint16_t getHandle(); uint16_t getHandle();
BLERemoteCharacteristic *getRemoteCharacteristic(); BLERemoteCharacteristic *getRemoteCharacteristic();
BLEUUID getUUID(); BLEUUID getUUID();
@ -34,24 +58,50 @@ public:
uint16_t readUInt16(void); uint16_t readUInt16(void);
uint32_t readUInt32(void); uint32_t readUInt32(void);
String toString(void); String toString(void);
void writeValue(uint8_t *data, size_t length, bool response = false); bool writeValue(uint8_t *data, size_t length, bool response = false);
void writeValue(String newValue, bool response = false); bool writeValue(String newValue, bool response = false);
void writeValue(uint8_t newValue, bool response = false); bool writeValue(uint8_t newValue, bool response = false);
void setAuth(esp_gatt_auth_req_t auth); void setAuth(uint8_t auth);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
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);
#endif
private: private:
friend class BLERemoteCharacteristic; friend class BLERemoteCharacteristic;
BLERemoteDescriptor(uint16_t handle, BLEUUID uuid, BLERemoteCharacteristic *pRemoteCharacteristic);
uint16_t m_handle; // Server handle of this descriptor. /***************************************************************************
BLEUUID m_uuid; // UUID of this descriptor. * Common private properties *
String m_value; // Last received value of the descriptor. ***************************************************************************/
uint16_t m_handle; // Server handle of this descriptor.
BLEUUID m_uuid; // UUID of this descriptor.
String m_value; // Last received value of the descriptor.
uint8_t m_auth;
BLERemoteCharacteristic *m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. BLERemoteCharacteristic *m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated.
FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
FreeRTOS::Semaphore m_semaphoreWriteDescrEvt = FreeRTOS::Semaphore("WriteDescrEvt"); FreeRTOS::Semaphore m_semaphoreWriteDescrEvt = FreeRTOS::Semaphore("WriteDescrEvt");
esp_gatt_auth_req_t m_auth;
/***************************************************************************
* Common private declarations *
***************************************************************************/
BLERemoteDescriptor(uint16_t handle, BLEUUID uuid, BLERemoteCharacteristic *pRemoteCharacteristic);
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLERemoteDescriptor(BLERemoteCharacteristic *pRemoteCharacteristic, const struct ble_gatt_dsc *dsc);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */

View file

@ -3,12 +3,21 @@
* *
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <sstream> #include <sstream>
#include "BLERemoteService.h" #include "BLERemoteService.h"
@ -17,97 +26,14 @@
#include <esp_err.h> #include <esp_err.h>
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#pragma GCC diagnostic warning "-Wunused-but-set-parameter" /***************************************************************************
* Common functions *
BLERemoteService::BLERemoteService(esp_gatt_id_t srvcId, BLEClient *pClient, uint16_t startHandle, uint16_t endHandle) { ***************************************************************************/
log_v(">> BLERemoteService()");
m_srvcId = srvcId;
m_pClient = pClient;
m_uuid = BLEUUID(m_srvcId);
m_haveCharacteristics = false;
m_startHandle = startHandle;
m_endHandle = endHandle;
log_v("<< BLERemoteService()");
}
BLERemoteService::~BLERemoteService() { BLERemoteService::~BLERemoteService() {
removeCharacteristics(); removeCharacteristics();
} }
/*
static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
if (id1.id.inst_id != id2.id.inst_id) {
return false;
}
if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
return false;
}
return true;
} // compareSrvcId
*/
/**
* @brief Handle GATT Client events
*/
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<std::string, BLERemoteCharacteristic*>(
BLEUUID(evtParam->get_char.char_id.uuid).toString().c_str(),
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) {
log_e("esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
break;
}
//m_semaphoreGetCharEvt.give();
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_characteristicMapByHandle) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
/** /**
* @brief Get the remote characteristic object for the characteristic UUID. * @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid. * @param [in] uuid Remote characteristic uuid.
@ -144,52 +70,6 @@ BLERemoteCharacteristic *BLERemoteService::getCharacteristic(BLEUUID uuid) {
return nullptr; return nullptr;
} // getCharacteristic } // getCharacteristic
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
*/
void BLERemoteService::retrieveCharacteristics() {
log_v(">> getCharacteristics() for service: %s", getUUID().toString().c_str());
removeCharacteristics(); // Forget any previous characteristics.
uint16_t offset = 0;
esp_gattc_char_elem_t result;
while (true) {
uint16_t count = 1; // only room for 1 result allocated, so go one by one
esp_gatt_status_t status =
::esp_ble_gattc_get_all_char(getClient()->getGattcIf(), getClient()->getConnId(), m_startHandle, m_endHandle, &result, &count, offset);
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
break;
}
if (status != ESP_GATT_OK) { // If we got an error, end.
log_e("esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
break;
}
if (count == 0) { // If we failed to get any new records, end.
break;
}
log_d("Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
// We now have a new characteristic ... let us add that to our set of known characteristics
BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(result.char_handle, BLEUUID(result.uuid), result.properties, this);
m_characteristicMap.insert(
std::pair<std::string, BLERemoteCharacteristic *>(pNewRemoteCharacteristic->getUUID().toString().c_str(), pNewRemoteCharacteristic)
);
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic *>(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.
log_v("<< getCharacteristics()");
} // getCharacteristics
/** /**
* @brief Retrieve a map of all the characteristics of this service. * @brief Retrieve a map of all the characteristics of this service.
* @return A map of all the characteristics of this service. * @return A map of all the characteristics of this service.
@ -248,10 +128,6 @@ uint16_t BLERemoteService::getEndHandle() {
return m_endHandle; return m_endHandle;
} // getEndHandle } // getEndHandle
esp_gatt_id_t *BLERemoteService::getSrvcId() {
return &m_srvcId;
} // getSrvcId
uint16_t BLERemoteService::getStartHandle() { uint16_t BLERemoteService::getStartHandle() {
return m_startHandle; return m_startHandle;
} // getStartHandle } // getStartHandle
@ -330,5 +206,222 @@ String BLERemoteService::toString() {
return res; return res;
} // toString } // toString
#endif /* CONFIG_BLUEDROID_ENABLED */ /***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLERemoteService::BLERemoteService(esp_gatt_id_t srvcId, BLEClient *pClient, uint16_t startHandle, uint16_t endHandle) {
log_v(">> BLERemoteService()");
m_srvcId = srvcId;
m_pClient = pClient;
m_uuid = BLEUUID(m_srvcId);
m_haveCharacteristics = false;
m_startHandle = startHandle;
m_endHandle = endHandle;
log_v("<< BLERemoteService()");
}
esp_gatt_id_t *BLERemoteService::getSrvcId() {
return &m_srvcId;
} // getSrvcId
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
*/
void BLERemoteService::retrieveCharacteristics() {
log_v(">> getCharacteristics() for service: %s", getUUID().toString().c_str());
removeCharacteristics(); // Forget any previous characteristics.
uint16_t offset = 0;
esp_gattc_char_elem_t result;
while (true) {
uint16_t count = 1; // only room for 1 result allocated, so go one by one
esp_gatt_status_t status =
::esp_ble_gattc_get_all_char(getClient()->getGattcIf(), getClient()->getConnId(), m_startHandle, m_endHandle, &result, &count, offset);
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
break;
}
if (status != ESP_GATT_OK) { // If we got an error, end.
log_e("esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
break;
}
if (count == 0) { // If we failed to get any new records, end.
break;
}
log_d("Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
// We now have a new characteristic ... let us add that to our set of known characteristics
BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(result.char_handle, BLEUUID(result.uuid), result.properties, this);
m_characteristicMap.insert(
std::pair<std::string, BLERemoteCharacteristic *>(pNewRemoteCharacteristic->getUUID().toString().c_str(), pNewRemoteCharacteristic)
);
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic *>(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.
log_v("<< getCharacteristics()");
} // getCharacteristics
/**
* @brief Handle GATT Client events
*/
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<std::string, BLERemoteCharacteristic*>(
BLEUUID(evtParam->get_char.char_id.uuid).toString().c_str(),
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) {
log_e("esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
break;
}
//m_semaphoreGetCharEvt.give();
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_characteristicMapByHandle) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
#endif
/***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLERemoteService::BLERemoteService(BLEClient *pClient, const struct ble_gatt_svc *service) {
log_v(">> BLERemoteService()");
m_pClient = pClient;
switch (service->uuid.u.type) {
case BLE_UUID_TYPE_16: m_uuid = BLEUUID(service->uuid.u16.value); break;
case BLE_UUID_TYPE_32: m_uuid = BLEUUID(service->uuid.u32.value); break;
case BLE_UUID_TYPE_128: m_uuid = BLEUUID(const_cast<ble_uuid128_t *>(&service->uuid.u128)); break;
default: break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
m_haveCharacteristics = false;
log_v("<< BLERemoteService(): %s", m_uuid.toString().c_str());
}
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
*/
void BLERemoteService::retrieveCharacteristics() {
log_v(">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
int rc = 0;
BLETaskData taskData(const_cast<BLERemoteService *>(this));
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), m_startHandle, m_endHandle, BLERemoteService::characteristicDiscCB, &taskData);
if (rc != 0) {
log_e("ble_gattc_disc_all_chrs: rc=%d %s", rc, BLEUtils::returnCodeToString(rc));
return;
}
BLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
if (rc == 0 || rc == BLE_HS_EDONE) {
log_d("<< retrieveCharacteristics()");
return;
}
log_e("<< retrieveCharacteristics() rc=%d %s", rc, BLEUtils::returnCodeToString(rc));
} // retrieveCharacteristics
/**
* @brief Callback for characteristic discovery.
* @return success == 0 or error code.
*/
int BLERemoteService::characteristicDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *chr, void *arg) {
log_d("Characteristic Discovered >> status: %d handle: %d", error->status, (error->status == 0) ? chr->val_handle : -1);
BLETaskData *pTaskData = (BLETaskData *)arg;
BLERemoteService *service = (BLERemoteService *)pTaskData->m_pInstance;
if (error->status == BLE_HS_ENOTCONN) {
log_e("<< Characteristic Discovery; Not connected");
BLEUtils::taskRelease(*pTaskData, error->status);
return error->status;
}
// Make sure the discovery is for this device
if (service->getClient()->getConnId() != conn_handle) {
return 0;
}
if (error->status == 0) {
// Found a service - add it to the vector
BLERemoteCharacteristic *pRemoteCharacteristic = new BLERemoteCharacteristic(service, chr);
service->m_characteristicMap.insert(
std::pair<std::string, BLERemoteCharacteristic *>(pRemoteCharacteristic->getUUID().toString().c_str(), pRemoteCharacteristic)
);
service->m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic *>(chr->val_handle, pRemoteCharacteristic));
return 0;
}
BLEUtils::taskRelease(*pTaskData, error->status);
service->m_haveCharacteristics = true;
log_d("<< Characteristic Discovered");
return error->status;
}
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
@ -11,7 +15,11 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <map> #include <map>
@ -19,6 +27,19 @@
#include "BLERemoteCharacteristic.h" #include "BLERemoteCharacteristic.h"
#include "BLEUUID.h" #include "BLEUUID.h"
#include "RTOS.h" #include "RTOS.h"
#include "BLEUtils.h"
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gatt.h>
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEClient; class BLEClient;
class BLERemoteCharacteristic; class BLERemoteCharacteristic;
@ -28,15 +49,18 @@ class BLERemoteCharacteristic;
*/ */
class BLERemoteService { class BLERemoteService {
public: public:
virtual ~BLERemoteService(); /***************************************************************************
* Common public declarations *
***************************************************************************/
// Public methods virtual ~BLERemoteService();
BLERemoteCharacteristic *getCharacteristic(const char *uuid); // Get the specified characteristic reference. BLERemoteCharacteristic *getCharacteristic(const char *uuid); // Get the specified characteristic reference.
BLERemoteCharacteristic *getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. BLERemoteCharacteristic *getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference.
BLERemoteCharacteristic *getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. BLERemoteCharacteristic *getCharacteristic(uint16_t uuid); // Get the specified characteristic reference.
std::map<std::string, BLERemoteCharacteristic *> *getCharacteristics(); std::map<std::string, BLERemoteCharacteristic *> *getCharacteristics();
std::map<uint16_t, BLERemoteCharacteristic *> *getCharacteristicsByHandle(); // Get the characteristics map. std::map<uint16_t, BLERemoteCharacteristic *> *getCharacteristicsByHandle(); // Get the characteristics map.
void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic *> **pCharacteristicMap); void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic *> **pCharacteristicMap);
void retrieveCharacteristics();
BLEClient *getClient(void); // Get a reference to the client associated with this service. BLEClient *getClient(void); // Get a reference to the client associated with this service.
uint16_t getHandle(); // Get the handle of this service. uint16_t getHandle(); // Get the handle of this service.
@ -46,24 +70,12 @@ public:
String toString(void); String toString(void);
private: private:
// Private constructor ... never meant to be created by a user application.
BLERemoteService(esp_gatt_id_t srvcId, BLEClient *pClient, uint16_t startHandle, uint16_t endHandle);
// Friends
friend class BLEClient; friend class BLEClient;
friend class BLERemoteCharacteristic; friend class BLERemoteCharacteristic;
// Private methods /***************************************************************************
void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server. * Common private properties *
esp_gatt_id_t *getSrvcId(void); ***************************************************************************/
uint16_t getStartHandle(); // Get the start handle for this service.
uint16_t getEndHandle(); // Get the end handle for this service.
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam);
void removeCharacteristics();
// Properties
// We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID.
std::map<std::string, BLERemoteCharacteristic *> m_characteristicMap; std::map<std::string, BLERemoteCharacteristic *> m_characteristicMap;
@ -74,12 +86,47 @@ private:
bool m_haveCharacteristics; // Have we previously obtained the characteristics. bool m_haveCharacteristics; // Have we previously obtained the characteristics.
BLEClient *m_pClient; BLEClient *m_pClient;
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
esp_gatt_id_t m_srvcId;
BLEUUID m_uuid; // The UUID of this service. BLEUUID m_uuid; // The UUID of this service.
uint16_t m_startHandle; // The starting handle of this service. uint16_t m_startHandle; // The starting handle of this service.
uint16_t m_endHandle; // The ending handle of this service. uint16_t m_endHandle; // The ending handle of this service.
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_gatt_id_t m_srvcId;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
uint16_t getStartHandle(); // Get the start handle for this service.
uint16_t getEndHandle(); // Get the end handle for this service.
void removeCharacteristics();
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
// Private constructor ... never meant to be created by a user application.
BLERemoteService(esp_gatt_id_t srvcId, BLEClient *pClient, uint16_t startHandle, uint16_t endHandle);
esp_gatt_id_t *getSrvcId();
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *evtParam);
#endif
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLERemoteService(BLEClient *pClient, const struct ble_gatt_svc *service);
static int characteristicDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *chr, void *arg);
#endif
}; // BLERemoteService }; // BLERemoteService
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */

View file

@ -6,12 +6,21 @@
* *
* Update: April, 2021 * Update: April, 2021
* add BLE5 support * add BLE5 support
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <esp_err.h> #include <esp_err.h>
@ -23,12 +32,17 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* Constructor * @brief Constructor
*/ */
BLEScan::BLEScan() { BLEScan::BLEScan() {
memset(&m_scan_params, 0, sizeof(m_scan_params)); // Initialize all params memset(&m_scan_params, 0, sizeof(m_scan_params)); // Initialize all params
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. #if defined(CONFIG_BLUEDROID_ENABLED)
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan.
m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
m_scan_params.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE; m_scan_params.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE;
@ -38,15 +52,246 @@ BLEScan::BLEScan() {
m_shouldParse = true; m_shouldParse = true;
setInterval(100); setInterval(100);
setWindow(100); setWindow(100);
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data).
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
m_scan_params.filter_duplicates = 1; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
m_ignoreResults = false;
m_pTaskData = nullptr;
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
m_maxResults = 0xFF;
m_stopped = true;
m_wantDuplicates = false;
m_shouldParse = true;
// This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
setInterval(100);
// The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec)
setWindow(100);
#endif
} // BLEScan } // BLEScan
/**
* @brief Scan destructor, release any allocated resources.
*/
BLEScan::~BLEScan() {
clearResults();
}
/**
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
* @return N/A.
*/
void BLEScan::setActiveScan(bool active) {
#if defined(CONFIG_BLUEDROID_ENABLED)
if (active) {
m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
} else {
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
}
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_scan_params.passive = !active;
#endif
} // setActiveScan
/**
* @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.
* @param [in] shouldParse True if we wish to parse advertised package or raw payload. Default is true.
*/
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks *pAdvertisedDeviceCallbacks, bool wantDuplicates, bool shouldParse) {
m_wantDuplicates = wantDuplicates;
#if defined(CONFIG_NIMBLE_ENABLED)
setDuplicateFilter(!wantDuplicates);
#endif
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
m_shouldParse = shouldParse;
} // setAdvertisedDeviceCallbacks
/**
* @brief Set the interval to scan.
* @param [in] The interval in msecs.
*/
void BLEScan::setInterval(uint16_t intervalMSecs) {
#if defined(CONFIG_BLUEDROID_ENABLED)
m_scan_params.scan_interval = intervalMSecs / 0.625;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_scan_params.itvl = intervalMSecs / 0.625;
#endif
} // setInterval
/**
* @brief Set the window to actively scan.
* @param [in] windowMSecs How long to actively scan.
*/
void BLEScan::setWindow(uint16_t windowMSecs) {
#if defined(CONFIG_BLUEDROID_ENABLED)
m_scan_params.scan_window = windowMSecs / 0.625;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_scan_params.window = windowMSecs / 0.625;
#endif
} // setWindow
// 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) {
log_i("erase device: %s", address.toString().c_str());
BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString().c_str())->second;
m_scanResults.m_vectorAdvertisedDevices.erase(address.toString().c_str());
delete advertisedDevice;
}
/**
* @brief Dump the scan results to the log.
*/
void BLEScanResults::dump() {
log_v(">> Dump scan results:");
for (int i = 0; i < getCount(); i++) {
log_d("- %s", getDevice(i).toString().c_str());
}
} // dump
/**
* @brief Return the count of devices found in the last scan.
* @return The number of devices found in the last scan.
*/
int BLEScanResults::getCount() {
return m_vectorAdvertisedDevices.size();
} // getCount
/**
* @brief Return the specified device at the given index.
* The index should be between 0 and getCount()-1.
* @param [in] i The index of the device.
* @return The device at the specified index.
*/
BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t 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();
}
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#if defined(SOC_BLE_50_SUPPORTED)
void BLEScan::setExtendedScanCallback(BLEExtAdvertisingCallbacks *cb) {
m_pExtendedScanCb = cb;
}
/**
* @brief This function is used to enable scanning.
*
* @param[in] duration : Scan duration
* @param[in] period : Time interval from when the Controller started its last Scan Duration until it begins the subsequent Scan Duration.
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::startExtScan(uint32_t duration, uint16_t period) {
esp_err_t rc = esp_ble_gap_start_ext_scan(duration, period);
if (rc) {
log_e("extended scan start failed: %d", rc);
}
return rc;
}
esp_err_t BLEScan::stopExtScan() {
esp_err_t rc = esp_ble_gap_stop_ext_scan();
if (rc) {
log_e("extended scan stop failed: %d", rc);
}
return rc;
}
void BLEScan::setPeriodicScanCallback(BLEPeriodicScanCallbacks *cb) {
m_pPeriodicScanCb = cb;
}
/**
* @brief This function is used to set the extended scan parameters to be used on the advertising channels.
*
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::setExtScanParams() {
esp_ble_ext_scan_params_t ext_scan_params = {
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE,
.cfg_mask = ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK | ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK,
.uncoded_cfg = {BLE_SCAN_TYPE_ACTIVE, 40, 40},
.coded_cfg = {BLE_SCAN_TYPE_ACTIVE, 40, 40},
};
esp_err_t rc = esp_ble_gap_set_ext_scan_params(&ext_scan_params);
if (rc) {
log_e("set extend scan params error, error code = %x", rc);
}
return rc;
}
/**
* @brief This function is used to set the extended scan parameters to be used on the advertising channels.
*
* @param[in] params : scan parameters
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::setExtScanParams(esp_ble_ext_scan_params_t *ext_scan_params) {
esp_err_t rc = esp_ble_gap_set_ext_scan_params(ext_scan_params);
if (rc) {
log_e("set extend scan params error, error code = %x", rc);
}
return rc;
}
#endif // SOC_BLE_50_SUPPORTED
/** /**
* @brief Handle GAP events related to scans. * @brief Handle GAP events related to scans.
* @param [in] event The event type for this event. * @param [in] event The event type for this event.
* @param [in] param Parameter data for this event. * @param [in] param Parameter data for this event.
*/ */
void BLEScan::handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { void BLEScan::handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) { switch (event) {
// --------------------------- // ---------------------------
@ -118,6 +363,7 @@ void BLEScan::handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_
advertisedDevice->setAddress(advertisedAddress); advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setRSSI(param->scan_rst.rssi); advertisedDevice->setRSSI(param->scan_rst.rssi);
advertisedDevice->setAdFlag(param->scan_rst.flag); advertisedDevice->setAdFlag(param->scan_rst.flag);
advertisedDevice->setAdvType(param->scan_rst.ble_evt_type);
if (m_shouldParse) { if (m_shouldParse) {
advertisedDevice->parseAdvertisement((uint8_t *)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len); advertisedDevice->parseAdvertisement((uint8_t *)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len);
} else { } else {
@ -245,126 +491,6 @@ void BLEScan::handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_
} // End switch } // End switch
} // gapEventHandler } // gapEventHandler
/**
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
* @return N/A.
*/
void BLEScan::setActiveScan(bool active) {
if (active) {
m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
} else {
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
}
} // setActiveScan
/**
* @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.
* @param [in] shouldParse True if we wish to parse advertised package or raw payload. Default is true.
*/
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks *pAdvertisedDeviceCallbacks, bool wantDuplicates, bool shouldParse) {
m_wantDuplicates = wantDuplicates;
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
m_shouldParse = shouldParse;
} // setAdvertisedDeviceCallbacks
#ifdef SOC_BLE_50_SUPPORTED
void BLEScan::setExtendedScanCallback(BLEExtAdvertisingCallbacks *cb) {
m_pExtendedScanCb = cb;
}
/**
* @brief This function is used to set the extended scan parameters to be used on the advertising channels.
*
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::setExtScanParams() {
esp_ble_ext_scan_params_t ext_scan_params = {
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE,
.cfg_mask = ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK | ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK,
.uncoded_cfg = {BLE_SCAN_TYPE_ACTIVE, 40, 40},
.coded_cfg = {BLE_SCAN_TYPE_ACTIVE, 40, 40},
};
esp_err_t rc = esp_ble_gap_set_ext_scan_params(&ext_scan_params);
if (rc) {
log_e("set extend scan params error, error code = %x", rc);
}
return rc;
}
/**
* @brief This function is used to set the extended scan parameters to be used on the advertising channels.
*
* @param[in] params : scan parameters
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::setExtScanParams(esp_ble_ext_scan_params_t *ext_scan_params) {
esp_err_t rc = esp_ble_gap_set_ext_scan_params(ext_scan_params);
if (rc) {
log_e("set extend scan params error, error code = %x", rc);
}
return rc;
}
/**
* @brief This function is used to enable scanning.
*
* @param[in] duration : Scan duration
* @param[in] period : Time interval from when the Controller started its last Scan Duration until it begins the subsequent Scan Duration.
*
* @return - ESP_OK : success
* - other : failed
*
*/
esp_err_t BLEScan::startExtScan(uint32_t duration, uint16_t period) {
esp_err_t rc = esp_ble_gap_start_ext_scan(duration, period);
if (rc) {
log_e("extended scan start failed: %d", rc);
}
return rc;
}
esp_err_t BLEScan::stopExtScan() {
esp_err_t rc;
rc = esp_ble_gap_stop_ext_scan();
return rc;
}
void BLEScan::setPeriodicScanCallback(BLEPeriodicScanCallbacks *cb) {
m_pPeriodicScanCb = cb;
}
#endif // SOC_BLE_50_SUPPORTED
/**
* @brief Set the interval to scan.
* @param [in] The interval in msecs.
*/
void BLEScan::setInterval(uint16_t intervalMSecs) {
m_scan_params.scan_interval = intervalMSecs / 0.625;
} // setInterval
/**
* @brief Set the window to actively scan.
* @param [in] windowMSecs How long to actively scan.
*/
void BLEScan::setWindow(uint16_t windowMSecs) {
m_scan_params.scan_window = windowMSecs / 0.625;
} // setWindow
/** /**
* @brief Start scanning. * @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan. * @param [in] duration The duration in seconds for which to scan.
@ -425,7 +551,7 @@ BLEScanResults *BLEScan::start(uint32_t duration, bool is_continue) {
* @brief Stop an in progress scan. * @brief Stop an in progress scan.
* @return N/A. * @return N/A.
*/ */
void BLEScan::stop() { bool BLEScan::stop() {
log_v(">> stop()"); log_v(">> stop()");
esp_err_t errRc = ::esp_ble_gap_stop_scanning(); esp_err_t errRc = ::esp_ble_gap_stop_scanning();
@ -435,67 +561,303 @@ void BLEScan::stop() {
if (errRc != ESP_OK) { if (errRc != ESP_OK) {
log_e("esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); log_e("esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
return; return false;
} }
log_v("<< stop()"); log_v("<< stop()");
return true;
} // stop } // stop
// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address #endif // CONFIG_BLUEDROID_ENABLED
void BLEScan::erase(BLEAddress address) {
log_i("erase device: %s", address.toString().c_str()); /***************************************************************************
BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString().c_str())->second; * NimBLE functions *
m_scanResults.m_vectorAdvertisedDevices.erase(address.toString().c_str()); ***************************************************************************/
delete advertisedDevice;
#if defined(CONFIG_NIMBLE_ENABLED)
/**
* @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen.
* @param [in] enabled If true, scanned devices will only be reported once.
* @details The controller has a limited buffer and will start reporting
* duplicate devices once the limit is reached.
*/
void BLEScan::setDuplicateFilter(bool enabled) {
m_scan_params.filter_duplicates = enabled;
} // setDuplicateFilter
/**
* @brief Get the status of the scanner.
* @return true if scanning or scan starting.
*/
bool BLEScan::isScanning() {
return ble_gap_disc_active();
} }
/** /**
* @brief Dump the scan results to the log. * @brief Handle GAP events related to scans.
* @param [in] event The event type for this event.
* @param [in] param Parameter data for this event.
*/ */
void BLEScanResults::dump() { int BLEScan::handleGAPEvent(ble_gap_event *event, void *arg) {
log_v(">> Dump scan results:"); BLEScan *pScan = (BLEScan *)arg;
for (int i = 0; i < getCount(); i++) {
log_d("- %s", getDevice(i).toString().c_str());
}
} // dump
/** switch (event->type) {
* @brief Return the count of devices found in the last scan. case BLE_GAP_EVENT_DISC:
* @return The number of devices found in the last scan. {
*/ if (pScan->m_ignoreResults) {
int BLEScanResults::getCount() { log_i("Scan op in progress - ignoring results");
return m_vectorAdvertisedDevices.size(); return 0;
} // getCount }
/** const auto &disc = event->disc;
* @brief Return the specified device at the given index. const auto event_type = disc.event_type;
* The index should be between 0 and getCount()-1. const bool isLegacyAdv = true;
* @param [in] i The index of the device. BLEAddress advertisedAddress(disc.addr);
* @return The device at the specified index.
*/ BLEClient *client = BLEDevice::getClientByAddress(advertisedAddress);
BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) { if (client != nullptr && client->isConnected()) {
uint32_t x = 0; log_i("Client %s connected - ignoring event", advertisedAddress.toString().c_str());
BLEAdvertisedDevice dev = *m_vectorAdvertisedDevices.begin()->second; return 0;
for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) { }
dev = *it->second;
if (x == i) { BLEAdvertisedDevice *advertisedDevice = nullptr;
break;
// If we've seen this device before get a pointer to it from the vector
for (auto &it : pScan->m_scanResults.m_vectorAdvertisedDevices) {
if (it.second->getAddress() == advertisedAddress) {
advertisedDevice = it.second;
break;
}
}
// If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device.
if (advertisedDevice == nullptr) {
// Check if we have reach the scan results limit, ignore this one if so.
// We still need to store each device when maxResults is 0 to be able to append the scan results
if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF && (pScan->m_scanResults.m_vectorAdvertisedDevices.size() >= pScan->m_maxResults)) {
return 0;
}
if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
log_i("Scan response without advertisement: %s", advertisedAddress.toString().c_str());
}
advertisedDevice = new BLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAddressType(event->disc.addr.type);
advertisedDevice->setAdvType(event_type);
pScan->m_scanResults.m_vectorAdvertisedDevices.insert(
std::pair<std::string, BLEAdvertisedDevice *>(advertisedAddress.toString().c_str(), advertisedDevice)
);
log_i("New advertiser: %s", advertisedAddress.toString().c_str());
} else if (advertisedDevice != nullptr) {
log_i("Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
return 0;
}
advertisedDevice->setRSSI(event->disc.rssi);
if (pScan->m_shouldParse) {
advertisedDevice->parseAdvertisement((uint8_t *)event->disc.data, event->disc.length_data);
} else {
advertisedDevice->setPayload((uint8_t *)event->disc.data, event->disc.length_data, event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
}
advertisedDevice->setScan(pScan);
if (pScan->m_pAdvertisedDeviceCallbacks) {
// If not active scanning or scan response is not available
// report the result to the callback now.
if (pScan->m_scan_params.passive || !isLegacyAdv || !advertisedDevice->isScannable()) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice);
// Otherwise, wait for the scan response so we can report the complete data.
} else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice);
}
}
// If not storing results and we have invoked the callback, delete the device.
if (pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) {
pScan->erase(advertisedAddress);
}
return 0;
} }
x++;
}
return dev;
}
BLEScanResults *BLEScan::getResults() { case BLE_GAP_EVENT_DISC_COMPLETE:
{
log_d("discovery complete; reason=%d", event->disc_complete.reason);
// If a device advertised with scan response available and it was not received
// the callback would not have been invoked, so do it here.
if (pScan->m_pAdvertisedDeviceCallbacks) {
for (auto &it : pScan->m_scanResults.m_vectorAdvertisedDevices) {
if (!it.second->m_callbackSent) {
pScan->m_pAdvertisedDeviceCallbacks->onResult(*(it.second));
}
}
}
if (pScan->m_maxResults == 0) {
pScan->clearResults();
}
if (pScan->m_scanCompleteCB != nullptr) {
pScan->m_scanCompleteCB(pScan->m_scanResults);
}
if (pScan->m_pTaskData != nullptr) {
BLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
}
return 0;
}
default: return 0;
}
} // gapEventHandler
/**
* @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] scanCompleteCB A function to be called when scanning has completed.
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return True if scan started or false if there was an error.
*/
bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) {
log_d(">> start(duration=%d)", duration);
if (!is_continue) {
clearResults();
}
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
// Save the duration in the case that the host is reset so we can reuse it.
m_duration = duration;
// If 0 duration specified then we assume a continuous scan is desired.
if (duration == 0) {
duration = BLE_HS_FOREVER;
} else {
// convert duration to milliseconds
duration = duration * 1000;
}
// Set the flag to ignore the results while we are deleting the vector
if (!is_continue) {
m_ignoreResults = true;
}
int rc = ble_gap_disc(BLEDevice::m_ownAddrType, duration, &m_scan_params, BLEScan::handleGAPEvent, this);
switch (rc) {
case 0:
case BLE_HS_EALREADY: break;
case BLE_HS_EBUSY: log_e("Unable to scan - connection in progress."); break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED: log_e("Unable to scan - Host Reset"); break;
default: log_e("Error initiating GAP discovery procedure; rc=%d, %s", rc, BLEUtils::returnCodeToString(rc)); break;
}
m_ignoreResults = false;
log_d("<< start()");
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
return true;
} // start
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return The BLEScanResults.
*/
BLEScanResults *BLEScan::start(uint32_t duration, bool is_continue) {
if (duration == 0) {
log_w("Blocking scan called with duration = forever");
}
if (m_pTaskData != nullptr) {
log_e("Scan already in progress");
return &m_scanResults;
}
BLETaskData taskData(this);
m_pTaskData = &taskData;
if (start(duration, nullptr, is_continue)) {
BLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
}
m_pTaskData = nullptr;
return &m_scanResults; return &m_scanResults;
} } // start
void BLEScan::clearResults() { /**
for (auto _dev : m_scanResults.m_vectorAdvertisedDevices) { * @brief Stop an in progress scan.
delete _dev.second; * @return True if successful.
*/
bool BLEScan::stop() {
log_d(">> stop()");
int rc = ble_gap_disc_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
log_e("Failed to cancel scan; rc=%d", rc);
return false;
} }
m_scanResults.m_vectorAdvertisedDevices.clear();
if (m_maxResults == 0) {
clearResults();
}
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
if (m_pTaskData != nullptr) {
BLEUtils::taskRelease(*m_pTaskData);
}
log_d("<< stop()");
return true;
} // stop
/**
* @brief Called when host reset, we set a flag to stop scanning until synced.
*/
void BLEScan::onHostReset() {
m_ignoreResults = true;
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ /**
* @brief If the host reset and re-synced this is called.
* If the application was scanning indefinitely with a callback, restart it.
*/
void BLEScan::onHostSync() {
m_ignoreResults = false;
if (m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
start(m_duration, m_scanCompleteCB);
}
}
#endif // CONFIG_NIMBLE_ENABLED
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Jul 1, 2017 * Created on: Jul 1, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ #ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_
@ -11,22 +15,51 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gap_ble_api.h>
/***************************************************************************
* Common includes *
***************************************************************************/
// #include <vector>
#include <string> #include <string>
#include "BLEAdvertisedDevice.h" #include "BLEAdvertisedDevice.h"
#include "BLEClient.h" #include "BLEClient.h"
#include "BLEUtils.h"
#include "RTOS.h" #include "RTOS.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_ble_api.h>
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#endif
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEAdvertisedDevice; class BLEAdvertisedDevice;
class BLEAdvertisedDeviceCallbacks; class BLEAdvertisedDeviceCallbacks;
class BLEExtAdvertisingCallbacks; class BLEExtAdvertisingCallbacks;
class BLEClient; class BLEClient;
class BLEScan; class BLEScan;
class BLEPeriodicScanCallbacks; class BLEPeriodicScanCallbacks;
struct BLETaskData;
/***************************************************************************
* Bluedroid type definitions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
struct esp_ble_periodic_adv_sync_estab_param_t { struct esp_ble_periodic_adv_sync_estab_param_t {
uint8_t status; /*!< periodic advertising sync status */ uint8_t status; /*!< periodic advertising sync status */
uint16_t sync_handle; /*!< periodic advertising sync handle */ uint16_t sync_handle; /*!< periodic advertising sync handle */
@ -37,6 +70,7 @@ struct esp_ble_periodic_adv_sync_estab_param_t {
uint16_t period_adv_interval; /*!< periodic advertising interval */ uint16_t period_adv_interval; /*!< periodic advertising interval */
uint8_t adv_clk_accuracy; /*!< periodic advertising clock accuracy */ uint8_t adv_clk_accuracy; /*!< periodic advertising clock accuracy */
}; };
#endif
/** /**
* @brief The result of having performed a scan. * @brief The result of having performed a scan.
@ -47,12 +81,21 @@ struct esp_ble_periodic_adv_sync_estab_param_t {
*/ */
class BLEScanResults { class BLEScanResults {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
void dump(); void dump();
int getCount(); int getCount();
BLEAdvertisedDevice getDevice(uint32_t i); BLEAdvertisedDevice getDevice(uint32_t i);
private: private:
friend BLEScan; friend BLEScan;
/***************************************************************************
* Common private properties *
***************************************************************************/
std::map<std::string, BLEAdvertisedDevice *> m_vectorAdvertisedDevices; std::map<std::string, BLEAdvertisedDevice *> m_vectorAdvertisedDevices;
}; };
@ -63,37 +106,52 @@ private:
*/ */
class BLEScan { class BLEScan {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
~BLEScan();
void setActiveScan(bool active); void setActiveScan(bool active);
void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks *pAdvertisedDeviceCallbacks, bool wantDuplicates = false, bool shouldParse = true); void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks *pAdvertisedDeviceCallbacks, bool wantDuplicates = false, bool shouldParse = true);
void setInterval(uint16_t intervalMSecs); void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs); void setWindow(uint16_t windowMSecs);
bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false); bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);
BLEScanResults *start(uint32_t duration, bool is_continue = false); BLEScanResults *start(uint32_t duration, bool is_continue = false);
void stop(); bool stop();
void erase(BLEAddress address); void erase(BLEAddress address);
BLEScanResults *getResults(); BLEScanResults *getResults();
void clearResults(); void clearResults();
#ifdef SOC_BLE_50_SUPPORTED /***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(SOC_BLE_50_SUPPORTED) && defined(CONFIG_BLUEDROID_ENABLED)
void setExtendedScanCallback(BLEExtAdvertisingCallbacks *cb); void setExtendedScanCallback(BLEExtAdvertisingCallbacks *cb);
void setPeriodicScanCallback(BLEPeriodicScanCallbacks *cb); void setPeriodicScanCallback(BLEPeriodicScanCallbacks *cb);
esp_err_t stopExtScan(); esp_err_t stopExtScan();
esp_err_t setExtScanParams(); esp_err_t setExtScanParams();
esp_err_t setExtScanParams(esp_ble_ext_scan_params_t *ext_scan_params);
esp_err_t startExtScan(uint32_t duration, uint16_t period); esp_err_t startExtScan(uint32_t duration, uint16_t period);
esp_err_t setExtScanParams(esp_ble_ext_scan_params_t *ext_scan_params);
private:
BLEExtAdvertisingCallbacks *m_pExtendedScanCb = nullptr;
BLEPeriodicScanCallbacks *m_pPeriodicScanCb = nullptr;
#endif // SOC_BLE_50_SUPPORTED #endif // SOC_BLE_50_SUPPORTED
private: /***************************************************************************
BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. * NimBLE public declarations *
friend class BLEDevice; ***************************************************************************/
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
#if defined(CONFIG_NIMBLE_ENABLED)
void setDuplicateFilter(bool enabled);
bool isScanning();
void clearDuplicateCache();
#endif
private:
friend class BLEDevice;
/***************************************************************************
* Common private properties *
***************************************************************************/
esp_ble_scan_params_t m_scan_params;
BLEAdvertisedDeviceCallbacks *m_pAdvertisedDeviceCallbacks = nullptr; BLEAdvertisedDeviceCallbacks *m_pAdvertisedDeviceCallbacks = nullptr;
bool m_stopped = true; bool m_stopped = true;
bool m_shouldParse = true; bool m_shouldParse = true;
@ -101,21 +159,79 @@ private:
BLEScanResults m_scanResults; BLEScanResults m_scanResults;
bool m_wantDuplicates; bool m_wantDuplicates;
void (*m_scanCompleteCB)(BLEScanResults scanResults); void (*m_scanCompleteCB)(BLEScanResults scanResults);
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_scan_params_t m_scan_params;
#if defined(SOC_BLE_50_SUPPORTED)
BLEExtAdvertisingCallbacks *m_pExtendedScanCb = nullptr;
BLEPeriodicScanCallbacks *m_pPeriodicScanCb = nullptr;
#endif // SOC_BLE_50_SUPPORTED
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint32_t m_duration;
ble_gap_disc_params m_scan_params;
bool m_ignoreResults;
BLETaskData *m_pTaskData;
uint8_t m_maxResults;
#endif
/***************************************************************************
* Common private definitions *
***************************************************************************/
BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton.
/***************************************************************************
* Bluedroid private definitions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE private definitions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void onHostReset();
void onHostSync();
static int handleGAPEvent(ble_gap_event *event, void *arg);
#endif
}; // BLEScan }; // BLEScan
class BLEPeriodicScanCallbacks { class BLEPeriodicScanCallbacks {
public: public:
virtual ~BLEPeriodicScanCallbacks() {} /***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLEPeriodicScanCallbacks() {}
virtual void onLostSync(uint16_t sync_handle) {}
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
virtual void onCreateSync(esp_bt_status_t status) {} virtual void onCreateSync(esp_bt_status_t status) {}
virtual void onCancelSync(esp_bt_status_t status) {} virtual void onCancelSync(esp_bt_status_t status) {}
virtual void onTerminateSync(esp_bt_status_t status) {} virtual void onTerminateSync(esp_bt_status_t status) {}
virtual void onLostSync(uint16_t sync_handle) {}
virtual void onSync(esp_ble_periodic_adv_sync_estab_param_t) {} virtual void onSync(esp_ble_periodic_adv_sync_estab_param_t) {}
virtual void onReport(esp_ble_gap_periodic_adv_report_t params) {} virtual void onReport(esp_ble_gap_periodic_adv_report_t params) {}
virtual void onStop(esp_bt_status_t status) {} virtual void onStop(esp_bt_status_t status) {}
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */

View file

@ -3,77 +3,114 @@
* *
* Created on: Dec 17, 2017 * Created on: Dec 17, 2017
* Author: chegewara * Author: chegewara
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on chegewara's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "BLESecurity.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "BLESecurity.h"
#include "BLEUtils.h"
#include "esp32-hal-log.h"
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_hs.h>
#endif
/***************************************************************************
* Common properties *
***************************************************************************/
uint32_t BLESecurity::m_passkey = BLE_SM_DEFAULT_PASSKEY;
/***************************************************************************
* Common functions *
***************************************************************************/
BLESecurity::BLESecurity() {} BLESecurity::BLESecurity() {}
BLESecurity::~BLESecurity() {} BLESecurity::~BLESecurity() {}
/*
* @brief Set requested authentication mode void BLESecurity::setAuthenticationMode(uint8_t auth_req) {
*/
void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
m_authReq = 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 #if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t));
#elif defined(CONFIG_NIMBLE_ENABLED)
BLESecurity::setAuthenticationMode(
(auth_req & BLE_SM_PAIR_AUTHREQ_BOND) != 0, (auth_req & BLE_SM_PAIR_AUTHREQ_MITM) != 0, (auth_req & BLE_SM_PAIR_AUTHREQ_SC) != 0
);
#endif
} }
/** void BLESecurity::setCapability(uint8_t iocap) {
* @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; m_iocap = iocap;
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
} // setCapability #elif defined(CONFIG_NIMBLE_ENABLED)
ble_hs_cfg.sm_io_cap = iocap;
#endif
}
/**
* @brief Init encryption key by server
* @param key_size is value between 7 and 16
*/
void BLESecurity::setInitEncryptionKey(uint8_t init_key) { void BLESecurity::setInitEncryptionKey(uint8_t init_key) {
m_initKey = init_key; m_initKey = init_key;
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t));
} // setInitEncryptionKey #elif defined(CONFIG_NIMBLE_ENABLED)
ble_hs_cfg.sm_our_key_dist = init_key;
#endif
}
/**
* @brief Init encryption key by client
* @param key_size is value between 7 and 16
*/
void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { void BLESecurity::setRespEncryptionKey(uint8_t resp_key) {
m_respKey = resp_key; m_respKey = resp_key;
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t));
} // setRespEncryptionKey #elif defined(CONFIG_NIMBLE_ENABLED)
ble_hs_cfg.sm_their_key_dist = resp_key;
#endif
}
/**
*
*
*/
void BLESecurity::setKeySize(uint8_t key_size) { void BLESecurity::setKeySize(uint8_t key_size) {
#if defined(CONFIG_BLUEDROID_ENABLED)
m_keySize = key_size; m_keySize = key_size;
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t));
} //setKeySize #endif
}
/**
* Setup for static PIN connection, call it first and then call setAuthenticationMode eventually to change it
*/
void BLESecurity::setStaticPIN(uint32_t pin) { void BLESecurity::setStaticPIN(uint32_t pin) {
uint32_t passkey = pin; m_passkey = pin;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); #if defined(CONFIG_BLUEDROID_ENABLED)
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &m_passkey, sizeof(uint32_t));
setCapability(ESP_IO_CAP_OUT); setCapability(ESP_IO_CAP_OUT);
setKeySize(); setKeySize();
setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY);
setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
#elif defined(CONFIG_NIMBLE_ENABLED)
setCapability(BLE_HS_IO_DISPLAY_ONLY);
setKeySize();
setAuthenticationMode(false, false, true); // No bonding, no MITM, secure connection only
setInitEncryptionKey(BLE_HS_KEY_DIST_ENC_KEY | BLE_HS_KEY_DIST_ID_KEY);
#endif
} }
/** /***************************************************************************
* @brief Debug function to display what keys are exchanged by peers * Bluedroid functions *
*/ ***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) {
char *key_str = nullptr; char *key_str = nullptr;
switch (key_type) { switch (key_type) {
@ -89,7 +126,34 @@ char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) {
default: key_str = (char *)"INVALID BLE KEY TYPE"; break; default: key_str = (char *)"INVALID BLE KEY TYPE"; break;
} }
return key_str; return key_str;
} // esp_key_type_to_str }
#endif
#endif /* CONFIG_BLUEDROID_ENABLED */ /***************************************************************************
* NimBLE functions *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void BLESecurity::setAuthenticationMode(bool bonding, bool mitm, bool sc) {
m_authReq = bonding ? BLE_SM_PAIR_AUTHREQ_BOND : 0;
m_authReq |= mitm ? BLE_SM_PAIR_AUTHREQ_MITM : 0;
m_authReq |= sc ? BLE_SM_PAIR_AUTHREQ_SC : 0;
ble_hs_cfg.sm_bonding = bonding;
ble_hs_cfg.sm_mitm = mitm;
ble_hs_cfg.sm_sc = sc;
}
bool BLESecurity::startSecurity(uint16_t connHandle, int *rcPtr) {
int rc = ble_gap_security_initiate(connHandle);
if (rc != 0) {
log_e("ble_gap_security_initiate: rc=%d %s", rc, BLEUtils::returnCodeToString(rc));
}
if (rcPtr) {
*rcPtr = rc;
}
return rc == 0 || rc == BLE_HS_EALREADY;
}
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,74 +3,148 @@
* *
* Created on: Dec 17, 2017 * Created on: Dec 17, 2017
* Author: chegewara * Author: chegewara
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on chegewara's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ #ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_
#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ #define COMPONENTS_CPP_UTILS_BLESECURITY_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include "WString.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_ble_api.h> #include <esp_gap_ble_api.h>
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#endif
/***************************************************************************
* Common definitions *
***************************************************************************/
#define BLE_SM_DEFAULT_PASSKEY 123456
/***************************************************************************
* Forward declarations *
***************************************************************************/
class BLEServer;
class BLEClient;
/**
* @brief Security management class
*/
class BLESecurity { class BLESecurity {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLESecurity(); BLESecurity();
virtual ~BLESecurity(); virtual ~BLESecurity();
void setAuthenticationMode(esp_ble_auth_req_t auth_req); static void setAuthenticationMode(uint8_t auth_req);
void setCapability(esp_ble_io_cap_t iocap); static void setCapability(uint8_t iocap);
void setInitEncryptionKey(uint8_t init_key); static void setInitEncryptionKey(uint8_t init_key);
void setRespEncryptionKey(uint8_t resp_key); static void setRespEncryptionKey(uint8_t resp_key);
void setKeySize(uint8_t key_size = 16); static void setKeySize(uint8_t key_size = 16);
void setStaticPIN(uint32_t pin); static void setStaticPIN(uint32_t pin);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static char *esp_key_type_to_str(esp_ble_key_type_t key_type); static char *esp_key_type_to_str(esp_ble_key_type_t key_type);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static void setAuthenticationMode(bool bonding, bool mitm, bool sc);
static bool startSecurity(uint16_t connHandle, int *rcPtr = nullptr);
#endif
private: private:
esp_ble_auth_req_t m_authReq; friend class BLEServer;
esp_ble_io_cap_t m_iocap; friend class BLEClient;
uint8_t m_initKey;
uint8_t m_respKey; /***************************************************************************
uint8_t m_keySize; * Common private properties *
***************************************************************************/
static uint8_t m_iocap;
static uint8_t m_authReq;
static uint8_t m_initKey;
static uint8_t m_respKey;
static uint32_t m_passkey;
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static uint8_t m_keySize;
#endif
}; // BLESecurity }; // BLESecurity
/* /**
* @brief Callbacks to handle GAP events related to authorization * @brief Callbacks to handle GAP events related to authorization
*/ */
class BLESecurityCallbacks { class BLESecurityCallbacks {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
virtual ~BLESecurityCallbacks(){}; 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; 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; 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; 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; virtual bool onConfirmPIN(uint32_t pin) = 0;
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0;
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
virtual void onAuthenticationComplete(ble_gap_conn_desc *) = 0;
#endif
}; // BLESecurityCallbacks }; // BLESecurityCallbacks
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ #endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_

View file

@ -3,14 +3,23 @@
* *
* Created on: Apr 16, 2017 * Created on: Apr 16, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/***************************************************************************
* Common includes *
***************************************************************************/
#include <esp_bt.h> #include <esp_bt.h>
#include <esp_bt_main.h>
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "BLEDevice.h" #include "BLEDevice.h"
#include "BLEServer.h" #include "BLEServer.h"
@ -21,6 +30,27 @@
#include <unordered_set> #include <unordered_set>
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/***************************************************************************
* Bluedroid includes *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_bt_main.h>
#endif
/***************************************************************************
* NimBLE includes *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <services/gap/ble_svc_gap.h>
#include <services/gatt/ble_svc_gatt.h>
#endif
/***************************************************************************
* Common functions *
***************************************************************************/
/** /**
* @brief Construct a %BLE Server * @brief Construct a %BLE Server
* *
@ -28,8 +58,17 @@
* the BLEDevice class. * the BLEDevice class.
*/ */
BLEServer::BLEServer() { BLEServer::BLEServer() {
m_appId = ESP_GATT_IF_NONE; #ifdef CONFIG_BLUEDROID_ENABLED
m_gatts_if = ESP_GATT_IF_NONE; m_gatts_if = ESP_GATT_IF_NONE;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait));
m_svcChanged = false;
#endif
m_appId = ESP_GATT_IF_NONE;
m_gattsStarted = false;
m_connectedCount = 0; m_connectedCount = 0;
m_connId = ESP_GATT_IF_NONE; m_connId = ESP_GATT_IF_NONE;
m_pServerCallbacks = nullptr; m_pServerCallbacks = nullptr;
@ -37,7 +76,9 @@ BLEServer::BLEServer() {
void BLEServer::createApp(uint16_t appId) { void BLEServer::createApp(uint16_t appId) {
m_appId = appId; m_appId = appId;
#ifdef CONFIG_BLUEDROID_ENABLED
registerApp(appId); registerApp(appId);
#endif
} // createApp } // createApp
/** /**
@ -64,7 +105,9 @@ BLEService *BLEServer::createService(const char *uuid) {
*/ */
BLEService *BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { BLEService *BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) {
log_v(">> createService - %s", uuid.toString().c_str()); log_v(">> createService - %s", uuid.toString().c_str());
#ifdef CONFIG_BLUEDROID_ENABLED
m_semaphoreCreateEvt.take("createService"); m_semaphoreCreateEvt.take("createService");
#endif
// Check that a service with the supplied UUID does not already exist. // Check that a service with the supplied UUID does not already exist.
if (m_serviceMap.getByUUID(uuid) != nullptr) { if (m_serviceMap.getByUUID(uuid) != nullptr) {
@ -76,7 +119,14 @@ BLEService *BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. 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. pService->executeCreate(this); // Perform the API calls to actually create the service.
#ifdef CONFIG_BLUEDROID_ENABLED
m_semaphoreCreateEvt.wait("createService"); m_semaphoreCreateEvt.wait("createService");
#endif
#ifdef CONFIG_NIMBLE_ENABLED
m_semaphoreCreateEvt.give();
serviceChanged();
#endif
log_v("<< createService"); log_v("<< createService");
return pService; return pService;
@ -121,6 +171,190 @@ uint32_t BLEServer::getConnectedCount() {
return m_connectedCount; return m_connectedCount;
} // getConnectedCount } // getConnectedCount
void BLEServer::start() {
if (m_gattsStarted) {
return;
}
#ifdef CONFIG_NIMBLE_ENABLED
int rc = ble_gatts_start();
if (rc != 0) {
log_e("ble_gatts_start; rc=%d, %s", rc, BLEUtils::returnCodeToString(rc));
return;
}
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
ble_gatts_show_local();
#endif
BLEService *svc = m_serviceMap.getFirst();
while (svc != nullptr) {
if (svc->m_removed == 0) {
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
if (rc != 0) {
abort();
}
}
BLECharacteristic *chr = svc->m_characteristicMap.getFirst();
while (chr != nullptr) {
if ((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
m_notifyChrVec.push_back(chr);
}
chr = svc->m_characteristicMap.getNext();
}
svc = m_serviceMap.getNext();
}
#endif
m_gattsStarted = true;
}
/**
* @brief Set the server callbacks.
*
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
* disconnecting. This function can be called to register a callback handler that will be invoked when these
* events are detected.
*
* @param [in] pCallbacks The callbacks to be invoked.
*/
void BLEServer::setCallbacks(BLEServerCallbacks *pCallbacks) {
m_pServerCallbacks = pCallbacks;
} // setCallbacks
/*
* Remove service
*/
void BLEServer::removeService(BLEService *service) {
#if defined(CONFIG_BLUEDROID_ENABLED)
service->stop();
service->executeDelete();
m_serviceMap.removeService(service);
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
if (service->m_removed == 0) {
int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0);
if (rc != 0) {
return;
}
service->m_removed = NIMBLE_ATT_REMOVE_DELETE;
serviceChanged();
m_serviceMap.removeService(service);
BLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
}
#endif
}
/**
* @brief Start advertising.
*
* Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
void BLEServer::startAdvertising() {
log_v(">> startAdvertising");
BLEDevice::startAdvertising();
log_v("<< startAdvertising");
} // startAdvertising
/* 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<uint16_t, conn_status_t>::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<uint16_t, conn_status_t> BLEServer::getPeerDevices(bool _client) {
return m_connectedServersMap;
}
uint16_t BLEServer::getPeerMTU(uint16_t conn_id) {
#if defined(CONFIG_BLUEDROID_ENABLED)
return m_connectedServersMap.find(conn_id)->second.mtu;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
return ble_att_mtu(conn_id);
#endif
}
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<uint16_t, conn_status_t>(conn_id, status));
}
bool BLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
return m_connectedServersMap.erase(conn_id) > 0;
}
void BLEServerCallbacks::onConnect(BLEServer *pServer) {
log_d("BLEServerCallbacks", ">> onConnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect
void BLEServerCallbacks::onDisconnect(BLEServer *pServer) {
log_d("BLEServerCallbacks", ">> onDisconnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onDisconnect()");
} // onDisconnect
/***************************************************************************
* Bluedroid functions *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/**
* 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) {
log_e("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.
log_v("<< connect(), rc=%d", rc == ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
/**
* 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);
}
uint16_t BLEServer::getGattsIf() { uint16_t BLEServer::getGattsIf() {
return m_gatts_if; return m_gatts_if;
} }
@ -288,69 +522,7 @@ void BLEServer::registerApp(uint16_t m_appId) {
log_v("<< registerApp"); log_v("<< registerApp");
} // registerApp } // registerApp
/** // Bluedroid callbacks
* @brief Set the server callbacks.
*
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
* disconnecting. This function can be called to register a callback handler that will be invoked when these
* events are detected.
*
* @param [in] pCallbacks The callbacks to be invoked.
*/
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.
*
* Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
void BLEServer::startAdvertising() {
log_v(">> startAdvertising");
BLEDevice::startAdvertising();
log_v("<< 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) {
log_e("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.
log_v("<< connect(), rc=%d", rc == ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
void BLEServerCallbacks::onConnect(BLEServer *pServer) {
log_d("BLEServerCallbacks", ">> onConnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect
void BLEServerCallbacks::onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) { void BLEServerCallbacks::onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) {
log_d("BLEServerCallbacks", ">> onConnect(): Default"); log_d("BLEServerCallbacks", ">> onConnect(): Default");
@ -358,12 +530,6 @@ void BLEServerCallbacks::onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t
log_d("BLEServerCallbacks", "<< onConnect()"); log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect } // onConnect
void BLEServerCallbacks::onDisconnect(BLEServer *pServer) {
log_d("BLEServerCallbacks", ">> onDisconnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onDisconnect()");
} // onDisconnect
void BLEServerCallbacks::onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) { void BLEServerCallbacks::onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) {
log_d("BLEServerCallbacks", ">> onDisconnect(): Default"); log_d("BLEServerCallbacks", ">> onDisconnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
@ -371,57 +537,435 @@ void BLEServerCallbacks::onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param
} // onDisconnect } // onDisconnect
void BLEServerCallbacks::onMtuChanged(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) { void BLEServerCallbacks::onMtuChanged(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) {
[[maybe_unused]]
uint16_t mtu = param->mtu.mtu;
log_d("BLEServerCallbacks", ">> onMtuChanged(): Default"); log_d("BLEServerCallbacks", ">> onMtuChanged(): Default");
log_d("BLEServerCallbacks", "Device: %s MTU: %d", BLEDevice::toString().c_str(), param->mtu.mtu); log_d("BLEServerCallbacks", "Device: %s MTU: %d", BLEDevice::toString().c_str(), mtu);
log_d("BLEServerCallbacks", "<< onMtuChanged()"); log_d("BLEServerCallbacks", "<< onMtuChanged()");
} // onMtuChanged } // onMtuChanged
/* multi connect support */ #endif
/* TODO do some more tweaks */
void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { /***************************************************************************
// set mtu in conn_status_t * NimBLE functions *
const std::map<uint16_t, conn_status_t>::iterator it = m_connectedServersMap.find(conn_id); ***************************************************************************/
if (it != m_connectedServersMap.end()) {
it->second.mtu = mtu; #if defined(CONFIG_NIMBLE_ENABLED)
std::swap(m_connectedServersMap[conn_id], it->second);
uint16_t BLEServer::getHandle() {
return getConnId();
}
/**
* @brief Resets the GATT server, used when services are added/removed after initialization.
*/
void BLEServer::resetGATT() {
if (getConnectedCount() > 0) {
return;
}
BLEDevice::stopAdvertising();
ble_gatts_reset();
ble_svc_gap_init();
ble_svc_gatt_init();
BLEService *svc = m_serviceMap.getFirst();
while (svc != nullptr) {
if (svc->m_removed > 0) {
if (svc->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
m_serviceMap.removeService(svc);
delete svc;
}
} else {
svc->start();
}
svc = m_serviceMap.getNext();
}
m_svcChanged = false;
m_gattsStarted = false;
}
/**
* @brief Handle a GATT Server Event.
*
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*
*/
int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) {
BLEServer *server = (BLEServer *)arg;
log_v(">> handleGAPEvent: %s", BLEUtils::gapEventToString(event->type));
int rc = 0;
struct ble_gap_conn_desc desc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
{
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
log_e("Connection failed");
BLEDevice::startAdvertising();
} else {
server->m_connId = event->connect.conn_handle;
server->addPeerDevice((void *)server, false, event->connect.conn_handle);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
if (rc != 0) {
return 0;
}
if (server->m_pServerCallbacks != nullptr) {
server->m_pServerCallbacks->onConnect(server);
server->m_pServerCallbacks->onConnect(server, &desc);
}
server->m_connectedCount++;
}
return 0;
} // BLE_GAP_EVENT_CONNECT
case BLE_GAP_EVENT_DISCONNECT:
{
// If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing.
switch (event->disconnect.reason) {
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
log_d("Disconnect - host reset, rc=%d", event->disconnect.reason);
BLEDevice::onReset(event->disconnect.reason);
break;
default: break;
}
if (server->removePeerDevice(event->disconnect.conn.conn_handle, false)) {
server->m_connectedCount--;
}
if (server->m_svcChanged) {
server->resetGATT();
}
if (server->m_pServerCallbacks != nullptr) {
server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
}
return 0;
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE:
{
log_i("subscribe event; attr_handle=%d, subscribed: %s", event->subscribe.attr_handle, (event->subscribe.cur_notify ? "true" : "false"));
for (auto &it : server->m_notifyChrVec) {
if (it->getHandle() == event->subscribe.attr_handle) {
uint16_t properties = it->getProperties();
if ((properties & BLE_GATT_CHR_F_READ_AUTHEN) || (properties & BLE_GATT_CHR_F_READ_AUTHOR) || (properties & BLE_GATT_CHR_F_READ_ENC)) {
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
if (rc != 0) {
break;
}
if (!desc.sec_state.encrypted) {
BLESecurity::startSecurity(event->subscribe.conn_handle);
}
}
it->setSubscribe(event);
break;
}
}
return 0;
} // BLE_GAP_EVENT_SUBSCRIBE
case BLE_GAP_EVENT_MTU:
{
log_i("mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value);
rc = ble_gap_conn_find(event->mtu.conn_handle, &desc);
if (rc != 0) {
return 0;
}
if (server->m_pServerCallbacks != nullptr) {
server->m_pServerCallbacks->onMtuChanged(server, &desc, event->mtu.value);
}
return 0;
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX:
{
BLECharacteristic *pChar = nullptr;
for (auto &it : server->m_notifyChrVec) {
if (it->getHandle() == event->notify_tx.attr_handle) {
pChar = it;
}
}
if (pChar == nullptr) {
return 0;
}
BLECharacteristicCallbacks::Status statusRC;
if (event->notify_tx.indication) {
if (event->notify_tx.status != 0) {
if (event->notify_tx.status == BLE_HS_EDONE) {
statusRC = BLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if (rc == BLE_HS_ETIMEOUT) {
statusRC = BLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
statusRC = BLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
return 0;
}
server->clearIndicateWait(event->notify_tx.conn_handle);
} else {
if (event->notify_tx.status == 0) {
statusRC = BLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
statusRC = BLECharacteristicCallbacks::Status::ERROR_GATT;
}
}
pChar->m_pCallbacks->onStatus(pChar, statusRC, event->notify_tx.status);
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE:
{
log_d("Advertising Complete");
BLEDevice::getAdvertising()->advCompleteCB();
return 0;
}
case BLE_GAP_EVENT_CONN_UPDATE:
{
log_d("Connection parameters updated.");
return 0;
} // BLE_GAP_EVENT_CONN_UPDATE
case BLE_GAP_EVENT_REPEAT_PAIRING:
{
/* We already have a bond with the peer, but it is attempting to
* establish a new secure link. This app sacrifices security for
* convenience: just throw away the old bond and accept the new link.
*/
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
if (rc != 0) {
return BLE_GAP_REPEAT_PAIRING_IGNORE;
}
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
* continue with the pairing operation.
*/
return BLE_GAP_REPEAT_PAIRING_RETRY;
} // BLE_GAP_EVENT_REPEAT_PAIRING
case BLE_GAP_EVENT_ENC_CHANGE:
{
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
if (rc != 0) {
return BLE_ATT_ERR_INVALID_HANDLE;
}
if (BLEDevice::m_securityCallbacks != nullptr) {
BLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc);
} else if (server->m_pServerCallbacks != nullptr) {
server->m_pServerCallbacks->onAuthenticationComplete(&desc);
}
return 0;
} // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_PASSKEY_ACTION:
{
struct ble_sm_io pkey = {0, 0};
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action;
// backward compatibility
pkey.passkey = BLESecurity::m_passkey;
// if the (static)passkey is the default, check the callback for custom value
// both values default to the same.
if (pkey.passkey == BLE_SM_DEFAULT_PASSKEY) {
if (server->m_pServerCallbacks != nullptr) {
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
}
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
log_d("BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
log_d("Passkey on device's display: %d", event->passkey.params.numcmp);
pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future
if (BLEDevice::m_securityCallbacks != nullptr) {
pkey.numcmp_accept = BLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp);
} else if (server->m_pServerCallbacks != nullptr) {
pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
log_d("BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
//TODO: Handle out of band pairing
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
static uint8_t tem_oob[16] = {0};
pkey.action = event->passkey.params.action;
for (int i = 0; i < 16; i++) {
pkey.oob[i] = tem_oob[i];
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
log_d("BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
log_d("Enter the passkey");
pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future
if (BLEDevice::m_securityCallbacks != nullptr) {
pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest();
} else if (server->m_pServerCallbacks != nullptr) {
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
log_d("BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
log_d("No passkey action required");
}
log_d("<< handleGATTServerEvent");
return 0;
} // BLE_GAP_EVENT_PASSKEY_ACTION
default: break;
}
log_d("<< handleGATTServerEvent");
return 0;
}
/**
* @brief Request an Update the connection parameters:
* * Can only be used after a connection has been established.
* @param [in] conn_handle The connection handle of the peer to send the request to.
* @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void BLEServer::updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) {
ble_gap_upd_params params;
params.latency = latency;
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if (rc != 0) {
log_e("Update params error: %d, %s", rc, BLEUtils::returnCodeToString(rc));
}
} // updateConnParams
bool BLEServer::setIndicateWait(uint16_t conn_handle) {
for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if (m_indWait[i] == conn_handle) {
return false;
}
}
return true;
}
void BLEServer::clearIndicateWait(uint16_t conn_handle) {
for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if (m_indWait[i] == conn_handle) {
m_indWait[i] = BLE_HS_CONN_HANDLE_NONE;
return;
}
} }
} }
std::map<uint16_t, conn_status_t> BLEServer::getPeerDevices(bool _client) { /**
return m_connectedServersMap; * @brief Disconnect the specified client with optional reason.
} * @param [in] connId Connection Id of the client to disconnect.
* @param [in] reason code for disconnecting.
* @return NimBLE host return code.
*/
int BLEServer::disconnect(uint16_t connId, uint8_t reason) {
log_d(">> disconnect()");
uint16_t BLEServer::getPeerMTU(uint16_t conn_id) { int rc = ble_gap_terminate(connId, reason);
return m_connectedServersMap.find(conn_id)->second.mtu; if (rc != 0) {
} log_e("ble_gap_terminate failed: rc=%d %s", rc, BLEUtils::returnCodeToString(rc));
}
void BLEServer::addPeerDevice(void *peer, bool _client, uint16_t conn_id) { log_d("<< disconnect()");
conn_status_t status = {.peer_device = peer, .connected = true, .mtu = 23}; return rc;
} // disconnect
m_connectedServersMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
}
bool BLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
return m_connectedServersMap.erase(conn_id) > 0;
}
/* multi connect support */
/** /**
* Update connection parameters can be called only after connection has been established * @brief Set the service changed flag
*/ */
void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { void BLEServer::serviceChanged() {
esp_ble_conn_update_params_t conn_params; if (m_gattsStarted) {
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); m_svcChanged = true;
conn_params.latency = latency; }
conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms } // serviceChanged
conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms // NimBLE callbacks
esp_ble_gap_update_conn_params(&conn_params);
void BLEServerCallbacks::onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) {
log_d("BLEServerCallbacks", ">> onConnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect
void BLEServerCallbacks::onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) {
log_d("BLEServerCallbacks", ">> onDisconnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onDisconnect()");
} // onDisconnect
void BLEServerCallbacks::onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *desc, uint16_t mtu) {
log_d("BLEServerCallbacks", ">> onMtuChanged(): Default");
log_d("BLEServerCallbacks", "Device: %s MTU: %d", BLEDevice::toString().c_str(), mtu);
log_d("BLEServerCallbacks", "<< onMtuChanged()");
} // onMtuChanged
uint32_t BLEServerCallbacks::onPassKeyRequest() {
log_d("BLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
} }
void BLEServer::disconnect(uint16_t connId) { void BLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc *) {
esp_ble_gatts_close(m_gatts_if, connId); log_d("BLEServerCallbacks", "onAuthenticationComplete: default");
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ bool BLEServerCallbacks::onConfirmPIN(uint32_t pin) {
log_d("BLEServerCallbacks", "onConfirmPIN: default: true");
return true;
}
#endif // CONFIG_NIMBLE_ENABLED
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Apr 16, 2017 * Created on: Apr 16, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_ #ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_
@ -11,13 +15,16 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gatts_api.h>
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include <string> #include <string>
#include <string.h> #include <string.h>
// #include "BLEDevice.h" #include "BLEDevice.h"
#include "BLEConnInfo.h"
#include "BLEUUID.h" #include "BLEUUID.h"
#include "BLEAdvertising.h" #include "BLEAdvertising.h"
#include "BLECharacteristic.h" #include "BLECharacteristic.h"
@ -25,24 +32,51 @@
#include "BLESecurity.h" #include "BLESecurity.h"
#include "RTOS.h" #include "RTOS.h"
#include "BLEAddress.h" #include "BLEAddress.h"
#include "BLEUtils.h"
#include "BLEUtils.h"
/*****************************************************************************
* Bluedroid includes *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h>
#endif
/*****************************************************************************
* NimBLE includes and definitions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gatt.h>
#define ESP_GATT_IF_NONE BLE_HS_CONN_HANDLE_NONE
#define NIMBLE_ATT_REMOVE_HIDE 1
#define NIMBLE_ATT_REMOVE_DELETE 2
#endif
/*****************************************************************************
* Forward declarations *
*****************************************************************************/
class BLEServerCallbacks; class BLEServerCallbacks;
/* TODO possibly refactor this struct */ class BLEService;
typedef struct { class BLECharacteristic;
void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here class BLEDevice;
bool connected; // do we need it? class BLESecurity;
uint16_t mtu; // every peer device negotiate own mtu class BLEAdvertising;
} conn_status_t;
/** /**
* @brief A data structure that manages the %BLE servers owned by a BLE server. * @brief A data structure that manages the %BLE services owned by a BLE server.
*/ */
class BLEServiceMap { class BLEServiceMap {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEService *getByHandle(uint16_t handle); BLEService *getByHandle(uint16_t handle);
BLEService *getByUUID(const char *uuid); BLEService *getByUUID(const char *uuid);
BLEService *getByUUID(BLEUUID uuid, uint8_t inst_id = 0); 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 setByHandle(uint16_t handle, BLEService *service);
void setByUUID(const char *uuid, BLEService *service); void setByUUID(const char *uuid, BLEService *service);
void setByUUID(BLEUUID uuid, BLEService *service); void setByUUID(BLEUUID uuid, BLEService *service);
@ -52,7 +86,19 @@ public:
void removeService(BLEService *service); void removeService(BLEService *service);
int getRegisteredServiceCount(); int getRegisteredServiceCount();
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
#endif
private: private:
/***************************************************************************
* Common private properties *
***************************************************************************/
std::map<uint16_t, BLEService *> m_handleMap; std::map<uint16_t, BLEService *> m_handleMap;
std::map<BLEService *, String> m_uuidMap; std::map<BLEService *, String> m_uuidMap;
std::map<BLEService *, String>::iterator m_iterator; std::map<BLEService *, String>::iterator m_iterator;
@ -63,6 +109,16 @@ private:
*/ */
class BLEServer { class BLEServer {
public: public:
/***************************************************************************
* Common public properties *
***************************************************************************/
uint16_t m_appId;
/***************************************************************************
* Common public declarations *
***************************************************************************/
uint32_t getConnectedCount(); uint32_t getConnectedCount();
BLEService *createService(const char *uuid); BLEService *createService(const char *uuid);
BLEService *createService(BLEUUID uuid, uint32_t numHandles = 15, uint8_t inst_id = 0); BLEService *createService(BLEUUID uuid, uint32_t numHandles = 15, uint8_t inst_id = 0);
@ -72,12 +128,9 @@ public:
void removeService(BLEService *service); void removeService(BLEService *service);
BLEService *getServiceByUUID(const char *uuid); BLEService *getServiceByUUID(const char *uuid);
BLEService *getServiceByUUID(BLEUUID uuid); BLEService *getServiceByUUID(BLEUUID uuid);
bool connect(BLEAddress address); void start();
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 */ // Connection management functions
std::map<uint16_t, conn_status_t> getPeerDevices(bool client); std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
void addPeerDevice(void *peer, bool is_client, uint16_t conn_id); void addPeerDevice(void *peer, bool is_client, uint16_t conn_id);
bool removePeerDevice(uint16_t conn_id, bool client); bool removePeerDevice(uint16_t conn_id, bool client);
@ -86,28 +139,95 @@ public:
uint16_t getPeerMTU(uint16_t conn_id); uint16_t getPeerMTU(uint16_t conn_id);
uint16_t getConnId(); uint16_t getConnId();
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
bool connect(BLEAddress address);
void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout);
void disconnect(uint16_t connId);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint16_t getHandle();
void updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout);
int disconnect(uint16_t connId, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
#endif
private: private:
BLEServer();
friend class BLEService; friend class BLEService;
friend class BLECharacteristic; friend class BLECharacteristic;
friend class BLEDevice; friend class BLEDevice;
esp_ble_adv_data_t m_adv_data; friend class BLESecurity;
// BLEAdvertising m_bleAdvertising; friend class BLEAdvertising;
/***************************************************************************
* Common private properties *
***************************************************************************/
uint16_t m_connId; uint16_t m_connId;
uint32_t m_connectedCount; uint32_t m_connectedCount;
uint16_t m_gatts_if; bool m_gattsStarted;
std::map<uint16_t, conn_status_t> m_connectedServersMap; std::map<uint16_t, conn_status_t> m_connectedServersMap;
FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt");
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
BLEServiceMap m_serviceMap; BLEServiceMap m_serviceMap;
BLEServerCallbacks *m_pServerCallbacks = nullptr; BLEServerCallbacks *m_pServerCallbacks = nullptr;
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
uint16_t m_gatts_if;
esp_ble_adv_data_t m_adv_data;
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
bool m_svcChanged;
uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS];
std::vector<BLECharacteristic *> m_notifyChrVec;
ble_hs_adv_fields m_adv_data;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
BLEServer();
void createApp(uint16_t appId); void createApp(uint16_t appId);
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
uint16_t getGattsIf(); uint16_t getGattsIf();
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void registerApp(uint16_t); void registerApp(uint16_t);
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE private declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void serviceChanged();
void resetGATT();
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
static int handleGATTServerEvent(struct ble_gap_event *event, void *arg);
#endif
}; // BLEServer }; // BLEServer
/** /**
@ -115,37 +235,38 @@ private:
*/ */
class BLEServerCallbacks { class BLEServerCallbacks {
public: public:
virtual ~BLEServerCallbacks(){}; /***************************************************************************
/** * Common public declarations *
* @brief Handle a new client connection. ***************************************************************************/
*
* When a new client connects, we are invoked.
*
* @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.
*
* When an existing client disconnects, we are invoked.
*
* @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
*/
virtual void onDisconnect(BLEServer *pServer);
virtual void onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param);
/** virtual ~BLEServerCallbacks(){};
* @brief Handle a new client connection. virtual void onConnect(BLEServer *pServer);
* virtual void onDisconnect(BLEServer *pServer);
* When the MTU changes this method is invoked.
* /***************************************************************************
* @param [in] pServer A reference to the %BLE server that received the client connection. * Bluedroid public declarations *
* @param [in] param A reference to esp_ble_gatts_cb_param_t. ***************************************************************************/
*/
#if defined(CONFIG_BLUEDROID_ENABLED)
virtual void onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param);
virtual void onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param);
virtual void onMtuChanged(BLEServer *pServer, esp_ble_gatts_cb_param_t *param); virtual void onMtuChanged(BLEServer *pServer, esp_ble_gatts_cb_param_t *param);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
virtual void onConnect(BLEServer *pServer, ble_gap_conn_desc *desc);
virtual void onDisconnect(BLEServer *pServer, ble_gap_conn_desc *desc);
virtual void onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *desc, uint16_t mtu);
virtual uint32_t onPassKeyRequest();
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc);
virtual bool onConfirmPIN(uint32_t pin);
#endif
}; // BLEServerCallbacks }; // BLEServerCallbacks
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */

View file

@ -3,6 +3,10 @@
* *
* Created on: Mar 25, 2017 * Created on: Mar 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
// A service is identified by a UUID. A service is also the container for one or more characteristics. // A service is identified by a UUID. A service is also the container for one or more characteristics.
@ -11,10 +15,13 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_err.h>
#include <esp_gatts_api.h>
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include <esp_err.h>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -25,8 +32,24 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/*****************************************************************************
* Bluedroid includes *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h>
#endif
/*****************************************************************************
* Common definitions *
*****************************************************************************/
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
/*****************************************************************************
* Common functions *
*****************************************************************************/
/** /**
* @brief Construct an instance of the BLEService * @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
@ -44,10 +67,32 @@ BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) {
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_pServer = nullptr; m_pServer = nullptr;
//m_serializeMutex.setName("BLEService"); //m_serializeMutex.setName("BLEService");
m_lastCreatedCharacteristic = nullptr;
m_numHandles = numHandles; m_numHandles = numHandles;
#ifdef CONFIG_BLUEDROID_ENABLED
m_lastCreatedCharacteristic = nullptr;
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
m_pSvcDef = nullptr;
#endif
} // BLEService } // BLEService
BLEService::~BLEService() {
#if defined(CONFIG_NIMBLE_ENABLED)
if (m_pSvcDef != nullptr) {
if (m_pSvcDef->characteristics != nullptr) {
for (int i = 0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
if (m_pSvcDef->characteristics[i].descriptors) {
delete (m_pSvcDef->characteristics[i].descriptors);
}
}
delete (m_pSvcDef->characteristics);
}
delete (m_pSvcDef);
}
#endif
}
/** /**
* @brief Create the service. * @brief Create the service.
* Create the service. * Create the service.
@ -56,8 +101,9 @@ BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) {
*/ */
void BLEService::executeCreate(BLEServer *pServer) { void BLEService::executeCreate(BLEServer *pServer) {
log_v(">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); log_v(">> executeCreate() - Creating service with uuid: %s", getUUID().toString().c_str());
m_pServer = pServer; m_pServer = pServer;
#if defined(CONFIG_BLUEDROID_ENABLED)
m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT
esp_gatt_srvc_id_t srvc_id; esp_gatt_srvc_id_t srvc_id;
@ -73,6 +119,7 @@ void BLEService::executeCreate(BLEServer *pServer) {
} }
m_semaphoreCreateEvt.wait("executeCreate"); m_semaphoreCreateEvt.wait("executeCreate");
#endif
log_v("<< executeCreate"); log_v("<< executeCreate");
} // executeCreate } // executeCreate
@ -83,6 +130,7 @@ void BLEService::executeCreate(BLEServer *pServer) {
*/ */
void BLEService::executeDelete() { void BLEService::executeDelete() {
#if defined(CONFIG_BLUEDROID_ENABLED)
log_v(">> executeDelete()"); log_v(">> executeDelete()");
m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT
@ -95,6 +143,11 @@ void BLEService::executeDelete() {
m_semaphoreDeleteEvt.wait("executeDelete"); m_semaphoreDeleteEvt.wait("executeDelete");
log_v("<< executeDelete"); log_v("<< executeDelete");
#endif
#if defined(CONFIG_NIMBLE_ENABLED) && CONFIG_BT_NIMBLE_DYNAMIC_SERVICE
ble_gatts_delete_svc(&m_uuid.getNative()->u);
#endif
} // executeDelete } // executeDelete
/** /**
@ -114,49 +167,11 @@ BLEUUID BLEService::getUUID() {
return m_uuid; return m_uuid;
} // getUUID } // getUUID
/**
* @brief Start the service.
* Here we wish to start the service which means that we will respond to partner requests about it.
* Starting a service also means that we can create the corresponding characteristics.
* @return Start the service.
*/
void BLEService::start() {
// 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().
//
log_v(">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str());
if (m_handle == NULL_HANDLE) {
log_e("<< !!! We attempted to start a service but don't know its handle!");
return;
}
BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst();
while (pCharacteristic != nullptr) {
m_lastCreatedCharacteristic = pCharacteristic;
pCharacteristic->executeCreate(this);
pCharacteristic = m_characteristicMap.getNext();
}
// Start each of the characteristics ... these are found in the m_characteristicMap.
m_semaphoreStartEvt.take("start");
esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreStartEvt.wait("start");
log_v("<< start()");
} // start
/** /**
* @brief Stop the service. * @brief Stop the service.
*/ */
void BLEService::stop() { void BLEService::stop() {
#if defined(CONFIG_BLUEDROID_ENABLED)
// We ask the BLE runtime to start the service and then create each of the characteristics. // 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 // 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(). // obtained as a result of calling esp_ble_gatts_create_service().
@ -176,13 +191,19 @@ void BLEService::stop() {
m_semaphoreStopEvt.wait("stop"); m_semaphoreStopEvt.wait("stop");
log_v("<< stop()"); log_v("<< stop()");
} // start #endif
#if defined(CONFIG_NIMBLE_ENABLED)
log_w("NimBLE does not support stopping a service. Ignoring request.");
#endif
} // stop
/** /**
* @brief Set the handle associated with this service. * @brief Set the handle associated with this service.
* @param [in] handle The handle associated with the service. * @param [in] handle The handle associated with the service.
*/ */
void BLEService::setHandle(uint16_t handle) { void BLEService::setHandle(uint16_t handle) {
#if defined(CONFIG_BLUEDROID_ENABLED)
log_v(">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); log_v(">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str());
if (m_handle != NULL_HANDLE) { if (m_handle != NULL_HANDLE) {
log_e("!!! Handle is already set %.2x", m_handle); log_e("!!! Handle is already set %.2x", m_handle);
@ -190,6 +211,11 @@ void BLEService::setHandle(uint16_t handle) {
} }
m_handle = handle; m_handle = handle;
log_v("<< setHandle"); log_v("<< setHandle");
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
log_w("NimBLE does not support manually setting the handle of a service. Ignoring request.");
#endif
} // setHandle } // setHandle
/** /**
@ -213,14 +239,25 @@ void BLEService::addCharacteristic(BLECharacteristic *pCharacteristic) {
log_d("Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), toString().c_str()); log_d("Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), toString().c_str());
// Check that we don't add the same characteristic twice. // Check that we don't add the same characteristic twice.
if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { BLECharacteristic *pExisting = m_characteristicMap.getByUUID(pCharacteristic->getUUID());
if (pExisting != nullptr) {
log_w("<< Adding a new characteristic with the same UUID as a previous one"); log_w("<< Adding a new characteristic with the same UUID as a previous one");
//return;
} }
// Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID #if defined(CONFIG_NIMBLE_ENABLED)
// but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. if (pExisting != nullptr) {
m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); pExisting->m_removed = 0;
} else
#endif
{
// Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID
// but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT.
m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID());
}
#if defined(CONFIG_NIMBLE_ENABLED)
getServer()->serviceChanged();
#endif
log_v("<< addCharacteristic()"); log_v("<< addCharacteristic()");
} // addCharacteristic } // addCharacteristic
@ -247,6 +284,54 @@ BLECharacteristic *BLEService::createCharacteristic(BLEUUID uuid, uint32_t prope
return pCharacteristic; return pCharacteristic;
} // createCharacteristic } // createCharacteristic
BLECharacteristic *BLEService::getCharacteristic(const char *uuid) {
return getCharacteristic(BLEUUID(uuid));
}
BLECharacteristic *BLEService::getCharacteristic(BLEUUID uuid) {
return m_characteristicMap.getByUUID(uuid);
}
/**
* @brief Return a string representation of this service.
* A service is defined by:
* * Its UUID
* * Its handle
* @return A string representation of this service.
*/
String BLEService::toString() {
String res = "UUID: " + getUUID().toString();
char hex[5];
snprintf(hex, sizeof(hex), "%04x", getHandle());
res += ", handle: 0x";
res += hex;
return res;
} // toString
/**
* @brief Get the BLE server associated with this service.
* @return The BLEServer associated with this service.
*/
BLEServer *BLEService::getServer() {
return m_pServer;
} // getServer
/*****************************************************************************
* Bluedroid functions *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/**
* @brief Get the last created characteristic.
* It is lamentable that this function has to exist. It returns the last created characteristic.
* We need this because the descriptor API is built around the notion that a new descriptor, when created,
* is associated with the last characteristics created and we need that information.
* @return The last created characteristic.
*/
BLECharacteristic *BLEService::getLastCreatedCharacteristic() {
return m_lastCreatedCharacteristic;
} // getLastCreatedCharacteristic
/** /**
* @brief Handle a GATTS server event. * @brief Handle a GATTS server event.
*/ */
@ -347,48 +432,222 @@ void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t
m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); m_characteristicMap.handleGATTServerEvent(event, gatts_if, param);
} // handleGATTServerEvent } // handleGATTServerEvent
BLECharacteristic *BLEService::getCharacteristic(const char *uuid) { /**
return getCharacteristic(BLEUUID(uuid)); * @brief Start the service.
} * Here we wish to start the service which means that we will respond to partner requests about it.
* Starting a service also means that we can create the corresponding characteristics.
* @return Start the service.
*/
BLECharacteristic *BLEService::getCharacteristic(BLEUUID uuid) { bool BLEService::start() {
return m_characteristicMap.getByUUID(uuid); // 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().
//
log_v(">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str());
if (m_handle == NULL_HANDLE) {
log_e("<< !!! We attempted to start a service but don't know its handle!");
return false;
}
BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst();
while (pCharacteristic != nullptr) {
m_lastCreatedCharacteristic = pCharacteristic;
pCharacteristic->executeCreate(this);
pCharacteristic = m_characteristicMap.getNext();
}
// Start each of the characteristics ... these are found in the m_characteristicMap.
m_semaphoreStartEvt.take("start");
esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
m_semaphoreStartEvt.wait("start");
log_v("<< start()");
return true;
} // start
#endif // CONFIG_BLUEDROID_ENABLED
/*****************************************************************************
* NimBLE functions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
/**
* @brief Remove a characteristic from the service. Check if the characteristic was already removed and if so, check if this
* is being called to delete the object and do so if requested. Otherwise, ignore the call and return.
* @param [in] pCharacteristic - The characteristic to remove.
* @param [in] deleteChr - If true, delete the characteristic.
*/
void BLEService::removeCharacteristic(BLECharacteristic *pCharacteristic, bool deleteChr) {
if (pCharacteristic->m_removed > 0) {
if (deleteChr) {
BLECharacteristic *pExisting = m_characteristicMap.getByUUID(pCharacteristic->getUUID());
if (pExisting != nullptr) {
m_characteristicMap.removeCharacteristic(pExisting);
delete pExisting;
}
}
return;
}
pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
getServer()->serviceChanged();
} }
/** /**
* @brief Return a string representation of this service. * @brief Builds the database of characteristics/descriptors for the service
* A service is defined by: * and registers it with the NimBLE stack.
* * Its UUID * @return bool success/failure .
* * Its handle
* @return A string representation of this service.
*/ */
String BLEService::toString() { bool BLEService::start() {
String res = "UUID: " + getUUID().toString(); log_d(">> start(): Starting service: %s", toString().c_str());
char hex[5];
snprintf(hex, sizeof(hex), "%04x", getHandle());
res += ", handle: 0x";
res += hex;
return res;
} // toString
/** // Rebuild the service definition if the server attributes have changed.
* @brief Get the last created characteristic. if (getServer()->m_svcChanged && m_pSvcDef != nullptr) {
* It is lamentable that this function has to exist. It returns the last created characteristic. if (m_pSvcDef[0].characteristics) {
* We need this because the descriptor API is built around the notion that a new descriptor, when created, if (m_pSvcDef[0].characteristics[0].descriptors) {
* is associated with the last characteristics created and we need that information. delete (m_pSvcDef[0].characteristics[0].descriptors);
* @return The last created characteristic. }
*/ delete (m_pSvcDef[0].characteristics);
BLECharacteristic *BLEService::getLastCreatedCharacteristic() { }
return m_lastCreatedCharacteristic; delete (m_pSvcDef);
} // getLastCreatedCharacteristic m_pSvcDef = nullptr;
}
/** if (m_pSvcDef == nullptr) {
* @brief Get the BLE server associated with this service. // Nimble requires an array of services to be sent to the api
* @return The BLEServer associated with this service. // Since we are adding 1 at a time we create an array of 2 and set the type
*/ // of the second service to 0 to indicate the end of the array.
BLEServer *BLEService::getServer() { ble_gatt_svc_def *svc = new ble_gatt_svc_def[2]{};
return m_pServer; ble_gatt_chr_def *pChr_a = nullptr;
} // getServer ble_gatt_dsc_def *pDsc_a = nullptr;
#endif /* CONFIG_BLUEDROID_ENABLED */ svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
svc[0].uuid = (const ble_uuid_t *)&(m_uuid.getNative()->u);
svc[0].includes = nullptr;
int removedCount = 0;
BLECharacteristic *pCharacteristic;
pCharacteristic = m_characteristicMap.getFirst();
while (pCharacteristic != nullptr) {
if (pCharacteristic->m_removed > 0) {
if (pCharacteristic->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
m_characteristicMap.removeCharacteristic(pCharacteristic);
delete pCharacteristic;
} else {
++removedCount;
}
} else {
pCharacteristic->executeCreate(this);
}
pCharacteristic = m_characteristicMap.getNext();
}
size_t numChrs = m_characteristicMap.getRegisteredCharacteristicCount() - removedCount;
log_d("Adding %d characteristics for service %s", numChrs, toString().c_str());
if (!numChrs) {
svc[0].characteristics = nullptr;
} else {
// Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
// of the characteristics for the service. We create 1 extra and set it to null
// for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs + 1]{};
uint8_t i = 0;
pCharacteristic = m_characteristicMap.getFirst();
while (pCharacteristic != nullptr) {
if (pCharacteristic->m_removed <= 0) {
removedCount = 0;
BLEDescriptor *pDescriptor;
pDescriptor = pCharacteristic->m_descriptorMap.getFirst();
while (pDescriptor != nullptr) {
if (pDescriptor->m_removed > 0) {
if (pDescriptor->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
pCharacteristic->m_descriptorMap.removeDescriptor(pDescriptor);
delete pDescriptor;
} else {
++removedCount;
}
}
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
}
size_t numDscs = pCharacteristic->m_descriptorMap.getRegisteredDescriptorCount() - removedCount;
log_d("Adding %d descriptors for characteristic %s", numDscs, pCharacteristic->getUUID().toString().c_str());
if (!numDscs) {
pChr_a[i].descriptors = nullptr;
} else {
// Must have last descriptor uuid = 0 so we have to create 1 extra
pDsc_a = new ble_gatt_dsc_def[numDscs + 1]{};
uint8_t d = 0;
pDescriptor = pCharacteristic->m_descriptorMap.getFirst();
while (pDescriptor != nullptr) {
if (pDescriptor->m_removed <= 0) {
pDsc_a[d].uuid = (const ble_uuid_t *)&(pDescriptor->m_bleUUID.getNative()->u);
pDsc_a[d].att_flags = pDescriptor->m_permissions;
pDsc_a[d].min_key_size = 0;
pDsc_a[d].access_cb = BLEDescriptor::handleGATTServerEvent;
pDsc_a[d].arg = pDescriptor;
++d;
}
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
}
pDsc_a[numDscs].uuid = nullptr;
pChr_a[i].descriptors = pDsc_a;
}
pChr_a[i].uuid = (const ble_uuid_t *)&(pCharacteristic->m_bleUUID.getNative()->u);
pChr_a[i].access_cb = BLECharacteristic::handleGATTServerEvent;
pChr_a[i].arg = pCharacteristic;
pChr_a[i].flags = pCharacteristic->m_properties;
pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle;
++i;
}
pCharacteristic = m_characteristicMap.getNext();
}
pChr_a[numChrs].uuid = nullptr;
svc[0].characteristics = pChr_a;
}
// end of services must indicate to api with type = 0
svc[1].type = 0;
m_pSvcDef = svc;
}
int rc = ble_gatts_count_cfg((const ble_gatt_svc_def *)m_pSvcDef);
if (rc != 0) {
log_e("ble_gatts_count_cfg failed, rc= %d, %s", rc, BLEUtils::returnCodeToString(rc));
return false;
}
rc = ble_gatts_add_svcs((const ble_gatt_svc_def *)m_pSvcDef);
if (rc != 0) {
log_e("ble_gatts_add_svcs, rc= %d, %s", rc, BLEUtils::returnCodeToString(rc));
return false;
}
log_d("<< start()");
return true;
} // start
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Mar 25, 2017 * Created on: Mar 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ #ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_
@ -11,15 +15,38 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gatts_api.h> /*****************************************************************************
* Common includes *
*****************************************************************************/
#include "BLECharacteristic.h" #include "BLECharacteristic.h"
#include "BLEServer.h" #include "BLEServer.h"
#include "BLEUUID.h" #include "BLEUUID.h"
#include "BLEUtils.h"
#include "RTOS.h" #include "RTOS.h"
/*****************************************************************************
* Bluedroid includes *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatts_api.h>
#endif
/*****************************************************************************
* NimBLE includes *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gatt.h>
#endif
/*****************************************************************************
* Forward declarations *
*****************************************************************************/
class BLEServer; class BLEServer;
/** /**
@ -27,6 +54,10 @@ class BLEServer;
*/ */
class BLECharacteristicMap { class BLECharacteristicMap {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
void setByUUID(BLECharacteristic *pCharacteristic, const char *uuid); void setByUUID(BLECharacteristic *pCharacteristic, const char *uuid);
void setByUUID(BLECharacteristic *pCharacteristic, BLEUUID uuid); void setByUUID(BLECharacteristic *pCharacteristic, BLEUUID uuid);
void setByHandle(uint16_t handle, BLECharacteristic *pCharacteristic); void setByHandle(uint16_t handle, BLECharacteristic *pCharacteristic);
@ -36,9 +67,30 @@ public:
BLECharacteristic *getFirst(); BLECharacteristic *getFirst();
BLECharacteristic *getNext(); BLECharacteristic *getNext();
String toString(); String toString();
int getRegisteredCharacteristicCount();
void removeCharacteristic(BLECharacteristic *characteristic);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
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);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, ble_gatt_access_ctxt *ctxt, void *arg);
#endif
private: private:
/***************************************************************************
* Common private properties *
***************************************************************************/
std::map<BLECharacteristic *, String> m_uuidMap; std::map<BLECharacteristic *, String> m_uuidMap;
std::map<uint16_t, BLECharacteristic *> m_handleMap; std::map<uint16_t, BLECharacteristic *> m_handleMap;
std::map<BLECharacteristic *, String>::iterator m_iterator; std::map<BLECharacteristic *, String>::iterator m_iterator;
@ -46,10 +98,19 @@ private:
/** /**
* @brief The model of a %BLE service. * @brief The model of a %BLE service.
*
*/ */
class BLEService { class BLEService {
public: public:
/***************************************************************************
* Common properties *
***************************************************************************/
uint8_t m_instId = 0;
/***************************************************************************
* Common public declarations *
***************************************************************************/
void addCharacteristic(BLECharacteristic *pCharacteristic); void addCharacteristic(BLECharacteristic *pCharacteristic);
BLECharacteristic *createCharacteristic(const char *uuid, uint32_t properties); BLECharacteristic *createCharacteristic(const char *uuid, uint32_t properties);
BLECharacteristic *createCharacteristic(BLEUUID uuid, uint32_t properties); BLECharacteristic *createCharacteristic(BLEUUID uuid, uint32_t properties);
@ -60,40 +121,81 @@ public:
BLECharacteristic *getCharacteristic(BLEUUID uuid); BLECharacteristic *getCharacteristic(BLEUUID uuid);
BLEUUID getUUID(); BLEUUID getUUID();
BLEServer *getServer(); BLEServer *getServer();
void start(); bool start();
void stop(); void stop();
String toString(); String toString();
uint16_t getHandle(); uint16_t getHandle();
uint8_t m_instId = 0;
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
void removeCharacteristic(BLECharacteristic *pCharacteristic, bool deleteChr = false);
#endif
private: private:
BLEService(const char *uuid, uint16_t numHandles);
BLEService(BLEUUID uuid, uint16_t numHandles);
friend class BLEServer; friend class BLEServer;
friend class BLEServiceMap; friend class BLEServiceMap;
friend class BLEDescriptor; friend class BLEDescriptor;
friend class BLECharacteristic; friend class BLECharacteristic;
friend class BLEDevice; friend class BLEDevice;
/***************************************************************************
* Common private properties *
***************************************************************************/
BLECharacteristicMap m_characteristicMap; BLECharacteristicMap m_characteristicMap;
uint16_t m_handle; uint16_t m_handle;
BLECharacteristic *m_lastCreatedCharacteristic = nullptr; BLECharacteristic *m_lastCreatedCharacteristic = nullptr;
BLEServer *m_pServer = nullptr; BLEServer *m_pServer = nullptr;
BLEUUID m_uuid; 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_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt");
FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt");
uint16_t m_numHandles; uint16_t m_numHandles;
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt");
FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt");
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
uint8_t m_removed;
ble_gatt_svc_def *m_pSvcDef;
#endif
/***************************************************************************
* Common private declarations *
***************************************************************************/
BLEService(const char *uuid, uint16_t numHandles);
BLEService(BLEUUID uuid, uint16_t numHandles);
~BLEService();
/***************************************************************************
* Common private declarations *
***************************************************************************/
void setHandle(uint16_t handle);
/***************************************************************************
* Bluedroid private declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLECharacteristic *getLastCreatedCharacteristic(); 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); #endif
//void setService(esp_gatt_srvc_id_t srvc_id);
}; // BLEService }; // BLEService
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */

View file

@ -3,16 +3,30 @@
* *
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include <stdio.h> #include <stdio.h>
#include <iomanip> #include <iomanip>
#include "BLEService.h" #include "BLEService.h"
/*****************************************************************************
* Common functions *
*****************************************************************************/
/** /**
* @brief Return the service by UUID. * @brief Return the service by UUID.
* @param [in] UUID The UUID to look up the service. * @param [in] UUID The UUID to look up the service.
@ -82,13 +96,6 @@ String BLEServiceMap::toString() {
return res; return res;
} // toString } // toString
void BLEServiceMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
// Invoke the handler for every Service we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
}
/** /**
* @brief Get the first service in the map. * @brief Get the first service in the map.
* @return The first service in the map. * @return The first service in the map.
@ -130,8 +137,21 @@ void BLEServiceMap::removeService(BLEService *service) {
* @return amount of registered services * @return amount of registered services
*/ */
int BLEServiceMap::getRegisteredServiceCount() { int BLEServiceMap::getRegisteredServiceCount() {
return m_handleMap.size(); return m_uuidMap.size();
} }
#endif /* CONFIG_BLUEDROID_ENABLED */ /*****************************************************************************
* Bluedroid functions *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
void BLEServiceMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
// Invoke the handler for every Service we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
}
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,12 +3,22 @@
* *
* Created on: Jun 21, 2017 * Created on: Jun 21, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include <string.h> #include <string.h>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
@ -18,25 +28,10 @@
#include "BLEUUID.h" #include "BLEUUID.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/** /*****************************************************************************
* @brief Copy memory from source to target but in reverse order. * Common functions *
* *****************************************************************************/
* When we move memory from one location it is normally:
*
* ```
* [0][1][2]...[n] -> [0][1][2]...[n]
* ```
*
* with this function, it is:
*
* ```
* [0][1][2]...[n] -> [n][n-1][n-2]...[0]
* ```
*
* @param [in] target The target of the copy
* @param [in] source The source of the copy
* @param [in] size The number of bytes to copy
*/
static void memrcpy(uint8_t *target, uint8_t *source, uint32_t size) { static void memrcpy(uint8_t *target, uint8_t *source, uint32_t size) {
assert(size > 0); 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
@ -48,29 +43,15 @@ static void memrcpy(uint8_t *target, uint8_t *source, uint32_t size) {
} }
} // memrcpy } // memrcpy
/** BLEUUID::BLEUUID() {
* @brief Create a UUID from a string. m_valueSet = false;
* } // BLEUUID
* Create a UUID from a string. There will be two possible stories here. Either the string represents
* a binary data field or the string represents a hex encoding of a UUID.
* For the hex encoding, here is an example:
*
* ```
* "beb5483e-36e1-4688-b7f5-ea07361b26a8"
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* 12345678-90ab-cdef-1234-567890abcdef
* ```
*
* This has a length of 36 characters. We need to parse this into 16 bytes.
*
* @param [in] value The string to build a UUID from.
*/
BLEUUID::BLEUUID(String value) { BLEUUID::BLEUUID(String value) {
//Serial.printf("BLEUUID constructor from String=\"%s\"\n", value.c_str());
m_valueSet = true; m_valueSet = true;
if (value.length() == 4) { if (value.length() == 4) {
m_uuid.len = ESP_UUID_LEN_16; UUID_LEN(m_uuid) = BLE_UUID_16_BITS;
m_uuid.uuid.uuid16 = 0; UUID_VAL_16(m_uuid) = 0;
for (int i = 0; i < value.length();) { for (int i = 0; i < value.length();) {
uint8_t MSB = value.c_str()[i]; uint8_t MSB = value.c_str()[i];
uint8_t LSB = value.c_str()[i + 1]; uint8_t LSB = value.c_str()[i + 1];
@ -81,12 +62,12 @@ BLEUUID::BLEUUID(String value) {
if (LSB > '9') { if (LSB > '9') {
LSB -= 7; LSB -= 7;
} }
m_uuid.uuid.uuid16 += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (2 - i) * 4; UUID_VAL_16(m_uuid) += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (2 - i) * 4;
i += 2; i += 2;
} }
} else if (value.length() == 8) { } else if (value.length() == 8) {
m_uuid.len = ESP_UUID_LEN_32; UUID_LEN(m_uuid) = BLE_UUID_32_BITS;
m_uuid.uuid.uuid32 = 0; UUID_VAL_32(m_uuid) = 0;
for (int i = 0; i < value.length();) { for (int i = 0; i < value.length();) {
uint8_t MSB = value.c_str()[i]; uint8_t MSB = value.c_str()[i];
uint8_t LSB = value.c_str()[i + 1]; uint8_t LSB = value.c_str()[i + 1];
@ -97,18 +78,14 @@ BLEUUID::BLEUUID(String value) {
if (LSB > '9') { if (LSB > '9') {
LSB -= 7; LSB -= 7;
} }
m_uuid.uuid.uuid32 += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (6 - i) * 4; UUID_VAL_32(m_uuid) += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (6 - i) * 4;
i += 2; i += 2;
} }
} else if (value.length() } else if (value.length() == 16) {
== 16) { // How we can have 16 byte length string representing 128 bit uuid??? needs to be investigated (lack of time) - maybe raw data encoded as String (128b==16B)? UUID_LEN(m_uuid) = BLE_UUID_128_BITS;
m_uuid.len = ESP_UUID_LEN_128; memrcpy(UUID_VAL_128(m_uuid), (uint8_t *)value.c_str(), 16);
memrcpy(m_uuid.uuid.uuid128, (uint8_t *)value.c_str(), 16);
} else if (value.length() == 36) { } else if (value.length() == 36) {
//log_d("36 characters:"); UUID_LEN(m_uuid) = BLE_UUID_128_BITS;
// 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 n = 0; int n = 0;
for (int i = 0; i < value.length();) { for (int i = 0; i < value.length();) {
if (value.c_str()[i] == '-') { if (value.c_str()[i] == '-') {
@ -123,7 +100,7 @@ BLEUUID::BLEUUID(String value) {
if (LSB > '9') { if (LSB > '9') {
LSB -= 7; LSB -= 7;
} }
m_uuid.uuid.uuid128[15 - n++] = ((MSB & 0x0F) << 4) | (LSB & 0x0F); UUID_VAL_128(m_uuid)[15 - n++] = ((MSB & 0x0F) << 4) | (LSB & 0x0F);
i += 2; i += 2;
} }
} else { } else {
@ -132,128 +109,73 @@ BLEUUID::BLEUUID(String value) {
} }
} //BLEUUID(String) } //BLEUUID(String)
/*
BLEUUID::BLEUUID(String value) {
this.BLEUUID(String(value.c_str(), value.length()));
} //BLEUUID(String)
*/
/**
* @brief Create a UUID from 16 bytes of memory.
*
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
BLEUUID::BLEUUID(uint8_t *pData, size_t size, bool msbFirst) { BLEUUID::BLEUUID(uint8_t *pData, size_t size, bool msbFirst) {
if (size != 16) { if (size != 16) {
log_e("ERROR: UUID length not 16 bytes"); log_e("ERROR: UUID length not 16 bytes");
return; return;
} }
m_uuid.len = ESP_UUID_LEN_128; UUID_LEN(m_uuid) = BLE_UUID_128_BITS;
if (msbFirst) { if (msbFirst) {
memrcpy(m_uuid.uuid.uuid128, pData, 16); memrcpy(UUID_VAL_128(m_uuid), pData, 16);
} else { } else {
memcpy(m_uuid.uuid.uuid128, pData, 16); memcpy(UUID_VAL_128(m_uuid), pData, 16);
} }
m_valueSet = true; m_valueSet = true;
} // BLEUUID } // BLEUUID
/**
* @brief Create a UUID from the 16bit value.
*
* @param [in] uuid The 16bit short form UUID.
*/
BLEUUID::BLEUUID(uint16_t uuid) { BLEUUID::BLEUUID(uint16_t uuid) {
m_uuid.len = ESP_UUID_LEN_16; UUID_LEN(m_uuid) = BLE_UUID_16_BITS;
m_uuid.uuid.uuid16 = uuid; UUID_VAL_16(m_uuid) = uuid;
m_valueSet = true; m_valueSet = true;
} // BLEUUID } // BLEUUID
/**
* @brief Create a UUID from the 32bit value.
*
* @param [in] uuid The 32bit short form UUID.
*/
BLEUUID::BLEUUID(uint32_t uuid) { BLEUUID::BLEUUID(uint32_t uuid) {
m_uuid.len = ESP_UUID_LEN_32; UUID_LEN(m_uuid) = BLE_UUID_32_BITS;
m_uuid.uuid.uuid32 = uuid; UUID_VAL_32(m_uuid) = uuid;
m_valueSet = true; m_valueSet = true;
} // BLEUUID } // BLEUUID
/** BLEUUID::BLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth) {
* @brief Create a UUID from the native UUID. UUID_LEN(m_uuid) = BLE_UUID_128_BITS;
* memcpy(UUID_VAL_128(m_uuid) + 12, &first, 4);
* @param [in] uuid The native UUID. memcpy(UUID_VAL_128(m_uuid) + 10, &second, 2);
*/ memcpy(UUID_VAL_128(m_uuid) + 8, &third, 2);
BLEUUID::BLEUUID(esp_bt_uuid_t uuid) { memcpy(UUID_VAL_128(m_uuid), &fourth, 8);
m_uuid = uuid;
m_valueSet = true; m_valueSet = true;
} // BLEUUID }
/**
* @brief Create a UUID from the ESP32 esp_gat_id_t.
*
* @param [in] gattId The data to create the UUID from.
*/
BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) {} // BLEUUID
BLEUUID::BLEUUID() {
m_valueSet = false;
} // BLEUUID
/**
* @brief Get the number of bits in this uuid.
* @return The number of bits in the UUID. One of 16, 32 or 128.
*/
uint8_t BLEUUID::bitSize() { uint8_t BLEUUID::bitSize() {
if (!m_valueSet) { if (!m_valueSet) {
return 0; return 0;
} }
switch (m_uuid.len) { switch (UUID_LEN(m_uuid)) {
case ESP_UUID_LEN_16: return 16; case BLE_UUID_16_BITS: return 16;
case ESP_UUID_LEN_32: return 32; case BLE_UUID_32_BITS: return 32;
case ESP_UUID_LEN_128: return 128; case BLE_UUID_128_BITS: return 128;
default: log_e("Unknown UUID length: %d", m_uuid.len); return 0; default: log_e("Unknown UUID length: %d", UUID_LEN(m_uuid)); return 0;
} // End of switch } // End of switch
} // bitSize } // bitSize
/** bool BLEUUID::equals(const BLEUUID &uuid) const {
* @brief Compare a UUID against this UUID.
*
* @param [in] uuid The UUID to compare against.
* @return True if the UUIDs are equal and false otherwise.
*/
bool BLEUUID::equals(BLEUUID uuid) {
//log_d("Comparing: %s to %s", toString().c_str(), uuid.toString().c_str());
if (!m_valueSet || !uuid.m_valueSet) { if (!m_valueSet || !uuid.m_valueSet) {
return false; return false;
} }
if (uuid.m_uuid.len != m_uuid.len) { if (UUID_LEN(uuid.m_uuid) != UUID_LEN(m_uuid)) {
return uuid.toString() == toString(); return uuid.toString() == toString();
} }
if (uuid.m_uuid.len == ESP_UUID_LEN_16) { if (UUID_LEN(uuid.m_uuid) == BLE_UUID_16_BITS) {
return uuid.m_uuid.uuid.uuid16 == m_uuid.uuid.uuid16; return UUID_VAL_16(uuid.m_uuid) == UUID_VAL_16(m_uuid);
} }
if (uuid.m_uuid.len == ESP_UUID_LEN_32) { if (UUID_LEN(uuid.m_uuid) == BLE_UUID_32_BITS) {
return uuid.m_uuid.uuid.uuid32 == m_uuid.uuid.uuid32; return UUID_VAL_32(uuid.m_uuid) == UUID_VAL_32(m_uuid);
} }
return memcmp(uuid.m_uuid.uuid.uuid128, m_uuid.uuid.uuid128, 16) == 0; return memcmp(UUID_VAL_128(uuid.m_uuid), UUID_VAL_128(m_uuid), 16) == 0;
} // equals } // equals
/**
* Create a BLEUUID from a string of the form:
* 0xNNNN
* 0xNNNNNNNN
* 0x<UUID>
* NNNN
* NNNNNNNN
* <UUID>
*/
BLEUUID BLEUUID::fromString(String _uuid) { BLEUUID BLEUUID::fromString(String _uuid) {
uint8_t start = 0; uint8_t start = 0;
if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
@ -273,117 +195,142 @@ BLEUUID BLEUUID::fromString(String _uuid) {
return BLEUUID(); return BLEUUID();
} // fromString } // fromString
/**
* @brief Get the native UUID value.
*
* @return The native UUID value or NULL if not set.
*/
esp_bt_uuid_t *BLEUUID::getNative() {
//log_d(">> getNative()")
if (m_valueSet == false) {
log_v("<< Return of un-initialized UUID!");
return nullptr;
}
//log_d("<< getNative()");
return &m_uuid;
} // getNative
/**
* @brief Convert a UUID to its 128 bit representation.
*
* A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
* will convert 16 or 32 bit representations to the full 128bit.
*/
BLEUUID BLEUUID::to128() { BLEUUID BLEUUID::to128() {
//log_v(">> toFull() - %s", toString().c_str()); if (!m_valueSet || UUID_LEN(m_uuid) == BLE_UUID_128_BITS) {
// If we either don't have a value or are already a 128 bit UUID, nothing further to do.
if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) {
return *this; return *this;
} }
// If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID. if (UUID_LEN(m_uuid) == BLE_UUID_16_BITS) {
if (m_uuid.len == ESP_UUID_LEN_16) { uint16_t temp = UUID_VAL_16(m_uuid);
uint16_t temp = m_uuid.uuid.uuid16; UUID_VAL_128(m_uuid)[15] = 0;
m_uuid.uuid.uuid128[15] = 0; UUID_VAL_128(m_uuid)[14] = 0;
m_uuid.uuid.uuid128[14] = 0; UUID_VAL_128(m_uuid)[13] = (temp >> 8) & 0xff;
m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff; UUID_VAL_128(m_uuid)[12] = temp & 0xff;
m_uuid.uuid.uuid128[12] = temp & 0xff; } else if (UUID_LEN(m_uuid) == BLE_UUID_32_BITS) {
uint16_t temp = UUID_VAL_32(m_uuid);
} else if (m_uuid.len == ESP_UUID_LEN_32) { UUID_VAL_128(m_uuid)[15] = (temp >> 24) & 0xff;
uint32_t temp = m_uuid.uuid.uuid32; UUID_VAL_128(m_uuid)[14] = (temp >> 16) & 0xff;
m_uuid.uuid.uuid128[15] = (temp >> 24) & 0xff; UUID_VAL_128(m_uuid)[13] = (temp >> 8) & 0xff;
m_uuid.uuid.uuid128[14] = (temp >> 16) & 0xff; UUID_VAL_128(m_uuid)[12] = temp & 0xff;
m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff;
m_uuid.uuid.uuid128[12] = temp & 0xff;
} }
// Set the fixed parts of the UUID. UUID_VAL_128(m_uuid)[11] = 0x00;
m_uuid.uuid.uuid128[11] = 0x00; UUID_VAL_128(m_uuid)[10] = 0x00;
m_uuid.uuid.uuid128[10] = 0x00; UUID_VAL_128(m_uuid)[9] = 0x10;
UUID_VAL_128(m_uuid)[8] = 0x00;
UUID_VAL_128(m_uuid)[7] = 0x80;
UUID_VAL_128(m_uuid)[6] = 0x00;
UUID_VAL_128(m_uuid)[5] = 0x00;
UUID_VAL_128(m_uuid)[4] = 0x80;
UUID_VAL_128(m_uuid)[3] = 0x5f;
UUID_VAL_128(m_uuid)[2] = 0x9b;
UUID_VAL_128(m_uuid)[1] = 0x34;
UUID_VAL_128(m_uuid)[0] = 0xfb;
m_uuid.uuid.uuid128[9] = 0x10; UUID_LEN(m_uuid) = BLE_UUID_128_BITS;
m_uuid.uuid.uuid128[8] = 0x00;
m_uuid.uuid.uuid128[7] = 0x80;
m_uuid.uuid.uuid128[6] = 0x00;
m_uuid.uuid.uuid128[5] = 0x00;
m_uuid.uuid.uuid128[4] = 0x80;
m_uuid.uuid.uuid128[3] = 0x5f;
m_uuid.uuid.uuid128[2] = 0x9b;
m_uuid.uuid.uuid128[1] = 0x34;
m_uuid.uuid.uuid128[0] = 0xfb;
m_uuid.len = ESP_UUID_LEN_128;
//log_d("<< toFull <- %s", toString().c_str());
return *this; return *this;
} // to128 } // to128
/** BLEUUID BLEUUID::to16() {
* @brief Get a string representation of the UUID. if (!m_valueSet || UUID_LEN(m_uuid) == BLE_UUID_16_BITS) {
* return *this;
* The format of a string is: }
* 01234567 8901 2345 6789 012345678901
* 0000180d-0000-1000-8000-00805f9b34fb if (UUID_LEN(m_uuid) == BLE_UUID_128_BITS) {
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 uint8_t base128[] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00};
* if (memcmp(UUID_VAL_128(m_uuid), base128, sizeof(base128)) == 0) {
* @return A string representation of the UUID. *this = BLEUUID(*(uint16_t *)(UUID_VAL_128(m_uuid) + 12));
*/ }
String BLEUUID::toString() { }
return *this;
}
String BLEUUID::toString() const {
if (!m_valueSet) { if (!m_valueSet) {
return "<NULL>"; // If we have no value, nothing to format. return "<NULL>"; // If we have no value, nothing to format.
} }
// If the UUIDs are 16 or 32 bit, pad correctly.
if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. if (UUID_LEN(m_uuid) == BLE_UUID_16_BITS) { // If the UUID is 16bit, pad correctly.
char hex[9]; char hex[9];
snprintf(hex, sizeof(hex), "%08x", m_uuid.uuid.uuid16); snprintf(hex, sizeof(hex), "%08x", UUID_VAL_16(m_uuid));
return String(hex) + "-0000-1000-8000-00805f9b34fb"; return String(hex) + "-0000-1000-8000-00805f9b34fb";
} // End 16bit UUID } // End 16bit UUID
if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. if (UUID_LEN(m_uuid) == BLE_UUID_32_BITS) { // If the UUID is 32bit, pad correctly.
char hex[9]; char hex[9];
snprintf(hex, sizeof(hex), "%08lx", m_uuid.uuid.uuid32); snprintf(hex, sizeof(hex), "%08lx", UUID_VAL_32(m_uuid));
return String(hex) + "-0000-1000-8000-00805f9b34fb"; return String(hex) + "-0000-1000-8000-00805f9b34fb";
} // End 32bit UUID } // End 32bit UUID
// The UUID is not 16bit or 32bit which means that it is 128bit. // The UUID is not 16bit or 32bit which means that it is 128bit.
//
// UUID string format:
// AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP
auto size = 37; // 32 for UUID data, 4 for '-' delimiters and one for a terminator == 37 chars auto size = 37; // 32 for UUID data, 4 for '-' delimiters and one for a terminator == 37 chars
char *hex = (char *)malloc(size); char *hex = (char *)malloc(size);
snprintf( snprintf(
hex, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", m_uuid.uuid.uuid128[15], m_uuid.uuid.uuid128[14], hex, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", UUID_VAL_128(m_uuid)[15], UUID_VAL_128(m_uuid)[14],
m_uuid.uuid.uuid128[13], m_uuid.uuid.uuid128[12], m_uuid.uuid.uuid128[11], m_uuid.uuid.uuid128[10], m_uuid.uuid.uuid128[9], m_uuid.uuid.uuid128[8], UUID_VAL_128(m_uuid)[13], UUID_VAL_128(m_uuid)[12], UUID_VAL_128(m_uuid)[11], UUID_VAL_128(m_uuid)[10], UUID_VAL_128(m_uuid)[9], UUID_VAL_128(m_uuid)[8],
m_uuid.uuid.uuid128[7], m_uuid.uuid.uuid128[6], m_uuid.uuid.uuid128[5], m_uuid.uuid.uuid128[4], m_uuid.uuid.uuid128[3], m_uuid.uuid.uuid128[2], UUID_VAL_128(m_uuid)[7], UUID_VAL_128(m_uuid)[6], UUID_VAL_128(m_uuid)[5], UUID_VAL_128(m_uuid)[4], UUID_VAL_128(m_uuid)[3], UUID_VAL_128(m_uuid)[2],
m_uuid.uuid.uuid128[1], m_uuid.uuid.uuid128[0] UUID_VAL_128(m_uuid)[1], UUID_VAL_128(m_uuid)[0]
); );
String res(hex); String res(hex);
free(hex); free(hex);
return res; return res;
} // toString } // toString
#endif /* CONFIG_BLUEDROID_ENABLED */ bool BLEUUID::operator==(const BLEUUID &rhs) const {
return equals(rhs);
}
bool BLEUUID::operator!=(const BLEUUID &rhs) const {
return !equals(rhs);
}
/*****************************************************************************
* Bluedroid functions *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLEUUID::BLEUUID(esp_bt_uuid_t uuid) {
m_uuid = uuid;
m_valueSet = true;
} // BLEUUID
BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) {} // BLEUUID
esp_bt_uuid_t *BLEUUID::getNative() {
if (m_valueSet == false) {
log_v("<< Return of un-initialized UUID!");
return nullptr;
}
return &m_uuid;
} // getNative
#endif
/*****************************************************************************
* NimBLE functions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLEUUID::BLEUUID(ble_uuid_any_t uuid) {
m_uuid = uuid;
m_valueSet = true;
} // BLEUUID
BLEUUID::BLEUUID(const ble_uuid128_t *uuid) {
m_uuid.u.type = BLE_UUID_TYPE_128;
memcpy(m_uuid.u128.value, uuid->value, 16);
m_valueSet = true;
} // BLEUUID
const ble_uuid_any_t *BLEUUID::getNative() const {
if (m_valueSet == false) {
log_v("<< Return of un-initialized UUID!");
return nullptr;
}
return &m_uuid;
} // getNative
#endif
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,42 +3,125 @@
* *
* Created on: Jun 21, 2017 * Created on: Jun 21, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_ #ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_
#define COMPONENTS_CPP_UTILS_BLEUUID_H_ #define COMPONENTS_CPP_UTILS_BLEUUID_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "WString.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if CONFIG_BLUEDROID_ENABLED #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include "WString.h"
/*****************************************************************************
* Bluedroid includes and definitions *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gatt_defs.h> #include <esp_gatt_defs.h>
#define BLE_UUID_16_BITS ESP_UUID_LEN_16
#define BLE_UUID_32_BITS ESP_UUID_LEN_32
#define BLE_UUID_128_BITS ESP_UUID_LEN_128
#define UUID_LEN(s) s.len
#define UUID_VAL_16(s) s.uuid.uuid16
#define UUID_VAL_32(s) s.uuid.uuid32
#define UUID_VAL_128(s) s.uuid.uuid128
#endif
/*****************************************************************************
* NimBLE includes and definitions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_uuid.h>
#define BLE_UUID_16_BITS BLE_UUID_TYPE_16
#define BLE_UUID_32_BITS BLE_UUID_TYPE_32
#define BLE_UUID_128_BITS BLE_UUID_TYPE_128
#define UUID_LEN(s) s.u.type
#define UUID_VAL_16(s) s.u16.value
#define UUID_VAL_32(s) s.u32.value
#define UUID_VAL_128(s) s.u128.value
#endif
/** /**
* @brief A model of a %BLE UUID. * @brief A model of a %BLE UUID.
*/ */
class BLEUUID { class BLEUUID {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEUUID(String uuid); BLEUUID(String uuid);
BLEUUID(uint16_t uuid); BLEUUID(uint16_t uuid);
BLEUUID(uint32_t uuid); BLEUUID(uint32_t uuid);
BLEUUID(esp_bt_uuid_t uuid);
BLEUUID(uint8_t *pData, size_t size, bool msbFirst); BLEUUID(uint8_t *pData, size_t size, bool msbFirst);
BLEUUID(esp_gatt_id_t gattId); BLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth);
BLEUUID(); BLEUUID();
uint8_t bitSize(); // Get the number of bits in this uuid. uint8_t bitSize(); // Get the number of bits in this uuid.
bool equals(BLEUUID uuid); bool equals(const BLEUUID &uuid) const;
esp_bt_uuid_t *getNative();
BLEUUID to128(); BLEUUID to128();
String toString(); BLEUUID to16();
String toString() const;
static BLEUUID fromString(String uuid); // Create a BLEUUID from a string static BLEUUID fromString(String uuid); // Create a BLEUUID from a string
bool operator==(const BLEUUID &rhs) const;
bool operator!=(const BLEUUID &rhs) const;
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
BLEUUID(esp_bt_uuid_t uuid);
BLEUUID(esp_gatt_id_t gattId);
esp_bt_uuid_t *getNative();
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
BLEUUID(ble_uuid_any_t uuid);
BLEUUID(const ble_uuid128_t *uuid);
const ble_uuid_any_t *getNative() const;
#endif
private: private:
esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. /***************************************************************************
* Common private properties *
***************************************************************************/
bool m_valueSet = false; // Is there a value set for this instance. bool m_valueSet = false; // Is there a value set for this instance.
/***************************************************************************
* Bluedroid private properties *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps.
#endif
/***************************************************************************
* NimBLE private properties *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
ble_uuid_any_t m_uuid; // The underlying UUID structure that this class wraps.
#endif
}; // BLEUUID }; // BLEUUID
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */

View file

@ -3,12 +3,22 @@
* *
* Created on: Mar 25, 2017 * Created on: Mar 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include "BLEAddress.h" #include "BLEAddress.h"
#include "BLEClient.h" #include "BLEClient.h"
#include "BLEUtils.h" #include "BLEUtils.h"
@ -17,17 +27,56 @@
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h> #include <freertos/event_groups.h>
#include <esp_bt.h> // ESP32 BLE #include <esp_bt.h>
#include <esp_bt_main.h> // ESP32 BLE
#include <esp_gap_ble_api.h> // ESP32 BLE #include <esp_err.h>
#include <esp_gattc_api.h> // ESP32 BLE #include <map>
#include <esp_err.h> // ESP32 ESP-IDF
#include <map> // Part of C++ STL
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/*****************************************************************************
* Bluedroid includes *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#endif
/*****************************************************************************
* NimBLE includes and definitions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_gap.h>
#include <host/ble_att.h>
#include <host/ble_hs.h>
#include <host/ble_sm.h>
#include <host/ble_l2cap.h>
#include <nimble/hci_common.h>
#include <nimble/ble.h>
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
#define CONFIG_NIMBLE_ENABLE_RETURN_CODE_TEXT
#define CONFIG_NIMBLE_ENABLE_ADVERTISMENT_TYPE_TEXT
#define CONFIG_NIMBLE_ENABLE_GAP_EVENT_CODE_TEXT
#endif
#ifndef CONFIG_NIMBLE_FREERTOS_TASK_BLOCK_BIT
#define CONFIG_NIMBLE_FREERTOS_TASK_BLOCK_BIT 31
#endif
constexpr uint32_t TASK_BLOCK_BIT = (1 << CONFIG_NIMBLE_FREERTOS_TASK_BLOCK_BIT);
#endif
/*****************************************************************************
* Bluedroid types and constants *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
typedef struct { typedef struct {
uint32_t assignedNumber; uint32_t assignedNumber;
const char *name; const char *name;
@ -593,13 +642,127 @@ static const gattService_t g_gattServices[] = {
#endif #endif
{"", "", 0} {"", "", 0}
}; };
#endif
/*****************************************************************************
* Common functions *
*****************************************************************************/
/**
* @brief Create a hex representation of data.
*
* @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] source The start of the binary data.
* @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;
}
if (target == nullptr) {
target = (uint8_t *)malloc(length * 2 + 1);
if (target == nullptr) {
log_e("buildHexData: malloc failed");
return nullptr;
}
}
char *startOfData = (char *)target;
for (int i = 0; i < length; i++) {
sprintf((char *)target, "%.2x", (char)*source);
source++;
target += 2;
}
// Handle the special case where there was no data.
if (length == 0) {
*startOfData = 0;
}
return startOfData;
} // buildHexData
/**
* @brief Given an advertising data type, return a string representation of the type.
*
* For details see ...
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*
* @return A string representation of the type.
*/
const char *BLEUtils::advDataTypeToString(uint8_t advType) {
switch (advType) {
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
case ESP_BLE_AD_TYPE_FLAG: // 0x01
return "ESP_BLE_AD_TYPE_FLAG";
case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02
return "ESP_BLE_AD_TYPE_16SRV_PART";
case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03
return "ESP_BLE_AD_TYPE_16SRV_CMPL";
case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04
return "ESP_BLE_AD_TYPE_32SRV_PART";
case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05
return "ESP_BLE_AD_TYPE_32SRV_CMPL";
case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06
return "ESP_BLE_AD_TYPE_128SRV_PART";
case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07
return "ESP_BLE_AD_TYPE_128SRV_CMPL";
case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08
return "ESP_BLE_AD_TYPE_NAME_SHORT";
case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09
return "ESP_BLE_AD_TYPE_NAME_CMPL";
case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a
return "ESP_BLE_AD_TYPE_TX_PWR";
case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b
return "ESP_BLE_AD_TYPE_DEV_CLASS";
case ESP_BLE_AD_TYPE_SM_TK: // 0x10
return "ESP_BLE_AD_TYPE_SM_TK";
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
return "ESP_BLE_AD_TYPE_INT_RANGE";
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
return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID";
case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16
return "ESP_BLE_AD_TYPE_SERVICE_DATA";
case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17
return "ESP_BLE_AD_TYPE_PUBLIC_TARGET";
case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18
return "ESP_BLE_AD_TYPE_RANDOM_TARGET";
case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19
return "ESP_BLE_AD_TYPE_APPEARANCE";
case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a
return "ESP_BLE_AD_TYPE_ADV_INT";
case ESP_BLE_AD_TYPE_32SOL_SRV_UUID: // 0x1f
return "ESP_BLE_AD_TYPE_32SOL_SRV_UUID";
case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20
return "ESP_BLE_AD_TYPE_32SERVICE_DATA";
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";
#endif
default: log_v(" adv data type: 0x%x", advType); return "";
} // End switch
} // advDataTypeToString
/*****************************************************************************
* Bluedroid functions *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
/** /**
* @brief Convert characteristic properties into a string representation. * @brief Convert characteristic properties into a string representation.
* @param [in] prop Characteristic properties. * @param [in] prop Characteristic properties.
* @return A string representation of characteristic properties. * @return A string representation of characteristic properties.
*/ */
String BLEUtils::characteristicPropertiesToString(esp_gatt_char_prop_t prop) { String BLEUtils::characteristicPropertiesToString(uint8_t prop) {
String res = "broadcast: "; String res = "broadcast: ";
res += ((prop & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "1" : "0"); res += ((prop & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "1" : "0");
res += ", read: "; res += ", read: ";
@ -617,6 +780,24 @@ String BLEUtils::characteristicPropertiesToString(esp_gatt_char_prop_t prop) {
return res; return res;
} // characteristicPropertiesToString } // characteristicPropertiesToString
/**
* @brief Build a printable string of memory range.
* Create a string representation of a piece of memory. Only printable characters will be included
* while those that are not printable will be replaced with '.'.
* @param [in] source Start of memory.
* @param [in] length Length of memory.
* @return A string representation of a piece of memory.
*/
String BLEUtils::buildPrintData(uint8_t *source, size_t length) {
String res;
for (int i = 0; i < length; i++) {
char c = *source;
res += (isprint(c) ? c : '.');
source++;
}
return res;
} // buildPrintData
/** /**
* @brief Convert an esp_gatt_id_t to a string. * @brief Convert an esp_gatt_id_t to a string.
*/ */
@ -668,71 +849,6 @@ String BLEUtils::adFlagsToString(uint8_t adFlags) {
return res; return res;
} // adFlagsToString } // adFlagsToString
/**
* @brief Given an advertising type, return a string representation of the type.
*
* For details see ...
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*
* @return A string representation of the type.
*/
const char *BLEUtils::advTypeToString(uint8_t advType) {
switch (advType) {
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
case ESP_BLE_AD_TYPE_FLAG: // 0x01
return "ESP_BLE_AD_TYPE_FLAG";
case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02
return "ESP_BLE_AD_TYPE_16SRV_PART";
case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03
return "ESP_BLE_AD_TYPE_16SRV_CMPL";
case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04
return "ESP_BLE_AD_TYPE_32SRV_PART";
case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05
return "ESP_BLE_AD_TYPE_32SRV_CMPL";
case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06
return "ESP_BLE_AD_TYPE_128SRV_PART";
case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07
return "ESP_BLE_AD_TYPE_128SRV_CMPL";
case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08
return "ESP_BLE_AD_TYPE_NAME_SHORT";
case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09
return "ESP_BLE_AD_TYPE_NAME_CMPL";
case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a
return "ESP_BLE_AD_TYPE_TX_PWR";
case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b
return "ESP_BLE_AD_TYPE_DEV_CLASS";
case ESP_BLE_AD_TYPE_SM_TK: // 0x10
return "ESP_BLE_AD_TYPE_SM_TK";
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
return "ESP_BLE_AD_TYPE_INT_RANGE";
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
return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID";
case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16
return "ESP_BLE_AD_TYPE_SERVICE_DATA";
case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17
return "ESP_BLE_AD_TYPE_PUBLIC_TARGET";
case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18
return "ESP_BLE_AD_TYPE_RANDOM_TARGET";
case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19
return "ESP_BLE_AD_TYPE_APPEARANCE";
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
return "ESP_BLE_AD_TYPE_32SERVICE_DATA";
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";
#endif
default: log_v(" adv data type: 0x%x", advType); return "";
} // End switch
} // advTypeToString
esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) { esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) {
esp_gatt_id_t retGattId; esp_gatt_id_t retGattId;
retGattId.uuid = uuid; retGattId.uuid = uuid;
@ -747,61 +863,6 @@ esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, bool is_prima
return retSrvcId; return retSrvcId;
} }
/**
* @brief Create a hex representation of data.
*
* @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] source The start of the binary data.
* @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;
}
if (target == nullptr) {
target = (uint8_t *)malloc(length * 2 + 1);
if (target == nullptr) {
log_e("buildHexData: malloc failed");
return nullptr;
}
}
char *startOfData = (char *)target;
for (int i = 0; i < length; i++) {
sprintf((char *)target, "%.2x", (char)*source);
source++;
target += 2;
}
// Handle the special case where there was no data.
if (length == 0) {
*startOfData = 0;
}
return startOfData;
} // buildHexData
/**
* @brief Build a printable string of memory range.
* Create a string representation of a piece of memory. Only printable characters will be included
* while those that are not printable will be replaced with '.'.
* @param [in] source Start of memory.
* @param [in] length Length of memory.
* @return A string representation of a piece of memory.
*/
String BLEUtils::buildPrintData(uint8_t *source, size_t length) {
String res;
for (int i = 0; i < length; i++) {
char c = *source;
res += (isprint(c) ? c : '.');
source++;
}
return res;
} // buildPrintData
/** /**
* @brief Convert a close/disconnect reason to a string. * @brief Convert a close/disconnect reason to a string.
* @param [in] reason The close reason. * @param [in] reason The close reason.
@ -1817,5 +1878,380 @@ const char *BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) {
} }
} // searchEventTypeToString } // searchEventTypeToString
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif // CONFIG_BLUEDROID_ENABLED
/*****************************************************************************
* NimBLE functions *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
/**
* @brief Construct a BLETaskData instance.
* @param [in] pInstance An instance of the class that will be waiting.
* @param [in] flags General purpose flags for the caller.
* @param [in] buf A buffer for data.
*/
BLETaskData::BLETaskData(void *pInstance, int flags, void *buf) : m_pInstance{pInstance}, m_flags{flags}, m_pBuf{buf}, m_pHandle{xTaskGetCurrentTaskHandle()} {}
/**
* @brief Destructor.
*/
BLETaskData::~BLETaskData() {}
/**
* @brief A function for checking validity of connection parameters.
* @param [in] params A pointer to the structure containing the parameters to check.
* @return valid == 0 or error code.
*/
int BLEUtils::checkConnParams(ble_gap_conn_params *params) {
/* Check connection interval min */
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) || (params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection interval max */
if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) || (params->itvl_max > BLE_HCI_CONN_ITVL_MAX) || (params->itvl_max < params->itvl_min)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection latency */
if (params->latency > BLE_HCI_CONN_LATENCY_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check supervision timeout */
if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || (params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check connection event length */
if (params->min_ce_len > params->max_ce_len) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
return 0;
}
/**
* @brief Converts a return code from the NimBLE stack to a text string.
* @param [in] rc The return code to convert.
* @return A string representation of the return code.
*/
const char *BLEUtils::returnCodeToString(int rc) {
#if defined(CONFIG_NIMBLE_ENABLE_RETURN_CODE_TEXT)
switch (rc) {
case 0: return "SUCCESS";
case BLE_HS_EAGAIN: return "Temporary failure; try again.";
case BLE_HS_EALREADY: return "Operation already in progress or completed.";
case BLE_HS_EINVAL: return "One or more arguments are invalid.";
case BLE_HS_EMSGSIZE: return "The provided buffer is too small.";
case BLE_HS_ENOENT: return "No entry matching the specified criteria.";
case BLE_HS_ENOMEM: return "Operation failed due to resource exhaustion.";
case BLE_HS_ENOTCONN: return "No open connection with the specified handle.";
case BLE_HS_ENOTSUP: return "Operation disabled at compile time.";
case BLE_HS_EAPP: return "Application callback behaved unexpectedly.";
case BLE_HS_EBADDATA: return "Command from peer is invalid.";
case BLE_HS_EOS: return "Mynewt OS error.";
case BLE_HS_ECONTROLLER: return "Event from controller is invalid.";
case BLE_HS_ETIMEOUT: return "Operation timed out.";
case BLE_HS_EDONE: return "Operation completed successfully.";
case BLE_HS_EBUSY: return "Operation cannot be performed until procedure completes.";
case BLE_HS_EREJECT: return "Peer rejected a connection parameter update request.";
case BLE_HS_EUNKNOWN: return "Unexpected failure; catch all.";
case BLE_HS_EROLE: return "Operation requires different role (e.g., central vs. peripheral).";
case BLE_HS_ETIMEOUT_HCI: return "HCI request timed out; controller unresponsive.";
case BLE_HS_ENOMEM_EVT: return "Controller failed to send event due to memory exhaustion (combined host-controller only).";
case BLE_HS_ENOADDR: return "Operation requires an identity address but none configured.";
case BLE_HS_ENOTSYNCED: return "Attempt to use the host before it is synced with controller.";
case BLE_HS_EAUTHEN: return "Insufficient authentication.";
case BLE_HS_EAUTHOR: return "Insufficient authorization.";
case BLE_HS_EENCRYPT: return "Insufficient encryption level.";
case BLE_HS_EENCRYPT_KEY_SZ: return "Insufficient key size.";
case BLE_HS_ESTORE_CAP: return "Storage at capacity.";
case BLE_HS_ESTORE_FAIL: return "Storage IO error.";
case BLE_HS_EPREEMPTED: return "Operation was preempted.";
case BLE_HS_EDISABLED: return "Operation disabled.";
case BLE_HS_ESTALLED: return "Operation stalled.";
case (0x0100 + BLE_ATT_ERR_INVALID_HANDLE): return "The attribute handle given was not valid on this server.";
case (0x0100 + BLE_ATT_ERR_READ_NOT_PERMITTED): return "The attribute cannot be read.";
case (0x0100 + BLE_ATT_ERR_WRITE_NOT_PERMITTED): return "The attribute cannot be written.";
case (0x0100 + BLE_ATT_ERR_INVALID_PDU): return "The attribute PDU was invalid.";
case (0x0100 + BLE_ATT_ERR_INSUFFICIENT_AUTHEN): return "The attribute requires authentication before it can be read or written.";
case (0x0100 + BLE_ATT_ERR_REQ_NOT_SUPPORTED): return "Attribute server does not support the request received from the client.";
case (0x0100 + BLE_ATT_ERR_INVALID_OFFSET): return "Offset specified was past the end of the attribute.";
case (0x0100 + BLE_ATT_ERR_INSUFFICIENT_AUTHOR): return "The attribute requires authorization before it can be read or written.";
case (0x0100 + BLE_ATT_ERR_PREPARE_QUEUE_FULL): return "Too many prepare writes have been queued.";
case (0x0100 + BLE_ATT_ERR_ATTR_NOT_FOUND): return "No attribute found within the given attribute handle range.";
case (0x0100 + BLE_ATT_ERR_ATTR_NOT_LONG): return "The attribute cannot be read or written using the Read Blob Request.";
case (0x0100 + BLE_ATT_ERR_INSUFFICIENT_KEY_SZ): return "The Encryption Key Size used for encrypting this link is insufficient.";
case (0x0100 + BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN): return "The attribute value length is invalid for the operation.";
case (0x0100 + BLE_ATT_ERR_UNLIKELY): return "The attribute request has encountered an error that was unlikely, could not be completed as requested.";
case (0x0100 + BLE_ATT_ERR_INSUFFICIENT_ENC): return "The attribute requires encryption before it can be read or written.";
case (0x0100 + BLE_ATT_ERR_UNSUPPORTED_GROUP):
return "The attribute type is not a supported grouping attribute as defined by a higher layer specification.";
case (0x0100 + BLE_ATT_ERR_INSUFFICIENT_RES): return "Insufficient Resources to complete the request.";
case (0x0200 + BLE_ERR_UNKNOWN_HCI_CMD): return "Unknown HCI Command";
case (0x0200 + BLE_ERR_UNK_CONN_ID): return "Unknown Connection Identifier";
case (0x0200 + BLE_ERR_HW_FAIL): return "Hardware Failure";
case (0x0200 + BLE_ERR_PAGE_TMO): return "Page Timeout";
case (0x0200 + BLE_ERR_AUTH_FAIL): return "Authentication Failure";
case (0x0200 + BLE_ERR_PINKEY_MISSING): return "PIN or Key Missing";
case (0x0200 + BLE_ERR_MEM_CAPACITY): return "Memory Capacity Exceeded";
case (0x0200 + BLE_ERR_CONN_SPVN_TMO): return "Connection Timeout";
case (0x0200 + BLE_ERR_CONN_LIMIT): return "Connection Limit Exceeded";
case (0x0200 + BLE_ERR_SYNCH_CONN_LIMIT): return "Synchronous Connection Limit To A Device Exceeded";
case (0x0200 + BLE_ERR_ACL_CONN_EXISTS): return "ACL Connection Already Exists";
case (0x0200 + BLE_ERR_CMD_DISALLOWED): return "Command Disallowed";
case (0x0200 + BLE_ERR_CONN_REJ_RESOURCES): return "Connection Rejected due to Limited Resources";
case (0x0200 + BLE_ERR_CONN_REJ_SECURITY): return "Connection Rejected Due To Security Reasons";
case (0x0200 + BLE_ERR_CONN_REJ_BD_ADDR): return "Connection Rejected due to Unacceptable BD_ADDR";
case (0x0200 + BLE_ERR_CONN_ACCEPT_TMO): return "Connection Accept Timeout Exceeded";
case (0x0200 + BLE_ERR_UNSUPPORTED): return "Unsupported Feature or Parameter Value";
case (0x0200 + BLE_ERR_INV_HCI_CMD_PARMS): return "Invalid HCI Command Parameters";
case (0x0200 + BLE_ERR_REM_USER_CONN_TERM): return "Remote User Terminated Connection";
case (0x0200 + BLE_ERR_RD_CONN_TERM_RESRCS): return "Remote Device Terminated Connection due to Low Resources";
case (0x0200 + BLE_ERR_RD_CONN_TERM_PWROFF): return "Remote Device Terminated Connection due to Power Off";
case (0x0200 + BLE_ERR_CONN_TERM_LOCAL): return "Connection Terminated By Local Host";
case (0x0200 + BLE_ERR_REPEATED_ATTEMPTS): return "Repeated Attempts";
case (0x0200 + BLE_ERR_NO_PAIRING): return "Pairing Not Allowed";
case (0x0200 + BLE_ERR_UNK_LMP): return "Unknown LMP PDU";
case (0x0200 + BLE_ERR_UNSUPP_REM_FEATURE): return "Unsupported Remote Feature / Unsupported LMP Feature";
case (0x0200 + BLE_ERR_SCO_OFFSET): return "SCO Offset Rejected";
case (0x0200 + BLE_ERR_SCO_ITVL): return "SCO Interval Rejected";
case (0x0200 + BLE_ERR_SCO_AIR_MODE): return "SCO Air Mode Rejected";
case (0x0200 + BLE_ERR_INV_LMP_LL_PARM): return "Invalid LMP Parameters / Invalid LL Parameters";
case (0x0200 + BLE_ERR_UNSPECIFIED): return "Unspecified Error";
case (0x0200 + BLE_ERR_UNSUPP_LMP_LL_PARM): return "Unsupported LMP Parameter Value / Unsupported LL Parameter Value";
case (0x0200 + BLE_ERR_NO_ROLE_CHANGE): return "Role Change Not Allowed";
case (0x0200 + BLE_ERR_LMP_LL_RSP_TMO): return "LMP Response Timeout / LL Response Timeout";
case (0x0200 + BLE_ERR_LMP_COLLISION): return "LMP Error Transaction Collision";
case (0x0200 + BLE_ERR_LMP_PDU): return "LMP PDU Not Allowed";
case (0x0200 + BLE_ERR_ENCRYPTION_MODE): return "Encryption Mode Not Acceptable";
case (0x0200 + BLE_ERR_LINK_KEY_CHANGE): return "Link Key cannot be Changed";
case (0x0200 + BLE_ERR_UNSUPP_QOS): return "Requested QoS Not Supported";
case (0x0200 + BLE_ERR_INSTANT_PASSED): return "Instant Passed";
case (0x0200 + BLE_ERR_UNIT_KEY_PAIRING): return "Pairing With Unit Key Not Supported";
case (0x0200 + BLE_ERR_DIFF_TRANS_COLL): return "Different Transaction Collision";
case (0x0200 + BLE_ERR_QOS_PARM): return "QoS Unacceptable Parameter";
case (0x0200 + BLE_ERR_QOS_REJECTED): return "QoS Rejected";
case (0x0200 + BLE_ERR_CHAN_CLASS): return "Channel Classification Not Supported";
case (0x0200 + BLE_ERR_INSUFFICIENT_SEC): return "Insufficient Security";
case (0x0200 + BLE_ERR_PARM_OUT_OF_RANGE): return "Parameter Out Of Mandatory Range";
case (0x0200 + BLE_ERR_PENDING_ROLE_SW): return "Role Switch Pending";
case (0x0200 + BLE_ERR_RESERVED_SLOT): return "Reserved Slot Violation";
case (0x0200 + BLE_ERR_ROLE_SW_FAIL): return "Role Switch Failed";
case (0x0200 + BLE_ERR_INQ_RSP_TOO_BIG): return "Extended Inquiry Response Too Large";
case (0x0200 + BLE_ERR_SEC_SIMPLE_PAIR): return "Secure Simple Pairing Not Supported By Host";
case (0x0200 + BLE_ERR_HOST_BUSY_PAIR): return "Host Busy - Pairing";
case (0x0200 + BLE_ERR_CONN_REJ_CHANNEL): return "Connection Rejected, No Suitable Channel Found";
case (0x0200 + BLE_ERR_CTLR_BUSY): return "Controller Busy";
case (0x0200 + BLE_ERR_CONN_PARMS): return "Unacceptable Connection Parameters";
case (0x0200 + BLE_ERR_DIR_ADV_TMO): return "Directed Advertising Timeout";
case (0x0200 + BLE_ERR_CONN_TERM_MIC): return "Connection Terminated due to MIC Failure";
case (0x0200 + BLE_ERR_CONN_ESTABLISHMENT): return "Connection Failed to be Established";
case (0x0200 + BLE_ERR_MAC_CONN_FAIL): return "MAC Connection Failed";
case (0x0200 + BLE_ERR_COARSE_CLK_ADJ): return "Coarse Clock Adjustment Rejected";
case (0x0300 + BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD): return "Invalid or unsupported incoming L2CAP sig command.";
case (0x0300 + BLE_L2CAP_SIG_ERR_MTU_EXCEEDED): return "Incoming packet too large.";
case (0x0300 + BLE_L2CAP_SIG_ERR_INVALID_CID): return "No channel with specified ID.";
case (0x0400 + BLE_SM_ERR_PASSKEY): return "The user input of passkey failed, for example, the user canceled the operation.";
case (0x0400 + BLE_SM_ERR_OOB): return "The OOB data is not available.";
case (0x0400 + BLE_SM_ERR_AUTHREQ):
return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices.";
case (0x0400 + BLE_SM_ERR_CONFIRM_MISMATCH): return "The confirm value does not match the calculated compare value.";
case (0x0400 + BLE_SM_ERR_PAIR_NOT_SUPP): return "Pairing is not supported by the device.";
case (0x0400 + BLE_SM_ERR_ENC_KEY_SZ): return "The resultant encryption key size is insufficient for the security requirements of this device.";
case (0x0400 + BLE_SM_ERR_CMD_NOT_SUPP): return "The SMP command received is not supported on this device.";
case (0x0400 + BLE_SM_ERR_UNSPECIFIED): return "Pairing failed due to an unspecified reason.";
case (0x0400 + BLE_SM_ERR_REPEATED):
return "Pairing or authentication procedure disallowed, too little time has elapsed since last pairing request or security request.";
case (0x0400 + BLE_SM_ERR_INVAL): return "Command length is invalid or that a parameter is outside of the specified range.";
case (0x0400 + BLE_SM_ERR_DHKEY): return "DHKey Check value received doesn't match the one calculated by the local device.";
case (0x0400 + BLE_SM_ERR_NUMCMP): return "Confirm values in the numeric comparison protocol do not match.";
case (0x0400 + BLE_SM_ERR_ALREADY): return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process.";
case (0x0400 + BLE_SM_ERR_CROSS_TRANS):
return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport.";
case (0x0500 + BLE_SM_ERR_PASSKEY): return "The user input of passkey failed or the user canceled the operation.";
case (0x0500 + BLE_SM_ERR_OOB): return "The OOB data is not available.";
case (0x0500 + BLE_SM_ERR_AUTHREQ):
return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices.";
case (0x0500 + BLE_SM_ERR_CONFIRM_MISMATCH): return "The confirm value does not match the calculated compare value.";
case (0x0500 + BLE_SM_ERR_PAIR_NOT_SUPP): return "Pairing is not supported by the device.";
case (0x0500 + BLE_SM_ERR_ENC_KEY_SZ): return "The resultant encryption key size is insufficient for the security requirements of this device.";
case (0x0500 + BLE_SM_ERR_CMD_NOT_SUPP): return "The SMP command received is not supported on this device.";
case (0x0500 + BLE_SM_ERR_UNSPECIFIED): return "Pairing failed due to an unspecified reason.";
case (0x0500 + BLE_SM_ERR_REPEATED):
return "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request.";
case (0x0500 + BLE_SM_ERR_INVAL): return "Command length is invalid or a parameter is outside of the specified range.";
case (0x0500 + BLE_SM_ERR_DHKEY):
return "Indicates to the remote device that the DHKey Check value received doesn't match the one calculated by the local device.";
case (0x0500 + BLE_SM_ERR_NUMCMP): return "Confirm values in the numeric comparison protocol do not match.";
case (0x0500 + BLE_SM_ERR_ALREADY): return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process.";
case (0x0500 + BLE_SM_ERR_CROSS_TRANS):
return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport.";
default: return "Unknown";
}
#else // #if defined(CONFIG_NIMBLE_ENABLE_RETURN_CODE_TEXT)
return "";
#endif // #if defined(CONFIG_NIMBLE_ENABLE_RETURN_CODE_TEXT)
}
/**
* @brief Utility function to log the gap event info.
* @param [in] event A pointer to the gap event structure.
* @param [in] arg Unused.
*/
void BLEUtils::dumpGapEvent(ble_gap_event *event, void *arg) {
#if defined(CONFIG_NIMBLE_ENABLE_GAP_EVENT_CODE_TEXT)
log_d("Received a GAP event: %s", gapEventToString(event->type));
#endif
}
/**
* @brief Convert a GAP event type to a string representation.
* @param [in] eventType The type of event.
* @return A string representation of the event type.
*/
const char *BLEUtils::gapEventToString(uint8_t eventType) {
#if defined(CONFIG_NIMBLE_ENABLE_GAP_EVENT_CODE_TEXT)
switch (eventType) {
case BLE_GAP_EVENT_CONNECT: //0
return "BLE_GAP_EVENT_CONNECT ";
case BLE_GAP_EVENT_DISCONNECT: //1
return "BLE_GAP_EVENT_DISCONNECT";
case BLE_GAP_EVENT_CONN_UPDATE: //3
return "BLE_GAP_EVENT_CONN_UPDATE";
case BLE_GAP_EVENT_CONN_UPDATE_REQ: //4
return "BLE_GAP_EVENT_CONN_UPDATE_REQ";
case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: //5
return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ";
case BLE_GAP_EVENT_TERM_FAILURE: //6
return "BLE_GAP_EVENT_TERM_FAILURE";
case BLE_GAP_EVENT_DISC: //7
return "BLE_GAP_EVENT_DISC";
case BLE_GAP_EVENT_DISC_COMPLETE: //8
return "BLE_GAP_EVENT_DISC_COMPLETE";
case BLE_GAP_EVENT_ADV_COMPLETE: //9
return "BLE_GAP_EVENT_ADV_COMPLETE";
case BLE_GAP_EVENT_ENC_CHANGE: //10
return "BLE_GAP_EVENT_ENC_CHANGE";
case BLE_GAP_EVENT_PASSKEY_ACTION: //11
return "BLE_GAP_EVENT_PASSKEY_ACTION";
case BLE_GAP_EVENT_NOTIFY_RX: //12
return "BLE_GAP_EVENT_NOTIFY_RX";
case BLE_GAP_EVENT_NOTIFY_TX: //13
return "BLE_GAP_EVENT_NOTIFY_TX";
case BLE_GAP_EVENT_SUBSCRIBE: //14
return "BLE_GAP_EVENT_SUBSCRIBE";
case BLE_GAP_EVENT_MTU: //15
return "BLE_GAP_EVENT_MTU";
case BLE_GAP_EVENT_IDENTITY_RESOLVED: //16
return "BLE_GAP_EVENT_IDENTITY_RESOLVED";
case BLE_GAP_EVENT_REPEAT_PAIRING: //17
return "BLE_GAP_EVENT_REPEAT_PAIRING";
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: //18
return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE";
case BLE_GAP_EVENT_EXT_DISC: //19
return "BLE_GAP_EVENT_EXT_DISC";
#ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these
case BLE_GAP_EVENT_PERIODIC_SYNC: //20
return "BLE_GAP_EVENT_PERIODIC_SYNC";
case BLE_GAP_EVENT_PERIODIC_REPORT: //21
return "BLE_GAP_EVENT_PERIODIC_REPORT";
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: //22
return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST";
case BLE_GAP_EVENT_SCAN_REQ_RCVD: //23
return "BLE_GAP_EVENT_SCAN_REQ_RCVD";
#endif
default: log_d("gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type";
}
#else // #if defined(CONFIG_NIMBLE_ENABLE_GAP_EVENT_CODE_TEXT)
return "";
#endif // #if defined(CONFIG_NIMBLE_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString
/**
* @brief Convert characteristic properties into a string representation.
* @param [in] prop Characteristic properties.
* @return A string representation of characteristic properties.
*/
String BLEUtils::characteristicPropertiesToString(uint8_t prop) {
String res = "broadcast: ";
res += ((prop & BLE_GATT_CHR_PROP_BROADCAST) ? "1" : "0");
res += ", read: ";
res += ((prop & BLE_GATT_CHR_PROP_READ) ? "1" : "0");
res += ", write_nr: ";
res += ((prop & BLE_GATT_CHR_PROP_WRITE_NO_RSP) ? "1" : "0");
res += ", write: ";
res += ((prop & BLE_GATT_CHR_PROP_WRITE) ? "1" : "0");
res += ", notify: ";
res += ((prop & BLE_GATT_CHR_PROP_NOTIFY) ? "1" : "0");
res += ", indicate: ";
res += ((prop & BLE_GATT_CHR_PROP_INDICATE) ? "1" : "0");
res += ", auth_sign_write: ";
res += ((prop & BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE) ? "1" : "0");
res += ", extended: ";
res += ((prop & BLE_GATT_CHR_PROP_EXTENDED) ? "1" : "0");
return res;
} // characteristicPropertiesToString
/**
* @brief Blocks the calling task until released or timeout.
* @param [in] taskData A pointer to the task data structure.
* @param [in] timeout The time to wait in milliseconds.
* @return True if the task completed, false if the timeout was reached.
*/
bool BLEUtils::taskWait(const BLETaskData &taskData, uint32_t timeout) {
ble_npl_time_t ticks;
if (timeout == BLE_NPL_TIME_FOREVER) {
ticks = BLE_NPL_TIME_FOREVER;
} else {
ble_npl_time_ms_to_ticks(timeout, &ticks);
}
uint32_t notificationValue;
xTaskNotifyWait(0, TASK_BLOCK_BIT, &notificationValue, 0);
if (notificationValue & TASK_BLOCK_BIT) {
return true;
}
return xTaskNotifyWait(0, TASK_BLOCK_BIT, nullptr, ticks) == pdTRUE;
} // taskWait
/**
* @brief Release a task.
* @param [in] taskData A pointer to the task data structure.
* @param [in] flags A return value to set in the task data structure.
*/
void BLEUtils::taskRelease(const BLETaskData &taskData, int flags) {
taskData.m_flags = flags;
if (taskData.m_pHandle != nullptr) {
xTaskNotify(static_cast<TaskHandle_t>(taskData.m_pHandle), TASK_BLOCK_BIT, eSetBits);
}
} // taskRelease
#endif // CONFIG_NIMBLE_ENABLED
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,6 +3,10 @@
* *
* Created on: Mar 25, 2017 * Created on: Mar 25, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_ #ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_
@ -11,24 +15,99 @@
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
#include <esp_gattc_api.h> // ESP32 BLE
#include <esp_gatts_api.h> // ESP32 BLE /*****************************************************************************
#include <esp_gap_ble_api.h> // ESP32 BLE * Common includes *
*****************************************************************************/
#include <string> #include <string>
#include "BLEClient.h" #include "BLEAddress.h"
#include "WString.h"
#include <freertos/task.h>
/*****************************************************************************
* Bluedroid includes *
*****************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
#include <esp_gap_ble_api.h>
#endif
/*****************************************************************************
* NimBLE includes *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
#include <stdlib.h>
#include <climits>
#include <host/ble_gap.h>
#endif
/*****************************************************************************
* Common types *
*****************************************************************************/
typedef struct {
void *peer_device; // peer device BLEClient or BLEServer
bool connected; // connection status
uint16_t mtu; // negotiated MTU per peer device
} conn_status_t;
/*****************************************************************************
* NimBLE types *
*****************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
/**
* @brief A structure to hold data for a task that is waiting for a response.
* @details This structure is used in conjunction with BLEUtils::taskWait() and BLEUtils::taskRelease().
* All items are optional, the m_pHandle will be set in taskWait().
*/
struct BLETaskData {
BLETaskData(void *pInstance = nullptr, int flags = 0, void *buf = nullptr);
~BLETaskData();
void *m_pInstance{nullptr};
mutable int m_flags{0};
void *m_pBuf{nullptr};
private:
mutable void *m_pHandle{nullptr}; // semaphore or task handle
friend class BLEUtils;
};
#endif
/*****************************************************************************
* Forward declarations *
*****************************************************************************/
class BLEClient;
/** /**
* @brief A set of general %BLE utilities. * @brief A set of general %BLE utilities.
*/ */
class BLEUtils { class BLEUtils {
public: public:
static const char *addressTypeToString(esp_ble_addr_type_t type); /***************************************************************************
static String adFlagsToString(uint8_t adFlags); * Common public declarations *
static const char *advTypeToString(uint8_t advType); ***************************************************************************/
static char *buildHexData(uint8_t *target, uint8_t *source, uint8_t length); static char *buildHexData(uint8_t *target, uint8_t *source, uint8_t length);
static String buildPrintData(uint8_t *source, size_t length); static String buildPrintData(uint8_t *source, size_t length);
static String characteristicPropertiesToString(esp_gatt_char_prop_t prop); static const char *advDataTypeToString(uint8_t advType);
static String characteristicPropertiesToString(uint8_t prop);
/***************************************************************************
* Bluedroid public declarations *
***************************************************************************/
#if defined(CONFIG_BLUEDROID_ENABLED)
static const char *addressTypeToString(esp_ble_addr_type_t type);
static String adFlagsToString(uint8_t adFlags);
static const char *devTypeToString(esp_bt_dev_type_t type); 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_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 esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true);
@ -52,8 +131,22 @@ public:
static void registerByAddress(BLEAddress address, BLEClient *pDevice); static void registerByAddress(BLEAddress address, BLEClient *pDevice);
static void registerByConnId(uint16_t conn_id, BLEClient *pDevice); static void registerByConnId(uint16_t conn_id, BLEClient *pDevice);
static const char *searchEventTypeToString(esp_gap_search_evt_t searchEvt); static const char *searchEventTypeToString(esp_gap_search_evt_t searchEvt);
#endif
/***************************************************************************
* NimBLE public declarations *
***************************************************************************/
#if defined(CONFIG_NIMBLE_ENABLED)
static void dumpGapEvent(ble_gap_event *event, void *arg);
static const char *gapEventToString(uint8_t eventType);
static const char *returnCodeToString(int rc);
static int checkConnParams(ble_gap_conn_params *params);
static bool taskWait(const BLETaskData &taskData, uint32_t timeout);
static void taskRelease(const BLETaskData &taskData, int rc = 0);
#endif
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */

View file

@ -3,15 +3,29 @@
* *
* Created on: Jul 17, 2017 * Created on: Jul 17, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include "BLEValue.h" #include "BLEValue.h"
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
/*****************************************************************************
* Common functions *
*****************************************************************************/
BLEValue::BLEValue() { BLEValue::BLEValue() {
m_accumulation = ""; m_accumulation = "";
m_value = ""; m_value = "";
@ -120,5 +134,5 @@ void BLEValue::setValue(uint8_t *pData, size_t length) {
m_value = String((char *)pData, length); m_value = String((char *)pData, length);
} // setValue } // setValue
#endif /* CONFIG_BLUEDROID_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */

View file

@ -3,22 +3,36 @@
* *
* Created on: Jul 17, 2017 * Created on: Jul 17, 2017
* Author: kolban * Author: kolban
*
* Modified on: Feb 18, 2025
* Author: lucasssvaz (based on kolban's and h2zero's work)
* Description: Added support for NimBLE
*/ */
#ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_ #ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_
#define COMPONENTS_CPP_UTILS_BLEVALUE_H_ #define COMPONENTS_CPP_UTILS_BLEVALUE_H_
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "WString.h"
#if SOC_BLE_SUPPORTED #if SOC_BLE_SUPPORTED
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BLUEDROID_ENABLED) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)
/*****************************************************************************
* Common includes *
*****************************************************************************/
#include "WString.h"
/** /**
* @brief The model of a %BLE value. * @brief The model of a %BLE value.
*/ */
class BLEValue { class BLEValue {
public: public:
/***************************************************************************
* Common public declarations *
***************************************************************************/
BLEValue(); BLEValue();
void addPart(String part); void addPart(String part);
void addPart(uint8_t *pData, size_t length); void addPart(uint8_t *pData, size_t length);
@ -33,10 +47,15 @@ public:
void setValue(uint8_t *pData, size_t length); void setValue(uint8_t *pData, size_t length);
private: private:
/***************************************************************************
* Common private properties *
***************************************************************************/
String m_accumulation; String m_accumulation;
uint16_t m_readOffset; uint16_t m_readOffset;
String m_value; String m_value;
}; };
#endif /* CONFIG_BLUEDROID_ENABLED */
#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */
#endif /* SOC_BLE_SUPPORTED */ #endif /* SOC_BLE_SUPPORTED */
#endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */

View file

@ -1,5 +1,5 @@
name=BluetoothSerial name=BluetoothSerial
version=3.2.1 version=3.3.0
author=Evandro Copercini author=Evandro Copercini
maintainer=Evandro Copercini maintainer=Evandro Copercini
sentence=Simple UART to Classical Bluetooth bridge for ESP32 sentence=Simple UART to Classical Bluetooth bridge for ESP32

View file

@ -7,7 +7,9 @@
* Author: Thomas M. (ArcticSnowSky) * Author: Thomas M. (ArcticSnowSky)
*/ */
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) #include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#include "BTAddress.h" #include "BTAddress.h"
#include <string> #include <string>

View file

@ -10,7 +10,9 @@
#ifndef COMPONENTS_CPP_UTILS_BTADDRESS_H_ #ifndef COMPONENTS_CPP_UTILS_BTADDRESS_H_
#define COMPONENTS_CPP_UTILS_BTADDRESS_H_ #define COMPONENTS_CPP_UTILS_BTADDRESS_H_
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) #include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_bt_api.h> // ESP32 BT #include <esp_gap_bt_api.h> // ESP32 BT
#include <Arduino.h> #include <Arduino.h>

View file

@ -5,9 +5,11 @@
* Author: Thomas M. (ArcticSnowSky) * Author: Thomas M. (ArcticSnowSky)
*/ */
#ifndef __BTADVERTISEDDEVICE_H__ #pragma once
#define __BTADVERTISEDDEVICE_H__ #include "sdkconfig.h"
#include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#include "BTAddress.h" #include "BTAddress.h"
#include <string> #include <string>

View file

@ -6,7 +6,9 @@
*/ */
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) #include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
//#include <map> //#include <map>

View file

@ -5,8 +5,11 @@
* Author: Thomas M. (ArcticSnowSky) * Author: Thomas M. (ArcticSnowSky)
*/ */
#ifndef __BTSCAN_H__ #pragma once
#define __BTSCAN_H__ #include "sdkconfig.h"
#include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#include <map> #include <map>
#include <string> #include <string>

Some files were not shown because too many files have changed in this diff Show more