Merge branch 'master' into mesh

This commit is contained in:
h2zero 2022-04-13 20:40:18 -06:00
commit 356459d352
68 changed files with 3814 additions and 837 deletions

View file

@ -1,27 +0,0 @@
sudo: false
before_install:
- cd ${TMPDIR-/tmp}
- wget -q http://doxygen.nl/files/doxygen-1.9.0.src.tar.gz
- tar -xzvf doxygen-1.9.0.src.tar.gz
- mkdir doxygen_build
- cd doxygen_build
- cmake ../doxygen-1.9.0/
- make
- export PATH="${TMPDIR-/tmp}/doxygen_build/bin:$PATH"
- cd ${TRAVIS_BUILD_DIR}
branches:
only:
- master
script:
- doxygen ./docs/Doxyfile
deploy:
provider: pages
skip_cleanup: true
local_dir: docs/html
github_token: $GH_REPO_TOKEN
on:
branch: master

View file

@ -1,6 +1,38 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [1.3.3] - 2022-02-15
### Changed
- If attribute retrieval fails with a "not found" try again with the 16 bit version if a 128 bit base uuid is used.
### Fixed
- Memory leak when deleting client instance.
- IDf version check for data length extension.
- Memory leak when server services changed.
- Compiler warnings for non-esp32 devices.
## [1.3.2] - 2022-01-15
### Fixed
- Initialize advertising complete callback in NimBLEAdvertising constructor.
- Clear client disconnect timer in constructor before initializing.
- Fix missing data when reading large values.
- Fix missing data in notifications when using a large MTU size and more than 270 bytes of data are sent.
- Workaround fix added for cases when the task notification value is not cleared, causing various functions that should block not to block.
### Added
- `NimBLEClient::getLastError` : Gets the error code of the last function call that produces a return code from the stack.
- `NimBLECharacteristic::notify` : Overload method to send notifications/indications with custom values.
- Added conditional checks for ESP32 specific functions/values to support use of the library on non-esp32 devices.
- Added an alias to use the callback name from the original library `onMtuChanged`.
- `NimBLEClient::setDataLen` and `NimBLEServer::setDataLen`: Data length extension support (IDF version >= 4.3.2 only)
- Config option to set logging level for esp-nimble-cpp
### Changed
- Critical section calls now use the NimBLE API instead of FreeRTOS directly. This removes the need for a `portMUX_TYPE` variable in the class definitions.
- Removed unnecessary variables in `NimBLEService` and changed the constructor no no longer accept `numHandles` and `inst_id` parameters.
## [1.3.1] - 2021-08-04 ## [1.3.1] - 2021-08-04

View file

@ -39,6 +39,7 @@ idf_component_register(
"src/NimBLEDevice.cpp" "src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp" "src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp" "src/NimBLEEddystoneURL.cpp"
"src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp" "src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp" "src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp" "src/NimBLERemoteDescriptor.cpp"

45
Kconfig
View file

@ -1,5 +1,31 @@
menu "ESP-NimBLE-CPP configuration" menu "ESP-NimBLE-CPP configuration"
choice NIMBLE_CPP_LOG_LEVEL
prompt "NimBLE CPP log verbosity"
default NIMBLE_CPP_LOG_LEVEL_NONE
help
Select NimBLE CPP log verbosity level.
config NIMBLE_CPP_LOG_LEVEL_NONE
bool "No logs"
config NIMBLE_CPP_LOG_LEVEL_ERROR
bool "Error logs"
config NIMBLE_CPP_LOG_LEVEL_WARNING
bool "Warning logs"
config NIMBLE_CPP_LOG_LEVEL_INFO
bool "Info logs"
config NIMBLE_CPP_LOG_LEVEL_DEBUG
bool "Debug logs"
endchoice #NIMBLE_CPP_LOG_LEVEL
config NIMBLE_CPP_LOG_LEVEL
int
default 0 if NIMBLE_CPP_LOG_LEVEL_NONE
default 1 if NIMBLE_CPP_LOG_LEVEL_ERROR
default 2 if NIMBLE_CPP_LOG_LEVEL_WARNING
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log." bool "Show NimBLE return codes as text in debug log."
default "n" default "n"
@ -23,5 +49,24 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
Enabling this option will display advertisment types recieved Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log. while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory. This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
bool "Enable timestamps to be stored with attribute values."
default "n"
help
Enabling this option will store the timestamp when an attribute value is updated.
This allows for checking the last update time using getTimeStamp()
or getValue(time_t*). If disabled, the timestamp returned from these functions will be 0.
Disabling timestamps will reduce the memory used for each value.
config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
int "Initial attribute value size (bytes) for empty values."
range 1 512
default 20
help
Sets the default allocation size (bytes) for each attribute if not specified
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
endmenu endmenu

View file

@ -0,0 +1,29 @@
# Bluetooth 5.x features
## About extended advertising
Extended advertising allows for much more capability and flexibility.
* Allows for 251 bytes of advertisement data and up to 1650 bytes when chained (configuration dependant) vs 31.
* New PHY's (physical layers) that allow for faster data rate (2M PHY) or long range/slower data rates (CODED PHY) as well as the original 1M PHY.
* New periodic advertising, allowing the scanning device to sync with the advertisements of a beacon. This allows for the scanning device to sleep or perform other tasks before the next expected advertisement is sent, preserving cpu cycles and power (To be implemented).
<br>
## Enabling extended advertising
Extended advertising is supported when enabled with the config option `CONFIG_BT_NIMBLE_EXT_ADV` set to a value of 1. This is done in menuconfig under `Component config > Bluetooth > NimBLE options >
Enable extended advertising`.
When enabled the following will occur:
* `NimBLEScan::start` method will scan on both the 1M PHY and the coded PHY standards automatically.
* `NimBLEClient::connect` will use the primary PHY the device is listening on, unless specified (see below).
* `NimBLEClient::setConnectPhy` becomes available to specify the PHY's to connect with (default is all).
* `NimBLEAdvertising` is no longer available for use and is replaced by `NimBLEExtAdvertising`. `NimBLEDevice::getAdvertising` will now return an instance of `NimBLEExtAdvertising`.
* `NimBLEAdvertisementData` is no longer available for use and is replaced by `NimBLEExtAdvertisement`. This new class is where everything about the advertisement is configured, including the advertisement intervals and advertisement ended callback.

View file

@ -6,6 +6,24 @@ Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3 - Default value is 3
<br/> <br/>
`CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED`
Enable/disable storing the timestamp when an attribute value is updated
This allows for checking the last update time using getTimeStamp() or getValue(time_t*)
If disabled, the timestamp returned from these functions will be 0.
Disabling timestamps will reduce the memory used for each value.
1 = Enabled, 0 = Disabled; Default = Disabled
<br/>
`CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH`
Set the default allocation size (bytes) for each attribute.
If not specified when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
<br/>
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU` `CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
Sets the default MTU size. Sets the default MTU size.
@ -24,6 +42,13 @@ If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory. - Uses approx. 32kB of flash memory.
<br/> <br/>
`CONFIG_NIMBLE_CPP_LOG_LEVEL`
Define to set the debug log message level from the NimBLE CPP Wrapper.
If not defined it will use the same value as the Arduino core debug level.
Values: 0 = NONE, 1 = ERROR, 2 = WARNING, 3 = INFO, 4+ = DEBUG
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT` `CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
If defined, NimBLE host return codes will be printed as text in debug log messages. If defined, NimBLE host return codes will be printed as text in debug log messages.

View file

@ -1,4 +1,4 @@
# Doxyfile 1.8.18 # Doxyfile 1.9.1
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -32,13 +32,13 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places. # title of most generated pages and in a few other places.
# The default value is: My Project. # The default value is: My Project.
PROJECT_NAME = "esp-nimble-cpp / NimBLE-Arduino" PROJECT_NAME = esp-nimble-cpp
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.3.1 PROJECT_NUMBER = 1.3.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
@ -58,7 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If # entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used. # left blank the current directory will be used.
OUTPUT_DIRECTORY = docs OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and # directories (in 2 levels) under the output directory of each output format and
@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements. # documentation from any documented member that it re-implements.
# The default value is: YES. # The default value is: YES.
@ -315,7 +323,10 @@ OPTIMIZE_OUTPUT_SLICE = NO
# Note: For files without extension you can use no_extension as a placeholder. # Note: For files without extension you can use no_extension as a placeholder.
# #
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise # Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. # the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING = EXTENSION_MAPPING =
@ -449,6 +460,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0 LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which efficively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Build related configuration options # Build related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -512,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these # undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation # members will be included in the various overviews, but no documentation
@ -549,11 +580,18 @@ HIDE_IN_BODY_DOCS = YES
INTERNAL_DOCS = NO INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# names in lower-case letters. If set to YES, upper-case letters are also # able to match the capabilities of the underlying filesystem. In case the
# allowed. This is useful if you have classes or files whose names only differ # filesystem is case sensitive (i.e. it supports files in the same directory
# in case and if your file system supports case sensitive file names. Windows # whose names only differ in casing), the option must be set to YES to properly
# (including Cygwin) ands Mac users are advised to set this option to NO. # deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent. # The default value is: system dependent.
CASE_SENSE_NAMES = NO CASE_SENSE_NAMES = NO
@ -792,7 +830,10 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO. # The default value is: NO.
WARN_AS_ERROR = NO WARN_AS_ERROR = NO
@ -823,13 +864,15 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = . INPUT = ../CHANGELOG.md \
. \
../src
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # documentation (see:
# possible encodings. # https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8. # The default value is: UTF-8.
INPUT_ENCODING = UTF-8 INPUT_ENCODING = UTF-8
@ -842,13 +885,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not # need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen. # read by doxygen.
# #
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.ucf, *.qsf and *.ice.
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \ FILE_PATTERNS = *.c \
*.cc \ *.cc \
@ -911,11 +956,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is # Note that relative paths are relative to the directory from which doxygen is
# run. # run.
EXCLUDE = ./README.md \ EXCLUDE = ../src/nimconfig_rename.h
./src/FreeRTOS.h \
./src/FreeRTOS.cpp \
./examples \
./CMakelists.txt
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded # directories that are symbolic links (a Unix file system feature) are excluded
@ -1112,6 +1153,44 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
# YES then doxygen will add the directory of each input to the include path.
# The default value is: YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index # Configuration options related to the alphabetical class index
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1123,13 +1202,6 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will # In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored # can be used to specify a prefix (or a list of prefixes) that should be ignored
@ -1300,10 +1372,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be # If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development # generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: https://developer.apple.com/xcode/), introduced with OSX # environment (see:
# 10.5 (Leopard). To create a documentation set, doxygen will generate a # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# Makefile in the HTML output directory. Running make will produce the docset in # create a documentation set, doxygen will generate a Makefile in the HTML
# that directory and running make install will install the docset in # output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information. # genXcode/_index.html for more information.
@ -1345,8 +1418,8 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # (see:
# Windows. # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
# #
# The HTML Help Workshop contains a compiler that can convert all HTML output # The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@ -1376,7 +1449,7 @@ CHM_FILE =
HHC_LOCATION = HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated # The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO). # (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES. # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@ -1421,7 +1494,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace # Project output. For more information please see Qt Help Project / Namespace
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project. # The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@ -1429,8 +1503,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual # Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # Folders (see:
# folders). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc. # The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@ -1438,16 +1512,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom # filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # Filters (see:
# filters). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom # custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # Filters (see:
# filters). # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS = QHP_CUST_FILTER_ATTRS =
@ -1459,9 +1533,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's # The QHG_LOCATION tag can be used to specify the location (absolute path
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# generated .qhp file. # run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION = QHG_LOCATION =
@ -1542,8 +1616,8 @@ EXT_LINKS_IN_WINDOW = NO
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for # https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions. # the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png The default and svg Looks nicer but requires the # Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg tool. # pdf2svg or inkscape tool).
# The default value is: png. # The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@ -1588,7 +1662,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for # When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see: # the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details. # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best # Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG. # compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS. # The default value is: HTML-CSS.
@ -1618,7 +1692,8 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site # of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation. # example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES. # This tag requires that the tag USE_MATHJAX is set to YES.
@ -1665,7 +1740,8 @@ SERVER_BASED_SEARCH = NO
# #
# Doxygen ships with an example indexer (doxyindexer) and search engine # Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/). # Xapian (see:
# https://xapian.org/).
# #
# See the section "External Indexing and Searching" for details. # See the section "External Indexing and Searching" for details.
# The default value is: NO. # The default value is: NO.
@ -1678,8 +1754,9 @@ EXTERNAL_SEARCH = NO
# #
# Doxygen ships with an example indexer (doxyindexer) and search engine # Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/). See the section "External Indexing and # Xapian (see:
# Searching" for details. # https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES. # This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL = SEARCHENGINE_URL =
@ -1843,9 +1920,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate # If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# the PDF file directly from the LaTeX files. Set this option to YES, to get a # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# higher quality PDF documentation. # files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@ -2084,6 +2163,10 @@ DOCBOOK_PROGRAMLISTING = NO
GENERATE_AUTOGEN_DEF = NO GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the Perl module output # Configuration options related to the Perl module output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -2361,10 +2444,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to # but if the number exceeds 15, the total amount of fields shown is limited to
# 10. # 10.
# Minimum value: 0, maximum value: 100, default value: 10. # Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10 UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their # collaboration graphs will show the relations between templates and their
# instances. # instances.
@ -2554,9 +2659,11 @@ DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs. # files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc and
# plantuml temporary files.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES DOT_CLEANUP = YES

View file

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_client)

View file

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_extended_client
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,169 @@
/** NimBLE Extended Client Demo:
*
* Demonstrates the Bluetooth 5.x client capabilities.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" void app_main(void);
void scanEndedCB(NimBLEScanResults results);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
static NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10; /* 0 = scan forever */
/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ;
/* Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
};
void onDisconnect(NimBLEClient* pClient) {
printf("%s Disconnected - Starting scan\n", pClient->getPeerAddress().toString().c_str());
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
};
};
/* Define a class to handle the callbacks when advertisements are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD")))
{
printf("Found Our Service\n");
/* Ready to connect now */
doConnect = true;
/* Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/* stop scan before connecting */
NimBLEDevice::getScan()->stop();
}
};
};
/* Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
printf("Scan Ended\n");
if (!doConnect) { /* Don't start the scan while connecting */
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
}
}
/* Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallbacks, false);
/* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
* Combine these with OR ("|"), eg BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK;
*/
pClient->setConnectPhy(connectPhys);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
pClient->setConnectTimeout(10);
if (!pClient->connect(advDevice)) {
/* Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n");
return false;
}
printf("Connected to: %s RSSI: %d\n",
pClient->getPeerAddress().toString().c_str(),
pClient->getRssi());
/* Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
pSvc = pClient->getService(SERVICE_UUID);
if (pSvc) {
pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if (pChr) {
// Read the value of the characteristic.
if (pChr->canRead()) {
std::string value = pChr->readValue();
printf("Characteristic value: %s\n", value.c_str());
}
}
} else {
printf("ABCD service not found.\n");
}
NimBLEDevice::deleteClient(pClient);
printf("Done with this device!\n");
return true;
}
void connectTask (void * parameter){
/* Loop here until we find a device we want to connect to */
for (;;) {
if (doConnect) {
/* Found a device we want to connect to, do it now */
if (connectToServer()) {
printf("Success!, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
doConnect = false;
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelete(NULL);
}
void app_main (void) {
printf("Starting NimBLE Client\n");
/* Create a task to handle connecting to peers */
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
/* Initialize NimBLE, no device name specified as we are not advertising */
NimBLEDevice::init("");
NimBLEScan* pScan = NimBLEDevice::getScan();
/* create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
/* Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime, scanEndedCB);
printf("Scanning for peripherals\n");
}

View file

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_server)

View file

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_extended_server
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,139 @@
/** NimBLE Extended Server Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise a long data string on the CODED and 1M Phy's and
* starts a server allowing connection over either PHY's. It will advertise for
* 5 seconds then sleep for 20 seconds, if a client connects it will sleep once
* it has disconnected then repeats.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepSeconds = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
};
void onDisconnect(NimBLEServer* pServer) {
printf("Client disconnected - sleeping for %u seconds\n", sleepSeconds);
esp_deep_sleep_start();
};
};
/* Callback class to handle advertising events */
class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf("Client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %u seconds\n", sleepSeconds);
break;
default:
break;
}
esp_deep_sleep_start();
}
};
void app_main (void) {
NimBLEDevice::init("Extended advertiser");
/* Create the server and add the services/characteristics/descriptors */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the services */
pService->start();
/*
* Create an extended advertisement with the instance ID 0 and set the PHY's.
* Multiple instances can be added as long as the instance ID is incremented.
*/
NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy);
/* Set the advertisement as connectable */
extAdv.setConnectable(true);
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extAdv.setScannable(false); // The default is false, set here for demonstration.
/* Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */
extAdv.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Extended Advertising Demo.\r\n"
"Extended advertising allows for "
"251 bytes of data in a single advertisement,\r\n"
"or up to 1650 bytes with chaining.\r\n"
"This example message is 226 bytes long "
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks for advertising events */
pAdvertising->setCallbacks(new advertisingCallbacks);
/*
* NimBLEExtAdvertising::setInstanceData takes the instance ID and
* a reference to a `NimBLEExtAdvertisement` object. This sets the data
* that will be advertised for this instance ID, returns true if successful.
*
* Note: It is safe to create the advertisement as a local variable if setInstanceData
* is called before exiting the code block as the data will be copied.
*/
if (pAdvertising->setInstanceData(0, extAdv)) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);
}

View file

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_multi_advertiser)

View file

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_multi_advertiser
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,170 @@
/** NimBLE Multi Advertiser Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise 2 advertisements, and extended scannable instance
* and a connectable legacy instance. They will advertise for 5 seconds then
* sleep for 20 seconds. The extended scannable instance will use the scan
* request callback to update it's data when a scan response is requested.
*
* Created: on April 9 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepTime = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
printf("Client connected: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
};
void onDisconnect(NimBLEServer* pServer) {
printf("Client disconnected\n");
// if still advertising we won't sleep yet.
if (!pServer->getAdvertising()->isAdvertising()) {
printf("Sleeping for %u seconds\n", sleepTime);
esp_deep_sleep_start();
}
};
};
/* Callback class to handle advertising events */
class advCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf(" client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %u seconds\n", sleepTime);
break;
default:
break;
}
esp_deep_sleep_start();
}
bool m_updatedSR = false;
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) {
printf("Scan request for instance %u\n", inst_id);
// if the data has already been updated we don't need to change it again.
if (!m_updatedSR) {
printf("Updating scan data\n");
NimBLEExtAdvertisement sr;
sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!"));
pAdv->setScanResponseData(inst_id, sr);
m_updatedSR = true;
}
}
};
void app_main (void) {
NimBLEDevice::init("Multi advertiser");
/* Create a server for our legacy advertiser */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the service */
pService->start();
/* Create our multi advertising instances */
// extended scannable instance advertising on coded and 1m PHY's.
NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
// Legacy advertising as a connectable device.
NimBLEExtAdvertisement legacyConnectable;
// Optional scan response data.
NimBLEExtAdvertisement legacyScanResponse;
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extScannable.setScannable(true);
extScannable.setConnectable(false);
/* Set the initial data */
extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!"));
/* enable the scan response callback, we will use this to update the data. */
extScannable.enableScanRequestCallback(true);
/* Optional custom address for this advertisment. */
legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
/* Set the advertising data. */
legacyConnectable.setName("Legacy");
legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* Set the legacy and connectable flags. */
legacyConnectable.setLegacyAdvertising(true);
legacyConnectable.setConnectable(true);
/* Put some data in the scan response if desired. */
legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
/* Get the advertising ready */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(new advCallbacks);
/* Set instance data.
* Up to 5 instances can be used if configured in menuconfig, instance 0 is always available.
*
* We will set the extended scannable data on instance 0 and the legacy data on instance 1.
* Note that the legacy scan response data needs to be set to the same instance (1).
*/
if (pAdvertising->setInstanceData( 0, extScannable ) &&
pAdvertising->setInstanceData( 1, legacyConnectable ) &&
pAdvertising->setScanResponseData( 1, legacyScanResponse )) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepTime * 1000000);
}

View file

@ -45,13 +45,8 @@
/* of data as per HID Class standard */ /* of data as per HID Class standard */
/* Main items */ /* Main items */
#ifdef ARDUINO_ARCH_ESP32 #define HIDINPUT(size) (0x80 | size)
#define HIDINPUT(size) (0x80 | size) #define HIDOUTPUT(size) (0x90 | size)
#define HIDOUTPUT(size) (0x90 | size)
#else
#define INPUT(size) (0x80 | size)
#define OUTPUT(size) (0x90 | size)
#endif
#define FEATURE(size) (0xb0 | size) #define FEATURE(size) (0xb0 | size)
#define COLLECTION(size) (0xa0 | size) #define COLLECTION(size) (0xa0 | size)
#define END_COLLECTION(size) (0xc0 | size) #define END_COLLECTION(size) (0xc0 | size)

View file

@ -16,11 +16,8 @@
* See also: * See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLE2904.h" #include "NimBLE2904.h"
@ -86,5 +83,4 @@ void NimBLE2904::setUnit(uint16_t unit) {
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue((uint8_t*) &m_data, sizeof(m_data));
} // setUnit } // setUnit
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif

View file

@ -14,11 +14,8 @@
#ifndef MAIN_NIMBLE2904_H_ #ifndef MAIN_NIMBLE2904_H_
#define MAIN_NIMBLE2904_H_ #define MAIN_NIMBLE2904_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
@ -82,6 +79,5 @@ private:
BLE2904_Data m_data; BLE2904_Data m_data;
}; // BLE2904 }; // BLE2904
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLE2904_H_ */ #endif /* MAIN_NIMBLE2904_H_ */

View file

@ -11,7 +11,7 @@
* Created on: Jul 2, 2017 * Created on: Jul 2, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include <algorithm> #include <algorithm>

View file

@ -14,10 +14,15 @@
#ifndef COMPONENTS_NIMBLEADDRESS_H_ #ifndef COMPONENTS_NIMBLEADDRESS_H_
#define COMPONENTS_NIMBLEADDRESS_H_ #define COMPONENTS_NIMBLEADDRESS_H_
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "nimble/ble.h" #include "nimble/ble.h"
#else
#include "nimble/nimble/include/nimble/ble.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max

View file

@ -11,17 +11,17 @@
* Created on: Jul 3, 2017 * Created on: Jul 3, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLEAdvertisedDevice.h" #include "NimBLEAdvertisedDevice.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLEAdvertisedDevice"; static const char* LOG_TAG = "NimBLEAdvertisedDevice";
@ -71,7 +71,7 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device. * @return The appearance of the advertised device.
*/ */
uint16_t NimBLEAdvertisedDevice::getAppearance() { uint16_t NimBLEAdvertisedDevice::getAppearance() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -89,7 +89,7 @@ uint16_t NimBLEAdvertisedDevice::getAppearance() {
* @return The advertisement interval in 0.625ms units. * @return The advertisement interval in 0.625ms units.
*/ */
uint16_t NimBLEAdvertisedDevice::getAdvInterval() { uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -107,7 +107,7 @@ uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
* @return The preferred min connection interval in 1.25ms units. * @return The preferred min connection interval in 1.25ms units.
*/ */
uint16_t NimBLEAdvertisedDevice::getMinInterval() { uint16_t NimBLEAdvertisedDevice::getMinInterval() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -125,7 +125,7 @@ uint16_t NimBLEAdvertisedDevice::getMinInterval() {
* @return The preferred max connection interval in 1.25ms units. * @return The preferred max connection interval in 1.25ms units.
*/ */
uint16_t NimBLEAdvertisedDevice::getMaxInterval() { uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -143,7 +143,7 @@ uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
* @return The manufacturer data of the advertised device. * @return The manufacturer data of the advertised device.
*/ */
std::string NimBLEAdvertisedDevice::getManufacturerData() { std::string NimBLEAdvertisedDevice::getManufacturerData() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -161,7 +161,7 @@ std::string NimBLEAdvertisedDevice::getManufacturerData() {
* @return The URI data. * @return The URI data.
*/ */
std::string NimBLEAdvertisedDevice::getURI() { std::string NimBLEAdvertisedDevice::getURI() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -179,7 +179,7 @@ std::string NimBLEAdvertisedDevice::getURI() {
* @return The name of the advertised device. * @return The name of the advertised device.
*/ */
std::string NimBLEAdvertisedDevice::getName() { std::string NimBLEAdvertisedDevice::getName() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 || if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0) findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
@ -216,7 +216,7 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
* @brief Get the number of target addresses. * @brief Get the number of target addresses.
* @return The number of addresses. * @return The number of addresses.
*/ */
size_t NimBLEAdvertisedDevice::getTargetAddressCount() { uint8_t NimBLEAdvertisedDevice::getTargetAddressCount() {
uint8_t count = 0; uint8_t count = 0;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR); count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
@ -234,7 +234,7 @@ size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) { NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
ble_hs_adv_field *field = nullptr; ble_hs_adv_field *field = nullptr;
uint8_t count = 0; uint8_t count = 0;
uint8_t data_loc = 0xFF; size_t data_loc = ULONG_MAX;
index++; index++;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc); count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
@ -244,7 +244,7 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc); count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
} }
if(count > 0 && data_loc != 0xFF) { if(count > 0 && data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc]; field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
@ -266,9 +266,9 @@ NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) { std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
ble_hs_adv_field *field = nullptr; ble_hs_adv_field *field = nullptr;
uint8_t bytes; uint8_t bytes;
uint8_t data_loc = findServiceData(index, &bytes); size_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) { if(data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc]; field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > bytes) { if(field->length > bytes) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1); return std::string((char*)(field->value + bytes), field->length - bytes - 1);
@ -288,9 +288,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
ble_hs_adv_field *field = nullptr; ble_hs_adv_field *field = nullptr;
uint8_t bytes; uint8_t bytes;
uint8_t index = 0; uint8_t index = 0;
uint8_t data_loc = findServiceData(index, &bytes); size_t data_loc = findServiceData(index, &bytes);
size_t plSize = m_payload.size() - 2;
uint8_t uuidBytes = uuid.bitSize() / 8; uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t plSize = m_payload.size() - 2;
while(data_loc < plSize) { while(data_loc < plSize) {
field = (ble_hs_adv_field *)&m_payload[data_loc]; field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -315,9 +315,9 @@ std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) { NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
ble_hs_adv_field *field = nullptr; ble_hs_adv_field *field = nullptr;
uint8_t bytes; uint8_t bytes;
uint8_t data_loc = findServiceData(index, &bytes); size_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) { if(data_loc != ULONG_MAX) {
field = (ble_hs_adv_field *)&m_payload[data_loc]; field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length >= bytes) { if(field->length >= bytes) {
return NimBLEUUID(field->value, bytes, false); return NimBLEUUID(field->value, bytes, false);
@ -332,10 +332,10 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
* @brief Find the service data at the index. * @brief Find the service data at the index.
* @param [in] index The index of the service data to find. * @param [in] index The index of the service data to find.
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID. * @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
* @return The index in the vector where the data is located, 0xFF if not found. * @return The index in the vector where the data is located, ULONG_MAX if not found.
*/ */
uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) { size_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
uint8_t data_loc = 0; size_t data_loc = 0;
uint8_t found = 0; uint8_t found = 0;
*bytes = 0; *bytes = 0;
@ -360,7 +360,7 @@ uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
return data_loc; return data_loc;
} }
return 0xFF; return ULONG_MAX;
} }
@ -368,7 +368,7 @@ uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
* @brief Get the count of advertised service data UUIDS * @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector. * @return The number of service data UUIDS in the vector.
*/ */
size_t NimBLEAdvertisedDevice::getServiceDataCount() { uint8_t NimBLEAdvertisedDevice::getServiceDataCount() {
uint8_t count = 0; uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16); count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
@ -386,7 +386,7 @@ size_t NimBLEAdvertisedDevice::getServiceDataCount() {
*/ */
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) { NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
uint8_t count = 0; uint8_t count = 0;
uint8_t data_loc = 0; size_t data_loc = 0;
uint8_t uuidBytes = 0; uint8_t uuidBytes = 0;
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
ble_hs_adv_field *field = nullptr; ble_hs_adv_field *field = nullptr;
@ -433,7 +433,7 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @brief Get the number of services advertised * @brief Get the number of services advertised
* @return The count of services in the advertising packet. * @return The count of services in the advertising packet.
*/ */
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() { uint8_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
uint8_t count = 0; uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16); count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
@ -469,7 +469,7 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
* @return The TX Power of the advertised device. * @return The TX Power of the advertised device.
*/ */
int8_t NimBLEAdvertisedDevice::getTXPower() { int8_t NimBLEAdvertisedDevice::getTXPower() {
uint8_t data_loc = 0; size_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) { if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
@ -583,17 +583,60 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
} // haveTXPower } // haveTXPower
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) { #if CONFIG_BT_NIMBLE_EXT_ADV
ble_hs_adv_field *field = nullptr; /**
uint8_t data = 0; * @brief Get the set ID of the extended advertisement.
uint8_t length = m_payload.size(); * @return The set ID.
uint8_t count = 0; */
uint8_t NimBLEAdvertisedDevice::getSetId() {
return m_sid;
} // getSetId
if(length < 2) {
/**
* @brief Get the primary PHY used by this advertisement.
* @return The PHY type, one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
*/
uint8_t NimBLEAdvertisedDevice::getPrimaryPhy() {
return m_primPhy;
} // getPrimaryPhy
/**
* @brief Get the primary PHY used by this advertisement.
* @return The PHY type, one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
uint8_t NimBLEAdvertisedDevice::getSecondaryPhy() {
return m_secPhy;
} // getSecondaryPhy
/**
* @brief Get the periodic interval of the advertisement.
* @return The periodic advertising interval, 0 if not periodic advertising.
*/
uint16_t NimBLEAdvertisedDevice::getPeriodicInterval() {
return m_periodicItvl;
} // getPeriodicInterval
#endif
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, size_t * data_loc) {
ble_hs_adv_field *field = nullptr;
size_t length = m_payload.size();
size_t data = 0;
uint8_t count = 0;
if (length < 3) {
return count; return count;
} }
while (length > 1) { while (length > 2) {
field = (ble_hs_adv_field*)&m_payload[data]; field = (ble_hs_adv_field*)&m_payload[data];
if (field->length >= length) { if (field->length >= length) {
@ -601,7 +644,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
} }
if (field->type == type) { if (field->type == type) {
switch(type) { switch (type) {
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16: case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
case BLE_HS_ADV_TYPE_COMP_UUIDS16: case BLE_HS_ADV_TYPE_COMP_UUIDS16:
count += field->length / 2; count += field->length / 2;
@ -627,8 +670,8 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
break; break;
} }
if(data_loc != nullptr) { if (data_loc != nullptr) {
if(index == 0 || count >= index) { if (index == 0 || count >= index) {
break; break;
} }
} }
@ -638,7 +681,7 @@ uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_
data += 1 + field->length; data += 1 + field->length;
} }
if(data_loc != nullptr && field != nullptr) { if (data_loc != nullptr && field != nullptr) {
*data_loc = data; *data_loc = data;
} }
@ -659,8 +702,13 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
* @brief Set the adFlag for this device. * @brief Set the adFlag for this device.
* @param [in] advType The advertisement flag data from the advertisement. * @param [in] advType The advertisement flag data from the advertisement.
*/ */
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) { void NimBLEAdvertisedDevice::setAdvType(uint8_t advType, bool isLegacyAdv) {
m_advType = advType; m_advType = advType;
#if CONFIG_BT_NIMBLE_EXT_ADV
m_isLegacyAdv = isLegacyAdv;
#else
(void)isLegacyAdv;
#endif
} // setAdvType } // setAdvType
@ -705,10 +753,10 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val; res += val;
} }
if(haveServiceData()) { if (haveServiceData()) {
size_t count = getServiceDataCount(); uint8_t count = getServiceDataCount();
res += "\nService Data:"; res += "\nService Data:";
for(size_t i = 0; i < count; i++) { for(uint8_t i = 0; i < count; i++) {
res += "\nUUID: " + std::string(getServiceDataUUID(i)); res += "\nUUID: " + std::string(getServiceDataUUID(i));
res += ", Data: " + getServiceData(i); res += ", Data: " + getServiceData(i);
} }
@ -784,6 +832,33 @@ size_t NimBLEAdvertisedDevice::getPayloadLength() {
} // getPayloadLength } // getPayloadLength
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) /**
#endif /* CONFIG_BT_ENABLED */ * @brief Check if this device is advertising as connectable.
* @return True if the device is connectable.
*/
bool NimBLEAdvertisedDevice::isConnectable() {
#if CONFIG_BT_NIMBLE_EXT_ADV
if (m_isLegacyAdv) {
return m_advType == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND ||
m_advType == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
}
#endif
return (m_advType & BLE_HCI_ADV_CONN_MASK) ||
(m_advType & BLE_HCI_ADV_DIRECT_MASK);
} // isConnectable
/**
* @brief Check if this advertisement is a legacy or extended type
* @return True if legacy (Bluetooth 4.x), false if extended (bluetooth 5.x).
*/
bool NimBLEAdvertisedDevice::isLegacyAdvertisement() {
#if CONFIG_BT_NIMBLE_EXT_ADV
return m_isLegacyAdv;
# else
return true;
#endif
} // isLegacyAdvertisement
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */

View file

@ -14,20 +14,22 @@
#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_
#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEScan.h" #include "NimBLEScan.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_hs_adv.h" #include "host/ble_hs_adv.h"
#else
#include "nimble/nimble/host/include/host/ble_hs_adv.h"
#endif
#include <map> #include <map>
#include <vector> #include <vector>
#include <time.h>
class NimBLEScan; class NimBLEScan;
@ -69,7 +71,7 @@ public:
std::string getName(); std::string getName();
int getRSSI(); int getRSSI();
NimBLEScan* getScan(); NimBLEScan* getScan();
size_t getServiceDataCount(); uint8_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0); std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid); std::string getServiceData(const NimBLEUUID &uuid);
@ -109,9 +111,9 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0); NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0); NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount(); uint8_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0); NimBLEAddress getTargetAddress(uint8_t index = 0);
size_t getTargetAddressCount(); uint8_t getTargetAddressCount();
int8_t getTXPower(); int8_t getTXPower();
uint8_t* getPayload(); uint8_t* getPayload();
uint8_t getAdvLength(); uint8_t getAdvLength();
@ -131,16 +133,30 @@ public:
bool haveTargetAddress(); bool haveTargetAddress();
bool haveURI(); bool haveURI();
std::string toString(); std::string toString();
bool isConnectable();
bool isLegacyAdvertisement();
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId();
uint8_t getPrimaryPhy();
uint8_t getSecondaryPhy();
uint16_t getPeriodicInterval();
#endif
private: private:
friend class NimBLEScan; friend class NimBLEScan;
void setAddress(NimBLEAddress address); void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType); void setAdvType(uint8_t advType, bool isLegacyAdv);
void setPayload(const uint8_t *payload, uint8_t length, bool append); void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi); void setRSSI(int rssi);
uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr); #if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t findServiceData(uint8_t index, uint8_t* bytes); void setSetId(uint8_t sid) { m_sid = sid; }
void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
void setSecondaryPhy(uint8_t phy) { m_secPhy = phy; }
void setPeriodicInterval(uint16_t itvl) { m_periodicItvl = itvl; }
#endif
uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t * data_loc = nullptr);
size_t findServiceData(uint8_t index, uint8_t* bytes);
NimBLEAddress m_address = NimBLEAddress(""); NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType; uint8_t m_advType;
@ -148,6 +164,13 @@ private:
time_t m_timestamp; time_t m_timestamp;
bool m_callbackSent; bool m_callbackSent;
uint8_t m_advLength; uint8_t m_advLength;
#if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv;
uint8_t m_sid;
uint8_t m_primPhy;
uint8_t m_secPhy;
uint16_t m_periodicItvl;
#endif
std::vector<uint8_t> m_payload; std::vector<uint8_t> m_payload;
}; };
@ -171,6 +194,5 @@ public:
virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0;
}; };
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */ #endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */

View file

@ -13,13 +13,16 @@
* Author: kolban * Author: kolban
* *
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#endif
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLEServer.h" #include "NimBLEServer.h"
@ -68,6 +71,7 @@ void NimBLEAdvertising::reset() {
m_advDataSet = false; m_advDataSet = false;
// Set this to non-zero to prevent auto start if host reset before started by app. // Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER; m_duration = BLE_HS_FOREVER;
m_advCompCB = nullptr;
} // reset } // reset
@ -410,7 +414,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// If already advertising just return // If already advertising just return
if(ble_gap_adv_active()) { if(ble_gap_adv_active()) {
NIMBLE_LOGW(LOG_TAG, "Advertising already active"); NIMBLE_LOGW(LOG_TAG, "Advertising already active");
return false; return true;
} }
// Save the duration incase of host reset so we can restart with the same params // Save the duration incase of host reset so we can restart with the same params
@ -623,7 +627,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
&m_advParams, &m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : (pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent, NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this); (void*)this);
#else #else
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration, rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this); &m_advParams, NimBLEAdvertising::handleGapEvent, this);
@ -632,6 +636,10 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
case 0: case 0:
break; break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_EINVAL: case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long"); NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break; break;
@ -653,29 +661,27 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
break; break;
} }
if(rc != 0) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
return true; return (rc == 0 || rc == BLE_HS_EALREADY);
} // start } // start
/** /**
* @brief Stop advertising. * @brief Stop advertising.
* @return True if advertising stopped successfully.
*/ */
void NimBLEAdvertising::stop() { bool NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop"); NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop(); int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) { if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
return; return false;
} }
NIMBLE_LOGD(LOG_TAG, "<< stop"); NIMBLE_LOGD(LOG_TAG, "<< stop");
return true;
} // stop } // stop
@ -1028,5 +1034,4 @@ std::string NimBLEAdvertisementData::getPayload() {
return m_payload; return m_payload;
} // getPayload } // getPayload
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* CONFIG_BT_ENABLED */

View file

@ -14,13 +14,17 @@
#ifndef MAIN_BLEADVERTISING_H_ #ifndef MAIN_BLEADVERTISING_H_
#define MAIN_BLEADVERTISING_H_ #define MAIN_BLEADVERTISING_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max
@ -87,7 +91,7 @@ public:
void addServiceUUID(const char* serviceUUID); void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID); void removeServiceUUID(const NimBLEUUID &serviceUUID);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr); bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop(); bool stop();
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setName(const std::string &name); void setName(const std::string &name);
void setManufacturerData(const std::string &data); void setManufacturerData(const std::string &data);
@ -109,6 +113,7 @@ public:
private: private:
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync(); void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg); static int handleGapEvent(struct ble_gap_event *event, void *arg);
@ -132,6 +137,5 @@ private:
std::vector<uint8_t> m_uri; std::vector<uint8_t> m_uri;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_BLEADVERTISING_H_ */ #endif /* MAIN_BLEADVERTISING_H_ */

447
src/NimBLEAttValue.h Normal file
View file

@ -0,0 +1,447 @@
/*
* NimBLEAttValue.h
*
* Created: on March 18, 2021
* Author H2zero
*
*/
#ifndef MAIN_NIMBLEATTVALUE_H_
#define MAIN_NIMBLEATTVALUE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
#include <Arduino.h>
#endif
#include "NimBLELog.h"
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include <string>
#include <vector>
#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
#endif
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# include <time.h>
#endif
#if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
#endif
/* Used to determine if the type passed to a template has a c_str() and length() method. */
template <typename T, typename = void, typename = void>
struct Has_c_str_len : std::false_type {};
template <typename T>
struct Has_c_str_len<T, decltype(void(std::declval<T &>().c_str())),
decltype(void(std::declval<T &>().length()))> : std::true_type {};
/**
* @brief A specialized container class to hold BLE attribute values.
* @details This class is designed to be more memory efficient than using\n
* standard container types for value storage, while being convertable to\n
* many different container classes.
*/
class NimBLEAttValue
{
uint8_t* m_attr_value = nullptr;
uint16_t m_attr_max_len = 0;
uint16_t m_attr_len = 0;
uint16_t m_capacity = 0;
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp = 0;
#endif
void deepCopy(const NimBLEAttValue & source);
public:
/**
* @brief Default constructor.
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initial value from a buffer.
* @param value A pointer to the initial value to set.
* @param[in] len The size in bytes of the value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const uint8_t *value, uint16_t len,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initializer list.
* @param list An initializer list containing the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(std::initializer_list<uint8_t> list,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(list.begin(), (uint16_t)list.size(), max_len){}
/**
* @brief Construct with an initial value from a const char string.
* @param value A pointer to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){}
/**
* @brief Construct with an initial value from a std::string.
* @param str A std::string containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.data(), (uint16_t)str.length(), max_len){}
/**
* @brief Construct with an initial value from a std::vector<uint8_t>.
* @param vec A std::vector<uint8_t> containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::vector<uint8_t> vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue(&vec[0], (uint16_t)vec.size(), max_len){}
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/**
* @brief Construct with an initial value from an Arduino String.
* @param str An Arduino String containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)str.c_str(), str.length(), max_len){}
#endif
/** @brief Copy constructor */
NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); }
/** @brief Move constructor */
NimBLEAttValue(NimBLEAttValue && source) { *this = std::move(source); }
/** @brief Destructor */
~NimBLEAttValue();
/** @brief Returns the max size in bytes */
uint16_t max_size() const { return m_attr_max_len; }
/** @brief Returns the currently allocated capacity in bytes */
uint16_t capacity() const { return m_capacity; }
/** @brief Returns the current length of the value in bytes */
uint16_t length() const { return m_attr_len; }
/** @brief Returns the current size of the value in bytes */
uint16_t size() const { return m_attr_len; }
/** @brief Returns a pointer to the internal buffer of the value */
const uint8_t* data() const { return m_attr_value; }
/** @brief Returns a pointer to the internal buffer of the value as a const char* */
const char* c_str() const { return (const char*)m_attr_value; }
/** @brief Iterator begin */
const uint8_t* begin() const { return m_attr_value; }
/** @brief Iterator end */
const uint8_t* end() const { return m_attr_value + m_attr_len; }
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
/** @brief Returns a timestamp of when the value was last updated */
time_t getTimeStamp() const { return m_timestamp; }
/** @brief Set the timestamp to the current time */
void setTimeStamp() { m_timestamp = time(nullptr); }
/**
* @brief Set the timestamp to the specified time
* @param[in] t The timestamp value to set
*/
void setTimeStamp(time_t t) { m_timestamp = t; }
#else
time_t getTimeStamp() const { return 0; }
void setTimeStamp() { }
void setTimeStamp(time_t t) { }
#endif
/**
* @brief Set the value from a buffer
* @param[in] value A ponter to a buffer containing the value.
* @param[in] len The length of the value in bytes.
* @returns True if successful.
*/
bool setValue(const uint8_t *value, uint16_t len);
/**
* @brief Set value to the value of const char*.
* @param [in] s A ponter to a const char value to set.
*/
bool setValue(const char* s) {
return setValue((uint8_t*)s, (uint16_t)strlen(s)); }
/**
* @brief Get a pointer to the value buffer with timestamp.
* @param[in] timestamp A ponter to a time_t variable to store the timestamp.
* @returns A pointer to the internal value buffer.
*/
const uint8_t* getValue(time_t *timestamp);
/**
* @brief Append data to the value.
* @param[in] value A ponter to a data buffer with the value to append.
* @param[in] len The length of the value to append in bytes.
* @returns A reference to the appended NimBLEAttValue.
*/
NimBLEAttValue& append(const uint8_t *value, uint16_t len);
/*********************** Template Functions ************************/
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @details Only used for types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T &s) {
return setValue((uint8_t*)&s, sizeof(T));
}
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
setValue(const T & s) {
return setValue((uint8_t*)s.c_str(), (uint16_t)s.length());
}
/**
* @brief Template to return the value as a <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than\n
* <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is\n
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && size() < sizeof(T)) {
return T();
}
return *((T *)getValue(timestamp));
}
/*********************** Operators ************************/
/** @brief Subscript operator */
uint8_t operator [](int pos) const {
assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; }
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const {
return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
/** @brief Operator; Get the value as a std::string. */
operator std::string() const {
return std::string((char*)m_attr_value, m_attr_len); }
/** @brief Operator; Get the value as a const uint8_t*. */
operator const uint8_t*() const { return m_attr_value; }
/** @brief Operator; Append another NimBLEAttValue. */
NimBLEAttValue& operator +=(const NimBLEAttValue & source) {
return append(source.data(), source.size()); }
/** @brief Operator; Set the value from a std::string source. */
NimBLEAttValue& operator =(const std::string & source) {
setValue((uint8_t*)source.data(), (uint16_t)source.size()); return *this; }
/** @brief Move assignment operator */
NimBLEAttValue& operator =(NimBLEAttValue && source);
/** @brief Copy assignment operator */
NimBLEAttValue& operator =(const NimBLEAttValue & source);
/** @brief Equality operator */
bool operator ==(const NimBLEAttValue & source) {
return (m_attr_len == source.size()) ?
memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; }
/** @brief Inequality operator */
bool operator !=(const NimBLEAttValue & source){ return !(*this == source); }
#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/** @brief Operator; Get the value as an Arduino String value. */
operator String() const { return String((char*)m_attr_value); }
#endif
};
inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) {
m_attr_value = (uint8_t*)calloc(init_len + 1, 1);
assert(m_attr_value && "No Mem");
m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len);
m_attr_len = 0;
m_capacity = init_len;
setTimeStamp(0);
}
inline NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
}
inline NimBLEAttValue::~NimBLEAttValue() {
if(m_attr_value != nullptr) {
free(m_attr_value);
}
}
inline NimBLEAttValue& NimBLEAttValue::operator =(NimBLEAttValue && source) {
if (this != &source){
free(m_attr_value);
m_attr_value = source.m_attr_value;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
source.m_attr_value = nullptr;
}
return *this;
}
inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) {
if (this != &source) {
deepCopy(source);
}
return *this;
}
inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) {
uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1);
assert(res && "deepCopy: realloc failed");
ble_npl_hw_enter_critical();
m_attr_value = res;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1);
ble_npl_hw_exit_critical(0);
}
inline const uint8_t* NimBLEAttValue::getValue(time_t *timestamp) {
if(timestamp != nullptr) {
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
#else
*timestamp = 0;
#endif
}
return m_attr_value;
}
inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) {
if (len > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "value exceeds max, len=%u, max=%u",
len, m_attr_max_len);
return false;
}
uint8_t *res = m_attr_value;
if (len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (len + 1));
m_capacity = len;
}
assert(res && "setValue: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return true;
}
inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len) {
if (len < 1) {
return *this;
}
if ((m_attr_len + len) > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u",
len, m_attr_max_len);
return *this;
}
uint8_t* res = m_attr_value;
uint16_t new_len = m_attr_len + len;
if (new_len > m_capacity) {
res = (uint8_t*)realloc(m_attr_value, (new_len + 1));
m_capacity = new_len;
}
assert(res && "append: realloc failed");
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
#else
time_t t = 0;
#endif
ble_npl_hw_enter_critical();
m_attr_value = res;
memcpy(m_attr_value + m_attr_len, value, len);
m_attr_len = new_len;
m_attr_value[m_attr_len] = '\0';
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return *this;
}
#endif /*(CONFIG_BT_ENABLED) */
#endif /* MAIN_NIMBLEATTVALUE_H_ */

View file

@ -11,7 +11,7 @@
* Created on: Jan 4, 2018 * Created on: Jan 4, 2018
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include <string.h> #include <string.h>

View file

@ -9,11 +9,9 @@
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
#include "NimBLE2904.h" #include "NimBLE2904.h"
@ -32,27 +30,29 @@ static const char* LOG_TAG = "NimBLECharacteristic";
* @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.
* @param [in] properties - Properties for the characteristic. * @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to. * @param [in] pService - pointer to the service instance this characteristic belongs to.
*/ */
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService) NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties,
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { uint16_t max_len, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {
} }
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic. * @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic. * @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to. * @param [in] pService - pointer to the service instance this characteristic belongs to.
*/ */
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) { NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties,
uint16_t max_len, NimBLEService* pService)
: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
m_uuid = uuid; m_uuid = uuid;
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_properties = properties; m_properties = properties;
m_pCallbacks = &defaultCallback; m_pCallbacks = &defaultCallback;
m_pService = pService; m_pService = pService;
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_timestamp = 0;
m_removed = 0; m_removed = 0;
} // NimBLECharacteristic } // NimBLECharacteristic
@ -234,17 +234,14 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
/** /**
* @brief Retrieve the current value of the characteristic. * @brief Retrieve the current value of the characteristic.
* @return A std::string containing the current characteristic value. * @return The NimBLEAttValue containing the current characteristic value.
*/ */
std::string NimBLECharacteristic::getValue(time_t *timestamp) { NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
std::string retVal = m_value;
if(timestamp != nullptr) { if(timestamp != nullptr) {
*timestamp = m_timestamp; m_value.getValue(timestamp);
} }
portEXIT_CRITICAL(&m_valMux);
return retVal; return m_value;
} // getValue } // getValue
@ -253,11 +250,7 @@ std::string NimBLECharacteristic::getValue(time_t *timestamp) {
* @return The length of the current characteristic data. * @return The length of the current characteristic data.
*/ */
size_t NimBLECharacteristic::getDataLength() { size_t NimBLECharacteristic::getDataLength() {
portENTER_CRITICAL(&m_valMux); return m_value.size();
size_t len = m_value.length();
portEXIT_CRITICAL(&m_valMux);
return len;
} }
@ -289,27 +282,27 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc); pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
} }
portENTER_CRITICAL(&pCharacteristic->m_valMux); ble_npl_hw_enter_critical();
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(), rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size());
pCharacteristic->m_value.length()); ble_npl_hw_exit_critical(0);
portEXIT_CRITICAL(&pCharacteristic->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_CHR: { case BLE_GATT_ACCESS_OP_WRITE_CHR: {
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { uint16_t att_max_len = pCharacteristic->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
uint8_t buf[BLE_ATT_ATTR_MAX_LEN]; uint8_t buf[att_max_len];
size_t len = ctxt->om->om_len; size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len); memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next; os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next); next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){ while(next != NULL){
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) { if((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
memcpy(&buf[len], next->om_data, next->om_len); memcpy(&buf[len], next->om_data, next->om_len);
@ -389,26 +382,60 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
/** /**
* @brief Send an indication.\n * @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value.\n
* An indication will block waiting for a positive confirmation from the client.
*/ */
void NimBLECharacteristic::indicate() { void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
notify(false); notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate } // indicate
/** /**
* @brief Send a notification.\n * @brief Send an indication.
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n * @param[in] value A pointer to the data to send.
* A notification will not block; it is a fire and forget. * @param[in] length The length of the data to send.
*/
void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) {
notify(value, length, false);
} // indicate
/**
* @brief Send an indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
*/
void NimBLECharacteristic::indicate(const std::vector<uint8_t>& value) {
notify(value.data(), value.size(), false);
} // indicate
/**
* @brief Send a notification or indication.
* @param[in] is_notification if true sends a notification, false sends an indication. * @param[in] is_notification if true sends a notification, false sends an indication.
*/ */
void NimBLECharacteristic::notify(bool is_notification) { void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength()); notify(m_value.data(), m_value.length(), is_notification);
} // notify
/**
* @brief Send a notification or indication.
* @param[in] value A std::vector<uint8_t> containing the value to send as the notification value.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(const std::vector<uint8_t>& value, bool is_notification) {
notify(value.data(), value.size(), is_notification);
} // notify
/**
* @brief Send a notification or indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length);
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) && if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
!(m_properties & NIMBLE_PROPERTY::INDICATE)) !(m_properties & NIMBLE_PROPERTY::INDICATE))
{ {
@ -424,15 +451,13 @@ void NimBLECharacteristic::notify(bool is_notification) {
m_pCallbacks->onNotify(this); m_pCallbacks->onNotify(this);
std::string value = getValue();
size_t length = value.length();
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || 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_AUTHOR) ||
(m_properties & BLE_GATT_CHR_F_READ_ENC); (m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0; int rc = 0;
for (auto &it : m_subscribedVec) { for (auto &it : m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first); uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first) - 3;
// check if connected and subscribed // check if connected and subscribed
if(_mtu == 0 || it.second == 0) { if(_mtu == 0 || it.second == 0) {
@ -448,8 +473,8 @@ void NimBLECharacteristic::notify(bool is_notification) {
} }
} }
if (length > _mtu - 3) { if (length > _mtu) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu);
} }
if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) { if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
@ -467,7 +492,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
// don't create the m_buf until we are sure to send the data or else // 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 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. // 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.data(), length); os_mbuf *om = ble_hs_mbuf_from_flat(value, length);
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) { if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) { if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
@ -511,40 +536,30 @@ NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
/** /**
* @brief Set the value of the characteristic. * @brief Set the value of the characteristic from a data buffer .
* @param [in] data The data to set for the characteristic. * @param [in] data The data buffer to set for the characteristic.
* @param [in] length The length of the data in bytes. * @param [in] length The number of bytes in the data buffer.
*/ */
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) { void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) #if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
char* pHex = NimBLEUtils::buildHexData(nullptr, data, length); char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s",
length, pHex, getUUID().toString().c_str());
free(pHex); free(pHex);
#endif #endif
if (length > BLE_ATT_ATTR_MAX_LEN) { m_value.setValue(data, length);
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return;
}
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue"); NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue } // setValue
/** /**
* @brief Set the value of the characteristic from string data.\n * @brief Set the value of the characteristic from a `std::vector<uint8_t>`.\n
* We set the value of the characteristic from the bytes contained in the string. * @param [in] vec The std::vector<uint8_t> reference to set the characteristic value from.
* @param [in] value the std::string value of the characteristic.
*/ */
void NimBLECharacteristic::setValue(const std::string &value) { void NimBLECharacteristic::setValue(const std::vector<uint8_t>& vec) {
setValue((uint8_t*)(value.data()), value.length()); return setValue((uint8_t*)&vec[0], vec.size());
} // setValue }// setValue
/** /**
@ -641,6 +656,4 @@ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacter
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default"); NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
} }
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -13,13 +13,15 @@
#ifndef MAIN_NIMBLECHARACTERISTIC_H_ #ifndef MAIN_NIMBLECHARACTERISTIC_H_
#define MAIN_NIMBLECHARACTERISTIC_H_ #define MAIN_NIMBLECHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_hs.h" #include "host/ble_hs.h"
#else
#include "nimble/nimble/host/include/host/ble_hs.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max
@ -42,6 +44,7 @@ typedef enum {
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
#include "NimBLEAttValue.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -63,11 +66,13 @@ public:
uint16_t properties = uint16_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid, NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties = uint16_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
~NimBLECharacteristic(); ~NimBLECharacteristic();
@ -75,64 +80,93 @@ public:
uint16_t getHandle(); uint16_t getHandle();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string toString(); std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLECharacteristicCallbacks*
getCallbacks();
void indicate(); void indicate();
void indicate(const uint8_t* value, size_t length);
void indicate(const std::vector<uint8_t>& value);
void notify(bool is_notification = true); void notify(bool is_notification = true);
void notify(const uint8_t* value, size_t length, bool is_notification = true);
void notify(const std::vector<uint8_t>& value, bool is_notification = true);
size_t getSubscribedCount(); size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
void addDescriptor(NimBLEDescriptor *pDescriptor); void addDescriptor(NimBLEDescriptor *pDescriptor);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid); NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle); NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false); void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false);
NimBLEService* getService();
std::string getValue(time_t *timestamp = nullptr); uint16_t getProperties();
NimBLEAttValue getValue(time_t *timestamp = nullptr);
size_t getDataLength(); size_t getDataLength();
/**
* @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
void setValue(const uint8_t* data, size_t size); void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value); void setValue(const std::vector<uint8_t>& vec);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);;
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristicCallbacks* getCallbacks();
/*********************** Template Functions ************************/
/** /**
* @brief Convenience template to set the characteristic value to <type\>val. * @brief Template to set the characteristic value to <type\>val.
* @param [in] s The value to set. * @param [in] s The value to set.
*/ */
template<typename T> template<typename T>
void setValue(const T &s) { void setValue(const T &s) { m_value.setValue<T>(s); }
setValue((uint8_t*)&s, sizeof(T));
/**
* @brief Template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
} }
NimBLEService* getService(); /**
uint16_t getProperties(); * @brief Template to send a notification from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
* @param[in] value The <type\>value to set.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
void
#else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
notify(const T& value, bool is_notification = true) {
notify((uint8_t*)value.c_str(), value.length(), is_notification);
}
/**
* @brief Template to send an indication from a class type that has a c_str() and length() method.
* @tparam T The a reference to a class containing the data to send.
* @param[in] value The <type\>value to set.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
void
#else
typename std::enable_if<Has_c_str_len<T>::value, void>::type
#endif
indicate(const T& value) {
indicate((uint8_t*)value.c_str(), value.length());
}
private: private:
@ -149,10 +183,8 @@ private:
uint16_t m_properties; uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks; NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService; NimBLEService* m_pService;
std::string m_value; NimBLEAttValue m_value;
std::vector<NimBLEDescriptor*> m_dscVec; std::vector<NimBLEDescriptor*> m_dscVec;
portMUX_TYPE m_valMux;
time_t m_timestamp;
uint8_t m_removed; uint8_t m_removed;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec; std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
@ -185,7 +217,7 @@ public:
ERROR_INDICATE_FAILURE ERROR_INDICATE_FAILURE
}Status; }Status;
virtual ~NimBLECharacteristicCallbacks(); virtual ~NimBLECharacteristicCallbacks();
virtual void onRead(NimBLECharacteristic* pCharacteristic); virtual void onRead(NimBLECharacteristic* pCharacteristic);
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc); virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onWrite(NimBLECharacteristic* pCharacteristic); virtual void onWrite(NimBLECharacteristic* pCharacteristic);
@ -195,6 +227,5 @@ public:
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue); virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */
#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ #endif /*MAIN_NIMBLECHARACTERISTIC_H_*/

View file

@ -11,11 +11,8 @@
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h" #include "NimBLEClient.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
@ -23,9 +20,13 @@
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include <climits>
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "nimble/nimble_port.h" #include "nimble/nimble_port.h"
#else
#include "nimble/porting/nimble/include/nimble/nimble_port.h"
#endif
static const char* LOG_TAG = "NimBLEClient"; static const char* LOG_TAG = "NimBLEClient";
static NimBLEClientCallbacks defaultCallbacks; static NimBLEClientCallbacks defaultCallbacks;
@ -63,6 +64,12 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_deleteCallbacks = false; m_deleteCallbacks = false;
m_pTaskData = nullptr; m_pTaskData = nullptr;
m_connEstablished = false; m_connEstablished = false;
m_lastErr = 0;
#if CONFIG_BT_NIMBLE_EXT_ADV
m_phyMask = BLE_GAP_LE_PHY_1M_MASK |
BLE_GAP_LE_PHY_2M_MASK |
BLE_GAP_LE_PHY_CODED_MASK;
#endif
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@ -73,6 +80,7 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
memset(&m_dcTimer, 0, sizeof(m_dcTimer));
ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(), ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
NimBLEClient::dcTimerCb, this); NimBLEClient::dcTimerCb, this);
} // NimBLEClient } // NimBLEClient
@ -91,6 +99,8 @@ NimBLEClient::~NimBLEClient() {
delete m_pClientCallbacks; delete m_pClientCallbacks;
} }
ble_npl_callout_deinit(&m_dcTimer);
} // ~NimBLEClient } // ~NimBLEClient
@ -205,7 +215,8 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
m_peerAddress = address; m_peerAddress = address;
} }
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
m_pTaskData = &taskData; m_pTaskData = &taskData;
int rc = 0; int rc = 0;
@ -214,9 +225,22 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/ */
do { do {
#if CONFIG_BT_NIMBLE_EXT_ADV
rc = ble_gap_ext_connect(NimBLEDevice::m_own_addr_type,
&peerAddr_t,
m_connectTimeout,
m_phyMask,
&m_pConnParams,
&m_pConnParams,
&m_pConnParams,
NimBLEClient::handleGapEvent,
this);
#else
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t, rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
m_connectTimeout, &m_pConnParams, m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this); NimBLEClient::handleGapEvent, this);
#endif
switch (rc) { switch (rc) {
case 0: case 0:
break; break;
@ -251,11 +275,17 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
} while (rc == BLE_HS_EBUSY); } while (rc == BLE_HS_EBUSY);
m_lastErr = rc;
if(rc != 0) { if(rc != 0) {
m_pTaskData = nullptr; m_pTaskData = nullptr;
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
// Wait for the connect timeout time +1 second for the connection to complete // Wait for the connect timeout time +1 second for the connection to complete
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) { if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
m_pTaskData = nullptr; m_pTaskData = nullptr;
@ -273,6 +303,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
return false; return false;
} else if(taskData.rc != 0){ } else if(taskData.rc != 0){
m_lastErr = taskData.rc;
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
taskData.rc, taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc)); NimBLEUtils::returnCodeToString(taskData.rc));
@ -305,7 +336,8 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
* @return True on success. * @return True on success.
*/ */
bool NimBLEClient::secureConnection() { bool NimBLEClient::secureConnection() {
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
int retryCount = 1; int retryCount = 1;
@ -314,14 +346,20 @@ bool NimBLEClient::secureConnection() {
int rc = NimBLEDevice::startSecurity(m_conn_id); int rc = NimBLEDevice::startSecurity(m_conn_id);
if(rc != 0){ if(rc != 0){
m_lastErr = rc;
m_pTaskData = nullptr; m_pTaskData = nullptr;
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--); } while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--);
if(taskData.rc != 0){ if(taskData.rc != 0){
m_lastErr = taskData.rc;
return false; return false;
} }
@ -372,10 +410,26 @@ int NimBLEClient::disconnect(uint8_t reason) {
} }
NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
m_lastErr = rc;
return rc; return rc;
} // disconnect } // disconnect
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Set the PHY types to use when connecting to a server.
* @param [in] mask A bitmask indicating what PHYS to connect with.\n
* The available bits are:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
*/
void NimBLEClient::setConnectPhy(uint8_t mask) {
m_phyMask = mask;
}
#endif
/** /**
* @brief Set the connection paramaters to use when connecting to a server. * @brief Set the connection paramaters to use when connecting to a server.
* @param [in] minInterval The minimum connection interval in 1.25ms units. * @param [in] minInterval The minimum connection interval in 1.25ms units.
@ -436,6 +490,29 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
} // updateConnParams } // updateConnParams
/**
* @brief Request an update of the data packet length.
* * Can only be used after a connection has been established.
* @details Sends a data length update request to the server the client is connected to.
* The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes.
* The server needs to support the Bluetooth 4.2 specifications, to be capable of DLE.
* @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB).
*/
void NimBLEClient::setDataLen(uint16_t tx_octets) {
#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \
(ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432
return;
#else
uint16_t tx_time = (tx_octets + 14) * 8;
int rc = ble_gap_set_data_len(m_conn_id, tx_octets, tx_time);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
#endif
} // setDataLen
/** /**
* @brief Get detailed information about the current peer connection. * @brief Get detailed information about the current peer connection.
*/ */
@ -512,6 +589,7 @@ int NimBLEClient::getRssi() {
if(rc != 0) { if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s",
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
m_lastErr = rc;
return 0; return 0;
} }
@ -568,14 +646,31 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
return m_servicesVector.back(); return m_servicesVector.back();
} }
// If the request was successful but 16/32 bit service not found // If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid. // try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 || if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32) uuid.bitSize() == BLE_UUID_TYPE_32)
{ {
NimBLEUUID uuid128(uuid); NimBLEUUID uuid128(uuid);
uuid128.to128(); uuid128.to128();
return getService(uuid128); if(retrieveServices(&uuid128)) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if(retrieveServices(&uuid16)) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
}
}
} }
} }
@ -639,7 +734,8 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
} }
int rc = 0; int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
if(uuid_filter == nullptr) { if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData); rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData);
@ -650,11 +746,18 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_lastErr = rc;
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
// wait until we have all the services // wait until we have all the services
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
m_lastErr = taskData.rc;
if(taskData.rc == 0){ if(taskData.rc == 0){
NIMBLE_LOGD(LOG_TAG, "<< retrieveServices"); NIMBLE_LOGD(LOG_TAG, "<< retrieveServices");
@ -698,7 +801,7 @@ int NimBLEClient::serviceDiscoveredCB(
if(error->status == BLE_HS_EDONE) { if(error->status == BLE_HS_EDONE) {
pTaskData->rc = 0; pTaskData->rc = 0;
} else { } else {
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", NIMBLE_LOGE(LOG_TAG, "serviceDiscoveredCB() rc=%d %s",
error->status, error->status,
NimBLEUtils::returnCodeToString(error->status)); NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status; pTaskData->rc = error->status;
@ -706,7 +809,7 @@ int NimBLEClient::serviceDiscoveredCB(
xTaskNotifyGive(pTaskData->task); xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG,"<< << Service Discovered"); NIMBLE_LOGD(LOG_TAG,"<< Service Discovered");
return error->status; return error->status;
} }
@ -717,11 +820,11 @@ int NimBLEClient::serviceDiscoveredCB(
* @param [in] characteristicUUID The characteristic whose value we wish to read. * @param [in] characteristicUUID The characteristic whose value we wish to read.
* @returns characteristic value or an empty string if not found * @returns characteristic value or an empty string if not found
*/ */
std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) { NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) {
NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
std::string ret = ""; NimBLEAttValue ret;
NimBLERemoteService* pService = getService(serviceUUID); NimBLERemoteService* pService = getService(serviceUUID);
if(pService != nullptr) { if(pService != nullptr) {
@ -731,7 +834,7 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
} }
} }
NIMBLE_LOGD(LOG_TAG, "<<getValue"); NIMBLE_LOGD(LOG_TAG, "<< getValue");
return ret; return ret;
} // getValue } // getValue
@ -745,7 +848,7 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @returns true if successful otherwise false * @returns true if successful otherwise false
*/ */
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value, bool response) const NimBLEAttValue &value, bool response)
{ {
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@ -806,7 +909,7 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] arg A pointer to the client instance that registered for this callback. * @param [in] arg A pointer to the client instance that registered for this callback.
*/ */
/*STATIC*/ /*STATIC*/
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg; NimBLEClient* client = (NimBLEClient*)arg;
int rc; int rc;
@ -924,19 +1027,14 @@ uint16_t NimBLEClient::getMTU() {
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
(*characteristic)->toString().c_str()); (*characteristic)->toString().c_str());
time_t t = time(nullptr); uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om);
portENTER_CRITICAL(&(*characteristic)->m_valMux); (*characteristic)->m_value.setValue(event->notify_rx.om->om_data, data_len);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = t;
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) { if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
(*characteristic)->toString().c_str()); (*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data, (*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len, data_len, !event->notify_rx.indication);
!event->notify_rx.indication);
} }
break; break;
} }
@ -1034,7 +1132,7 @@ uint16_t NimBLEClient::getMTU() {
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp); NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future // Compatibility only - Do not use, should be removed the in future
if(NimBLEDevice::m_securityCallbacks != nullptr) { if(NimBLEDevice::m_securityCallbacks != nullptr) {
@ -1136,6 +1234,15 @@ std::string NimBLEClient::toString() {
} // toString } // toString
/**
* @brief Get the last error code reported by the NimBLE host
* @return int, the NimBLE error code.
*/
int NimBLEClient::getLastError() {
return m_lastErr;
} // getLastError
void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) { void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) {
NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default");
} }
@ -1170,5 +1277,4 @@ bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){
return true; return true;
} }
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED

View file

@ -14,16 +14,14 @@
#ifndef MAIN_NIMBLECLIENT_H_ #ifndef MAIN_NIMBLECLIENT_H_
#define MAIN_NIMBLECLIENT_H_ #define MAIN_NIMBLECLIENT_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLEConnInfo.h" #include "NimBLEConnInfo.h"
#include "NimBLEAttValue.h"
#include "NimBLEAdvertisedDevice.h" #include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h" #include "NimBLERemoteService.h"
@ -54,9 +52,9 @@ public:
NimBLERemoteService* getService(const NimBLEUUID &uuid); NimBLERemoteService* getService(const NimBLEUUID &uuid);
void deleteServices(); void deleteServices();
size_t deleteService(const NimBLEUUID &uuid); size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); NimBLEAttValue getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value, bool response = false); const NimBLEAttValue &value, bool response = false);
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle); NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected(); bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks, void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
@ -71,8 +69,13 @@ public:
uint16_t scanInterval=16, uint16_t scanWindow=16); uint16_t scanInterval=16, uint16_t scanWindow=16);
void updateConnParams(uint16_t minInterval, uint16_t maxInterval, void updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout); uint16_t latency, uint16_t timeout);
void setDataLen(uint16_t tx_octets);
void discoverAttributes(); void discoverAttributes();
NimBLEConnInfo getConnInfo(); NimBLEConnInfo getConnInfo();
int getLastError();
#if CONFIG_BT_NIMBLE_EXT_ADV
void setConnectPhy(uint8_t mask);
#endif
private: private:
NimBLEClient(const NimBLEAddress &peerAddress); NimBLEClient(const NimBLEAddress &peerAddress);
@ -90,6 +93,7 @@ private:
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr); bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress; NimBLEAddress m_peerAddress;
int m_lastErr;
uint16_t m_conn_id; uint16_t m_conn_id;
bool m_connEstablished; bool m_connEstablished;
bool m_deleteCallbacks; bool m_deleteCallbacks;
@ -97,6 +101,9 @@ private:
NimBLEClientCallbacks* m_pClientCallbacks; NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t* m_pTaskData; ble_task_data_t* m_pTaskData;
ble_npl_callout m_dcTimer; ble_npl_callout m_dcTimer;
#if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t m_phyMask;
#endif
std::vector<NimBLERemoteService*> m_servicesVector; std::vector<NimBLERemoteService*> m_servicesVector;
@ -158,6 +165,5 @@ public:
virtual bool onConfirmPIN(uint32_t pin); virtual bool onConfirmPIN(uint32_t pin);
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_NIMBLECLIENT_H_ */ #endif /* MAIN_NIMBLECLIENT_H_ */

View file

@ -11,11 +11,9 @@
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
@ -30,28 +28,32 @@ static NimBLEDescriptorCallbacks defaultCallbacks;
/** /**
* @brief NimBLEDescriptor constructor. * @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/ */
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic) NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) { : NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {
} }
/** /**
* @brief NimBLEDescriptor constructor. * @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/ */
NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len, NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic) NimBLECharacteristic* pCharacteristic)
{ : m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) {
m_uuid = uuid; m_uuid = uuid;
m_value.attr_len = 0; // Initial length is 0.
m_value.attr_max_len = max_len; // Maximum length of the data.
m_handle = NULL_HANDLE; // Handle is initially unknown. m_handle = NULL_HANDLE; // Handle is initially unknown.
m_pCharacteristic = pCharacteristic; m_pCharacteristic = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback. m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_properties = 0; m_properties = 0;
m_removed = 0; m_removed = 0;
@ -87,7 +89,6 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
* @brief NimBLEDescriptor destructor. * @brief NimBLEDescriptor destructor.
*/ */
NimBLEDescriptor::~NimBLEDescriptor() { NimBLEDescriptor::~NimBLEDescriptor() {
free(m_value.attr_value); // Release the storage we created in the constructor.
} // ~NimBLEDescriptor } // ~NimBLEDescriptor
/** /**
@ -104,7 +105,7 @@ uint16_t NimBLEDescriptor::getHandle() {
* @return The length (in bytes) of the value of this descriptor. * @return The length (in bytes) of the value of this descriptor.
*/ */
size_t NimBLEDescriptor::getLength() { size_t NimBLEDescriptor::getLength() {
return m_value.attr_len; return m_value.size();
} // getLength } // getLength
@ -118,10 +119,14 @@ NimBLEUUID NimBLEDescriptor::getUUID() {
/** /**
* @brief Get the value of this descriptor. * @brief Get the value of this descriptor.
* @return A pointer to the value of this descriptor. * @return The NimBLEAttValue of this descriptor.
*/ */
uint8_t* NimBLEDescriptor::getValue() { NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) {
return m_value.attr_value; if (timestamp != nullptr) {
m_value.getValue(timestamp);
}
return m_value;
} // getValue } // getValue
@ -130,7 +135,7 @@ uint8_t* NimBLEDescriptor::getValue() {
* @return A std::string instance containing a copy of the descriptor's value. * @return A std::string instance containing a copy of the descriptor's value.
*/ */
std::string NimBLEDescriptor::getStringValue() { std::string NimBLEDescriptor::getStringValue() {
return std::string((char *) m_value.attr_value, m_value.attr_len); return std::string(m_value);
} }
@ -145,6 +150,9 @@ NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() {
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) { struct ble_gatt_access_ctxt *ctxt, void *arg) {
(void)conn_handle;
(void)attr_handle;
const ble_uuid_t *uuid; const ble_uuid_t *uuid;
int rc; int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
@ -161,24 +169,27 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
if(ctxt->om->om_pkthdr_len > 8) { if(ctxt->om->om_pkthdr_len > 8) {
pDescriptor->m_pCallbacks->onRead(pDescriptor); pDescriptor->m_pCallbacks->onRead(pDescriptor);
} }
portENTER_CRITICAL(&pDescriptor->m_valMux);
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength()); ble_npl_hw_enter_critical();
portEXIT_CRITICAL(&pDescriptor->m_valMux); rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size());
ble_npl_hw_exit_critical(0);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_DSC: { case BLE_GATT_ACCESS_OP_WRITE_DSC: {
if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) { uint16_t att_max_len = pDescriptor->m_value.max_size();
if (ctxt->om->om_len > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
uint8_t buf[pDescriptor->m_value.attr_max_len]; uint8_t buf[att_max_len];
size_t len = ctxt->om->om_len; size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len); memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next; os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next); next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){ while(next != NULL){
if((len + next->om_len) > pDescriptor->m_value.attr_max_len) { if((len + next->om_len) > att_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
memcpy(&buf[len], next->om_data, next->om_len); memcpy(&buf[len], next->om_data, next->om_len);
@ -230,25 +241,19 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
* @param [in] length The length of the data in bytes. * @param [in] length The length of the data in bytes.
*/ */
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) { void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
if (length > m_value.attr_max_len) { m_value.setValue(data, length);
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return;
}
portENTER_CRITICAL(&m_valMux);
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
portEXIT_CRITICAL(&m_valMux);
} // setValue } // setValue
/** /**
* @brief Set the value of the descriptor. * @brief Set the value of the descriptor from a `std::vector<uint8_t>`.\n
* @param [in] value The value of the descriptor in string form. * @param [in] vec The std::vector<uint8_t> reference to set the descriptor value from.
*/ */
void NimBLEDescriptor::setValue(const std::string &value) { void NimBLEDescriptor::setValue(const std::vector<uint8_t>& vec) {
setValue((uint8_t*) value.data(), value.length()); return setValue((uint8_t*)&vec[0], vec.size());
} // setValue } // setValue
/** /**
* @brief Set the characteristic this descriptor belongs to. * @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptior belongs to. * @param [in] pChar A pointer to the characteristic this descriptior belongs to.
@ -277,6 +282,7 @@ NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {}
* @param [in] pDescriptor The descriptor that is the source of the event. * @param [in] pDescriptor The descriptor that is the source of the event.
*/ */
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) { void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) {
(void)pDescriptor;
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
} // onRead } // onRead
@ -286,8 +292,8 @@ void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) {
* @param [in] pDescriptor The descriptor that is the source of the event. * @param [in] pDescriptor The descriptor that is the source of the event.
*/ */
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) { void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) {
(void)pDescriptor;
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite } // onWrite
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */

View file

@ -14,25 +14,16 @@
#ifndef MAIN_NIMBLEDESCRIPTOR_H_ #ifndef MAIN_NIMBLEDESCRIPTOR_H_
#define MAIN_NIMBLEDESCRIPTOR_H_ #define MAIN_NIMBLEDESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "NimBLEAttValue.h"
#include <string> #include <string>
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 */
} attr_value_t;
class NimBLEService; class NimBLEService;
class NimBLECharacteristic; class NimBLECharacteristic;
class NimBLEDescriptorCallbacks; class NimBLEDescriptorCallbacks;
@ -56,24 +47,36 @@ public:
uint16_t getHandle(); uint16_t getHandle();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string toString(); std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
NimBLECharacteristic* getCharacteristic();
size_t getLength(); size_t getLength();
uint8_t* getValue(); NimBLEAttValue getValue(time_t *timestamp = nullptr);
std::string getStringValue(); std::string getStringValue();
void setValue(const uint8_t* data, size_t size); void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value); void setValue(const std::vector<uint8_t>& vec);
NimBLECharacteristic* getCharacteristic();
/*********************** Template Functions ************************/
/** /**
* @brief Convenience template to set the descriptor value to <type\>val. * @brief Template to set the characteristic value to <type\>val.
* @param [in] s The value to set. * @param [in] s The value to set.
*/ */
template<typename T> template<typename T>
void setValue(const T &s) { void setValue(const T &s) { m_value.setValue<T>(s); }
setValue((uint8_t*)&s, sizeof(T));
/**
* @brief Template to convert the descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
return m_value.getValue<T>(timestamp, skipSizeCheck);
} }
private: private:
@ -91,8 +94,7 @@ private:
NimBLEDescriptorCallbacks* m_pCallbacks; NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic; NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties; uint8_t m_properties;
attr_value_t m_value; NimBLEAttValue m_value;
portMUX_TYPE m_valMux;
uint8_t m_removed; uint8_t m_removed;
}; // NimBLEDescriptor }; // NimBLEDescriptor
@ -113,6 +115,5 @@ public:
#include "NimBLE2904.h" #include "NimBLE2904.h"
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */ #endif /* MAIN_NIMBLEDESCRIPTOR_H_ */

View file

@ -11,27 +11,45 @@
* Created on: Mar 16, 2017 * Created on: Mar 16, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "esp_err.h" #ifdef ESP_PLATFORM
#include "esp_bt.h" # include "esp_err.h"
#include "nvs_flash.h" # include "esp_bt.h"
#include "esp_nimble_hci.h" # include "nvs_flash.h"
#include "nimble/nimble_port.h" # if defined(CONFIG_NIMBLE_CPP_IDF)
#include "nimble/nimble_port_freertos.h" # include "esp_nimble_hci.h"
#include "host/ble_hs.h" # include "nimble/nimble_port.h"
#include "host/ble_hs_pvcy.h" # include "nimble/nimble_port_freertos.h"
#include "host/util/util.h" # include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h" # include "host/ble_hs_pvcy.h"
#include "services/gatt/ble_svc_gatt.h" # include "host/util/util.h"
# include "services/gap/ble_svc_gap.h"
# include "services/gatt/ble_svc_gatt.h"
# else
# include "nimble/esp_port/esp-hci/include/esp_nimble_hci.h"
# endif
#else
# include "nimble/nimble/controller/include/controller/ble_phy.h"
#endif
#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS #ifndef CONFIG_NIMBLE_CPP_IDF
#include "esp32-hal-bt.h" # include "nimble/porting/nimble/include/nimble/nimble_port.h"
# include "nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h"
# include "nimble/nimble/host/include/host/ble_hs.h"
# include "nimble/nimble/host/include/host/ble_hs_pvcy.h"
# include "nimble/nimble/host/util/include/host/util/util.h"
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
#endif
#if defined(ESP_PLATFORM) && defined(CONFIG_ENABLE_ARDUINO_DEPENDS)
# include "esp32-hal-bt.h"
#endif #endif
#include "NimBLELog.h" #include "NimBLELog.h"
@ -51,7 +69,11 @@ NimBLEServer* NimBLEDevice::m_pServer = nullptr;
uint32_t NimBLEDevice::m_passkey = 123456; uint32_t NimBLEDevice::m_passkey = 123456;
bool NimBLEDevice::m_synced = false; bool NimBLEDevice::m_synced = false;
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# else
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
# endif
#endif #endif
NimBLEMeshNode* NimBLEDevice::m_pMeshNode = nullptr; NimBLEMeshNode* NimBLEDevice::m_pMeshNode = nullptr;
@ -65,9 +87,10 @@ std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList; std::vector<NimBLEAddress> NimBLEDevice::m_whiteList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC; uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
#ifdef ESP_PLATFORM
uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE; uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE;
uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE; uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
#endif
/** /**
* @brief Create a new mesh node. * @brief Create a new mesh node.
@ -121,6 +144,45 @@ NimBLEMeshNode* NimBLEDevice::getMeshNode() {
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Get the instance of the advertising object.
* @return A pointer to the advertising object.
*/
NimBLEExtAdvertising* NimBLEDevice::getAdvertising() {
if(m_bleAdvertising == nullptr) {
m_bleAdvertising = new NimBLEExtAdvertising();
}
return m_bleAdvertising;
}
/**
* @brief Convenience function to begin advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
*/
bool NimBLEDevice::startAdvertising(uint8_t inst_id,
int duration,
int max_events) {
return getAdvertising()->start(inst_id, duration, max_events);
} // startAdvertising
/**
* @brief Convenience function to stop advertising a data set.
* @param [in] inst_id The extended advertisement instance ID to stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEDevice::stopAdvertising(uint8_t inst_id) {
return getAdvertising()->stop(inst_id);
} // stopAdvertising
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/** /**
* @brief Get the instance of the advertising object. * @brief Get the instance of the advertising object.
* @return A pointer to the advertising object. * @return A pointer to the advertising object.
@ -135,17 +197,19 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() {
/** /**
* @brief Convenience function to begin advertising. * @brief Convenience function to begin advertising.
* @return True if advertising started successfully.
*/ */
void NimBLEDevice::startAdvertising() { bool NimBLEDevice::startAdvertising() {
getAdvertising()->start(); return getAdvertising()->start();
} // startAdvertising } // startAdvertising
# endif
/** /**
* @brief Convenience function to stop advertising. * @brief Convenience function to stop all advertising.
* @return True if advertising stopped successfully.
*/ */
void NimBLEDevice::stopAdvertising() { bool NimBLEDevice::stopAdvertising() {
getAdvertising()->stop(); return getAdvertising()->stop();
} // stopAdvertising } // stopAdvertising
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
@ -156,7 +220,8 @@ void NimBLEDevice::stopAdvertising() {
* try and release/delete it. * try and release/delete it.
*/ */
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
/* STATIC */ NimBLEScan* NimBLEDevice::getScan() { /* STATIC */
NimBLEScan* NimBLEDevice::getScan() {
if (m_pScan == nullptr) { if (m_pScan == nullptr) {
m_pScan = new NimBLEScan(); m_pScan = new NimBLEScan();
} }
@ -173,7 +238,8 @@ void NimBLEDevice::stopAdvertising() {
* @return A reference to the new client object. * @return A reference to the new client object.
*/ */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) { /* STATIC */
NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) { if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d", NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
m_cList.size(), NIMBLE_MAX_CONNECTIONS); m_cList.size(), NIMBLE_MAX_CONNECTIONS);
@ -191,7 +257,8 @@ void NimBLEDevice::stopAdvertising() {
* Checks if it is connected or trying to connect and disconnects/stops it first. * Checks if it is connected or trying to connect and disconnects/stops it first.
* @param [in] pClient A pointer to the client object. * @param [in] pClient A pointer to the client object.
*/ */
/* STATIC */ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) { /* STATIC */
bool NimBLEDevice::deleteClient(NimBLEClient* pClient) {
if(pClient == nullptr) { if(pClient == nullptr) {
return false; return false;
} }
@ -235,7 +302,8 @@ void NimBLEDevice::stopAdvertising() {
* @brief Get the list of created client objects. * @brief Get the list of created client objects.
* @return A pointer to the list of clients. * @return A pointer to the list of clients.
*/ */
/* STATIC */std::list<NimBLEClient*>* NimBLEDevice::getClientList() { /* STATIC */
std::list<NimBLEClient*>* NimBLEDevice::getClientList() {
return &m_cList; return &m_cList;
} // getClientList } // getClientList
@ -244,7 +312,8 @@ void NimBLEDevice::stopAdvertising() {
* @brief Get the number of created client objects. * @brief Get the number of created client objects.
* @return Number of client objects created. * @return Number of client objects created.
*/ */
/* STATIC */size_t NimBLEDevice::getClientListSize() { /* STATIC */
size_t NimBLEDevice::getClientListSize() {
return m_cList.size(); return m_cList.size();
} // getClientList } // getClientList
@ -254,7 +323,8 @@ void NimBLEDevice::stopAdvertising() {
* @param [in] conn_id The client connection ID to search for. * @param [in] conn_id The client connection ID to search for.
* @return A pointer to the client object with the spcified connection ID. * @return A pointer to the client object with the spcified connection ID.
*/ */
/* STATIC */NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { /* STATIC */
NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if((*it)->getConnId() == conn_id) { if((*it)->getConnId() == conn_id) {
return (*it); return (*it);
@ -270,7 +340,8 @@ void NimBLEDevice::stopAdvertising() {
* @param [in] peer_addr The address of the peer to search for. * @param [in] peer_addr The address of the peer to search for.
* @return A pointer to the client object with the peer address. * @return A pointer to the client object with the peer address.
*/ */
/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) { /* STATIC */
NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if((*it)->getPeerAddress().equals(peer_addr)) { if((*it)->getPeerAddress().equals(peer_addr)) {
return (*it); return (*it);
@ -284,7 +355,8 @@ void NimBLEDevice::stopAdvertising() {
* @brief Finds the first disconnected client in the list. * @brief Finds the first disconnected client in the list.
* @return A pointer to the first client object that is not connected to a peer. * @return A pointer to the first client object that is not connected to a peer.
*/ */
/* STATIC */NimBLEClient* NimBLEDevice::getDisconnectedClient() { /* STATIC */
NimBLEClient* NimBLEDevice::getDisconnectedClient() {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
if(!(*it)->isConnected()) { if(!(*it)->isConnected()) {
return (*it); return (*it);
@ -295,7 +367,7 @@ void NimBLEDevice::stopAdvertising() {
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#ifdef ESP_PLATFORM
/** /**
* @brief Set the transmission power. * @brief Set the transmission power.
* @param [in] powerLevel The power level to set, can be one of: * @param [in] powerLevel The power level to set, can be one of:
@ -321,12 +393,15 @@ void NimBLEDevice::stopAdvertising() {
* * ESP_BLE_PWR_TYPE_SCAN = 10, For scan * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan
* * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
*/ */
/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { /* STATIC */
void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) {
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType); NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType);
esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel);
if (errRc != ESP_OK) { if (errRc != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc);
} }
NIMBLE_LOGD(LOG_TAG, "<< setPower"); NIMBLE_LOGD(LOG_TAG, "<< setPower");
} // setPower } // setPower
@ -348,9 +423,8 @@ void NimBLEDevice::stopAdvertising() {
* * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
* @return the power level currently used by the type specified. * @return the power level currently used by the type specified.
*/ */
/* STATIC */
/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) { int NimBLEDevice::getPower(esp_ble_power_type_t powerType) {
switch(esp_ble_tx_power_get(powerType)) { switch(esp_ble_tx_power_get(powerType)) {
case ESP_PWR_LVL_N12: case ESP_PWR_LVL_N12:
return -12; return -12;
@ -373,13 +447,25 @@ void NimBLEDevice::stopAdvertising() {
} }
} // getPower } // getPower
#else
void NimBLEDevice::setPower(int dbm) {
ble_phy_txpwr_set(dbm);
}
int NimBLEDevice::getPower() {
return ble_phy_txpwr_get();
}
#endif
/** /**
* @brief Get our device address. * @brief Get our device address.
* @return A NimBLEAddress object of our public address if we have one, * @return A NimBLEAddress object of our public address if we have one,
* if not then our current random address. * if not then our current random address.
*/ */
/* STATIC*/ NimBLEAddress NimBLEDevice::getAddress() { /* STATIC*/
NimBLEAddress NimBLEDevice::getAddress() {
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0}; ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) { if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
@ -396,7 +482,8 @@ void NimBLEDevice::stopAdvertising() {
* @brief Return a string representation of the address of this device. * @brief Return a string representation of the address of this device.
* @return A string representation of this device address. * @return A string representation of this device address.
*/ */
/* STATIC */ std::string NimBLEDevice::toString() { /* STATIC */
std::string NimBLEDevice::toString() {
return getAddress().toString(); return getAddress().toString();
} // toString } // toString
@ -406,7 +493,8 @@ void NimBLEDevice::stopAdvertising() {
* @param [in] mtu Value to set local mtu: * @param [in] mtu Value to set local mtu:
* * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527. * * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527.
*/ */
/* STATIC */int NimBLEDevice::setMTU(uint16_t mtu) { /* STATIC */
int NimBLEDevice::setMTU(uint16_t mtu) {
NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
int rc = ble_att_set_preferred_mtu(mtu); int rc = ble_att_set_preferred_mtu(mtu);
@ -424,11 +512,13 @@ void NimBLEDevice::stopAdvertising() {
* @brief Get local MTU value set. * @brief Get local MTU value set.
* @return The current preferred MTU setting. * @return The current preferred MTU setting.
*/ */
/* STATIC */uint16_t NimBLEDevice::getMTU() { /* STATIC */
uint16_t NimBLEDevice::getMTU() {
return ble_att_preferred_mtu(); return ble_att_preferred_mtu();
} }
#ifdef ESP_PLATFORM
/** /**
* @brief Set the duplicate filter cache size for filtering scanned devices. * @brief Set the duplicate filter cache size for filtering scanned devices.
* @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n * @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n
@ -474,6 +564,7 @@ void NimBLEDevice::setScanFilterMode(uint8_t mode) {
m_scanFilterMode = mode; m_scanFilterMode = mode;
} }
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
/** /**
@ -496,7 +587,7 @@ int NimBLEDevice::getNumBonds() {
/** /**
* @brief Deletes all bonding information. * @brief Deletes all bonding information.
*/ */
/*STATIC*/ /*STATIC*/
void NimBLEDevice::deleteAllBonds() { void NimBLEDevice::deleteAllBonds() {
ble_store_clear(); ble_store_clear();
} }
@ -576,6 +667,7 @@ NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
* @param [in] address The address to check for in the whitelist. * @param [in] address The address to check for in the whitelist.
* @returns true if the address is in the whitelist. * @returns true if the address is in the whitelist.
*/ */
/*STATIC*/
bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) { bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) {
for (auto &it : m_whiteList) { for (auto &it : m_whiteList) {
if (it == address) { if (it == address) {
@ -592,6 +684,7 @@ bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) {
* @param [in] address The address to add to the whitelist. * @param [in] address The address to add to the whitelist.
* @returns true if successful. * @returns true if successful.
*/ */
/*STATIC*/
bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) { bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
if (NimBLEDevice::onWhiteList(address)) { if (NimBLEDevice::onWhiteList(address)) {
return true; return true;
@ -623,6 +716,7 @@ bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
* @param [in] address The address to remove from the whitelist. * @param [in] address The address to remove from the whitelist.
* @returns true if successful. * @returns true if successful.
*/ */
/*STATIC*/
bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) { bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) {
if (!NimBLEDevice::onWhiteList(address)) { if (!NimBLEDevice::onWhiteList(address)) {
return true; return true;
@ -662,6 +756,7 @@ bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) {
* @brief Gets the count of addresses in the whitelist. * @brief Gets the count of addresses in the whitelist.
* @returns The number of addresses in the whitelist. * @returns The number of addresses in the whitelist.
*/ */
/*STATIC*/
size_t NimBLEDevice::getWhiteListCount() { size_t NimBLEDevice::getWhiteListCount() {
return m_whiteList.size(); return m_whiteList.size();
} }
@ -672,6 +767,7 @@ size_t NimBLEDevice::getWhiteListCount() {
* @param [in] index The vector index to retrieve the address from. * @param [in] index The vector index to retrieve the address from.
* @returns the NimBLEAddress at the whitelist index or nullptr if not found. * @returns the NimBLEAddress at the whitelist index or nullptr if not found.
*/ */
/*STATIC*/
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) { NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
if (index > m_whiteList.size()) { if (index > m_whiteList.size()) {
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index); NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
@ -685,7 +781,8 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
* @brief Host reset, we pass the message so we don't make calls until resynced. * @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset. * @param [in] reason The reason code for the reset.
*/ */
/* STATIC */ void NimBLEDevice::onReset(int reason) /* STATIC */
void NimBLEDevice::onReset(int reason)
{ {
if(!m_synced) { if(!m_synced) {
return; return;
@ -709,7 +806,8 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
/** /**
* @brief Host resynced with controller, all clear to make calls to the stack. * @brief Host resynced with controller, all clear to make calls to the stack.
*/ */
/* STATIC */ void NimBLEDevice::onSync(void) /* STATIC */
void NimBLEDevice::onSync(void)
{ {
NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); NIMBLE_LOGI(LOG_TAG, "NimBle host synced.");
// This check is needed due to potentially being called multiple times in succession // This check is needed due to potentially being called multiple times in succession
@ -722,6 +820,14 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
int rc = ble_hs_util_ensure_addr(0); int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0); assert(rc == 0);
#ifndef ESP_PLATFORM
rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d", rc);
return;
}
#endif
// Yield for houskeeping before returning to operations. // Yield for houskeeping before returning to operations.
// Occasionally triggers exception without. // Occasionally triggers exception without.
taskYIELD(); taskYIELD();
@ -747,9 +853,11 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
/** /**
* @brief The main host task. * @brief The main host task.
*/ */
/* STATIC */ void NimBLEDevice::host_task(void *param) /* STATIC */
void NimBLEDevice::host_task(void *param)
{ {
NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started"); NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */ /* This function will return only when nimble_port_stop() is executed */
nimble_port_run(); nimble_port_run();
@ -761,9 +869,11 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
* @brief Initialize the %BLE environment. * @brief Initialize the %BLE environment.
* @param [in] deviceName The device name of the device. * @param [in] deviceName The device name of the device.
*/ */
/* STATIC */ void NimBLEDevice::init(const std::string &deviceName) { /* STATIC */
void NimBLEDevice::init(const std::string &deviceName) {
if(!initialized){ if(!initialized){
int rc=0; int rc=0;
#ifdef ESP_PLATFORM
esp_err_t errRc = ESP_OK; esp_err_t errRc = ESP_OK;
#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS #ifdef CONFIG_ENABLE_ARDUINO_DEPENDS
@ -783,7 +893,7 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#ifdef CONFIG_IDF_TARGET_ESP32C3 #if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
#else #else
bt_cfg.mode = ESP_BT_MODE_BLE; bt_cfg.mode = ESP_BT_MODE_BLE;
@ -795,6 +905,7 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
ESP_ERROR_CHECK(esp_nimble_hci_init()); ESP_ERROR_CHECK(esp_nimble_hci_init());
#endif
nimble_port_init(); nimble_port_init();
// Setup callbacks for host events // Setup callbacks for host events
@ -819,9 +930,10 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
nimble_port_freertos_init(NimBLEDevice::host_task); nimble_port_freertos_init(NimBLEDevice::host_task);
} }
// Wait for host and controller to sync before returning and accepting new tasks // Wait for host and controller to sync before returning and accepting new tasks
while(!m_synced){ while(!m_synced){
vTaskDelay(1 / portTICK_PERIOD_MS); taskYIELD();
} }
initialized = true; // Set the initialization flag to ensure we are only initialized once. initialized = true; // Set the initialization flag to ensure we are only initialized once.
@ -833,16 +945,17 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
* @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing. * @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing.
* @note If clearAll is true when called, any references to the created objects become invalid. * @note If clearAll is true when called, any references to the created objects become invalid.
*/ */
/* STATIC */ void NimBLEDevice::deinit(bool clearAll) { /* STATIC */
void NimBLEDevice::deinit(bool clearAll) {
int ret = nimble_port_stop(); int ret = nimble_port_stop();
if (ret == 0) { if (ret == 0) {
nimble_port_deinit(); nimble_port_deinit();
#ifdef ESP_PLATFORM
ret = esp_nimble_hci_and_controller_deinit(); ret = esp_nimble_hci_and_controller_deinit();
if (ret != ESP_OK) { if (ret != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
} }
#endif
initialized = false; initialized = false;
m_synced = false; m_synced = false;
@ -889,6 +1002,7 @@ NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
* @brief Check if the initialization is complete. * @brief Check if the initialization is complete.
* @return true if initialized. * @return true if initialized.
*/ */
/*STATIC*/
bool NimBLEDevice::getInitialized() { bool NimBLEDevice::getInitialized() {
return initialized; return initialized;
} // getInitialized } // getInitialized
@ -900,7 +1014,8 @@ bool NimBLEDevice::getInitialized() {
* @param mitm If true we are capable of man in the middle protection, false if not. * @param mitm If true we are capable of man in the middle protection, false if not.
* @param sc If true we will perform secure connection pairing, false we will use legacy pairing. * @param sc If true we will perform secure connection pairing, false we will use legacy pairing.
*/ */
/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) { /*STATIC*/
void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) {
NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc); NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc);
ble_hs_cfg.sm_bonding = bonding; ble_hs_cfg.sm_bonding = bonding;
ble_hs_cfg.sm_mitm = mitm; ble_hs_cfg.sm_mitm = mitm;
@ -917,7 +1032,8 @@ bool NimBLEDevice::getInitialized() {
* * 0x08 BLE_SM_PAIR_AUTHREQ_SC * * 0x08 BLE_SM_PAIR_AUTHREQ_SC
* * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. * * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
*/ */
/*STATIC*/void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { /*STATIC*/
void NimBLEDevice::setSecurityAuth(uint8_t auth_req) {
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
(auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0,
(auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0);
@ -933,7 +1049,8 @@ bool NimBLEDevice::getInitialized() {
* * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability * * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
* * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability * * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
*/ */
/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) { /*STATIC*/
void NimBLEDevice::setSecurityIOCap(uint8_t iocap) {
ble_hs_cfg.sm_io_cap = iocap; ble_hs_cfg.sm_io_cap = iocap;
} // setSecurityIOCap } // setSecurityIOCap
@ -947,7 +1064,8 @@ bool NimBLEDevice::getInitialized() {
* * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
* * 0x08: BLE_SM_PAIR_KEY_DIST_LINK * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/ */
/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { /*STATIC*/
void NimBLEDevice::setSecurityInitKey(uint8_t init_key) {
ble_hs_cfg.sm_our_key_dist = init_key; ble_hs_cfg.sm_our_key_dist = init_key;
} // setsSecurityInitKey } // setsSecurityInitKey
@ -961,7 +1079,8 @@ bool NimBLEDevice::getInitialized() {
* * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
* * 0x08: BLE_SM_PAIR_KEY_DIST_LINK * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/ */
/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) { /*STATIC*/
void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) {
ble_hs_cfg.sm_their_key_dist = resp_key; ble_hs_cfg.sm_their_key_dist = resp_key;
} // setsSecurityRespKey } // setsSecurityRespKey
@ -970,7 +1089,8 @@ bool NimBLEDevice::getInitialized() {
* @brief Set the passkey the server will ask for when pairing. * @brief Set the passkey the server will ask for when pairing.
* @param [in] pin The passkey to use. * @param [in] pin The passkey to use.
*/ */
/*STATIC*/void NimBLEDevice::setSecurityPasskey(uint32_t pin) { /*STATIC*/
void NimBLEDevice::setSecurityPasskey(uint32_t pin) {
m_passkey = pin; m_passkey = pin;
} // setSecurityPasskey } // setSecurityPasskey
@ -979,7 +1099,8 @@ bool NimBLEDevice::getInitialized() {
* @brief Get the current passkey used for pairing. * @brief Get the current passkey used for pairing.
* @return The current passkey. * @return The current passkey.
*/ */
/*STATIC*/uint32_t NimBLEDevice::getSecurityPasskey() { /*STATIC*/
uint32_t NimBLEDevice::getSecurityPasskey() {
return m_passkey; return m_passkey;
} // getSecurityPasskey } // getSecurityPasskey
@ -989,11 +1110,13 @@ bool NimBLEDevice::getInitialized() {
* @param [in] callbacks Pointer to NimBLESecurityCallbacks class * @param [in] callbacks Pointer to NimBLESecurityCallbacks class
* @deprecated For backward compatibility, New code should use client/server callback methods. * @deprecated For backward compatibility, New code should use client/server callback methods.
*/ */
/*STATIC*/
void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) { void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
NimBLEDevice::m_securityCallbacks = callbacks; NimBLEDevice::m_securityCallbacks = callbacks;
} // setSecurityCallbacks } // setSecurityCallbacks
#ifdef ESP_PLATFORM
/** /**
* @brief Set the own address type. * @brief Set the own address type.
* @param [in] own_addr_type Own Bluetooth Device address type.\n * @param [in] own_addr_type Own Bluetooth Device address type.\n
@ -1004,6 +1127,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
* * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
* @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address. * @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
*/ */
/*STATIC*/
void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) { void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
m_own_addr_type = own_addr_type; m_own_addr_type = own_addr_type;
switch (own_addr_type) { switch (own_addr_type) {
@ -1027,18 +1151,15 @@ void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
break; break;
} }
} // setOwnAddrType } // setOwnAddrType
#endif
/** /**
* @brief Start the connection securing and authorization for this connection. * @brief Start the connection securing and authorization for this connection.
* @param conn_id The connection id of the peer device. * @param conn_id The connection id of the peer device.
* @returns NimBLE stack return code, 0 = success. * @returns NimBLE stack return code, 0 = success.
*/ */
/* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) { /* STATIC */
/* if(m_securityCallbacks != nullptr) { int NimBLEDevice::startSecurity(uint16_t conn_id) {
m_securityCallbacks->onSecurityRequest();
}
*/
int rc = ble_gap_security_initiate(conn_id); int rc = ble_gap_security_initiate(conn_id);
if(rc != 0){ if(rc != 0){
NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
@ -1053,7 +1174,8 @@ void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
* @param [in] address The address to look for. * @param [in] address The address to look for.
* @return True if ignoring. * @return True if ignoring.
*/ */
/*STATIC*/ bool NimBLEDevice::isIgnored(const NimBLEAddress &address) { /*STATIC*/
bool NimBLEDevice::isIgnored(const NimBLEAddress &address) {
for(auto &it : m_ignoreList) { for(auto &it : m_ignoreList) {
if(it.equals(address)){ if(it.equals(address)){
return true; return true;
@ -1068,7 +1190,8 @@ void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
* @brief Add a device to the ignore list. * @brief Add a device to the ignore list.
* @param [in] address The address of the device we want to ignore. * @param [in] address The address of the device we want to ignore.
*/ */
/*STATIC*/ void NimBLEDevice::addIgnored(const NimBLEAddress &address) { /*STATIC*/
void NimBLEDevice::addIgnored(const NimBLEAddress &address) {
m_ignoreList.push_back(address); m_ignoreList.push_back(address);
} }
@ -1077,7 +1200,8 @@ void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
* @brief Remove a device from the ignore list. * @brief Remove a device from the ignore list.
* @param [in] address The address of the device we want to remove from the list. * @param [in] address The address of the device we want to remove from the list.
*/ */
/*STATIC*/void NimBLEDevice::removeIgnored(const NimBLEAddress &address) { /*STATIC*/
void NimBLEDevice::removeIgnored(const NimBLEAddress &address) {
for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) { for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) {
if((*it).equals(address)){ if((*it).equals(address)){
m_ignoreList.erase(it); m_ignoreList.erase(it);
@ -1091,6 +1215,7 @@ void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
* @brief Set a custom callback for gap events. * @brief Set a custom callback for gap events.
* @param [in] handler The function to call when gap events occur. * @param [in] handler The function to call when gap events occur.
*/ */
/*STATIC*/
void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
m_customGapHandler = handler; m_customGapHandler = handler;
int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL); int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL);
@ -1102,5 +1227,4 @@ void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
} }
} // setCustomGapHandler } // setCustomGapHandler
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED

View file

@ -14,17 +14,20 @@
#ifndef MAIN_NIMBLEDEVICE_H_ #ifndef MAIN_NIMBLEDEVICE_H_
#define MAIN_NIMBLEDEVICE_H_ #define MAIN_NIMBLEDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h" #include "NimBLEScan.h"
#endif #endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEAdvertising.h" # if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
# endif
#endif #endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@ -41,7 +44,9 @@
#include "NimBLESecurity.h" #include "NimBLESecurity.h"
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "esp_bt.h" #ifdef ESP_PLATFORM
# include "esp_bt.h"
#endif
#include <map> #include <map>
#include <string> #include <string>
@ -115,8 +120,17 @@ public:
static NimBLEMeshNode* createMeshNode(NimBLEUUID uuid, uint8_t type); static NimBLEMeshNode* createMeshNode(NimBLEUUID uuid, uint8_t type);
static NimBLEMeshNode* getMeshNode(); static NimBLEMeshNode* getMeshNode();
#ifdef ESP_PLATFORM
static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); 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 int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT);
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
#else
static void setPower(int dbm);
static int getPower();
#endif
static void setCustomGapHandler(gap_event_handler handler); static void setCustomGapHandler(gap_event_handler handler);
static void setSecurityAuth(bool bonding, bool mitm, bool sc); static void setSecurityAuth(bool bonding, bool mitm, bool sc);
static void setSecurityAuth(uint8_t auth_req); static void setSecurityAuth(uint8_t auth_req);
@ -126,20 +140,26 @@ public:
static void setSecurityPasskey(uint32_t pin); static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey(); static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks); static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id); static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu); static int setMTU(uint16_t mtu);
static uint16_t getMTU(); static uint16_t getMTU();
static bool isIgnored(const NimBLEAddress &address); static bool isIgnored(const NimBLEAddress &address);
static void addIgnored(const NimBLEAddress &address); static void addIgnored(const NimBLEAddress &address);
static void removeIgnored(const NimBLEAddress &address); static void removeIgnored(const NimBLEAddress &address);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
static NimBLEAdvertising* getAdvertising(); # if CONFIG_BT_NIMBLE_EXT_ADV
static void startAdvertising(); static NimBLEExtAdvertising* getAdvertising();
static void stopAdvertising(); static bool startAdvertising(uint8_t inst_id,
int duration = 0,
int max_events = 0);
static bool stopAdvertising(uint8_t inst_id);
static bool stopAdvertising();
# else
static NimBLEAdvertising* getAdvertising();
static bool startAdvertising();
static bool stopAdvertising();
# endif
#endif #endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@ -176,6 +196,10 @@ private:
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
# if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisement;
# endif
#endif #endif
static void onReset(int reason); static void onReset(int reason);
@ -192,7 +216,11 @@ private:
#endif #endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* m_bleAdvertising;
# else
static NimBLEAdvertising* m_bleAdvertising; static NimBLEAdvertising* m_bleAdvertising;
# endif
#endif #endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@ -204,8 +232,10 @@ private:
static ble_gap_event_listener m_listener; static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler; static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type; static uint8_t m_own_addr_type;
#ifdef ESP_PLATFORM
static uint16_t m_scanDuplicateSize; static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode; static uint8_t m_scanFilterMode;
#endif
static std::vector<NimBLEAddress> m_whiteList; static std::vector<NimBLEAddress> m_whiteList;
static NimBLEMeshNode* m_pMeshNode; static NimBLEMeshNode* m_pMeshNode;
}; };

View file

@ -11,12 +11,14 @@
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*/ */
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEEddystoneTLM.h" #include "NimBLEEddystoneTLM.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <stdio.h>
#include <cstring> #include <cstring>
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
@ -124,30 +126,30 @@ std::string NimBLEEddystoneTLM::toString() {
out += " C\n"; out += " C\n";
out += "Adv. Count "; out += "Adv. Count ";
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); snprintf(val, sizeof(val), "%" PRIu32, ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
out += val; out += val;
out += "\n"; out += "\n";
out += "Time in seconds "; out += "Time in seconds ";
snprintf(val, sizeof(val), "%d", rawsec/10); snprintf(val, sizeof(val), "%" PRIu32, rawsec/10);
out += val; out += val;
out += "\n"; out += "\n";
out += "Time "; out += "Time ";
snprintf(val, sizeof(val), "%04d", rawsec / 864000); snprintf(val, sizeof(val), "%04" PRIu32, rawsec / 864000);
out += val; out += val;
out += "."; out += ".";
snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 36000) % 24);
out += val; out += val;
out += ":"; out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 600) % 60);
out += val; out += val;
out += ":"; out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 10) % 60);
out += val; out += val;
out += "\n"; out += "\n";

View file

@ -14,6 +14,7 @@
#ifndef _NimBLEEddystoneTLM_H_ #ifndef _NimBLEEddystoneTLM_H_
#define _NimBLEEddystoneTLM_H_ #define _NimBLEEddystoneTLM_H_
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include <string> #include <string>

View file

@ -11,7 +11,7 @@
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEEddystoneURL.h" #include "NimBLEEddystoneURL.h"

View file

@ -0,0 +1,870 @@
/*
* NimBLEExtAdvertising.cpp
*
* Created: on February 6, 2022
* Author H2zero
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#endif
#include "NimBLEExtAdvertising.h"
#include "NimBLEDevice.h"
#include "NimBLEServer.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
static NimBLEExtAdvertisingCallbacks defaultCallbacks;
static const char* LOG_TAG = "NimBLEExtAdvertising";
/**
* @brief Destructor: deletes callback instances if requested.
*/
NimBLEExtAdvertising::~NimBLEExtAdvertising() {
if(m_deleteCallbacks && m_pCallbacks != &defaultCallbacks) {
delete m_pCallbacks;
}
}
/**
* @brief Register the extended advertisement data.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] adv The extended advertisement instance with the data to set.
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv) {
adv.m_params.sid = inst_id;
// Legacy advertising as connectable requires the scannable flag also.
if (adv.m_params.legacy_pdu && adv.m_params.connectable) {
adv.m_params.scannable = true;
}
// If connectable or not scannable disable the callback for scan response requests
if (adv.m_params.connectable || !adv.m_params.scannable) {
adv.m_params.scan_req_notif = false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
if (!pServer->m_gattsStarted) {
pServer->start();
}
}
int rc = ble_gap_ext_adv_configure(inst_id,
&adv.m_params,
NULL,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEExtAdvertising::handleGapEvent,
NULL);
#else
int rc = ble_gap_ext_adv_configure(inst_id,
&data.m_params,
NULL,
NimBLEExtAdvertising::handleGapEvent,
NULL);
#endif
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d", rc);
} else {
os_mbuf *buf;
buf = os_msys_get_pkthdr(adv.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d", rc);
return false;
} else {
if (adv.m_params.scannable && !adv.m_params.legacy_pdu) {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
} else {
rc = ble_gap_ext_adv_set_data(inst_id, buf);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc);
} else {
if (adv.m_advAddress != NimBLEAddress("")) {
ble_addr_t addr;
memcpy(&addr.val, adv.m_advAddress.getNative(), 6);
// Custom advertising address must be random.
addr.type = BLE_OWN_ADDR_RANDOM;
rc = ble_gap_ext_adv_set_addr(inst_id, &addr);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d", rc);
return false;
}
}
}
}
return (rc == 0);
}
/**
* @brief Set the scan response data for a legacy advertisement.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] lsr A reference to a NimBLEExtAdvertisement that contains the data.
*/
bool NimBLEExtAdvertising::setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & lsr) {
os_mbuf *buf = os_msys_get_pkthdr(lsr.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
int rc = os_mbuf_append(buf, &lsr.m_payload[0], lsr.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d", rc);
return false;
} else {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
}
return (rc == 0);
}
/**
* @brief Start extended advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::start(uint8_t inst_id, int duration, int max_events) {
NIMBLE_LOGD(LOG_TAG, ">> Extended Advertising start");
// If Host is not synced we cannot start advertising.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return false;
}
int rc = ble_gap_ext_adv_start(inst_id, duration / 10, max_events);
switch (rc) {
case 0:
m_advStatus[inst_id] = true;
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Value Error");
break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
NIMBLE_LOGD(LOG_TAG, "<< Extended Advertising start");
return (rc == 0 || rc == BLE_HS_EALREADY);
} // start
/**
* @brief Stop and remove this instance data from the advertisement set.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeInstance(uint8_t inst_id) {
if (stop(inst_id)) {
int rc = ble_gap_ext_adv_remove(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
return true;
}
return false;
} // removeInstance
/**
* @brief Stop and remove all advertising instance data.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeAll() {
if (stop()) {
int rc = ble_gap_ext_adv_clear();
if (rc == 0 || rc == BLE_HS_EALREADY) {
return true;
} else {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
}
return false;
} // removeAll
/**
* @brief Stop advertising this instance data.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop(uint8_t inst_id) {
int rc = ble_gap_ext_adv_stop(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
m_advStatus[inst_id] = false;
return true;
} // stop
/**
* @brief Stop all advertisements.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop() {
int rc = ble_gap_ext_adv_clear();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
for(auto it : m_advStatus) {
it = false;
}
return true;
} // stop
/**
* @brief Set a callback to call when the advertisement stops.
* @param [in] pCallbacks A pointer to a callback to be invoked when an advertisment stops.
* @param [in] deleteCallbacks if true callback class will be deleted when advertising is destructed.
*/
void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks,
bool deleteCallbacks) {
if (pCallbacks != nullptr){
m_pCallbacks = pCallbacks;
m_deleteCallbacks = deleteCallbacks;
} else {
m_pCallbacks = &defaultCallbacks;
}
} // setCallbacks
/**
* @brief Check if currently advertising.
* @param [in] inst_id The instance ID of the advertised data to get the status of.
* @return True if advertising is active.
*/
bool NimBLEExtAdvertising::isActive(uint8_t inst_id) {
return m_advStatus[inst_id];
} // isAdvertising
/**
* @brief Check if any instances are currently advertising.
* @return True if any instance is active.
*/
bool NimBLEExtAdvertising::isAdvertising() {
for (auto it : m_advStatus) {
if (it) {
return true;
}
}
return false;
} // isAdvertising
/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
void NimBLEExtAdvertising::onHostSync() {
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
for(auto it : m_advStatus) {
it = false;
}
} // onHostSync
/**
* @brief Handler for gap events when not using peripheral role.
* @param [in] event the event data.
* @param [in] arg pointer to the advertising instance.
*/
/*STATIC*/
int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
(void)arg;
NimBLEExtAdvertising* pAdv = NimBLEDevice::getAdvertising();
switch (event->type) {
case BLE_GAP_EVENT_ADV_COMPLETE: {
switch (event->adv_complete.reason) {
// Don't call the callback if host reset, we want to
// preserve the active flag until re-sync to restart advertising.
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
break;
}
pAdv->m_advStatus[event->adv_complete.instance] = false;
pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason,
event->adv_complete.instance);
break;
}
case BLE_GAP_EVENT_SCAN_REQ_RCVD: {
pAdv->m_pCallbacks->onScanRequest(pAdv, event->scan_req_rcvd.instance,
NimBLEAddress(event->scan_req_rcvd.scan_addr));
break;
}
}
return 0;
} // handleGapEvent
/** Default callback handlers */
void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising *pAdv,
int reason, uint8_t inst_id) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onStopped: Default");
} // onStopped
void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv,
uint8_t inst_id, NimBLEAddress addr) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onScanRequest: Default");
} // onScanRequest
/**
* @brief Construct a BLE extended advertisement.
* @param [in] priPhy The primary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
* @param [in] secPhy The secondary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy)
: m_advAddress("")
{
memset (&m_params, 0, sizeof(m_params));
m_params.own_addr_type = NimBLEDevice::m_own_addr_type;
m_params.primary_phy = priPhy;
m_params.secondary_phy = secPhy;
m_params.tx_power = 127;
} // NimBLEExtAdvertisement
/**
* @brief Sets wether the advertisement should use legacy (BLE 4.0, 31 bytes max) advertising.
* @param [in] val true = using legacy advertising.
*/
void NimBLEExtAdvertisement::setLegacyAdvertising(bool val) {
m_params.legacy_pdu = val;
} // setLegacyAdvertising
/**
* @brief Sets wether the advertisement has scan response data available.
* @param [in] val true = scan response is available.
*/
void NimBLEExtAdvertisement::setScannable(bool val) {
m_params.scannable = val;
} // setScannable
/**
* @brief Sets the transmission power level for this advertisement.
* @param [in] dbm the transmission power to use in dbm.
* @details The allowable value range depends on device hardware. \n
* The ESP32C3 and ESP32S3 have a range of -27 to +18.
*/
void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
m_params.tx_power = dbm;
}
/**
* @brief Sets wether this advertisement should advertise as a connectable device.
* @param [in] val True = connectable.
*/
void NimBLEExtAdvertisement::setConnectable(bool val) {
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_params.connectable = val;
#endif
} // setConnectable
/**
* @brief Set the address to use for this advertisement.
* @param [in] addr The address to use.
*/
void NimBLEExtAdvertisement::setAddress(const NimBLEAddress & addr) {
m_advAddress = addr;
// Must use random address type.
m_params.own_addr_type = BLE_OWN_ADDR_RANDOM;
}
/**
* @brief Sets The primary channels to advertise on.
* @param [in] ch37 Advertise on channel 37.
* @param [in] ch38 Advertise on channel 38.
* @param [in] ch39 Advertise on channel 39.
* @details This will set a bitmask using the input parameters to allow different \n
* combinations. If all inputs are false then all 3 channels will be used.
*/
void NimBLEExtAdvertisement::setPrimaryChannels(bool ch37, bool ch38, bool ch39) {
m_params.channel_map = (ch37 | (ch38 << 1) | (ch39 << 2));
} // setPrimaryChannels
/**
* @brief Set the filtering for the scan filter.
* @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_NONE;
return;
}
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_SCAN;
return;
}
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_CONN;
return;
}
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_BOTH;
return;
}
} // setScanFilter
/**
* @brief Sets the peer to directly advertise to.
* @param [in] addr The address of the peer to direct the advertisements.
*/
void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, addr.getNative(), 6);
peerAddr.type = addr.getType();
m_params.peer = peerAddr;
} // setDirectedPeer
/**
* @brief Enable or disable direct advertisements to the peer set with `NimBLEExtAdvertisement::setDirectedPeer`
* @param [in] val true = send directed advertisements to peer.
* @param [in] high_duty true = use fast advertising rate, default - true.
*/
void NimBLEExtAdvertisement::setDirected(bool val, bool high_duty) {
m_params.directed = val;
m_params.high_duty_directed = high_duty;
} // setDirected
/**
* @brief Set the minimum advertising interval.
* @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMinInterval(uint32_t mininterval) {
m_params.itvl_min = mininterval;
} // setMinInterval
/**
* @brief Set the maximum advertising interval.
* @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMaxInterval(uint32_t maxinterval) {
m_params.itvl_max = maxinterval;
} // setMaxInterval
/**
* @brief Set the primary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setPrimaryPhy(uint8_t phy) {
m_params.primary_phy = phy;
} // setPrimaryPhy
/**
* @brief Set the secondary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setSecondaryPhy(uint8_t phy) {
m_params.secondary_phy = phy;
} // setSecondaryPhy
/**
* @brief Sets whether the advertisement should be anonymous.
* @param [in] val Set to true to enable anonymous advertising.
*
* @details Anonymous advertising omits the device's address from the advertisement.
*/
void NimBLEExtAdvertisement::setAnonymous(bool val) {
m_params.anonymous = val;
} // setAnonymous
/**
* @brief Sets whether the scan response request callback should be called.
* @param [in] enable If true the scan response request callback will be called for this advertisement.
*/
void NimBLEExtAdvertisement::enableScanRequestCallback(bool enable) {
m_params.scan_req_notif = enable;
} // enableScanRequestCallback
/**
* @brief Clears the data stored in this instance, does not change settings.
* @details This will clear all data but preserves advertising parameter settings.
*/
void NimBLEExtAdvertisement::clearData() {
std::vector<uint8_t> swap;
std::swap(m_payload, swap);
}
/**
* @brief Get the size of the current data.
*/
size_t NimBLEExtAdvertisement::getDataSize() {
return m_payload.size();
} // getDataSize
/**
* @brief Set the advertisement data.
* @param [in] data The data to be set as the payload.
* @param [in] length The size of data.
* @details This will completely replace any data that was previously set.
*/
void NimBLEExtAdvertisement::setData(const uint8_t * data, size_t length) {
m_payload.assign(data, data + length);
} // setData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
*/
void NimBLEExtAdvertisement::addData(const std::string &data) {
addData((uint8_t*)data.data(), data.length());
} // addData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload.
*/
void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
m_payload.insert(m_payload.end(), data, data + length);
} // addData
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
*/
void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) {
char cdata[2];
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
} // setAppearance
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
*/
void NimBLEExtAdvertisement::setFlags(uint8_t flag) {
char cdata[3];
cdata[0] = 2;
cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01
cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
addData(std::string(cdata, 3));
} // setFlags
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEExtAdvertisement::setManufacturerData(const std::string &data) {
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
} // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
*/
void NimBLEExtAdvertisement::setURI(const std::string &uri) {
char cdata[2];
cdata[0] = uri.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_URI;
addData(std::string(cdata, 2) + uri);
} // setURI
/**
* @brief Set the complete name of this device.
* @param [in] name The name to advertise.
*/
void NimBLEExtAdvertisement::setName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
} // setName
/**
* @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices(const NimBLEUUID &uuid) {
setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices
/**
* @brief Set the complete list of 16 bit services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 16, v_uuid);
} // setCompleteServices16
/**
* @brief Set the complete list of 32 bit services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setCompleteServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 32, v_uuid);
} // setCompleteServices32
/**
* @brief Set a single service to advertise as a partial list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices(const NimBLEUUID &uuid) {
setServices(false, uuid.bitSize(), {uuid});
} // setPartialServices
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 16, v_uuid);
} // setPartialServices16
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEExtAdvertisement::setPartialServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 32, v_uuid);
} // setPartialServices32
/**
* @brief Utility function to create the list of service UUID's from a vector.
* @param [in] complete If true the vector is the complete set of services.
* @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
* @param [in] v_uuid The vector of service UUID's to advertise.
*/
void NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid)
{
char cdata[2];
cdata[0] = (size / 8) * v_uuid.size() + 1;
switch(size) {
case 16:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
break;
case 32:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
default:
return;
}
std::string uuids;
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
}
}
addData(std::string(cdata, 2) + uuids);
} // setServices
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data.
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
break;
}
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
break;
}
case 128: {
// [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data);
break;
}
default:
return;
}
} // setServiceData
/**
* @brief Set the short name.
* @param [in] name The short name of the device.
*/
void NimBLEExtAdvertisement::setShortName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
} // setShortName
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEExtAdvertisement::addTxPower() {
m_params.include_tx_power = 1;
} // addTxPower
/**
* @brief Set the preferred connection interval parameters.
* @param [in] min The minimum interval desired.
* @param [in] max The maximum interval desired.
*/
void NimBLEExtAdvertisement::setPreferredParams(uint16_t min, uint16_t max) {
uint8_t data[6];
data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
data[2] = min;
data[3] = min >> 8;
data[4] = max;
data[5] = max >> 8;
addData(data, 6);
} // setPreferredParams
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */

152
src/NimBLEExtAdvertising.h Normal file
View file

@ -0,0 +1,152 @@
/*
* NimBLEExtAdvertising.h
*
* Created: on February 6, 2022
* Author H2zero
*/
#ifndef MAIN_BLEEXTADVERTISING_H_
#define MAIN_BLEEXTADVERTISING_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include <vector>
class NimBLEExtAdvertisingCallbacks;
/**
* @brief Extended advertisement data
*/
class NimBLEExtAdvertisement {
public:
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M,
uint8_t secPhy = BLE_HCI_LE_PHY_1M);
void setAppearance(uint16_t appearance);
void setCompleteServices(const NimBLEUUID &uuid);
void setCompleteServices16(const std::vector<NimBLEUUID> &v_uuid);
void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid);
void setFlags(uint8_t flag);
void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid);
void setPartialServices16(const std::vector<NimBLEUUID> &v_uuid);
void setPartialServices32(const std::vector<NimBLEUUID> &v_uuid);
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name);
void setData(const uint8_t * data, size_t length);
void addData(const std::string &data);
void addData(const uint8_t * data, size_t length);
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
void setLegacyAdvertising(bool val);
void setConnectable(bool val);
void setScannable(bool val);
void setMinInterval(uint32_t mininterval);
void setMaxInterval(uint32_t maxinterval);
void setPrimaryPhy(uint8_t phy);
void setSecondaryPhy(uint8_t phy);
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setDirectedPeer(const NimBLEAddress & addr);
void setDirected(bool val, bool high_duty = true);
void setAnonymous(bool val);
void setPrimaryChannels(bool ch37, bool ch38, bool ch39);
void setTxPower(int8_t dbm);
void setAddress(const NimBLEAddress & addr);
void enableScanRequestCallback(bool enable);
void clearData();
size_t getDataSize();
private:
friend class NimBLEExtAdvertising;
void setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid);
std::vector<uint8_t> m_payload;
ble_gap_ext_adv_params m_params;
NimBLEAddress m_advAddress;
}; // NimBLEExtAdvertisement
/**
* @brief Extended advertising class.
*/
class NimBLEExtAdvertising {
public:
/**
* @brief Construct an extended advertising object.
*/
NimBLEExtAdvertising() :m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {}
~NimBLEExtAdvertising();
bool start(uint8_t inst_id, int duration = 0, int max_events = 0);
bool setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv);
bool setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & data);
bool removeInstance(uint8_t inst_id);
bool removeAll();
bool stop(uint8_t inst_id);
bool stop();
bool isActive(uint8_t inst_id);
bool isAdvertising();
void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks,
bool deleteCallbacks = true);
private:
friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
bool m_scanResp;
bool m_deleteCallbacks;
NimBLEExtAdvertisingCallbacks* m_pCallbacks;
ble_gap_ext_adv_params m_advParams;
std::vector<bool> m_advStatus;
};
/**
* @brief Callbacks associated with NimBLEExtAdvertising class.
*/
class NimBLEExtAdvertisingCallbacks {
public:
virtual ~NimBLEExtAdvertisingCallbacks() {};
/**
* @brief Handle an advertising stop event.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] reason The reason code for stopping the advertising.
* @param [in] inst_id The instance ID of the advertisement that was stopped.
*/
virtual void onStopped(NimBLEExtAdvertising *pAdv, int reason, uint8_t inst_id);
/**
* @brief Handle a scan response request.
* This is called when a scanning device requests a scan response.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] inst_id The instance ID of the advertisement that the scan response request was made.
* @param [in] addr The address of the device making the request.
*/
virtual void onScanRequest(NimBLEExtAdvertising *pAdv, uint8_t inst_id, NimBLEAddress addr);
}; // NimBLEExtAdvertisingCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* MAIN_BLEADVERTISING_H_ */

View file

@ -11,11 +11,9 @@
* Created on: Jan 03, 2018 * Created on: Jan 03, 2018
* Author: chegewara * Author: chegewara
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h" #include "NimBLEHIDDevice.h"
#include "NimBLE2904.h" #include "NimBLE2904.h"
@ -29,7 +27,7 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
* Here we create mandatory services described in bluetooth specification * Here we create mandatory services described in bluetooth specification
*/ */
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a)); m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40); m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812));
m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f)); m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
/* /*
@ -247,5 +245,4 @@ NimBLEService* NimBLEHIDDevice::batteryService() {
return m_batteryService; return m_batteryService;
} }
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_ENABLED)

View file

@ -15,11 +15,8 @@
#ifndef _BLEHIDDEVICE_H_ #ifndef _BLEHIDDEVICE_H_
#define _BLEHIDDEVICE_H_ #define _BLEHIDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
#include "NimBLEService.h" #include "NimBLEService.h"
@ -84,6 +81,6 @@ private:
NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
}; };
#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_ENABLED #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */
#endif /* _BLEHIDDEVICE_H_ */ #endif /* _BLEHIDDEVICE_H_ */

View file

@ -8,59 +8,73 @@
#ifndef MAIN_NIMBLELOG_H_ #ifndef MAIN_NIMBLELOG_H_
#define MAIN_NIMBLELOG_H_ #define MAIN_NIMBLELOG_H_
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#ifdef ARDUINO_ARCH_ESP32 #if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf
#include "syscfg/syscfg.h" # include "esp_log.h"
#include "modlog/modlog.h" # ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
// If Arduino is being used, strip out the colors and ignore log printing below ui setting. # define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) do { \
// Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) \
// otherwise no messages will be printed above that level. ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while(0)
#ifndef CORE_DEBUG_LEVEL # define NIMBLE_LOGD(tag, format, ...) \
#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__)
#endif
#if CORE_DEBUG_LEVEL >= 4 # define NIMBLE_LOGI(tag, format, ...) \
#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__)
#else
#define NIMBLE_LOGD( tag, format, ... ) (void)tag
#endif
#if CORE_DEBUG_LEVEL >= 3 # define NIMBLE_LOGW(tag, format, ...) \
#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__) NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__)
#else
#define NIMBLE_LOGI( tag, format, ... ) (void)tag
#endif
#if CORE_DEBUG_LEVEL >= 2 # define NIMBLE_LOGE(tag, format, ...) \
#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#else
#define NIMBLE_LOGW( tag, format, ... ) (void)tag
#endif
#if CORE_DEBUG_LEVEL >= 1 # define NIMBLE_LOGC(tag, format, ...) \
#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#else
#define NIMBLE_LOGE( tag, format, ... ) (void)tag
#endif
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__) #else // using Arduino
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
# include "nimble/console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL)
# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL
# else
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
# endif
#else # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
# define NIMBLE_LOGD( tag, format, ... ) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGD( tag, format, ... ) (void)tag
# endif
#include "esp_log.h" # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
# define NIMBLE_LOGI( tag, format, ... ) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGI( tag, format, ... ) (void)tag
# endif
#define NIMBLE_LOGE(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__) # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2
#define NIMBLE_LOGW(tag, format, ...) ESP_LOGW(tag, format, ##__VA_ARGS__) # define NIMBLE_LOGW( tag, format, ... ) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
#define NIMBLE_LOGI(tag, format, ...) ESP_LOGI(tag, format, ##__VA_ARGS__) # else
#define NIMBLE_LOGD(tag, format, ...) ESP_LOGD(tag, format, ##__VA_ARGS__) # define NIMBLE_LOGW( tag, format, ... ) (void)tag
#define NIMBLE_LOGC(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__) # endif
#endif /*ARDUINO_ARCH_ESP32*/ # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
# else
# define NIMBLE_LOGE( tag, format, ... ) (void)tag
# define NIMBLE_LOGC( tag, format, ... ) (void)tag
# endif
#endif /*CONFIG_BT_ENABLED*/ #endif /* CONFIG_NIMBLE_CPP_IDF */
#endif /*MAIN_NIMBLELOG_H_*/ #endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLELOG_H_ */

View file

@ -12,16 +12,15 @@
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h" #include "NimBLERemoteCharacteristic.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLERemoteCharacteristic"; static const char* LOG_TAG = "NimBLERemoteCharacteristic";
/** /**
@ -59,8 +58,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_charProp = chr->properties; m_charProp = chr->properties;
m_pRemoteService = pRemoteService; m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr; m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str()); NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic } // NimBLERemoteCharacteristic
@ -241,7 +238,8 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
} }
int rc = 0; int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
// If we don't know the end handle of this characteristic retrieve the next one in the service // If we don't know the end handle of this characteristic retrieve the next one in the service
// The end handle is the next characteristic definition handle -1. // The end handle is the next characteristic definition handle -1.
@ -256,6 +254,10 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) { if (taskData.rc != 0) {
@ -264,6 +266,10 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
} }
} }
if (m_handle == m_endHandle) {
return true;
}
desc_filter_t filter = {uuid_filter, &taskData}; desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
@ -277,6 +283,10 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) { if (taskData.rc != 0) {
@ -310,14 +320,31 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
return m_descriptorVector.back(); return m_descriptorVector.back();
} }
// If the request was successful but 16/32 bit descriptor not found // If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid. // try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 || if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32) uuid.bitSize() == BLE_UUID_TYPE_32)
{ {
NimBLEUUID uuid128(uuid); NimBLEUUID uuid128(uuid);
uuid128.to128(); uuid128.to128();
return getDescriptor(uuid128); if(retrieveDescriptors(&uuid128)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if(retrieveDescriptors(&uuid16)) {
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
}
}
} }
} }
@ -407,15 +434,12 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read. * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic. * @return The value of the remote characteristic.
*/ */
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) { NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
std::string value = m_value;
if(timestamp != nullptr) { if(timestamp != nullptr) {
*timestamp = m_timestamp; *timestamp = m_value.getTimeStamp();
} }
portEXIT_CRITICAL(&m_valMux);
return value; return m_value;
} }
@ -463,12 +487,12 @@ float NimBLERemoteCharacteristic::readFloat() {
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read. * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic. * @return The value of the remote characteristic.
*/ */
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) { NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle()); getUUID().toString().c_str(), getHandle(), getHandle());
NimBLEClient* pClient = getRemoteService()->getClient(); NimBLEClient* pClient = getRemoteService()->getClient();
std::string value; NimBLEAttValue value;
if (!pClient->isConnected()) { if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected"); NIMBLE_LOGE(LOG_TAG, "Disconnected");
@ -477,7 +501,8 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
int rc = 0; int rc = 0;
int retryCount = 1; int retryCount = 1;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(),0, &value}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, &value};
do { do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
@ -489,6 +514,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
return value; return value;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc; rc = taskData.rc;
@ -514,14 +543,11 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
} }
} while(rc != 0 && retryCount--); } while(rc != 0 && retryCount--);
time_t t = time(nullptr); value.setTimeStamp();
portENTER_CRITICAL(&m_valMux);
m_value = value; m_value = value;
m_timestamp = t;
if(timestamp != nullptr) { if(timestamp != nullptr) {
*timestamp = m_timestamp; *timestamp = value.getTimeStamp();
} }
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc); NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
return value; return value;
@ -546,16 +572,17 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
std::string *strBuf = (std::string*)pTaskData->buf; NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
int rc = error->status; int rc = error->status;
if(rc == 0) { if(rc == 0) {
if(attr) { if(attr) {
if(((*strBuf).length() + attr->om->om_len) > BLE_ATT_ATTR_MAX_LEN) { uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else { } else {
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len); NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
(*strBuf) += std::string((char*) attr->om->om_data, attr->om->om_len); valBuf->append(attr->om->om_data, data_len);
return 0; return 0;
} }
} }
@ -706,22 +733,33 @@ std::string NimBLERemoteCharacteristic::toString() {
/** /**
* @brief Write the new value for the characteristic. * @brief Write a new value to the remote characteristic from a std::vector<uint8_t>.
* @param [in] newValue The new value to write. * @param [in] vec A std::vector<uint8_t> value to write to the remote characteristic.
* @param [in] response Do we expect a response? * @param [in] response Whether we require a response from the write.
* @return false if not connected or cant perform write for some reason. * @return false if not connected or otherwise cannot perform write.
*/ */
bool NimBLERemoteCharacteristic::writeValue(const std::string &newValue, bool response) { bool NimBLERemoteCharacteristic::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)newValue.c_str(), newValue.length(), response); return writeValue((uint8_t*)&vec[0], vec.size(), response);
} // writeValue } // writeValue
/** /**
* @brief Write the new value for the characteristic from a data buffer. * @brief Write a new value to the remote characteristic from a const char*.
* @param [in] char_s A character string to write to the remote characteristic.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to the remote characteristic from a data buffer.
* @param [in] data A pointer to a data buffer. * @param [in] data A pointer to a data buffer.
* @param [in] length The length of the data in the data buffer. * @param [in] length The length of the data in the data buffer.
* @param [in] response Whether we require a response from the write. * @param [in] response Whether we require a response from the write.
* @return false if not connected or cant perform write for some reason. * @return false if not connected or otherwise cannot perform write.
*/ */
bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) { bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {
@ -745,7 +783,8 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
return (rc==0); return (rc==0);
} }
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
do { do {
if(length > mtu) { if(length > mtu) {
@ -765,6 +804,10 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc; rc = taskData.rc;
@ -819,6 +862,4 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
return 0; return 0;
} }
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -14,17 +14,16 @@
#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ #ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ #define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h" #include "NimBLERemoteService.h"
#include "NimBLERemoteDescriptor.h" #include "NimBLERemoteDescriptor.h"
#include <vector> #include <vector>
#include <functional> #include <functional>
#include "NimBLELog.h"
class NimBLERemoteService; class NimBLERemoteService;
class NimBLERemoteDescriptor; class NimBLERemoteDescriptor;
@ -62,47 +61,15 @@ public:
uint16_t getHandle(); uint16_t getHandle();
uint16_t getDefHandle(); uint16_t getDefHandle();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string readValue(time_t *timestamp = nullptr); NimBLEAttValue readValue(time_t *timestamp = nullptr);
std::string toString();
/** NimBLERemoteService* getRemoteService();
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = readValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()"))); uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()"))); uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()"))); uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()"))); float readFloat() __attribute__ ((deprecated("Use template readValue<float>()")));
std::string getValue(time_t *timestamp = nullptr); NimBLEAttValue getValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
bool subscribe(bool notifications = true, bool subscribe(bool notifications = true,
notify_callback notifyCallback = nullptr, notify_callback notifyCallback = nullptr,
@ -115,20 +82,74 @@ public:
bool writeValue(const uint8_t* data, bool writeValue(const uint8_t* data,
size_t length, size_t length,
bool response = false); bool response = false);
bool writeValue(const std::string &newValue, bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool response = false); bool writeValue(const char* s, bool response = false);
/*********************** Template Functions ************************/
/** /**
* @brief Convenience template to set the remote characteristic value to <type\>val. * @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write. * @param [in] s The value to write.
* @param [in] response True == request write response. * @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/ */
template<typename T> template<typename T>
bool writeValue(const T &s, bool response = false) { #ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response); return writeValue((uint8_t*)&s, sizeof(T), response);
} }
std::string toString(); /**
NimBLERemoteService* getRemoteService(); * @brief Template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
if(!skipSizeCheck && m_value.size() < sizeof(T)) return T();
return *((T *)m_value.getValue(timestamp));
}
/**
* @brief Template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
NimBLEAttValue value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
return *((T *)value.getValue(timestamp));
}
private: private:
@ -158,15 +179,12 @@ private:
uint16_t m_defHandle; uint16_t m_defHandle;
uint16_t m_endHandle; uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService; NimBLERemoteService* m_pRemoteService;
std::string m_value; NimBLEAttValue m_value;
notify_callback m_notifyCallback; notify_callback m_notifyCallback;
time_t m_timestamp;
portMUX_TYPE m_valMux;
// We maintain a vector of descriptors owned by this characteristic. // We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector; std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
}; // NimBLERemoteCharacteristic }; // NimBLERemoteCharacteristic
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */ #endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */

View file

@ -11,16 +11,16 @@
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteDescriptor.h" #include "NimBLERemoteDescriptor.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLERemoteDescriptor"; static const char* LOG_TAG = "NimBLERemoteDescriptor";
/** /**
@ -86,11 +86,7 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
* @deprecated Use readValue<uint8_t>(). * @deprecated Use readValue<uint8_t>().
*/ */
uint8_t NimBLERemoteDescriptor::readUInt8() { uint8_t NimBLERemoteDescriptor::readUInt8() {
std::string value = readValue(); return readValue<uint8_t>();
if (value.length() >= 1) {
return (uint8_t) value[0];
}
return 0;
} // readUInt8 } // readUInt8
@ -100,11 +96,7 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
* @deprecated Use readValue<uint16_t>(). * @deprecated Use readValue<uint16_t>().
*/ */
uint16_t NimBLERemoteDescriptor::readUInt16() { uint16_t NimBLERemoteDescriptor::readUInt16() {
std::string value = readValue(); return readValue<uint16_t>();
if (value.length() >= 2) {
return *(uint16_t*) value.data();
}
return 0;
} // readUInt16 } // readUInt16
@ -114,11 +106,7 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
* @deprecated Use readValue<uint32_t>(). * @deprecated Use readValue<uint32_t>().
*/ */
uint32_t NimBLERemoteDescriptor::readUInt32() { uint32_t NimBLERemoteDescriptor::readUInt32() {
std::string value = readValue(); return readValue<uint32_t>();
if (value.length() >= 4) {
return *(uint32_t*) value.data();
}
return 0;
} // readUInt32 } // readUInt32
@ -126,11 +114,11 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
* @brief Read the value of the remote descriptor. * @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor. * @return The value of the remote descriptor.
*/ */
std::string NimBLERemoteDescriptor::readValue() { NimBLEAttValue NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
std::string value; NimBLEAttValue value;
if (!pClient->isConnected()) { if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected"); NIMBLE_LOGE(LOG_TAG, "Disconnected");
@ -139,7 +127,8 @@ std::string NimBLERemoteDescriptor::readValue() {
int rc = 0; int rc = 0;
int retryCount = 1; int retryCount = 1;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(),0, &value}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, &value};
do { do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
@ -151,6 +140,10 @@ std::string NimBLERemoteDescriptor::readValue() {
return value; return value;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc; rc = taskData.rc;
@ -175,7 +168,7 @@ std::string NimBLERemoteDescriptor::readValue() {
} }
} while(rc != 0 && retryCount--); } while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %d rc=%d", value.length(), rc); NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %u rc=%d", value.length(), rc);
return value; return value;
} // readValue } // readValue
@ -188,6 +181,7 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg) struct ble_gatt_attr *attr, void *arg)
{ {
(void)attr;
ble_task_data_t *pTaskData = (ble_task_data_t*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT; NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT;
uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId(); uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
@ -198,16 +192,17 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
std::string *strBuf = (std::string*)pTaskData->buf; NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf;
int rc = error->status; int rc = error->status;
if(rc == 0) { if(rc == 0) {
if(attr) { if(attr) {
if(((*strBuf).length() + attr->om->om_len) > BLE_ATT_ATTR_MAX_LEN) { uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else { } else {
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len); NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len);
(*strBuf) += std::string((char*) attr->om->om_data, attr->om->om_len); valBuf->append(attr->om->om_data, data_len);
return 0; return 0;
} }
} }
@ -260,11 +255,33 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
/** /**
* @brief Write data to the BLE Remote Descriptor. * @brief Write a new value to a remote descriptor from a std::vector<uint8_t>.
* @param [in] vec A std::vector<uint8_t> value to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const std::vector<uint8_t>& vec, bool response) {
return writeValue((uint8_t*)&vec[0], vec.size(), response);
} // writeValue
/**
* @brief Write a new value to the remote descriptor from a const char*.
* @param [in] char_s A character string to write to the remote descriptor.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or otherwise cannot perform write.
*/
bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) {
return writeValue((uint8_t*)char_s, strlen(char_s), response);
} // writeValue
/**
* @brief Write a new value to a 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 write response. * @param [in] response True if we expect a write response.
* @return True if successful * @return false if not connected or otherwise cannot perform write.
*/ */
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) { bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
@ -289,7 +306,8 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
return (rc == 0); return (rc == 0);
} }
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
do { do {
if(length > mtu) { if(length > mtu) {
@ -310,6 +328,10 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc; rc = taskData.rc;
@ -340,15 +362,4 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
} // writeValue } // writeValue
/** #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
* @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.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -14,11 +14,9 @@
#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ #ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ #define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h" #include "NimBLERemoteCharacteristic.h"
@ -31,10 +29,53 @@ public:
uint16_t getHandle(); uint16_t getHandle();
NimBLERemoteCharacteristic* getRemoteCharacteristic(); NimBLERemoteCharacteristic* getRemoteCharacteristic();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string readValue(); NimBLEAttValue readValue();
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::vector<uint8_t>& v, bool response = false);
bool writeValue(const char* s, bool response = false);
/*********************** Template Functions ************************/
/** /**
* @brief A template to convert the remote descriptor data to <type\>. * @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used for non-arrays and types without a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<!std::is_array<T>::value && !Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
/**
* @brief Template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
* @details Only used if the <type\> has a `c_str()` method.
*/
template<typename T>
#ifdef _DOXYGEN_
bool
#else
typename std::enable_if<Has_c_str_len<T>::value, bool>::type
#endif
writeValue(const T& s, bool response = false) {
return writeValue((uint8_t*)s.c_str(), s.length(), response);
}
/**
* @brief Template to convert the remote descriptor data to <type\>.
* @tparam T The type to convert the data to. * @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>. * @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is * @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
@ -42,28 +83,10 @@ public:
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
*/ */
template<typename T> template<typename T>
T readValue(bool skipSizeCheck = false) { T readValue(bool skipSizeCheck = false) {
std::string value = readValue(); NimBLEAttValue value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T(); if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data(); return *((T *)value.data());
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
/**
* @brief Convenience template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
} }
private: private:
@ -81,6 +104,5 @@ private:
NimBLERemoteCharacteristic* m_pRemoteCharacteristic; NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
}; };
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */ #endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */

View file

@ -11,17 +11,17 @@
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h" #include "NimBLERemoteService.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <climits>
static const char* LOG_TAG = "NimBLERemoteService"; static const char* LOG_TAG = "NimBLERemoteService";
/** /**
@ -109,14 +109,31 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
return m_characteristicVector.back(); return m_characteristicVector.back();
} }
// If the request was successful but 16/32 bit characteristic not found // If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid. // try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 || if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
uuid.bitSize() == BLE_UUID_TYPE_32) uuid.bitSize() == BLE_UUID_TYPE_32)
{ {
NimBLEUUID uuid128(uuid); NimBLEUUID uuid128(uuid);
uuid128.to128(); uuid128.to128();
return getCharacteristic(uuid128); if (retrieveCharacteristics(&uuid128)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if(retrieveCharacteristics(&uuid16)) {
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
}
}
} }
} }
@ -198,7 +215,8 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
int rc = 0; int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {this, cur_task, 0, nullptr};
if(uuid_filter == nullptr) { if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
@ -220,6 +238,10 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
return false; return false;
} }
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){ if(taskData.rc == 0){
@ -234,7 +256,9 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
} }
} }
m_characteristicVector.back()->m_endHandle = getEndHandle(); if (m_characteristicVector.size() > 0) {
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
} }
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
@ -386,6 +410,4 @@ std::string NimBLERemoteService::toString() {
return res; return res;
} // toString } // toString
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View file

@ -14,11 +14,9 @@
#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_ #ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_
#define COMPONENTS_NIMBLEREMOTESERVICE_H_ #define COMPONENTS_NIMBLEREMOTESERVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h" #include "NimBLEClient.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
@ -83,6 +81,5 @@ private:
uint16_t m_endHandle; uint16_t m_endHandle;
}; // NimBLERemoteService }; // NimBLERemoteService
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */ #endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */

View file

@ -11,17 +11,16 @@
* Created on: Jul 1, 2017 * Created on: Jul 1, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h" #include "NimBLEScan.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <string> #include <string>
#include <climits>
static const char* LOG_TAG = "NimBLEScan"; static const char* LOG_TAG = "NimBLEScan";
@ -57,18 +56,27 @@ NimBLEScan::~NimBLEScan() {
* @param [in] param Parameter data for this event. * @param [in] param Parameter data for this event.
*/ */
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { /*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
(void)arg;
NimBLEScan* pScan = (NimBLEScan*)arg; NimBLEScan* pScan = NimBLEDevice::getScan();
switch(event->type) { switch(event->type) {
case BLE_GAP_EVENT_EXT_DISC:
case BLE_GAP_EVENT_DISC: { case BLE_GAP_EVENT_DISC: {
if(pScan->m_ignoreResults) { if(pScan->m_ignoreResults) {
NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results"); NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0; return 0;
} }
#if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEAddress advertisedAddress(event->disc.addr); const auto& disc = event->ext_disc;
const bool isLegacyAdv = disc.props & BLE_HCI_ADV_LEGACY_MASK;
const auto event_type = isLegacyAdv ? disc.legacy_event_type : disc.props;
#else
const auto& disc = event->disc;
const bool isLegacyAdv = true;
const auto event_type = disc.event_type;
#endif
NimBLEAddress advertisedAddress(disc.addr);
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
if(NimBLEDevice::isIgnored(advertisedAddress)) { if(NimBLEDevice::isIgnored(advertisedAddress)) {
@ -80,7 +88,12 @@ NimBLEScan::~NimBLEScan() {
// If we've seen this device before get a pointer to it from the vector // If we've seen this device before get a pointer to it from the vector
for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) { for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) {
if(it->getAddress() == advertisedAddress) { #if CONFIG_BT_NIMBLE_EXT_ADV
// Same address but different set ID should create a new advertised device.
if (it->getAddress() == advertisedAddress && it->getSetId() == disc.sid) {
#else
if (it->getAddress() == advertisedAddress) {
#endif
advertisedDevice = it; advertisedDevice = it;
break; break;
} }
@ -88,20 +101,27 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector. // 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. // Otherwise just update the relevant parameters of the already known device.
if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){ if (advertisedDevice == nullptr &&
(!isLegacyAdv || event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) {
// Check if we have reach the scan results limit, ignore this one if so. // 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 // 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 && if (pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
(pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) {
{
return 0; return 0;
} }
advertisedDevice = new NimBLEAdvertisedDevice(); advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress); advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type); advertisedDevice->setAdvType(event_type, isLegacyAdv);
#if CONFIG_BT_NIMBLE_EXT_ADV
advertisedDevice->setSetId(disc.sid);
advertisedDevice->setPrimaryPhy(disc.prim_phy);
advertisedDevice->setSecondaryPhy(disc.sec_phy);
advertisedDevice->setPeriodicInterval(disc.periodic_adv_itvl);
#endif
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else if(advertisedDevice != nullptr) { } else if (advertisedDevice != nullptr) {
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else { } else {
// Scan response from unknown device // Scan response from unknown device
@ -109,14 +129,14 @@ NimBLEScan::~NimBLEScan() {
} }
advertisedDevice->m_timestamp = time(nullptr); advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(event->disc.rssi); advertisedDevice->setRSSI(disc.rssi);
advertisedDevice->setPayload(event->disc.data, event->disc.length_data, advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv &&
event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP); event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP));
if (pScan->m_pAdvertisedDeviceCallbacks) { if (pScan->m_pAdvertisedDeviceCallbacks) {
// If not active scanning or scan response is not available // If not active scanning or scan response is not available
// report the result to the callback now. // or extended advertisement scanning, report the result to the callback now.
if(pScan->m_scan_params.passive || if(pScan->m_scan_params.passive || !isLegacyAdv ||
(advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND && (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
{ {
@ -124,7 +144,7 @@ NimBLEScan::~NimBLEScan() {
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise, wait for the scan response so we can report the complete data. // Otherwise, wait for the scan response so we can report the complete data.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { } else if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true; advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
} }
@ -284,7 +304,7 @@ bool NimBLEScan::isScanning() {
* @return True if scan started or false if there was an error. * @return True if scan started or false if there was an error.
*/ */
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration); NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration);
// Save the callback to be invoked when the scan completes. // Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB; m_scanCompleteCB = scanCompleteCB;
@ -305,9 +325,28 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
m_ignoreResults = true; m_ignoreResults = true;
} }
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params, # if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEScan::handleGapEvent, this); ble_gap_ext_disc_params scan_params;
scan_params.passive = m_scan_params.passive;
scan_params.itvl = m_scan_params.itvl;
scan_params.window = m_scan_params.window;
int rc = ble_gap_ext_disc(NimBLEDevice::m_own_addr_type,
duration/10,
0,
m_scan_params.filter_duplicates,
m_scan_params.filter_policy,
m_scan_params.limited,
&scan_params,
&scan_params,
NimBLEScan::handleGapEvent,
NULL);
#else
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type,
duration,
&m_scan_params,
NimBLEScan::handleGapEvent,
NULL);
#endif
switch(rc) { switch(rc) {
case 0: case 0:
if(!is_continue) { if(!is_continue) {
@ -358,10 +397,15 @@ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever");
} }
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr}; TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
ble_task_data_t taskData = {nullptr, cur_task, 0, nullptr};
m_pTaskData = &taskData; m_pTaskData = &taskData;
if(start(duration, nullptr, is_continue)) { if(start(duration, nullptr, is_continue)) {
#ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} }
@ -534,5 +578,4 @@ NimBLEAdvertisedDevice *NimBLEScanResults::getDevice(const NimBLEAddress &addres
return nullptr; return nullptr;
} }
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif /* CONFIG_BT_ENABLED */

View file

@ -13,16 +13,18 @@
*/ */
#ifndef COMPONENTS_NIMBLE_SCAN_H_ #ifndef COMPONENTS_NIMBLE_SCAN_H_
#define COMPONENTS_NIMBLE_SCAN_H_ #define COMPONENTS_NIMBLE_SCAN_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAdvertisedDevice.h" #include "NimBLEAdvertisedDevice.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
#include <vector> #include <vector>
@ -97,6 +99,5 @@ private:
uint8_t m_maxResults; uint8_t m_maxResults;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLE_SCAN_H_ */ #endif /* COMPONENTS_NIMBLE_SCAN_H_ */

View file

@ -12,7 +12,7 @@
* Author: chegewara * Author: chegewara
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLESecurity.h" #include "NimBLESecurity.h"

View file

@ -14,10 +14,16 @@
#ifndef COMPONENTS_NIMBLESECURITY_H_ #ifndef COMPONENTS_NIMBLESECURITY_H_
#define COMPONENTS_NIMBLESECURITY_H_ #define COMPONENTS_NIMBLESECURITY_H_
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max

View file

@ -12,19 +12,20 @@
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h" #include "NimBLEServer.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h" #include "services/gatt/ble_svc_gatt.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
#endif
static const char* LOG_TAG = "NimBLEServer"; static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks; static NimBLEServerCallbacks defaultCallbacks;
@ -41,7 +42,9 @@ NimBLEServer::NimBLEServer() {
// m_svcChgChrHdl = 0xffff; // Future Use // m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks; m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false; m_gattsStarted = false;
#if !CONFIG_BT_NIMBLE_EXT_ADV
m_advertiseOnDisconnect = true; m_advertiseOnDisconnect = true;
#endif
m_svcChanged = false; m_svcChanged = false;
m_deleteCallbacks = true; m_deleteCallbacks = true;
} // NimBLEServer } // NimBLEServer
@ -74,23 +77,19 @@ NimBLEService* NimBLEServer::createService(const char* uuid) {
/** /**
* @brief Create a %BLE Service. * @brief Create a %BLE Service.
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the new service.
* @param [in] numHandles The maximum number of handles associated with this service.
* @param [in] inst_id if we have multiple services with the same UUID we need
* to provide inst_id value different for each service.
* @return A reference to the new service object. * @return A reference to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) { NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
// TODO: add functionality to use inst_id for multiple services with same uuid
(void)inst_id;
// 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(getServiceByUUID(uuid) != nullptr) { if(getServiceByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
std::string(uuid).c_str()); std::string(uuid).c_str());
} }
NimBLEService* pService = new NimBLEService(uuid, numHandles, this); NimBLEService* pService = new NimBLEService(uuid);
m_svcVec.push_back(pService); // Save a reference to this service being on this server. m_svcVec.push_back(pService);
serviceChanged(); serviceChanged();
NIMBLE_LOGD(LOG_TAG, "<< createService"); NIMBLE_LOGD(LOG_TAG, "<< createService");
@ -142,15 +141,26 @@ NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
return nullptr; return nullptr;
} }
#if CONFIG_BT_NIMBLE_EXT_ADV
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return An advertising object.
*/
NimBLEExtAdvertising* NimBLEServer::getAdvertising() {
return NimBLEDevice::getAdvertising();
} // getAdvertising
#endif
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/** /**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server. * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
*
* @return An advertising object. * @return An advertising object.
*/ */
NimBLEAdvertising* NimBLEServer::getAdvertising() { NimBLEAdvertising* NimBLEServer::getAdvertising() {
return NimBLEDevice::getAdvertising(); return NimBLEDevice::getAdvertising();
} // getAdvertising } // getAdvertising
#endif
/** /**
* @brief Sends a service changed notification and resets the GATT server. * @brief Sends a service changed notification and resets the GATT server.
@ -181,7 +191,7 @@ void NimBLEServer::start() {
abort(); abort();
} }
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) #if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
ble_gatts_show_local(); ble_gatts_show_local();
#endif #endif
/*** Future use *** /*** Future use ***
@ -243,6 +253,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
} // disconnect } // disconnect
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/** /**
* @brief Set the server to automatically start advertising when a client disconnects. * @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] aod true == advertise, false == don't advertise. * @param [in] aod true == advertise, false == don't advertise.
@ -250,7 +261,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
void NimBLEServer::advertiseOnDisconnect(bool aod) { void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod; m_advertiseOnDisconnect = aod;
} // advertiseOnDisconnect } // advertiseOnDisconnect
#endif
/** /**
* @brief Return the number of connected clients. * @brief Return the number of connected clients.
@ -326,8 +337,9 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
* @param [in] param * @param [in] param
* *
*/ */
/*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { /*STATIC*/
NimBLEServer* server = (NimBLEServer*)arg; int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEServer* server = NimBLEDevice::getServer();
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
NimBLEUtils::gapEventToString(event->type)); NimBLEUtils::gapEventToString(event->type));
int rc = 0; int rc = 0;
@ -339,7 +351,9 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising */ /* Connection failed; resume advertising */
NIMBLE_LOGE(LOG_TAG, "Connection failed"); NIMBLE_LOGE(LOG_TAG, "Connection failed");
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
#endif
} }
else { else {
server->m_connectedPeersVec.push_back(event->connect.conn_handle); server->m_connectedPeersVec.push_back(event->connect.conn_handle);
@ -384,9 +398,11 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
server->m_pServerCallbacks->onDisconnect(server); server->m_pServerCallbacks->onDisconnect(server);
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn); server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
#if !CONFIG_BT_NIMBLE_EXT_ADV
if(server->m_advertiseOnDisconnect) { if(server->m_advertiseOnDisconnect) {
server->startAdvertising(); server->startAdvertising();
} }
#endif
return 0; return 0;
} // BLE_GAP_EVENT_DISCONNECT } // BLE_GAP_EVENT_DISCONNECT
@ -474,11 +490,15 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
return 0; return 0;
} // BLE_GAP_EVENT_NOTIFY_TX } // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "Advertising Complete"); case BLE_GAP_EVENT_ADV_COMPLETE:
NimBLEDevice::getAdvertising()->advCompleteCB(); #if CONFIG_BT_NIMBLE_EXT_ADV
return 0; case BLE_GAP_EVENT_SCAN_REQ_RCVD:
} return NimBLEExtAdvertising::handleGapEvent(event, arg);
#else
return NimBLEAdvertising::handleGapEvent(event, arg);
#endif
// BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
case BLE_GAP_EVENT_CONN_UPDATE: { case BLE_GAP_EVENT_CONN_UPDATE: {
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
@ -537,7 +557,7 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp); NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future // Compatibility only - Do not use, should be removed the in future
if(NimBLEDevice::m_securityCallbacks != nullptr) { if(NimBLEDevice::m_securityCallbacks != nullptr) {
@ -655,7 +675,9 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
serviceChanged(); serviceChanged();
#if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID()); NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
#endif
} }
@ -718,23 +740,53 @@ void NimBLEServer::resetGATT() {
} }
#if CONFIG_BT_NIMBLE_EXT_ADV
/** /**
* @brief Start advertising. * @brief Start advertising.
* * @param [in] inst_id The extended advertisement instance ID to start.
* Start the server advertising its existence. This is a convenience function and is equivalent to * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it. * retrieving the advertising object and invoking start upon it.
*/ */
void NimBLEServer::startAdvertising() { bool NimBLEServer::startAdvertising(uint8_t inst_id,
NimBLEDevice::startAdvertising(); int duration,
int max_events) {
return getAdvertising()->start(inst_id, duration, max_events);
} // startAdvertising } // startAdvertising
/**
* @brief Convenience function to stop advertising a data set.
* @param [in] inst_id The extended advertisement instance ID to stop advertising.
* @return True if advertising stopped successfully.
*/
bool NimBLEServer::stopAdvertising(uint8_t inst_id) {
return getAdvertising()->stop(inst_id);
} // stopAdvertising
#endif
#if !CONFIG_BT_NIMBLE_EXT_ADV|| defined(_DOXYGEN_)
/**
* @brief Start advertising.
* @return True if advertising started successfully.
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
bool NimBLEServer::startAdvertising() {
return getAdvertising()->start();
} // startAdvertising
#endif
/** /**
* @brief Stop advertising. * @brief Stop advertising.
* @return True if advertising stopped successfully.
*/ */
void NimBLEServer::stopAdvertising() { bool NimBLEServer::stopAdvertising() {
NimBLEDevice::stopAdvertising(); return getAdvertising()->stop();
} // startAdvertising } // stopAdvertising
/** /**
@ -772,7 +824,31 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
if(rc != 0) { if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
}// updateConnParams } // updateConnParams
/**
* @brief Request an update of the data packet length.
* * Can only be used after a connection has been established.
* @details Sends a data length update request to the peer.
* The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes.
* The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE.
* @param [in] conn_handle The connection handle of the peer to send the request to.
* @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB).
*/
void NimBLEServer::setDataLen(uint16_t conn_handle, uint16_t tx_octets) {
#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \
(ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432
return;
#else
uint16_t tx_time = (tx_octets + 14) * 8;
int rc = ble_gap_set_data_len(conn_handle, tx_octets, tx_time);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
#endif
} // setDataLen
bool NimBLEServer::setIndicateWait(uint16_t conn_handle) { bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
@ -842,6 +918,4 @@ bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
return true; return true;
} }
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View file

@ -14,18 +14,22 @@
#ifndef MAIN_NIMBLESERVER_H_ #ifndef MAIN_NIMBLESERVER_H_
#define MAIN_NIMBLESERVER_H_ #define MAIN_NIMBLESERVER_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define NIMBLE_ATT_REMOVE_HIDE 1 #define NIMBLE_ATT_REMOVE_HIDE 1
#define NIMBLE_ATT_REMOVE_DELETE 2 #define NIMBLE_ATT_REMOVE_DELETE 2
#define onMtuChanged onMTUChange
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#if CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h"
#else
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#endif
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLESecurity.h" #include "NimBLESecurity.h"
#include "NimBLEConnInfo.h" #include "NimBLEConnInfo.h"
@ -43,15 +47,22 @@ class NimBLEServer {
public: public:
size_t getConnectedCount(); size_t getConnectedCount();
NimBLEService* createService(const char* uuid); NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15, NimBLEService* createService(const NimBLEUUID &uuid);
uint8_t inst_id=0);
void removeService(NimBLEService* service, bool deleteSvc = false); void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service); void addService(NimBLEService* service);
NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks, void setCallbacks(NimBLEServerCallbacks* pCallbacks,
bool deleteCallbacks = true); bool deleteCallbacks = true);
void startAdvertising(); #if CONFIG_BT_NIMBLE_EXT_ADV
void stopAdvertising(); NimBLEExtAdvertising* getAdvertising();
bool startAdvertising(uint8_t inst_id,
int duration = 0,
int max_events = 0);
bool stopAdvertising(uint8_t inst_id);
#else
NimBLEAdvertising* getAdvertising();
bool startAdvertising();
#endif
bool stopAdvertising();
void start(); void start();
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0); NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0); NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
@ -61,12 +72,15 @@ public:
void updateConnParams(uint16_t conn_handle, void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval, uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout); uint16_t latency, uint16_t timeout);
void setDataLen(uint16_t conn_handle, uint16_t tx_octets);
uint16_t getPeerMTU(uint16_t conn_id); uint16_t getPeerMTU(uint16_t conn_id);
std::vector<uint16_t> getPeerDevices(); std::vector<uint16_t> getPeerDevices();
NimBLEConnInfo getPeerInfo(size_t index); NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address); NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id); NimBLEConnInfo getPeerIDInfo(uint16_t id);
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool); void advertiseOnDisconnect(bool);
#endif
private: private:
NimBLEServer(); NimBLEServer();
@ -75,9 +89,15 @@ private:
friend class NimBLEService; friend class NimBLEService;
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
#if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisementData;
#endif
bool m_gattsStarted; bool m_gattsStarted;
#if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect; bool m_advertiseOnDisconnect;
#endif
bool m_svcChanged; bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks; NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks; bool m_deleteCallbacks;
@ -168,7 +188,5 @@ public:
virtual bool onConfirmPIN(uint32_t pin); virtual bool onConfirmPIN(uint32_t pin);
}; // NimBLEServerCallbacks }; // NimBLEServerCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLESERVER_H_ */ #endif /* MAIN_NIMBLESERVER_H_ */

View file

@ -14,12 +14,10 @@
// 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.
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDevice.h"
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
@ -34,25 +32,19 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
/** /**
* @brief Construct an instance of the NimBLEService * @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] pServer A pointer to the server instance that this service belongs to.
*/ */
NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer) NimBLEService::NimBLEService(const char* uuid)
: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { : NimBLEService(NimBLEUUID(uuid)) {
} }
/** /**
* @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.
* @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] pServer A pointer to the server instance that this service belongs to.
*/ */
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) { NimBLEService::NimBLEService(const NimBLEUUID &uuid) {
m_uuid = uuid; m_uuid = uuid;
m_handle = NULL_HANDLE; m_handle = NULL_HANDLE;
m_pServer = pServer;
m_numHandles = numHandles;
m_pSvcDef = nullptr; m_pSvcDef = nullptr;
m_removed = 0; m_removed = 0;
@ -121,6 +113,12 @@ bool NimBLEService::start() {
// Rebuild the service definition if the server attributes have changed. // Rebuild the service definition if the server attributes have changed.
if(getServer()->m_svcChanged && m_pSvcDef != nullptr) { if(getServer()->m_svcChanged && m_pSvcDef != nullptr) {
if(m_pSvcDef[0].characteristics) {
if(m_pSvcDef[0].characteristics[0].descriptors) {
delete(m_pSvcDef[0].characteristics[0].descriptors);
}
delete(m_pSvcDef[0].characteristics);
}
delete(m_pSvcDef); delete(m_pSvcDef);
m_pSvcDef = nullptr; m_pSvcDef = nullptr;
} }
@ -258,10 +256,11 @@ uint16_t NimBLEService::getHandle() {
* @brief Create a new BLE Characteristic associated with this service. * @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic. * @param [in] uuid - The UUID of the characteristic.
* @param [in] properties - The properties of the characteristic. * @param [in] properties - The properties of the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic. * @return The new BLE characteristic.
*/ */
NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties) { NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) {
return createCharacteristic(NimBLEUUID(uuid), properties); return createCharacteristic(NimBLEUUID(uuid), properties, max_len);
} }
@ -269,10 +268,11 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
* @brief Create a new BLE Characteristic associated with this service. * @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic. * @param [in] uuid - The UUID of the characteristic.
* @param [in] properties - The properties of the characteristic. * @param [in] properties - The properties of the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold.
* @return The new BLE characteristic. * @return The new BLE characteristic.
*/ */
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) { NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this);
if (getCharacteristic(uuid) != nullptr) { if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s", NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
@ -429,8 +429,7 @@ std::string NimBLEService::toString() {
* @return The BLEServer associated with this service. * @return The BLEServer associated with this service.
*/ */
NimBLEServer* NimBLEService::getServer() { NimBLEServer* NimBLEService::getServer() {
return m_pServer; return NimBLEDevice::getServer();
}// getServer }// getServer
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // CONFIG_BT_ENABLED

View file

@ -14,11 +14,9 @@
#ifndef MAIN_NIMBLESERVICE_H_ #ifndef MAIN_NIMBLESERVICE_H_
#define MAIN_NIMBLESERVICE_H_ #define MAIN_NIMBLESERVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h" #include "NimBLEServer.h"
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
@ -36,8 +34,8 @@ class NimBLECharacteristic;
class NimBLEService { class NimBLEService {
public: public:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const char* uuid);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const NimBLEUUID &uuid);
~NimBLEService(); ~NimBLEService();
NimBLEServer* getServer(); NimBLEServer* getServer();
@ -52,12 +50,14 @@ public:
NimBLECharacteristic* createCharacteristic(const char* uuid, NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties = uint32_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid, NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties = uint32_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
void addCharacteristic(NimBLECharacteristic* pCharacteristic); void addCharacteristic(NimBLECharacteristic* pCharacteristic);
void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false); void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false);
@ -76,16 +76,12 @@ private:
friend class NimBLEDevice; friend class NimBLEDevice;
uint16_t m_handle; uint16_t m_handle;
NimBLEServer* m_pServer;
NimBLEUUID m_uuid; NimBLEUUID m_uuid;
uint16_t m_numHandles;
ble_gatt_svc_def* m_pSvcDef; ble_gatt_svc_def* m_pSvcDef;
uint8_t m_removed; uint8_t m_removed;
std::vector<NimBLECharacteristic*> m_chrVec; std::vector<NimBLECharacteristic*> m_chrVec;
}; // NimBLEService }; // NimBLEService
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_NIMBLESERVICE_H_ */ #endif /* MAIN_NIMBLESERVICE_H_ */

View file

@ -11,7 +11,8 @@
* Created on: Jun 21, 2017 * Created on: Jun 21, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
@ -234,8 +235,8 @@ const ble_uuid_any_t* NimBLEUUID::getNative() const {
/** /**
* @brief Convert a UUID to its 128 bit representation. * @brief Convert a UUID to its 128 bit representation.
* @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit.
* will convert 16 or 32 bit representations to the full 128bit. * This method will convert 16 or 32bit representations to the full 128bit.
* @return The NimBLEUUID converted to 128bit. * @return The NimBLEUUID converted to 128bit.
*/ */
const NimBLEUUID &NimBLEUUID::to128() { const NimBLEUUID &NimBLEUUID::to128() {
@ -256,6 +257,29 @@ const NimBLEUUID &NimBLEUUID::to128() {
} // to128 } // to128
/**
* @brief Convert 128 bit UUID to its 16 bit representation.
* @details A UUID can be internally represented as 16bit, 32bit or the full 128bit.
* This method will convert a 128bit uuid to 16bit if it contains the ble base uuid.
* @return The NimBLEUUID converted to 16bit if successful, otherwise the original uuid.
*/
const NimBLEUUID& NimBLEUUID::to16() {
if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_16) {
return *this;
}
if (m_uuid.u.type == BLE_UUID_TYPE_128) {
uint8_t base128[] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00,
0x00, 0x80, 0x00, 0x10, 0x00, 0x00};
if (memcmp(m_uuid.u128.value, base128, sizeof(base128)) == 0 ) {
*this = NimBLEUUID(*(uint16_t*)(m_uuid.u128.value + 12));
}
}
return *this;
}
/** /**
* @brief Get a string representation of the UUID. * @brief Get a string representation of the UUID.
* @details * @details

View file

@ -14,10 +14,16 @@
#ifndef COMPONENTS_NIMBLEUUID_H_ #ifndef COMPONENTS_NIMBLEUUID_H_
#define COMPONENTS_NIMBLEUUID_H_ #define COMPONENTS_NIMBLEUUID_H_
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_uuid.h" #include "host/ble_uuid.h"
#else
#include "nimble/nimble/host/include/host/ble_uuid.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max
@ -42,6 +48,7 @@ public:
bool equals(const NimBLEUUID &uuid) const; bool equals(const NimBLEUUID &uuid) const;
const ble_uuid_any_t* getNative() const; const ble_uuid_any_t* getNative() const;
const NimBLEUUID & to128(); const NimBLEUUID & to128();
const NimBLEUUID& to16();
std::string toString() const; std::string toString() const;
static NimBLEUUID fromString(const std::string &uuid); static NimBLEUUID fromString(const std::string &uuid);

View file

@ -6,12 +6,13 @@
* *
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h" #include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include "nimconfig.h"
#include <stdlib.h>
static const char* LOG_TAG = "NimBLEUtils"; static const char* LOG_TAG = "NimBLEUtils";
@ -358,6 +359,7 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
return "Unknown"; return "Unknown";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) #else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
(void)rc;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) #endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
} }
@ -385,6 +387,7 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
return "Unknown flag"; return "Unknown flag";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT) #else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT)
(void)advType;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT) #endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT)
} // adFlagsToString } // adFlagsToString
@ -432,8 +435,11 @@ char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t
* @param [in] arg Unused. * @param [in] arg Unused.
*/ */
void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){ void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
(void)arg;
#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type)); NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type));
#else
(void)event;
#endif #endif
} }
@ -520,6 +526,7 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
return "Unknown event type"; return "Unknown event type";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) #else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
(void)eventType;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) #endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString } // gapEventToString

View file

@ -8,10 +8,15 @@
#ifndef COMPONENTS_NIMBLEUTILS_H_ #ifndef COMPONENTS_NIMBLEUTILS_H_
#define COMPONENTS_NIMBLEUTILS_H_ #define COMPONENTS_NIMBLEUTILS_H_
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
@ -24,7 +29,7 @@ typedef struct {
void *pATT; void *pATT;
TaskHandle_t task; TaskHandle_t task;
int rc; int rc;
std::string *buf; void *buf;
} ble_task_data_t; } ble_task_data_t;

View file

@ -10,21 +10,68 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#include "nimconfig_rename.h" #include "nimconfig_rename.h"
#if defined(CONFIG_BT_ENABLED)
// Allows cpp wrapper to select the correct include paths when using esp-idf
#define CONFIG_NIMBLE_CPP_IDF
/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/* Cannot use server without advertise */
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
/* Enables the use of Arduino String class for attribute values */
#if defined __has_include
# if __has_include (<Arduino.h>)
# define NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# endif
#endif
#endif /* CONFIG_BT_ENABLED */
#ifdef _DOXYGEN_ #ifdef _DOXYGEN_
/** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */ /** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
/** @brief Un-comment to enable storing the timestamp when an attribute value is updated\n
* This allows for checking the last update time using getTimeStamp() or getValue(time_t*)\n
* If disabled, the timestamp returned from these functions will be 0.\n
* Disabling timestamps will reduce the memory used for each value.\n
* 1 = Enabled, 0 = Disabled; Default = Disabled
*/
#define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
/** @brief Uncomment to set the default allocation size (bytes) for each attribute if\n
* not specified when the constructor is called. This is also the size used when a remote\n
* characteristic or descriptor is constructed before a value is read/notifed.\n
* Increasing this will reduce reallocations but increase memory footprint.\n
* Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN)
*/
#define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
/** @brief Un-comment to change the default MTU size */ /** @brief Un-comment to change the default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255 #define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
/** @brief Un-comment to change default device name */ /** @brief Un-comment to change default device name */
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble" #define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
/** @brief Un-comment to see debug log messages from the NimBLE host /** @brief Un-comment to set the debug log messages level from the NimBLE host stack.\n
* Values: 0 = DEBUG, 1 = INFO, 2 = WARNING, 3 = ERROR, 4 = CRITICAL, 5+ = NONE\n
* Uses approx. 32kB of flash memory. * Uses approx. 32kB of flash memory.
*/ */
#define CONFIG_BT_NIMBLE_DEBUG #define CONFIG_BT_NIMBLE_LOG_LEVEL 5
/** @brief Un-comment to set the debug log messages level from the NimBLE CPP Wrapper.\n
* Values: 0 = NONE, 1 = ERROR, 2 = WARNING, 3 = INFO, 4+ = DEBUG\n
* Uses approx. 32kB of flash memory.
*/
#define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
/** @brief Un-comment to see NimBLE host return codes as text debug log messages. /** @brief Un-comment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory. * Uses approx. 7kB of flash memory.