From d821b77d3ba6bc84ab8b0ac28d1977dd7b85e9fb Mon Sep 17 00:00:00 2001 From: randogoth Date: Sun, 25 Oct 2026 17:48:20 +0200 Subject: [PATCH] feat: add Bluetooth LE mode, radio power budget and NINA firmware update path --- README.md | 20 +- arduino/libraries/ArduinoBLE/CHANGELOG | 26 + arduino/libraries/ArduinoBLE/LICENSE | 504 +++++ arduino/libraries/ArduinoBLE/README.md | 39 + .../libraries/ArduinoBLE/library.properties | 11 + arduino/libraries/ArduinoBLE/src/ArduinoBLE.h | 29 + .../ArduinoBLE/src/BLEAdvertisingData.cpp | 352 +++ .../ArduinoBLE/src/BLEAdvertisingData.h | 120 + .../ArduinoBLE/src/BLECharacteristic.cpp | 493 +++++ .../ArduinoBLE/src/BLECharacteristic.h | 132 ++ .../ArduinoBLE/src/BLEDescriptor.cpp | 229 ++ .../libraries/ArduinoBLE/src/BLEDescriptor.h | 73 + .../libraries/ArduinoBLE/src/BLEDevice.cpp | 578 +++++ arduino/libraries/ArduinoBLE/src/BLEDevice.h | 121 + .../libraries/ArduinoBLE/src/BLEProperty.h | 79 + .../libraries/ArduinoBLE/src/BLEService.cpp | 186 ++ arduino/libraries/ArduinoBLE/src/BLEService.h | 68 + .../src/BLEStringCharacteristic.cpp | 45 + .../ArduinoBLE/src/BLEStringCharacteristic.h | 39 + .../ArduinoBLE/src/BLETypedCharacteristic.h | 102 + .../src/BLETypedCharacteristics.cpp | 92 + .../ArduinoBLE/src/BLETypedCharacteristics.h | 95 + .../src/local/BLELocalAttribute.cpp | 69 + .../ArduinoBLE/src/local/BLELocalAttribute.h | 61 + .../src/local/BLELocalCharacteristic.cpp | 244 ++ .../src/local/BLELocalCharacteristic.h | 96 + .../src/local/BLELocalDescriptor.cpp | 69 + .../ArduinoBLE/src/local/BLELocalDescriptor.h | 52 + .../ArduinoBLE/src/local/BLELocalDevice.cpp | 472 ++++ .../ArduinoBLE/src/local/BLELocalDevice.h | 123 ++ .../ArduinoBLE/src/local/BLELocalService.cpp | 94 + .../ArduinoBLE/src/local/BLELocalService.h | 61 + .../src/remote/BLERemoteAttribute.cpp | 51 + .../src/remote/BLERemoteAttribute.h | 41 + .../src/remote/BLERemoteCharacteristic.cpp | 264 +++ .../src/remote/BLERemoteCharacteristic.h | 84 + .../src/remote/BLERemoteDescriptor.cpp | 134 ++ .../src/remote/BLERemoteDescriptor.h | 50 + .../ArduinoBLE/src/remote/BLERemoteDevice.cpp | 59 + .../ArduinoBLE/src/remote/BLERemoteDevice.h | 43 + .../src/remote/BLERemoteService.cpp | 67 + .../ArduinoBLE/src/remote/BLERemoteService.h | 53 + .../libraries/ArduinoBLE/src/utility/ATT.cpp | 1967 +++++++++++++++++ .../libraries/ArduinoBLE/src/utility/ATT.h | 177 ++ .../ArduinoBLE/src/utility/BLELinkedList.h | 147 ++ .../ArduinoBLE/src/utility/BLEUuid.cpp | 93 + .../ArduinoBLE/src/utility/BLEUuid.h | 44 + .../src/utility/CordioHCICustomDriver.h | 17 + .../libraries/ArduinoBLE/src/utility/GAP.cpp | 272 +++ .../libraries/ArduinoBLE/src/utility/GAP.h | 74 + .../libraries/ArduinoBLE/src/utility/GATT.cpp | 210 ++ .../libraries/ArduinoBLE/src/utility/GATT.h | 73 + .../libraries/ArduinoBLE/src/utility/HCI.cpp | 1559 +++++++++++++ .../libraries/ArduinoBLE/src/utility/HCI.h | 168 ++ .../src/utility/HCICordioTransport.cpp | 337 +++ .../src/utility/HCICordioTransport.h | 57 + .../src/utility/HCINinaSpiTransport.cpp | 209 ++ .../src/utility/HCINinaSpiTransport.h | 43 + .../src/utility/HCISilabsTransport.cpp | 130 ++ .../src/utility/HCISilabsTransport.h | 42 + .../ArduinoBLE/src/utility/HCITransport.h | 47 + .../src/utility/HCIUartTransport.cpp | 93 + .../ArduinoBLE/src/utility/HCIUartTransport.h | 46 + .../src/utility/HCIVirtualTransport.cpp | 144 ++ .../src/utility/HCIVirtualTransport.h | 50 + .../src/utility/HCIVirtualTransportAT.cpp | 112 + .../src/utility/HCIVirtualTransportAT.h | 42 + .../src/utility/HCIVirtualTransportRPC.cpp | 118 + .../src/utility/HCIVirtualTransportRPC.h | 52 + .../src/utility/HCIVirtualTransportZephyr.cpp | 231 ++ .../src/utility/HCIVirtualTransportZephyr.h | 44 + .../ArduinoBLE/src/utility/L2CAPSignaling.cpp | 510 +++++ .../ArduinoBLE/src/utility/L2CAPSignaling.h | 96 + .../src/utility/bitDescriptions.cpp | 30 + .../ArduinoBLE/src/utility/bitDescriptions.h | 41 + .../libraries/ArduinoBLE/src/utility/btct.cpp | 387 ++++ .../libraries/ArduinoBLE/src/utility/btct.h | 30 + .../src/utility/keyDistribution.cpp | 24 + .../ArduinoBLE/src/utility/keyDistribution.h | 29 + arduino/libraries/Arduino_SpiNINA/LICENSE | 373 ++++ arduino/libraries/Arduino_SpiNINA/README.md | 6 + .../Arduino_SpiNINA/library.properties | 10 + .../Arduino_SpiNINA/src/Arduino_SpiNINA.h | 13 + .../libraries/Arduino_SpiNINA/src/spi_drv.cpp | 457 ++++ .../libraries/Arduino_SpiNINA/src/spi_drv.h | 67 + .../libraries/Arduino_SpiNINA/src/spi_types.h | 88 + .../Arduino_SpiNINA/src/utility/debug.h | 104 + arduino/libraries/STRIX-NINA-LIBRARIES.md | 9 + arduino/libraries/WiFiNINA/CHANGELOG | 2 +- arduino/libraries/WiFiNINA/LICENSE.txt | 504 +++++ arduino/libraries/WiFiNINA/README.adoc | 9 +- arduino/libraries/WiFiNINA/STRIX.md | 3 - arduino/libraries/WiFiNINA/keywords.txt | 65 - arduino/libraries/WiFiNINA/library.properties | 7 +- arduino/libraries/WiFiNINA/src/WiFi.cpp | 329 +-- arduino/libraries/WiFiNINA/src/WiFi.h | 57 +- arduino/libraries/WiFiNINA/src/WiFiClient.cpp | 59 +- arduino/libraries/WiFiNINA/src/WiFiClient.h | 10 +- .../WiFiNINA/src/WiFiPreferences.cpp | 308 +++ .../libraries/WiFiNINA/src/WiFiPreferences.h | 74 + .../libraries/WiFiNINA/src/WiFiSSLClient.cpp | 10 + .../libraries/WiFiNINA/src/WiFiSSLClient.h | 2 + arduino/libraries/WiFiNINA/src/WiFiServer.cpp | 41 +- arduino/libraries/WiFiNINA/src/WiFiServer.h | 7 +- arduino/libraries/WiFiNINA/src/WiFiStorage.h | 2 +- arduino/libraries/WiFiNINA/src/WiFiUdp.cpp | 4 +- arduino/libraries/WiFiNINA/src/WiFiUdp.h | 2 +- .../libraries/WiFiNINA/src/utility/debug.h | 95 - .../src/utility/nano_rp2040_support.cpp | 85 + .../WiFiNINA/src/utility/server_drv.cpp | 113 +- .../WiFiNINA/src/utility/server_drv.h | 14 +- .../WiFiNINA/src/utility/spi_drv.cpp | 584 ----- .../libraries/WiFiNINA/src/utility/spi_drv.h | 109 - .../WiFiNINA/src/utility/wifi_drv.cpp | 710 +++++- .../libraries/WiFiNINA/src/utility/wifi_drv.h | 65 +- .../libraries/WiFiNINA/src/utility/wifi_spi.h | 263 +-- .../WiFiNINA/src/utility/wl_definitions.h | 4 +- .../libraries/WiFiNINA/src/utility/wl_types.h | 2 +- arduino/strix/API.md | 23 +- arduino/strix/strix.ino | 193 +- .../strix_nina_passthrough.ino | 57 + tools/ble_check.py | 75 + 122 files changed, 17008 insertions(+), 1371 deletions(-) create mode 100644 arduino/libraries/ArduinoBLE/CHANGELOG create mode 100644 arduino/libraries/ArduinoBLE/LICENSE create mode 100644 arduino/libraries/ArduinoBLE/README.md create mode 100644 arduino/libraries/ArduinoBLE/library.properties create mode 100644 arduino/libraries/ArduinoBLE/src/ArduinoBLE.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLECharacteristic.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLECharacteristic.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEDescriptor.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLEDescriptor.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEDevice.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLEDevice.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEProperty.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEService.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLEService.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLETypedCharacteristic.h create mode 100644 arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.h create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.h create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.h create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.h create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalDevice.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalDevice.h create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalService.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/local/BLELocalService.h create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.h create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.h create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.h create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.h create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/ATT.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/ATT.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/BLELinkedList.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/BLEUuid.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/BLEUuid.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/CordioHCICustomDriver.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/GAP.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/GAP.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/GATT.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/GATT.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCI.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCI.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCITransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/btct.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/btct.h create mode 100644 arduino/libraries/ArduinoBLE/src/utility/keyDistribution.cpp create mode 100644 arduino/libraries/ArduinoBLE/src/utility/keyDistribution.h create mode 100644 arduino/libraries/Arduino_SpiNINA/LICENSE create mode 100644 arduino/libraries/Arduino_SpiNINA/README.md create mode 100644 arduino/libraries/Arduino_SpiNINA/library.properties create mode 100644 arduino/libraries/Arduino_SpiNINA/src/Arduino_SpiNINA.h create mode 100644 arduino/libraries/Arduino_SpiNINA/src/spi_drv.cpp create mode 100644 arduino/libraries/Arduino_SpiNINA/src/spi_drv.h create mode 100644 arduino/libraries/Arduino_SpiNINA/src/spi_types.h create mode 100644 arduino/libraries/Arduino_SpiNINA/src/utility/debug.h create mode 100644 arduino/libraries/STRIX-NINA-LIBRARIES.md create mode 100644 arduino/libraries/WiFiNINA/LICENSE.txt delete mode 100644 arduino/libraries/WiFiNINA/STRIX.md delete mode 100644 arduino/libraries/WiFiNINA/keywords.txt create mode 100644 arduino/libraries/WiFiNINA/src/WiFiPreferences.cpp create mode 100644 arduino/libraries/WiFiNINA/src/WiFiPreferences.h delete mode 100644 arduino/libraries/WiFiNINA/src/utility/debug.h create mode 100644 arduino/libraries/WiFiNINA/src/utility/nano_rp2040_support.cpp delete mode 100644 arduino/libraries/WiFiNINA/src/utility/spi_drv.cpp delete mode 100644 arduino/libraries/WiFiNINA/src/utility/spi_drv.h create mode 100644 arduino/strix_nina_passthrough/strix_nina_passthrough.ino create mode 100644 tools/ble_check.py diff --git a/README.md b/README.md index e84e635..dc94d62 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A portable wireless True Random Number Generator built on an Arduino MKR Vidor 4 ## Design -Entropy comes from FPGA ring oscillators whose sampled timing jitter is XOR-combined into output bits, with no pseudo-random post-processing. A SAMD21 serves the results over USB, and over HTTP on its own WiFi access point when running without a computer (stationary mode on a charger). +Entropy comes from FPGA ring oscillators whose sampled timing jitter is XOR-combined into output bits, with no pseudo-random post-processing. A SAMD21 serves the results over USB, over HTTP on its own WiFi access point (stationary mode, on a charger), or over Bluetooth LE (mobile mode, on battery). - 4 independent generators × 90 ring oscillators. Each generator XORs its rings every 120 MHz clock (spatial) and then several consecutive bits (temporal, default 5): about 24 Mbps per generator. - Separate stream per generator, plus a combined stream of all healthy generators @@ -15,19 +15,20 @@ Entropy comes from FPGA ring oscillators whose sampled timing jitter is XOR-comb - Von Neumann extractor that can be toggled on the stream, statistics and amplifier paths - Raw ring captures and frequency measurement for characterization -The USB and HTTP API is documented in [arduino/strix/API.md](arduino/strix/API.md). +The USB, HTTP and BLE API is documented in [arduino/strix/API.md](arduino/strix/API.md). ## Layout - `fpga/rtl/`: Verilog sources; `fpga/sim/`: testbenches; `fpga/src/`: Quartus project - `arduino/strix/`: device firmware - `arduino/strix_capture/`: characterization tool firmware, used by `tools/jitter.py` -- `arduino/libraries/`: `StrixFPGA` (FPGA boot, SPI access, embedded FPGA image), `VidorBoot` (vendored subset of Arduino's VidorPeripherals) and `WiFiNINA` (Arduino's library, patched to drive the WiFi module through the FPGA's pin pass-through) +- `arduino/strix_nina_passthrough/`: connects USB to the WiFi module's UART for flashing its firmware +- `arduino/libraries/`: `StrixFPGA` (FPGA boot, SPI access, embedded FPGA image), `VidorBoot` (vendored subset of Arduino's VidorPeripherals), and Arduino's `WiFiNINA`, `Arduino_SpiNINA` and `ArduinoBLE`, patched to drive the WiFi module through the FPGA's pin pass-through (see `arduino/libraries/STRIX-NINA-LIBRARIES.md`) - `tools/`: FPGA image converter, jitter and statistics experiments, API check ## Development (Linux) -The toolchain is managed by [devbox](https://www.jetify.com/devbox): `arduino-cli`, Intel Quartus Prime Lite (Cyclone 10 LP only, through a Nix flake in `nix/`), Icarus Verilog, Python and Rust. +The toolchain is managed by [devbox](https://www.jetify.com/devbox): `arduino-cli`, Intel Quartus Prime Lite (Cyclone 10 LP only, through a Nix flake in `nix/`), Icarus Verilog, Rust, and [uv](https://docs.astral.sh/uv/) for the Python tools (`pyproject.toml`, `uv.lock`). 1. `devbox run setup`: install the Arduino SAMD core (libraries are vendored in `arduino/libraries/`) 2. `devbox run sim`: run the FPGA testbenches @@ -38,9 +39,18 @@ The toolchain is managed by [devbox](https://www.jetify.com/devbox): `arduino-cl Serial access requires membership in the `dialout` group. If a firmware leaves the board unable to stay on USB, enter the bootloader: hold reset while plugging in, release, then tap reset once quickly. For characterization, flash the capture tool with `devbox run upload-capture` and use `devbox run jitter `. `devbox run onod-build` builds the onod3000 test suite used by `jitter output` and `api-check`. +### WiFi module firmware + +BLE needs NINA firmware 3.0.0 or newer (the board shipped with 1.0.0). To flash Arduino's [nina-fw](https://github.com/arduino/nina-fw/releases) `NINA.bin`: + +1. Flash the passthrough: `devbox run -- arduino-cli compile -u -p /dev/ttyACM0 --fqbn arduino:samd:mkrvidor4000 --libraries arduino/libraries arduino/strix_nina_passthrough` +2. `devbox run -- uv run esptool --chip esp32 --port /dev/ttyACM0 --baud 115200 write-flash 0 NINA.bin` +3. Flash the STRIX firmware again (`devbox run upload`); `wifi probe` reports the module firmware version. + +The passthrough forwards reliably at 115200 baud, not at 460800. + ## TODO -- Bluetooth LE for mobile mode (needs a newer WiFi module firmware than the installed 1.0.0) - WiFi credentials stored on the device instead of compile-time defaults - Battery pack - 3D case diff --git a/arduino/libraries/ArduinoBLE/CHANGELOG b/arduino/libraries/ArduinoBLE/CHANGELOG new file mode 100644 index 0000000..d8a8471 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/CHANGELOG @@ -0,0 +1,26 @@ +ArduinoBLE ?.?.? - ????.??.?? + +ArduinoBLE 1.1.2 - 2019.11.12 + +* cordio: switch to lower power when polling with timeout +* Fixed issue with wrong types for disconnection event handling. Thanks @cparata + +ArduinoBLE 1.1.1 - 2019.09.05 + +* [Cordio] Fixed crashing when BLE.begin() is not called +* Fixed ACL RX packet fragmentation, affected devices that supported large MTUs +* Fixed BLECharacteristic::readValue(...) triggering a read request after a notification/indication was received +* Added support for case insensitive comparison of UUID or address string inputs +* Added (optional) company ID argument to BLE.setManufacturerData(...). Thanks @konikoni428 + +ArduinoBLE 1.1.0 - 2019.08.27 + +* Added BLE Central support + +ArduinoBLE 1.0.0 - 2019.07.31 + +* Added support for Arduino Nano 33 BLE boards + +ArduinoBLE 0.1.2 and older + +* Changes not recorded diff --git a/arduino/libraries/ArduinoBLE/LICENSE b/arduino/libraries/ArduinoBLE/LICENSE new file mode 100644 index 0000000..19e3071 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/arduino/libraries/ArduinoBLE/README.md b/arduino/libraries/ArduinoBLE/README.md new file mode 100644 index 0000000..08d6b3e --- /dev/null +++ b/arduino/libraries/ArduinoBLE/README.md @@ -0,0 +1,39 @@ +# ArduinoBLE + +[![Compile Examples Status](https://github.com/arduino-libraries/ArduinoBLE/workflows/Compile%20Examples/badge.svg)](https://github.com/arduino-libraries/ArduinoBLE/actions?workflow=Compile+Examples) [![Spell Check Status](https://github.com/arduino-libraries/ArduinoBLE/workflows/Spell%20Check/badge.svg)](https://github.com/arduino-libraries/ArduinoBLE/actions?workflow=Spell+Check) + +Enables Bluetooth® Low Energy connectivity on the Arduino MKR WiFi 1010, Arduino UNO WiFi Rev2, Arduino Nano 33 IoT, Arduino Nano 33 BLE, Arduino Portenta H7, Arduino Giga R1 and Arduino UNO R4 WiFi. + +This library supports creating a Bluetooth® Low Energy peripheral & central mode. + +For more information about this library please visit us at: +https://www.arduino.cc/en/Reference/ArduinoBLE + +## Firmware compatibility + +| Board | Library Version | Required Firmware | +| :--- | :--- | :--- | +| **MKR WiFi 1010, UNO WiFi Rev2,
Nano 33 IoT, Nano RP2040 Connect** | **v2.0.0 or later** | [NINA-W102](https://github.com/arduino/nina-fw) **v3.0.0+** | +| **MKR WiFi 1010, UNO WiFi Rev2,
Nano 33 IoT, Nano RP2040 Connect** | **Versions prior to v2.0.0** | [NINA-W102](https://github.com/arduino/nina-fw) **v1.2.0 – v2.0.0** | +| **Arduino UNO R4 WiFi** | All versions | [ESP32-S3](https://github.com/arduino/uno-r4-wifi-usb-bridge) **v0.2.0+** | + + +## License + +``` +Copyright (c) 2019 Arduino SA. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +``` diff --git a/arduino/libraries/ArduinoBLE/library.properties b/arduino/libraries/ArduinoBLE/library.properties new file mode 100644 index 0000000..ba37235 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/library.properties @@ -0,0 +1,11 @@ +name=ArduinoBLE +version=2.1.0 +author=Arduino +maintainer=Arduino +sentence=Enables Bluetooth® Low Energy connectivity on the Arduino MKR WiFi 1010, Arduino UNO WiFi Rev2, Arduino Nano 33 IoT, Arduino Nano 33 BLE, Nicla Sense ME and UNO R4 WiFi. +paragraph=This library supports creating a Bluetooth® Low Energy peripheral & central mode. +category=Communication +url=https://www.arduino.cc/en/Reference/ArduinoBLE +architectures=* +includes=ArduinoBLE.h +depends=Arduino_SpiNINA diff --git a/arduino/libraries/ArduinoBLE/src/ArduinoBLE.h b/arduino/libraries/ArduinoBLE/src/ArduinoBLE.h new file mode 100644 index 0000000..588d5cb --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/ArduinoBLE.h @@ -0,0 +1,29 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ARDUINO_BLE_H_ +#define _ARDUINO_BLE_H_ + +#include "local/BLELocalDevice.h" +#include "BLEProperty.h" +#include "BLEStringCharacteristic.h" +#include "BLETypedCharacteristics.h" +#include "utility/btct.h" + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.cpp b/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.cpp new file mode 100644 index 0000000..8010591 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.cpp @@ -0,0 +1,352 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEAdvertisingData.h" + +#define AD_FIELD_OVERHEAD (2) + +BLEAdvertisingData::BLEAdvertisingData() : + _dataLength(0), + _remainingLength(MAX_AD_DATA_LENGTH), + _rawData(NULL), + _rawDataLength(0), + _flags(0), + _hasFlags(false), + _localName(NULL), + _manufacturerData(NULL), + _manufacturerDataLength(0), + _manufacturerCompanyId(0), + _hasManufacturerCompanyId(false), + _advertisedServiceUuid(NULL), + _advertisedServiceUuidLength(0), + _serviceData(NULL), + _serviceDataLength(0) +{ +} + +BLEAdvertisingData::~BLEAdvertisingData() +{ +} + +inline bool BLEAdvertisingData::updateRemainingLength(int oldFieldLength, int newFieldLength) +{ + int updatedRemaining = _remainingLength + (oldFieldLength - newFieldLength); + if (updatedRemaining >= 0) { + _remainingLength = updatedRemaining; + return true; + } + return false; +} + +int BLEAdvertisingData::remainingLength() const +{ + return _remainingLength; +} + +int BLEAdvertisingData::availableForWrite() +{ + int available = (_remainingLength - AD_FIELD_OVERHEAD); + if (available < 0) available = 0; + return available; +} + +void BLEAdvertisingData::clear() +{ + _remainingLength = MAX_AD_DATA_LENGTH; + _rawData = NULL; + _rawDataLength = 0; + _hasFlags = false; + _localName = NULL; + _manufacturerData = NULL; + _manufacturerDataLength = 0; + _hasManufacturerCompanyId = false; + _advertisedServiceUuid = NULL; + _advertisedServiceUuidLength = 0; + _serviceData = NULL; + _serviceDataLength = 0; +} + +void BLEAdvertisingData::copy(const BLEAdvertisingData& adv) +{ + _remainingLength = adv._remainingLength; + _rawData = adv._rawData; + _rawDataLength = adv._rawDataLength; + _flags = adv._flags; + _hasFlags = adv._hasFlags; + _localName = adv._localName; + _manufacturerData = adv._manufacturerData; + _manufacturerDataLength = adv._manufacturerDataLength; + _manufacturerCompanyId = adv._manufacturerCompanyId; + _hasManufacturerCompanyId = adv._hasManufacturerCompanyId; + _advertisedServiceUuid = adv._advertisedServiceUuid; + _advertisedServiceUuidLength = adv._advertisedServiceUuidLength; + _serviceDataUuid = adv._serviceDataUuid; + _serviceData = adv._serviceData; + _serviceDataLength = adv._serviceDataLength; +} + +BLEAdvertisingData& BLEAdvertisingData::operator=(const BLEAdvertisingData &other) +{ + copy(other); + return *this; +} + +bool BLEAdvertisingData::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + BLEUuid uuid(advertisedServiceUuid); + int previousLength = (_advertisedServiceUuidLength > 0) ? (_advertisedServiceUuidLength + AD_FIELD_OVERHEAD) : 0; + bool success = updateRemainingLength(previousLength, (uuid.length() + AD_FIELD_OVERHEAD)); + if (success) { + _advertisedServiceUuid = advertisedServiceUuid; + _advertisedServiceUuidLength = uuid.length(); + } + return success; +} + +bool BLEAdvertisingData::setAdvertisedService(const BLEService& service) +{ + return setAdvertisedServiceUuid(service.uuid()); +} + +bool BLEAdvertisingData::setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength) +{ + int previousLength = 0; + if (_manufacturerDataLength) { + previousLength = _manufacturerDataLength + AD_FIELD_OVERHEAD; + if (_hasManufacturerCompanyId) { + previousLength += sizeof(_manufacturerCompanyId); + } + } + bool success = updateRemainingLength(previousLength, (manufacturerDataLength + AD_FIELD_OVERHEAD)); + if (success) { + _manufacturerData = manufacturerData; + _manufacturerDataLength = manufacturerDataLength; + _hasManufacturerCompanyId = false; + } + return success; +} + +bool BLEAdvertisingData::setManufacturerData(const uint16_t companyId, const uint8_t manufacturerData[], int manufacturerDataLength) +{ + int previousLength = 0; + if (_manufacturerDataLength) { + previousLength = _manufacturerDataLength + AD_FIELD_OVERHEAD; + if (_hasManufacturerCompanyId) { + previousLength += sizeof(_manufacturerCompanyId); + } + } + bool success = updateRemainingLength(previousLength, (manufacturerDataLength + sizeof(companyId) + AD_FIELD_OVERHEAD)); + if (success) { + _manufacturerData = manufacturerData; + _manufacturerDataLength = manufacturerDataLength; + _manufacturerCompanyId = companyId; + _hasManufacturerCompanyId = true; + } + return success; +} + +bool BLEAdvertisingData::setAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length) +{ + int previousLength = (_serviceDataLength > 0) ? (_serviceDataLength + sizeof(uuid) + AD_FIELD_OVERHEAD) : 0; + bool success = updateRemainingLength(previousLength, (length + sizeof(uuid) + AD_FIELD_OVERHEAD)); + if (success) { + _serviceDataUuid = uuid; + _serviceData = data; + _serviceDataLength = length; + } + return success; +} + +bool BLEAdvertisingData::setLocalName(const char *localName) +{ + int previousLength = (_localName && strlen(_localName) > 0) ? (strlen(_localName) + AD_FIELD_OVERHEAD) : 0; + bool success = updateRemainingLength(previousLength, (strlen(localName) + AD_FIELD_OVERHEAD)); + if (success) { + _localName = localName; + } + return success; +} + +bool BLEAdvertisingData::setRawData(const uint8_t* data, int length) +{ + if (length > MAX_AD_DATA_LENGTH) { + return false; + } + _rawData = data; + _rawDataLength = length; + return true; +} + +bool BLEAdvertisingData::setRawData(const BLEAdvertisingRawData& rawData) +{ + if (rawData.length > MAX_AD_DATA_LENGTH) { + return false; + } + _rawData = rawData.data; + _rawDataLength = rawData.length; + return true; +} + +bool BLEAdvertisingData::setFlags(uint8_t flags) +{ + int previousLength = (_hasFlags) ? (sizeof(_flags) + AD_FIELD_OVERHEAD) : 0; + bool success = updateRemainingLength(previousLength, (sizeof(flags) + AD_FIELD_OVERHEAD)); + if (success) { + _hasFlags = true; + _flags = flags; + } + return success; +} + +bool BLEAdvertisingData::updateData() +{ + // Success indicates whether all the fields have been inserted + bool success = true; + // Reset data + _dataLength = 0; + // If rawData is present, then only rawData is inserted in the advertising packet + if (_rawData && _rawDataLength) { + return addRawData(_rawData, _rawDataLength); + } + // Try to add flags into the current advertising packet + if (_hasFlags) { + success &= addFlags(_flags); + } + // Try to add Advertised service uuid into the current advertising packet + if (_advertisedServiceUuid) { + success &= addAdvertisedServiceUuid(_advertisedServiceUuid); + } + // Try to add Manufacturer data into the current advertising packet + if (_manufacturerData && _manufacturerDataLength) { + if (_hasManufacturerCompanyId) { + success &= addManufacturerData(_manufacturerCompanyId, _manufacturerData, _manufacturerDataLength); + } else { + success &= addManufacturerData(_manufacturerData, _manufacturerDataLength); + } + } + // Try to add Service data into the current advertising packet + if (_serviceData && _serviceDataLength) { + success &= addAdvertisedServiceData(_serviceDataUuid, _serviceData, _serviceDataLength); + } + // Try to add Local name into the current advertising packet + if (_localName) { + success &= addLocalName(_localName); + } + return success; +} + +uint8_t* BLEAdvertisingData::data() +{ + return _data; +} + +int BLEAdvertisingData::dataLength() const +{ + return _dataLength; +} + +bool BLEAdvertisingData::hasFlags() const +{ + return _hasFlags; +} + +bool BLEAdvertisingData::addLocalName(const char *localName) +{ + bool success = false; + if (strlen(localName) > (MAX_AD_DATA_LENGTH - AD_FIELD_OVERHEAD)) { + success = addField(BLEFieldShortLocalName, (uint8_t*)localName, (MAX_AD_DATA_LENGTH - AD_FIELD_OVERHEAD)); + } else { + success = addField(BLEFieldCompleteLocalName, localName); + } + return success; +} + +bool BLEAdvertisingData::addAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + BLEUuid uuid(advertisedServiceUuid); + int uuidLen = uuid.length(); + BLEAdField advField; + if (uuidLen > 2) { + advField = BLEFieldIncompleteAdvertisedService128; + } else { + advField = BLEFieldIncompleteAdvertisedService16; + } + return addField(advField, uuid.data(), uuidLen); +} + +bool BLEAdvertisingData::addManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength) +{ + return addField(BLEFieldManufacturerData, manufacturerData, manufacturerDataLength); +} + +bool BLEAdvertisingData::addManufacturerData(const uint16_t companyId, const uint8_t manufacturerData[], int manufacturerDataLength) +{ + int tempDataLength = manufacturerDataLength + sizeof(companyId); + uint8_t tempData[MAX_AD_DATA_LENGTH]; + memcpy(tempData, &companyId, sizeof(companyId)); + memcpy(&tempData[sizeof(companyId)], manufacturerData, manufacturerDataLength); + return addField(BLEFieldManufacturerData, tempData, tempDataLength); +} + +bool BLEAdvertisingData::addAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length) +{ + int tempDataLength = length + sizeof(uuid); + uint8_t tempData[MAX_AD_DATA_LENGTH]; + memcpy(tempData, &uuid, sizeof(uuid)); + memcpy(&tempData[sizeof(uuid)], data, length); + return addField(BLEFieldServiceData, tempData, tempDataLength); +} + +bool BLEAdvertisingData::addRawData(const uint8_t* data, int length) +{ + // Bypass addField to add the integral raw data + if (length > (MAX_AD_DATA_LENGTH - _dataLength)) { + // Not enough space + return false; + } + memcpy(&_data[_dataLength], data, length); + _dataLength += length; + return true; +} + +bool BLEAdvertisingData::addFlags(uint8_t flags) +{ + return addField(BLEFieldFlags, &flags, sizeof(flags)); +} + +bool BLEAdvertisingData::addField(BLEAdField field, const char* data) +{ + int dataLength = strlen(data); + return addField(field, (uint8_t *)data, dataLength); +} + +bool BLEAdvertisingData::addField(BLEAdField field, const uint8_t* data, int length) +{ + int fieldLength = length + AD_FIELD_OVERHEAD; // Considering data TYPE and LENGTH fields + if (fieldLength > (MAX_AD_DATA_LENGTH - _dataLength)) { + // Not enough space for storing this field + return false; + } + // Insert field into advertising data of the instance + _data[_dataLength++] = length + 1; + _data[_dataLength++] = field; + memcpy(&_data[_dataLength], data, length); + _dataLength += length; + return true; +} diff --git a/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.h b/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.h new file mode 100644 index 0000000..dc73605 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEAdvertisingData.h @@ -0,0 +1,120 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_ADVERTISING_DATA_H_ +#define _BLE_ADVERTISING_DATA_H_ + +#include +#include "utility/BLEUuid.h" +#include "BLEService.h" + +#define MAX_AD_DATA_LENGTH (31) + +enum BLEFlags { + BLEFlagsLimitedDiscoverable = 0x01, + BLEFlagsGeneralDiscoverable = 0x02, + BLEFlagsBREDRNotSupported = 0x04 +}; + +enum BLEAdField { + BLEFieldFlags = 0x01, + BLEFieldIncompleteAdvertisedService16 = 0x02, + BLEFieldCompleteAdvertisedService16 = 0x03, + BLEFieldIncompleteAdvertisedService128 = 0x06, + BLEFieldCompleteAdvertisedService128 = 0x07, + BLEFieldShortLocalName = 0x08, + BLEFieldCompleteLocalName = 0x09, + BLEFieldServiceData = 0x16, + BLEFieldManufacturerData = 0xFF, + + BLEAdFieldLast +}; + +struct BLEAdvertisingRawData { + uint8_t data[MAX_AD_DATA_LENGTH]; + int length; +}; + +class BLEAdvertisingData { +public: + BLEAdvertisingData(); + virtual ~BLEAdvertisingData(); + + int availableForWrite(); + void clear(); + void copy(const BLEAdvertisingData& adv); + BLEAdvertisingData& operator=(const BLEAdvertisingData &other); + + bool setAdvertisedService(const BLEService& service); + bool setAdvertisedServiceUuid(const char* advertisedServiceUuid); + bool setManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength); + bool setManufacturerData(const uint16_t companyId, const uint8_t manufacturerData[], int manufacturerDataLength); + bool setLocalName(const char *localName); + bool setAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length); + bool setRawData(const uint8_t* data, int length); + bool setRawData(const BLEAdvertisingRawData& data); + bool setFlags(uint8_t flags); + +protected: + friend class BLELocalDevice; + bool updateData(); + uint8_t* data(); + int dataLength() const; + int remainingLength() const; + bool hasFlags() const; + +private: + bool updateRemainingLength(int oldFieldLength, int newFieldLength); + + bool addAdvertisedServiceUuid(const char* advertisedServiceUuid); + bool addManufacturerData(const uint8_t manufacturerData[], int manufacturerDataLength); + bool addManufacturerData(const uint16_t companyId, const uint8_t manufacturerData[], int manufacturerDataLength); + bool addLocalName(const char *localName); + bool addAdvertisedServiceData(uint16_t uuid, const uint8_t data[], int length); + bool addRawData(const uint8_t* data, int length); + bool addFlags(uint8_t flags); + + bool addField(BLEAdField field, const char* data); + bool addField(BLEAdField field, const uint8_t* data, int length); + + uint8_t _data[MAX_AD_DATA_LENGTH]; + int _dataLength; + + int _remainingLength; + + const uint8_t* _rawData; + int _rawDataLength; + + uint8_t _flags; + bool _hasFlags; + const char* _localName; + + const uint8_t* _manufacturerData; + int _manufacturerDataLength; + uint16_t _manufacturerCompanyId; + bool _hasManufacturerCompanyId; + + const char* _advertisedServiceUuid; + int _advertisedServiceUuidLength; + uint16_t _serviceDataUuid; + const uint8_t* _serviceData; + int _serviceDataLength; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLECharacteristic.cpp b/arduino/libraries/ArduinoBLE/src/BLECharacteristic.cpp new file mode 100644 index 0000000..bfaee3f --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLECharacteristic.cpp @@ -0,0 +1,493 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEProperty.h" + +#include "local/BLELocalCharacteristic.h" +#include "remote/BLERemoteCharacteristic.h" + +#include "BLECharacteristic.h" + +extern "C" int strcasecmp(char const *a, char const *b); + +BLECharacteristic::BLECharacteristic() : + BLECharacteristic((BLELocalCharacteristic*)NULL) +{ +} + +BLECharacteristic::BLECharacteristic(BLELocalCharacteristic* local) : + _local(local), + _remote(NULL) +{ + if (_local) { + _local->retain(); + } +} + +BLECharacteristic::BLECharacteristic(BLERemoteCharacteristic* remote) : + _local(NULL), + _remote(remote) +{ + if (_remote) { + _remote->retain(); + } +} + +BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) : + BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, valueSize, fixedLength)) +{ +} + +BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, const char* value) : + BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, value)) +{ +} + +BLECharacteristic::BLECharacteristic(const BLECharacteristic& other) +{ + _local = other._local; + if (_local) { + _local->retain(); + } + + _remote = other._remote; + if (_remote) { + _remote->retain(); + } +} + +BLECharacteristic::~BLECharacteristic() +{ + if (_local && _local->release() == 0) { + delete _local; + } + + if (_remote && _remote->release() == 0) { + delete _remote; + } +} + +const char* BLECharacteristic::uuid() const +{ + if (_local) { + return _local->uuid(); + } + + if (_remote) { + return _remote->uuid(); + } + + return ""; +} + +uint8_t BLECharacteristic::properties() const +{ + if (_local) { + return _local->properties(); + } + + if (_remote) { + return _remote->properties(); + } + + return 0; +} + +int BLECharacteristic::valueSize() const +{ + if (_local) { + return _local->valueSize(); + } + + if (_remote) { + return _remote->valueLength(); + } + + return 0; +} + +const uint8_t* BLECharacteristic::value() const +{ + if (_local) { + return _local->value(); + } + + if (_remote) { + return _remote->value(); + } + + return NULL; +} + +int BLECharacteristic::valueLength() const +{ + if (_local) { + return _local->valueLength(); + } + + if (_remote) { + return _remote->valueLength(); + } + + return 0; +} + +uint8_t BLECharacteristic::operator[] (int offset) const +{ + if (_local) { + return (*_local)[offset]; + } + + if (_remote) { + return (*_remote)[offset]; + } + + return 0; +} + +int BLECharacteristic::readValue(uint8_t value[], int length) +{ + int bytesRead = 0; + + if (_local) { + bytesRead = min(length, _local->valueLength()); + + memcpy(value, _local->value(), bytesRead); + } + + if (_remote) { + // trigger a read if the updated value (notification/indication) + // has already been read and the characteristic is readable + if (_remote->updatedValueRead() && canRead()) { + if (!read()) { + // read failed + return 0; + } + } + + bytesRead = min(length, _remote->valueLength()); + + memcpy(value, _remote->value(), bytesRead); + } + + return bytesRead; +} + +int BLECharacteristic::readValue(void* value, int length) +{ + return readValue((uint8_t*)value, length); +} + +int BLECharacteristic::readValue(uint8_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::readValue(int8_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::readValue(uint16_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::readValue(int16_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::readValue(uint32_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::readValue(int32_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLECharacteristic::writeValue(const uint8_t value[], int length, bool withResponse) +{ + if (_local) { + return _local->writeValue(value, length); + } + + if (_remote) { + return _remote->writeValue(value, length, withResponse); + } + + return 0; +} + +int BLECharacteristic::writeValue(const void* value, int length, bool withResponse) +{ + return writeValue((const uint8_t*)value, length, withResponse); +} + +int BLECharacteristic::writeValue(const char* value, bool withResponse) +{ + if (_local) { + return _local->writeValue(value); + } + + if (_remote) { + return _remote->writeValue(value, withResponse); + } + + return 0; +} + +int BLECharacteristic::writeValue(uint8_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::writeValue(int8_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::writeValue(uint16_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::writeValue(int16_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::writeValue(uint32_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::writeValue(int32_t value, bool withResponse) +{ + return writeValue((uint8_t*)&value, sizeof(value), withResponse); +} + +int BLECharacteristic::broadcast() +{ + if (_local) { + return _local->broadcast(); + } + + return 0; +} + +bool BLECharacteristic::written() +{ + if (_local) { + return _local->written(); + } + + return false; +} + +bool BLECharacteristic::subscribed() +{ + if (_local) { + return _local->subscribed(); + } + + return false; +} + +bool BLECharacteristic::valueUpdated() +{ + if (_remote) { + return _remote->valueUpdated(); + } + + return false; +} + +void BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + if (_local) { + return _local->addDescriptor(descriptor); + } +} + +BLECharacteristic::operator bool() const +{ + return (_local != NULL) || (_remote != NULL); +} + +BLELocalCharacteristic* BLECharacteristic::local() +{ + return _local; +} + +void BLECharacteristic::setEventHandler(int event, BLECharacteristicEventHandler eventHandler) +{ + if (_local) { + _local->setEventHandler((BLECharacteristicEvent)event, eventHandler); + } + + if (_remote) { + _remote->setEventHandler((BLECharacteristicEvent)event, eventHandler); + } +} + +int BLECharacteristic::descriptorCount() const +{ + if (_remote) { + return _remote->descriptorCount(); + } + + return 0; +} + +bool BLECharacteristic::hasDescriptor(const char* uuid) const +{ + return hasDescriptor(uuid, 0); +} + +bool BLECharacteristic::hasDescriptor(const char* uuid, int index) const +{ + if (_remote) { + int count = 0; + int numDescriptors = _remote->descriptorCount(); + + for (int i = 0; i < numDescriptors; i++) { + BLERemoteDescriptor* d = _remote->descriptor(i); + + if (strcasecmp(uuid, d->uuid()) == 0) { + if (count == index) { + return true; + } + + count++; + } + } + } + + return false; +} + +BLEDescriptor BLECharacteristic::descriptor(int index) const +{ + if (_remote) { + return BLEDescriptor(_remote->descriptor(index)); + } + + return BLEDescriptor(); +} + +BLEDescriptor BLECharacteristic::descriptor(const char * uuid) const +{ + return descriptor(uuid, 0); +} + +BLEDescriptor BLECharacteristic::descriptor(const char * uuid, int index) const +{ + if (_remote) { + int count = 0; + int numDescriptors = _remote->descriptorCount(); + + for (int i = 0; i < numDescriptors; i++) { + BLERemoteDescriptor* d = _remote->descriptor(i); + + if (strcasecmp(uuid, d->uuid()) == 0) { + if (count == index) { + return BLEDescriptor(d); + } + + count++; + } + } + } + + return BLEDescriptor(); +} + +bool BLECharacteristic::canRead() +{ + if (_remote) { + return (properties() & BLERead) != 0; + } + + return false; +} + +bool BLECharacteristic::read() +{ + if (_remote) { + return _remote->read(); + } + + return false; +} + +bool BLECharacteristic::canWrite() +{ + if (_remote) { + return (properties() & (BLEWrite | BLEWriteWithoutResponse)) != 0; + } + + return false; +} + +bool BLECharacteristic::canSubscribe() +{ + if (_remote) { + return (properties() & (BLENotify | BLEIndicate)) != 0; + } + + return false; +} + +bool BLECharacteristic::subscribe() +{ + if (_remote) { + return _remote->writeCccd((properties() & BLEIndicate) ? 0x0002 : 0x0001); + } + + return false; +} + +bool BLECharacteristic::canUnsubscribe() +{ + return canSubscribe(); +} + +bool BLECharacteristic::unsubscribe() +{ + if (_remote) { + return _remote->writeCccd(0x0000); + } + + return false; +} diff --git a/arduino/libraries/ArduinoBLE/src/BLECharacteristic.h b/arduino/libraries/ArduinoBLE/src/BLECharacteristic.h new file mode 100644 index 0000000..da9721e --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLECharacteristic.h @@ -0,0 +1,132 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_CHARACTERISTIC_H_ +#define _BLE_CHARACTERISTIC_H_ + +#include + +#include "BLEDescriptor.h" + +enum BLECharacteristicEvent { + BLESubscribed = 0, + BLEUnsubscribed = 1, +//BLERead = 2, // defined in BLEProperties.h + BLEWritten = 3, + BLEUpdated = BLEWritten, // alias + + BLECharacteristicEventLast +}; + +class BLECharacteristic; +class BLEDevice; + +typedef void (*BLECharacteristicEventHandler)(BLEDevice device, BLECharacteristic characteristic); + +class BLELocalCharacteristic; +class BLERemoteCharacteristic; + +class BLECharacteristic { +public: + BLECharacteristic(); + BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false); + BLECharacteristic(const char* uuid, uint16_t permissions, const char* value); + BLECharacteristic(const BLECharacteristic& other); + virtual ~BLECharacteristic(); + + const char* uuid() const; + + uint8_t properties() const; + + int valueSize() const; + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int readValue(uint8_t value[], int length); + int readValue(void* value, int length); + int readValue(uint8_t& value); + int readValue(int8_t& value); + int readValue(uint16_t& value); + int readValue(int16_t& value); + int readValue(uint32_t& value); + int readValue(int32_t& value); + + int writeValue(const uint8_t value[], int length, bool withResponse = true); + int writeValue(const void* value, int length, bool withResponse = true); + int writeValue(const char* value, bool withResponse = true); + int writeValue(uint8_t value, bool withResponse = true); + int writeValue(int8_t value, bool withResponse = true); + int writeValue(uint16_t value, bool withResponse = true); + int writeValue(int16_t value, bool withResponse = true); + int writeValue(uint32_t value, bool withResponse = true); + int writeValue(int32_t value, bool withResponse = true); + + // deprecated, use writeValue(...) + int setValue(const uint8_t value[], int length) { return writeValue(value, length); } + int setValue(const char* value) { return writeValue(value); } + + int broadcast(); + + bool written(); + bool subscribed(); + bool valueUpdated(); + + void addDescriptor(BLEDescriptor& descriptor); + + operator bool() const; + + void setEventHandler(int event, BLECharacteristicEventHandler eventHandler); + + int descriptorCount() const; + bool hasDescriptor(const char* uuid) const; + bool hasDescriptor(const char* uuid, int index) const; + BLEDescriptor descriptor(int index) const; + BLEDescriptor descriptor(const char * uuid) const; + BLEDescriptor descriptor(const char * uuid, int index) const; + + bool canRead(); + bool read(); + bool canWrite(); + bool canSubscribe(); + bool subscribe(); + bool canUnsubscribe(); + bool unsubscribe(); + +protected: + friend class BLELocalCharacteristic; + friend class BLELocalService; + + BLECharacteristic(BLELocalCharacteristic* local); + + BLELocalCharacteristic* local(); + +protected: + friend class BLEDevice; + friend class BLEService; + friend class BLERemoteCharacteristic; + + BLECharacteristic(BLERemoteCharacteristic* remote); + +private: + BLELocalCharacteristic* _local; + BLERemoteCharacteristic* _remote; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEDescriptor.cpp b/arduino/libraries/ArduinoBLE/src/BLEDescriptor.cpp new file mode 100644 index 0000000..366b89a --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEDescriptor.cpp @@ -0,0 +1,229 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "local/BLELocalDescriptor.h" +#include "remote/BLERemoteDescriptor.h" + +#include "BLEDescriptor.h" + +BLEDescriptor::BLEDescriptor() : + BLEDescriptor((BLELocalDescriptor*)NULL) +{ +} + +BLEDescriptor::BLEDescriptor(BLELocalDescriptor* local) : + _local(local), + _remote(NULL) +{ + if (_local) { + _local->retain(); + } +} + +BLEDescriptor::BLEDescriptor(BLERemoteDescriptor* remote) : + _local(NULL), + _remote(remote) +{ + if (_remote) { + _remote->retain(); + } +} + +BLEDescriptor::BLEDescriptor(const char* uuid, const uint8_t value[], int valueSize) : + BLEDescriptor(new BLELocalDescriptor(uuid, value, valueSize)) +{ +} + +BLEDescriptor::BLEDescriptor(const char* uuid, const char* value) : + BLEDescriptor(new BLELocalDescriptor(uuid, value)) +{ +} + +BLEDescriptor::BLEDescriptor(const BLEDescriptor& other) +{ + _local = other._local; + if (_local) { + _local->retain(); + } + + _remote = other._remote; + if (_remote) { + _remote->retain(); + } +} + +BLEDescriptor::~BLEDescriptor() +{ + if (_local && _local->release() == 0) { + delete _local; + } + + if (_remote && _remote->release() == 0) { + delete _remote; + } +} + +const char* BLEDescriptor::uuid() const +{ + if (_local) { + return _local->uuid(); + } + + if (_remote) { + return _remote->uuid(); + } + + return ""; +} + +int BLEDescriptor::valueSize() const +{ + if (_local) { + return _local->valueSize(); + } + + if (_remote) { + return _remote->valueLength(); + } + + return 0; +} + +const uint8_t* BLEDescriptor::value() const +{ + if (_local) { + return _local->value(); + } + + if (_remote) { + return _remote->value(); + } + + return NULL; +} + +int BLEDescriptor::valueLength() const +{ + return valueSize(); +} + +uint8_t BLEDescriptor::operator[] (int offset) const +{ + if (_local) { + return (*_local)[offset]; + } + + if (_remote) { + return (*_remote)[offset]; + } + + return 0; +} + +int BLEDescriptor::readValue(uint8_t value[], int length) +{ + int bytesRead = 0; + + if (_local) { + bytesRead = min(length, _local->valueSize()); + + memcpy(value, _local->value(), bytesRead); + } + + if (_remote) { + if (!read()) { + // read failed + return 0; + } + + bytesRead = min(length, _remote->valueLength()); + + memcpy(value, _remote->value(), bytesRead); + } + + return bytesRead; +} + +int BLEDescriptor::readValue(void* value, int length) +{ + return readValue((uint8_t*)value, length); +} + +int BLEDescriptor::readValue(uint8_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLEDescriptor::readValue(int8_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLEDescriptor::readValue(uint16_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLEDescriptor::readValue(int16_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLEDescriptor::readValue(uint32_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +int BLEDescriptor::readValue(int32_t& value) +{ + value = 0; + + return readValue((uint8_t*)&value, sizeof(value)); +} + +BLEDescriptor::operator bool() const +{ + return (_local != NULL) || (_remote != NULL); +} + +bool BLEDescriptor::read() +{ + if (_remote) { + return _remote->read(); + } + + return false; +} + +BLELocalDescriptor* BLEDescriptor::local() +{ + return _local; +} diff --git a/arduino/libraries/ArduinoBLE/src/BLEDescriptor.h b/arduino/libraries/ArduinoBLE/src/BLEDescriptor.h new file mode 100644 index 0000000..800da12 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEDescriptor.h @@ -0,0 +1,73 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_DESCRIPTOR_H_ +#define _BLE_DESCRIPTOR_H_ + +#include + +class BLELocalDescriptor; +class BLERemoteDescriptor; + +class BLEDescriptor { +public: + BLEDescriptor(); + BLEDescriptor(const BLEDescriptor& other); + BLEDescriptor(const char* uuid, const uint8_t value[], int valueSize); + BLEDescriptor(const char* uuid, const char* value); + virtual ~BLEDescriptor(); + + const char* uuid() const; + + int valueSize() const; + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int readValue(uint8_t value[], int length); + int readValue(void* value, int length); + int readValue(uint8_t& value); + int readValue(int8_t& value); + int readValue(uint16_t& value); + int readValue(int16_t& value); + int readValue(uint32_t& value); + int readValue(int32_t& value); + + operator bool() const; + + bool read(); + +protected: + friend class BLELocalCharacteristic; + + BLEDescriptor(BLELocalDescriptor* local); + + BLELocalDescriptor* local(); + +protected: + friend class BLECharacteristic; + + BLEDescriptor(BLERemoteDescriptor* remote); + +private: + BLELocalDescriptor* _local; + BLERemoteDescriptor* _remote; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEDevice.cpp b/arduino/libraries/ArduinoBLE/src/BLEDevice.cpp new file mode 100644 index 0000000..e9ca9b9 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEDevice.cpp @@ -0,0 +1,578 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/ATT.h" +#include "utility/BLEUuid.h" +#include "utility/HCI.h" + +#include "remote/BLERemoteDevice.h" + +#include "BLEDevice.h" + +extern "C" int strcasecmp(char const *a, char const *b); + +BLEDevice::BLEDevice() : + _advertisementTypeMask(0), + _eirDataLength(0), + _rssi(127) +{ + memset(_address, 0x00, sizeof(_address)); +} + +BLEDevice::BLEDevice(uint8_t addressType, uint8_t address[6]) : + _addressType(addressType), + _advertisementTypeMask(0), + _eirDataLength(0), + _rssi(127) +{ + memcpy(_address, address, sizeof(_address)); +} + +BLEDevice::~BLEDevice() +{ +} + +void BLEDevice::poll() +{ + HCI.poll(); +} + +void BLEDevice::poll(unsigned long timeout) +{ + HCI.poll(timeout); +} + +bool BLEDevice::connected() const +{ + HCI.poll(); + + if (!(*this)) { + return false; + } + + return ATT.connected(_addressType, _address); +} + +bool BLEDevice::disconnect() +{ + return ATT.disconnect(_addressType, _address); +} + +String BLEDevice::address() const +{ + char result[18]; + sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", _address[5], _address[4], _address[3], _address[2], _address[1], _address[0]); + + return result; +} + +bool BLEDevice::hasLocalName() const +{ + return (localName().length() > 0); +} + +bool BLEDevice::hasAdvertisedServiceUuid() const +{ + return hasAdvertisedServiceUuid(0); +} + +bool BLEDevice::hasAdvertisedServiceUuid(int index) const +{ + return (advertisedServiceUuid(index).length() > 0); +} + +int BLEDevice::advertisedServiceUuidCount() const +{ + int advertisedServiceCount = 0; + + for (unsigned char i = 0; i < _eirDataLength;) { + int eirLength = _eirData[i++]; + int eirType = _eirData[i++]; + + if (eirType == 0x02 || eirType == 0x03 || eirType == 0x06 || eirType == 0x07) { + int uuidLength; + + if (eirType == 0x02 || eirType == 0x03) { + uuidLength = 2; + } else /*if (eirType == 0x06 || eirType == 0x07)*/ { + uuidLength = 16; + } + + for (int j = 0; j < (eirLength - 1); j += uuidLength) { + advertisedServiceCount++; + } + } + + i += (eirLength - 1); + } + + return advertisedServiceCount; +} + +String BLEDevice::localName() const +{ + String localName = ""; + + for (int i = 0; i < _eirDataLength;) { + int eirLength = _eirData[i++]; + int eirType = _eirData[i++]; + + if (eirType == 0x08 || eirType == 0x09) { + localName.reserve(eirLength - 1); + + for (int j = 0; j < (eirLength - 1); j++) { + localName += (char)_eirData[i + j]; + } + break; + } + + i += (eirLength - 1); + } + + return localName; +} + +String BLEDevice::advertisedServiceUuid() const +{ + return advertisedServiceUuid(0); +} + +String BLEDevice::advertisedServiceUuid(int index) const +{ + String serviceUuid; + int uuidIndex = 0; + + for (unsigned char i = 0; i < _eirDataLength;) { + int eirLength = _eirData[i++]; + int eirType = _eirData[i++]; + + if (eirType == 0x02 || eirType == 0x03 || eirType == 0x06 || eirType == 0x07) { + int uuidLength; + + if (eirType == 0x02 || eirType == 0x03) { + uuidLength = 2; + } else /*if (eirType == 0x06 || eirType == 0x07)*/ { + uuidLength = 16; + } + + for (int j = 0; j < (eirLength - 1); j += uuidLength) { + if (uuidIndex == index) { + serviceUuid = BLEUuid::uuidToString(&_eirData[i + j * uuidLength], uuidLength); + } + + uuidIndex++; + } + } + + i += (eirLength - 1); + } + + return serviceUuid; +} + +bool BLEDevice::hasAdvertisementData() const +{ + return (_eirDataLength > 0); +} + +int BLEDevice::advertisementDataLength() const +{ + return _eirDataLength; +} + +int BLEDevice::advertisementData(uint8_t value[], int length) const +{ + if (length > _eirDataLength) length = _eirDataLength; + + if (length) { + memcpy(value, _eirData, length); + } + + return length; +} + +bool BLEDevice::hasManufacturerData() const +{ + return (manufacturerDataLength() > 0); +} + +int BLEDevice::manufacturerDataLength() const +{ + int length = 0; + + for (int i = 0; i < _eirDataLength;) { + int eirLength = _eirData[i++]; + int eirType = _eirData[i++]; + + if (eirType == 0xFF) { + length = (eirLength - 1); + break; + } + + i += (eirLength - 1); + } + + return length; +} + +int BLEDevice::manufacturerData(uint8_t value[], int length) const +{ + for (int i = 0; i < _eirDataLength;) { + int eirLength = _eirData[i++]; + int eirType = _eirData[i++]; + + if (eirType == 0xFF) { + if (length > (eirLength - 1)) length = (eirLength - 1); + + memcpy(value, &_eirData[i], length); + break; + } + + i += (eirLength - 1); + } + + return length; +} + +int BLEDevice::rssi() +{ + uint16_t handle = ATT.connectionHandle(_addressType, _address); + + if (handle != 0xffff) { + return HCI.readRssi(handle); + } + + return _rssi; +} + +bool BLEDevice::connect() +{ + return ATT.connect(_addressType, _address); +} + +bool BLEDevice::discoverAttributes() +{ + return ATT.discoverAttributes(_addressType, _address, NULL); +} + +bool BLEDevice::discoverService(const char* serviceUuid) +{ + return ATT.discoverAttributes(_addressType, _address, serviceUuid); +} + +BLEDevice::operator bool() const +{ + uint8_t zeros[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}; + + return (memcmp(_address, zeros, sizeof(zeros)) != 0); +} + +bool BLEDevice::operator==(const BLEDevice& rhs) const +{ + return ((_addressType == rhs._addressType) && memcmp(_address, rhs._address, sizeof(_address)) == 0); +} + +bool BLEDevice::operator!=(const BLEDevice& rhs) const +{ + return ((_addressType != rhs._addressType) || memcmp(_address, rhs._address, sizeof(_address)) != 0); +} + +String BLEDevice::deviceName() +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + BLEService genericAccessService = service("1800"); + + if (genericAccessService) { + BLECharacteristic deviceNameCharacteristic = genericAccessService.characteristic("2a00"); + + if (deviceNameCharacteristic) { + deviceNameCharacteristic.read(); + + String result; + int valueLength = deviceNameCharacteristic.valueLength(); + const char* value = (const char*)deviceNameCharacteristic.value(); + + result.reserve(valueLength); + + for (int i = 0; i < valueLength; i++) { + result += value[i]; + } + + return result; + } + } + } + + return ""; +} + +int BLEDevice::appearance() +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + BLEService genericAccessService = service("1801"); + + if (genericAccessService) { + BLECharacteristic appearanceCharacteristic = genericAccessService.characteristic("2a01"); + + if (appearanceCharacteristic) { + appearanceCharacteristic.read(); + + uint16_t result = 0; + + memcpy (&result, appearanceCharacteristic.value(), min((int)sizeof(result), appearanceCharacteristic.valueLength())); + + return result; + } + } + } + + return 0; +} + +int BLEDevice::serviceCount() const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + return device->serviceCount(); + } + + return 0; +} + +bool BLEDevice::hasService(const char* uuid) const +{ + return hasService(uuid, 0); +} + +bool BLEDevice::hasService(const char* uuid, int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int count = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + BLERemoteService* s = device->service(i); + + if (strcasecmp(uuid, s->uuid()) == 0) { + if (count == index) { + return true; + } + + count++; + } + } + } + + return false; +} + +BLEService BLEDevice::service(int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + if (index < (int)device->serviceCount()) { + return BLEService(device->service(index)); + } + } + + return BLEService(); +} + +BLEService BLEDevice::service(const char * uuid) const +{ + return service(uuid, 0); +} + +BLEService BLEDevice::service(const char * uuid, int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int count = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + BLERemoteService* s = device->service(i); + + if (strcasecmp(uuid, s->uuid()) == 0) { + if (count == index) { + return BLEService(s); + } + + count++; + } + } + } + + return BLEService(); +} + +int BLEDevice::characteristicCount() const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int result = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + result += device->service(i)->characteristicCount(); + } + + return result; + } + + return 0; +} + +bool BLEDevice::hasCharacteristic(const char* uuid) const +{ + return hasCharacteristic(uuid, 0); +} + +bool BLEDevice::hasCharacteristic(const char* uuid, int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int count = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + BLERemoteService* s = device->service(i); + + int numCharacteristics = s->characteristicCount(); + + for (int j = 0; j < numCharacteristics; j++) { + BLERemoteCharacteristic* c = s->characteristic(j); + + + if (strcasecmp(c->uuid(), uuid) == 0) { + if (count == index) { + return true; + } + } + + count++; + } + } + } + + return false; +} + +BLECharacteristic BLEDevice::characteristic(int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int count = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + BLERemoteService* s = device->service(i); + + int numCharacteristics = s->characteristicCount(); + + for (int j = 0; j < numCharacteristics; j++) { + if (count == index) { + BLERemoteCharacteristic* c = s->characteristic(j); + + return BLECharacteristic(c); + } + + count++; + } + } + } + + return BLECharacteristic(); +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid) const +{ + return characteristic(uuid, 0); +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid, int index) const +{ + BLERemoteDevice* device = ATT.device(_addressType, _address); + + if (device) { + int count = 0; + int numServices = device->serviceCount(); + + for (int i = 0; i < numServices; i++) { + BLERemoteService* s = device->service(i); + + int numCharacteristics = s->characteristicCount(); + + for (int j = 0; j < numCharacteristics; j++) { + BLERemoteCharacteristic* c = s->characteristic(j); + + if (strcasecmp(c->uuid(), uuid) == 0) { + if (count == index) { + + return BLECharacteristic(c); + } + + count++; + } + } + } + } + + return BLECharacteristic(); +} + +bool BLEDevice::hasAddress(uint8_t addressType, uint8_t address[6]) +{ + return (_addressType == addressType) && (memcmp(_address, address, sizeof(_address)) == 0); +} + +void BLEDevice::setAdvertisementData(uint8_t type, uint8_t eirDataLength, uint8_t eirData[], int8_t rssi) +{ + _advertisementTypeMask = (1 << type); + _eirDataLength = eirDataLength; + memcpy(_eirData, eirData, eirDataLength); + _rssi = rssi; +} + +void BLEDevice::setScanResponseData(uint8_t eirDataLength, uint8_t eirData[], int8_t rssi) +{ + _advertisementTypeMask |= (1 << 0x04); + memcpy(&_eirData[_eirDataLength], eirData, eirDataLength); + _eirDataLength += eirDataLength; + _rssi = rssi; +} + +bool BLEDevice::discovered() +{ + // expect, 0x03 or 0x04 flag to be set + return (_advertisementTypeMask & 0x18) != 0; +} + diff --git a/arduino/libraries/ArduinoBLE/src/BLEDevice.h b/arduino/libraries/ArduinoBLE/src/BLEDevice.h new file mode 100644 index 0000000..bf71074 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEDevice.h @@ -0,0 +1,121 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_DEVICE_H_ +#define _BLE_DEVICE_H_ + +#include + +#include "BLEService.h" + +enum BLEDeviceEvent { + BLEConnected = 0, + BLEDisconnected = 1, + BLEDiscovered = 2, + + BLEDeviceLastEvent +}; + +class BLEDevice; + +typedef void (*BLEDeviceEventHandler)(BLEDevice device); + +class BLEDevice { +public: + BLEDevice(); + virtual ~BLEDevice(); + + virtual void poll(); + virtual void poll(unsigned long timeout); + + virtual bool connected() const; + virtual bool disconnect(); + + virtual String address() const; + + bool hasLocalName() const; + + bool hasAdvertisedServiceUuid() const; + bool hasAdvertisedServiceUuid(int index) const; + int advertisedServiceUuidCount() const; + + String localName() const; + String advertisedServiceUuid() const; + String advertisedServiceUuid(int index) const; + + bool hasAdvertisementData() const; + int advertisementDataLength() const; + int advertisementData(uint8_t value[], int length) const; + + bool hasManufacturerData() const; + int manufacturerDataLength() const; + int manufacturerData(uint8_t value[], int length) const; + + virtual int rssi(); + + bool connect(); + bool discoverAttributes(); + bool discoverService(const char* serviceUuid); + + virtual operator bool() const; + virtual bool operator==(const BLEDevice& rhs) const; + virtual bool operator!=(const BLEDevice& rhs) const; + + String deviceName(); + int appearance(); + + int serviceCount() const; + bool hasService(const char* uuid) const; + bool hasService(const char* uuid, int index) const; + BLEService service(int index) const; + BLEService service(const char * uuid) const; + BLEService service(const char * uuid, int index) const; + int characteristicCount() const; + bool hasCharacteristic(const char* uuid) const; + bool hasCharacteristic(const char* uuid, int index) const; + BLECharacteristic characteristic(int index) const; + BLECharacteristic characteristic(const char * uuid) const; + BLECharacteristic characteristic(const char * uuid, int index) const; + +protected: + friend class ATTClass; + friend class GAPClass; + + BLEDevice(uint8_t addressType, uint8_t address[6]); + +protected: + friend class GAPClass; + + bool hasAddress(uint8_t addressType, uint8_t address[6]); + + void setAdvertisementData(uint8_t type, uint8_t eirDataLength, uint8_t eirData[], int8_t rssi); + void setScanResponseData(uint8_t eirDataLength, uint8_t eirData[], int8_t rssi); + + bool discovered(); + +private: + uint8_t _addressType; + uint8_t _address[6]; + uint8_t _advertisementTypeMask; + uint8_t _eirDataLength; + uint8_t _eirData[31 * 2]; + int8_t _rssi; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEProperty.h b/arduino/libraries/ArduinoBLE/src/BLEProperty.h new file mode 100644 index 0000000..b9238ba --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEProperty.h @@ -0,0 +1,79 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// #include + +#ifndef _BLE_PROPERTY_H_ +#define _BLE_PROPERTY_H_ + +enum BLEProperty { + BLEBroadcast = 0x01, + BLERead = 0x02, + BLEWriteWithoutResponse = 0x04, + BLEWrite = 0x08, + BLENotify = 0x10, + BLEIndicate = 0x20, + BLEAuthSignedWrite = 1 << 6, + BLEExtProp = 1 << 7, +}; + +enum BLEPermission { + BLEEncryption = 1 << 9, + BLEAuthentication = 1 << 10, + BLEAuthorization = 1 << 11, + // BLEWriteEncryption = 1 << 11, + // BLEWriteAuthentication = 1 << 12, + // BLEWriteAuthorization = 1 << 13, +}; + +#define ESP_GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) /* 0x01 */ /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_READ (1 << 1) /* 0x02 */ /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) /* 0x04 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_WRITE (1 << 3) /* 0x08 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) /* 0x10 */ /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_INDICATE (1 << 5) /* 0x20 */ /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_AUTH (1 << 6) /* 0x40 */ /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */ +#define ESP_GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) /* 0x80 */ /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */ + +#define ESP_GATT_PERM_READ (1 << 0) /* bit 0 - 0x0001 */ /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 - 0x0002 */ /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 - 0x0004 */ /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE (1 << 4) /* bit 4 - 0x0010 */ /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 - 0x0020 */ /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 - 0x0040 */ /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 - 0x0080 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 - 0x0100 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */ +#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */ +#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */ + +enum BLE_GATT_PERM_ { + BLE_GATT_READ = 1 << 0, + READ_ENCRYPTED = 1 << 1, + READ_ENC_MITM = 1 << 2, + BLE_GATT_WRITE = 1 << 4, + WRITE_ENCRYPTED = 1 << 5, + WRITE_ENC_MITM = 1 << 6, + WRITE_SIGNED = 1 << 7, + WRITE_SIGNED_MITM = 1 << 8, + READ_AUTHORIZATION = 1 << 9, + WRITE_AUTHORIZATION = 1 << 10, +}; + + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEService.cpp b/arduino/libraries/ArduinoBLE/src/BLEService.cpp new file mode 100644 index 0000000..88687b6 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEService.cpp @@ -0,0 +1,186 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "local/BLELocalService.h" +#include "remote/BLERemoteService.h" + +#include "BLEService.h" + +extern "C" int strcasecmp(char const *a, char const *b); + +BLEService::BLEService() : + BLEService((BLELocalService*)NULL) +{ +} + +BLEService::BLEService(BLELocalService* local) : + _local(local), + _remote(NULL) +{ + if (_local) { + _local->retain(); + } +} + +BLEService::BLEService(BLERemoteService* remote) : + _local(NULL), + _remote(remote) +{ + if (_remote) { + _remote->retain(); + } +} + +BLEService::BLEService(const char* uuid) : + BLEService(new BLELocalService(uuid)) +{ +} + +BLEService::BLEService(const BLEService& other) +{ + _local = other._local; + if (_local) { + _local->retain(); + } + + _remote = other._remote; + if (_remote) { + _remote->retain(); + } +} + +void BLEService::clear() +{ + if (_local) { + _local->clear(); + } +} + +BLEService::~BLEService() +{ + if (_local && _local->release() == 0) { + delete _local; + } + + if (_remote && _remote->release() == 0) { + delete _remote; + } +} + +const char* BLEService::uuid() const +{ + if (_local) { + return _local->uuid(); + } + + if (_remote) { + return _remote->uuid(); + } + + return ""; +} + +void BLEService::addCharacteristic(BLECharacteristic& characteristic) +{ + if (_local) { + _local->addCharacteristic(characteristic); + } +} + +BLEService::operator bool() const +{ + return (_local != NULL) || (_remote != NULL); +} + +int BLEService::characteristicCount() const +{ + if (_remote) { + return _remote->characteristicCount(); + } + + return 0; +} + +bool BLEService::hasCharacteristic(const char* uuid) const +{ + return hasCharacteristic(uuid, 0); +} + +bool BLEService::hasCharacteristic(const char* uuid, int index) const +{ + if (_remote) { + int count = 0; + int numCharacteristics = _remote->characteristicCount(); + + for (int i = 0; i < numCharacteristics; i++) { + BLERemoteCharacteristic* c = _remote->characteristic(i); + + if (strcasecmp(uuid, c->uuid()) == 0) { + if (count == index) { + return true; + } + + count++; + } + } + } + + return false; +} + +BLECharacteristic BLEService::characteristic(int index) const +{ + if (_remote) { + return BLECharacteristic(_remote->characteristic(index)); + } + + return BLECharacteristic(); +} + +BLECharacteristic BLEService::characteristic(const char * uuid) const +{ + return characteristic(uuid, 0); +} + +BLECharacteristic BLEService::characteristic(const char * uuid, int index) const +{ + if (_remote) { + int count = 0; + int numCharacteristics = _remote->characteristicCount(); + + for (int i = 0; i < numCharacteristics; i++) { + BLERemoteCharacteristic* c = _remote->characteristic(i); + + if (strcasecmp(uuid, c->uuid()) == 0) { + if (count == index) { + return BLECharacteristic(c); + } + + count++; + } + } + } + + return BLECharacteristic(); +} + +BLELocalService* BLEService::local() +{ + return _local; +} diff --git a/arduino/libraries/ArduinoBLE/src/BLEService.h b/arduino/libraries/ArduinoBLE/src/BLEService.h new file mode 100644 index 0000000..5b2440d --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEService.h @@ -0,0 +1,68 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_SERVICE_H_ +#define _BLE_SERVICE_H_ + +#include "BLECharacteristic.h" + +class BLELocalService; +class BLERemoteService; + +class BLEService { +public: + BLEService(); + BLEService(const char* uuid); + BLEService(const BLEService& other); + virtual ~BLEService(); + + const char* uuid() const; + void clear(); + + void addCharacteristic(BLECharacteristic& characteristic); + + operator bool() const; + + int characteristicCount() const; + bool hasCharacteristic(const char* uuid) const; + bool hasCharacteristic(const char* uuid, int index) const; + BLECharacteristic characteristic(int index) const; + BLECharacteristic characteristic(const char * uuid) const; + BLECharacteristic characteristic(const char * uuid, int index) const; + +protected: + friend class GATTClass; + + BLEService(BLELocalService* local); + + BLELocalService* local(); + + void addCharacteristic(BLELocalCharacteristic* characteristic); + +protected: + friend class BLEDevice; + + BLEService(BLERemoteService* remote); + +private: + BLELocalService* _local; + BLERemoteService* _remote; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.cpp b/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.cpp new file mode 100644 index 0000000..a12e722 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.cpp @@ -0,0 +1,45 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEStringCharacteristic.h" + +BLEStringCharacteristic::BLEStringCharacteristic(const char* uuid, unsigned int properties, int valueSize) : + BLECharacteristic(uuid, properties, valueSize) +{ +} + +int BLEStringCharacteristic::writeValue(const String& value) +{ + return BLECharacteristic::writeValue(value.c_str()); +} + +String BLEStringCharacteristic::value(void) +{ + String str; + int length = BLECharacteristic::valueLength(); + const uint8_t* val = BLECharacteristic::value(); + + str.reserve(length); + + for (int i = 0; i < length; i++) { + str += (char)val[i]; + } + + return str; +} diff --git a/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.h b/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.h new file mode 100644 index 0000000..6560484 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLEStringCharacteristic.h @@ -0,0 +1,39 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_STRING_CHARACTERISTIC_H_ +#define _BLE_STRING_CHARACTERISTIC_H_ + +#include + +#include "BLECharacteristic.h" + +class BLEStringCharacteristic : public BLECharacteristic +{ +public: + BLEStringCharacteristic(const char* uuid, unsigned int properties, int valueSize); + + int writeValue(const String& value); + int setValue(const String& value) { return writeValue(value); } + String value(void); + +private: +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristic.h b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristic.h new file mode 100644 index 0000000..7777d36 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristic.h @@ -0,0 +1,102 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_TYPED_CHARACTERISTIC_H_ +#define _BLE_TYPED_CHARACTERISTIC_H_ + +#include "BLECharacteristic.h" + +template class BLETypedCharacteristic : public BLECharacteristic +{ +public: + BLETypedCharacteristic(const char* uuid, unsigned int permissions); + + int writeValue(T value); + int setValue(T value) { return writeValue(value); } + T value(void); + + int writeValueLE(T value); + int setValueLE(T value) { return writeValueLE(value); } + T valueLE(void); + + int writeValueBE(T value); + int setValueBE(T value) { return writeValueBE(value); } + T valueBE(void); + +private: + T byteSwap(T value); +}; + +template BLETypedCharacteristic::BLETypedCharacteristic(const char* uuid, unsigned int permissions) : + BLECharacteristic(uuid, permissions, sizeof(T), true) +{ + T value; + memset(&value, 0x00, sizeof(value)); + + writeValue(value); +} + +template int BLETypedCharacteristic::writeValue(T value) +{ + return BLECharacteristic::writeValue((uint8_t*)&value, sizeof(T)); +} + +template T BLETypedCharacteristic::value() +{ + T value; + + memcpy(&value, (unsigned char*)BLECharacteristic::value(), BLECharacteristic::valueSize()); + + return value; +} + +template int BLETypedCharacteristic::writeValueLE(T value) +{ + return writeValue(value); +} + +template T BLETypedCharacteristic::valueLE() +{ + return value(); +} + +template int BLETypedCharacteristic::writeValueBE(T value) +{ + return writeValue(byteSwap(value)); +} + +template T BLETypedCharacteristic::valueBE() +{ + return byteSwap(value()); +} + +template T BLETypedCharacteristic::byteSwap(T value) +{ + T result; + unsigned char* src = (unsigned char*)&value; + unsigned char* dst = (unsigned char*)&result; + + for (int i = 0; i < sizeof(T); i++) { + dst[i] = src[sizeof(T) - i - 1]; + } + + return result; +} + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.cpp b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.cpp new file mode 100644 index 0000000..800574e --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.cpp @@ -0,0 +1,92 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "BLETypedCharacteristics.h" + +BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} + +BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned int properties) : + BLETypedCharacteristic(uuid, properties) +{ +} diff --git a/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.h b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.h new file mode 100644 index 0000000..8e9a08c --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/BLETypedCharacteristics.h @@ -0,0 +1,95 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_TYPED_CHARACTERISTICS_H_ +#define _BLE_TYPED_CHARACTERISTICS_H_ + +#include "BLETypedCharacteristic.h" + +class BLEBoolCharacteristic : public BLETypedCharacteristic { +public: + BLEBoolCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEBooleanCharacteristic : public BLETypedCharacteristic { +public: + BLEBooleanCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLECharCharacteristic : public BLETypedCharacteristic { +public: + BLECharCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEUnsignedCharCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedCharCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEByteCharacteristic : public BLETypedCharacteristic { +public: + BLEByteCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEShortCharacteristic : public BLETypedCharacteristic { +public: + BLEShortCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEUnsignedShortCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedShortCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEWordCharacteristic : public BLETypedCharacteristic { +public: + BLEWordCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEIntCharacteristic : public BLETypedCharacteristic { +public: + BLEIntCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEUnsignedIntCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedIntCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLELongCharacteristic : public BLETypedCharacteristic { +public: + BLELongCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEUnsignedLongCharacteristic : public BLETypedCharacteristic { +public: + BLEUnsignedLongCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEFloatCharacteristic : public BLETypedCharacteristic { +public: + BLEFloatCharacteristic(const char* uuid, unsigned int permissions); +}; + +class BLEDoubleCharacteristic : public BLETypedCharacteristic { +public: + BLEDoubleCharacteristic(const char* uuid, unsigned int permissions); +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.cpp b/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.cpp new file mode 100644 index 0000000..9929fa4 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.cpp @@ -0,0 +1,69 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLELocalAttribute.h" + +BLELocalAttribute::BLELocalAttribute(const char* uuid) : + _uuid(uuid), + _refCount(0) +{ +} + +BLELocalAttribute::~BLELocalAttribute() +{ +} + +const char* BLELocalAttribute::uuid() const +{ + return _uuid.str(); +} + +const uint8_t* BLELocalAttribute::uuidData() const +{ + return _uuid.data(); +} + +uint8_t BLELocalAttribute::uuidLength() const +{ + return _uuid.length(); +} + +enum BLEAttributeType BLELocalAttribute::type() const +{ + return BLETypeUnknown; +} + +int BLELocalAttribute::retain() +{ + _refCount++; + + return _refCount; +} + +bool BLELocalAttribute::active() +{ + return _refCount > 0; +} + +int BLELocalAttribute::release() +{ + _refCount--; + + return _refCount; +} diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.h b/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.h new file mode 100644 index 0000000..53a0abf --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalAttribute.h @@ -0,0 +1,61 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_ATTRIBUTE_H_ +#define _BLE_LOCAL_ATTRIBUTE_H_ + +#include "utility/BLEUuid.h" + +#define BLE_ATTRIBUTE_TYPE_SIZE 2 + +enum BLEAttributeType { + BLETypeUnknown = 0x0000, + + BLETypeService = 0x2800, + BLETypeCharacteristic = 0x2803, + BLETypeDescriptor = 0x2900 +}; + +class BLELocalAttribute +{ +public: + BLELocalAttribute(const char* uuid); + virtual ~BLELocalAttribute(); + + const char* uuid() const; + + virtual enum BLEAttributeType type() const; + + int retain(); + int release(); + bool active(); + +protected: + friend class ATTClass; + friend class GATTClass; + + const uint8_t* uuidData() const; + uint8_t uuidLength() const; + +private: + BLEUuid _uuid; + int _refCount; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.cpp b/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.cpp new file mode 100644 index 0000000..dc8a819 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.cpp @@ -0,0 +1,244 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "BLELocalDevice.h" + +#include "utility/ATT.h" +#include "utility/GAP.h" +#include "utility/GATT.h" + +#include "BLELocalDescriptor.h" +#include "BLEProperty.h" + +#include "BLELocalCharacteristic.h" + +BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) : + BLELocalAttribute(uuid), + _properties((uint8_t)(permissions&0x000FF)), + _permissions((uint8_t)((permissions&0xFF00)>>8)), + _valueSize(min(valueSize, 512)), + _valueLength(0), + _fixedLength(fixedLength), + _handle(0x0000), + _broadcast(false), + _written(false), + _cccdValue(0x0000) +{ + memset(_eventHandlers, 0x00, sizeof(_eventHandlers)); + + if (permissions & (BLENotify | BLEIndicate)) { + BLELocalDescriptor* cccd = new BLELocalDescriptor("2902", (uint8_t*)&_cccdValue, sizeof(_cccdValue)); + + cccd->retain(); + _descriptors.add(cccd); + } + + _value = (uint8_t*)malloc(valueSize); +} + +BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value) : + BLELocalCharacteristic(uuid, permissions, strlen(value)) +{ + writeValue(value); +} +BLELocalCharacteristic::~BLELocalCharacteristic() +{ + for (unsigned int i = 0; i < descriptorCount(); i++) { + BLELocalDescriptor* d = descriptor(i); + + if (d->release() == 0) { + delete d; + } + } + + _descriptors.clear(); + + if (_value) { + free(_value); + } +} + +enum BLEAttributeType BLELocalCharacteristic::type() const +{ + return BLETypeCharacteristic; +} + +uint8_t BLELocalCharacteristic::properties() const +{ + return _properties; +} + +uint8_t BLELocalCharacteristic::permissions() const { + return _permissions; +} + +int BLELocalCharacteristic::valueSize() const +{ + return _valueSize; +} + +const uint8_t* BLELocalCharacteristic::value() const +{ + return _value; +} + +int BLELocalCharacteristic::valueLength() const +{ + return _valueLength; +} + +uint8_t BLELocalCharacteristic::operator[] (int offset) const +{ + return _value[offset]; +} + +int BLELocalCharacteristic::writeValue(const uint8_t value[], int length) +{ + _valueLength = min(length, _valueSize); + memcpy(_value, value, _valueLength); + + if (_fixedLength) { + _valueLength = _valueSize; + } + + if ((_properties & BLEIndicate) && (_cccdValue & 0x0002)) { + return ATT.handleInd(valueHandle(), _value, _valueLength); + } else if ((_properties & BLENotify) && (_cccdValue & 0x0001)) { + return ATT.handleNotify(valueHandle(), _value, _valueLength); + } + + if (_broadcast) { + uint16_t serviceUuid = GATT.serviceUuidForCharacteristic(this); + BLE.setAdvertisedServiceData(serviceUuid, value, _valueLength); + if (!ATT.connected() && GAP.advertising()) { + BLE.advertise(); + } + } + + return _valueLength; +} + +int BLELocalCharacteristic::writeValue(const char* value) +{ + return writeValue((uint8_t*)value, strlen(value)); +} + +int BLELocalCharacteristic::broadcast() +{ + if (_properties & BLEBroadcast) { + _broadcast = true; + + return 1; + } + + return 0; +} + +bool BLELocalCharacteristic::written() +{ + bool written = _written; + + _written = false; + + return written; +} + +bool BLELocalCharacteristic::subscribed() +{ + return (_cccdValue != 0x0000); +} + +void BLELocalCharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + BLELocalDescriptor* localDescriptor = descriptor.local(); + + if (localDescriptor) { + localDescriptor->retain(); + + _descriptors.add(localDescriptor); + } +} + +void BLELocalCharacteristic::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler) +{ + if (event < (sizeof(_eventHandlers) / sizeof(_eventHandlers[0]))) { + _eventHandlers[event] = eventHandler; + } +} + +void BLELocalCharacteristic::setHandle(uint16_t handle) +{ + _handle = handle; +} + +uint16_t BLELocalCharacteristic::handle() const +{ + return _handle; +} + +uint16_t BLELocalCharacteristic::valueHandle() const +{ + return (_handle + 1); +} + +unsigned int BLELocalCharacteristic::descriptorCount() const +{ + return _descriptors.size(); +} + +BLELocalDescriptor* BLELocalCharacteristic::descriptor(unsigned int index) const +{ + return _descriptors.get(index); +} + +void BLELocalCharacteristic::readValue(BLEDevice device, uint16_t offset, uint8_t value[], int length) +{ + if (_eventHandlers[BLERead]) { + _eventHandlers[BLERead](device, BLECharacteristic(this)); + } + + memcpy(value, _value + offset, length); +} + +void BLELocalCharacteristic::writeValue(BLEDevice device, const uint8_t value[], int length) +{ + _written = true; + + writeValue(value, length); + + if (_eventHandlers[BLEWritten]) { + _eventHandlers[BLEWritten](device, BLECharacteristic(this)); + } +} + +void BLELocalCharacteristic::writeCccdValue(BLEDevice device, uint16_t value) +{ + value &= 0x0003; + + if (_cccdValue != value) { + _cccdValue = value; + + BLECharacteristicEvent event = (_cccdValue) ? BLESubscribed : BLEUnsubscribed; + + if (_eventHandlers[event]) { + _eventHandlers[event](device, BLECharacteristic(this)); + } + } +} diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.h b/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.h new file mode 100644 index 0000000..331cdd5 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalCharacteristic.h @@ -0,0 +1,96 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_CHARACTERISTIC_H_ +#define _BLE_LOCAL_CHARACTERISTIC_H_ + +#include + +#include "BLECharacteristic.h" +#include "BLEDescriptor.h" + +#include "BLELocalAttribute.h" + +#include "utility/BLELinkedList.h" + +class BLELocalDescriptor; + +class BLELocalCharacteristic : public BLELocalAttribute { +public: + BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false); + BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value); + virtual ~BLELocalCharacteristic(); + + virtual enum BLEAttributeType type() const; + + uint8_t properties() const; + uint8_t permissions() const; + + int valueSize() const; + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int writeValue(const uint8_t value[], int length); + int writeValue(const char* value); + + int broadcast(); + + bool written(); + bool subscribed(); + + void addDescriptor(BLEDescriptor& descriptor); + + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler); + +protected: + friend class ATTClass; + friend class GATTClass; + + void setHandle(uint16_t handle); + uint16_t handle() const; + uint16_t valueHandle() const; + + unsigned int descriptorCount() const; + BLELocalDescriptor* descriptor(unsigned int index) const; + + void readValue(BLEDevice device, uint16_t offset, uint8_t value[], int length); + void writeValue(BLEDevice device, const uint8_t value[], int length); + void writeCccdValue(BLEDevice device, uint16_t value); + +private: + uint8_t _properties; + uint8_t _permissions; + int _valueSize; + uint8_t* _value; + uint16_t _valueLength; + bool _fixedLength; + + uint16_t _handle; + + bool _broadcast; + bool _written; + + uint16_t _cccdValue; + BLELinkedList _descriptors; + + BLECharacteristicEventHandler _eventHandlers[BLECharacteristicEventLast]; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.cpp b/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.cpp new file mode 100644 index 0000000..c466897 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.cpp @@ -0,0 +1,69 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "BLELocalDescriptor.h" + +BLELocalDescriptor::BLELocalDescriptor(const char* uuid, const uint8_t value[], int valueSize) : + BLELocalAttribute(uuid), + _value(value), + _valueSize(min(valueSize, 512)), + _handle(0x0000) +{ +} + +BLELocalDescriptor::BLELocalDescriptor(const char* uuid, const char* value) : + BLELocalDescriptor(uuid, (const uint8_t*)value, strlen(value)) +{ +} + +BLELocalDescriptor::~BLELocalDescriptor() +{ +} + +enum BLEAttributeType BLELocalDescriptor::type() const +{ + return BLETypeDescriptor; +} + +int BLELocalDescriptor::valueSize() const +{ + return _valueSize; +} + +const uint8_t* BLELocalDescriptor::value() const +{ + return _value; +} + +uint8_t BLELocalDescriptor::operator[] (int offset) const +{ + return _value[offset]; +} + +void BLELocalDescriptor::setHandle(uint16_t handle) +{ + _handle = handle; +} + +uint16_t BLELocalDescriptor::handle() const +{ + return _handle; +} diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.h b/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.h new file mode 100644 index 0000000..cf2fd67 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalDescriptor.h @@ -0,0 +1,52 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_DESCRIPTOR_H_ +#define _BLE_LOCAL_DESCRIPTOR_H_ + +#include + +#include "BLELocalAttribute.h" + +class BLELocalDescriptor : public BLELocalAttribute { +public: + BLELocalDescriptor(const char* uuid, const uint8_t value[], int valueSize); + BLELocalDescriptor(const char* uuid, const char* value); + virtual ~BLELocalDescriptor(); + + virtual enum BLEAttributeType type() const; + + int valueSize() const; + const uint8_t* value() const; + uint8_t operator[] (int offset) const; + +protected: + friend class GATTClass; + + void setHandle(uint16_t handle); + uint16_t handle() const; + +private: + const uint8_t* _value; + int _valueSize; + + uint16_t _handle; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalDevice.cpp b/arduino/libraries/ArduinoBLE/src/local/BLELocalDevice.cpp new file mode 100644 index 0000000..150fe40 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalDevice.cpp @@ -0,0 +1,472 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/ATT.h" +#include "utility/HCI.h" +#include "utility/GAP.h" +#include "utility/GATT.h" +#include "utility/L2CAPSignaling.h" + +#include "BLELocalDevice.h" + +#ifdef __ZEPHYR__ +#undef ARDUINO_PORTENTA_H7_M7 +#undef ARDUINO_OPTA +#undef ARDUINO_GIGA +#undef ARDUINO_NICLA_VISION +#undef ARDUINO_PORTENTA_C33 +#undef ARDUINO_UNO_Q +#endif + +#if defined(ARDUINO_PORTENTA_C33) +#include +#endif + +#if defined(PORTENTA_H7_PINS) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_OPTA) +#ifndef BT_REG_ON +#define BT_REG_ON PJ_12 +#endif +#elif defined(ARDUINO_NICLA_VISION) +#ifndef BT_REG_ON +#define BT_REG_ON PF_14 +#endif +#elif defined(ARDUINO_GIGA) +#ifndef BT_REG_ON +#define BT_REG_ON PA_10 +#endif +#endif + +BLELocalDevice::BLELocalDevice() +{ + _advertisingData.setFlags(BLEFlagsGeneralDiscoverable | BLEFlagsBREDRNotSupported); +} + +BLELocalDevice::~BLELocalDevice() +{ +} + +int BLELocalDevice::begin() +{ +#if defined(PORTENTA_H7_PINS) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_GIGA) || defined(ARDUINO_OPTA) + // BT_REG_ON -> HIGH + pinMode(BT_REG_ON, OUTPUT); + digitalWrite(BT_REG_ON, LOW); + delay(500); + digitalWrite(BT_REG_ON, HIGH); + delay(500); +#elif defined(ARDUINO_PORTENTA_C33) + + Serial5.begin(921600); + CEspChipManager::getInstance().initialize(); + auto _start = millis(); + while (millis() - _start < 500) { + if (Serial5.available()) { + Serial5.read(); + } + } + //pinMode(94, OUTPUT); + //digitalWrite(94, LOW); +#endif + + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + // set SS HIGH + digitalWrite(SPIWIFI_SS, HIGH); + + // set RTS HIGH + pinMode(NINA_RTS, OUTPUT); + digitalWrite(NINA_RTS, HIGH); + + // set CTS as input + pinMode(NINA_CTS, INPUT); +#endif + + if (!HCI.begin()) { + end(); + return 0; + } + + delay(100); + + if (HCI.reset() != 0) { + end(); +#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(TARGET_NANO_RP2040_CONNECT) || defined(ARDUINO_SAMD_MKRVIDOR4000) // STRIX + Serial.println("The initialization of the Bluetooth® Low Energy module failed."); + Serial.println("Please ensure your NINA firmware is version 3.0.0 or higher."); +#endif + return 0; + } + + uint8_t hciVer; + uint16_t hciRev; + uint8_t lmpVer; + uint16_t manufacturer; + uint16_t lmpSubVer; + + if (HCI.readLocalVersion(hciVer, hciRev, lmpVer, manufacturer, lmpSubVer) != 0) { + end(); + return 0; + } + + if (HCI.setEventMask(0x3FFFFFFFFFFFFFFF) != 0) { + end(); + return 0; + } + if (HCI.setLeEventMask(0x00000000000003FF) != 0) { + end(); + return 0; + } + + uint16_t pktLen; + uint8_t maxPkt; + + if (HCI.readLeBufferSize(pktLen, maxPkt) != 0) { + end(); + return 0; + } + + /// The HCI should allow automatic address resolution. + + // // If we have callbacks to remember bonded devices: + // if(HCI._getIRKs!=0){ + // uint8_t nIRKs = 0; + // uint8_t** BADDR_Type = new uint8_t*; + // uint8_t*** BADDRs = new uint8_t**; + // uint8_t*** IRKs = new uint8_t**; + // uint8_t* memcheck; + + + // if(!HCI._getIRKs(&nIRKs, BADDR_Type, BADDRs, IRKs)){ + // Serial.println("error"); + // } + // for(int i=0; irelease() == 0) { + delete c; + } + } + clear(); +} + +enum BLEAttributeType BLELocalService::type() const +{ + return BLETypeService; +} + +void BLELocalService::addCharacteristic(BLECharacteristic& characteristic) +{ + BLELocalCharacteristic* localCharacteristic = characteristic.local(); + + if (localCharacteristic) { + addCharacteristic(localCharacteristic); + } +} + +void BLELocalService::setHandles(uint16_t start, uint16_t end) +{ + _startHandle = start; + _endHandle = end; +} + +uint16_t BLELocalService::startHandle() const +{ + return _startHandle; +} + +uint16_t BLELocalService::endHandle() const +{ + return _endHandle; +} + +unsigned int BLELocalService::characteristicCount() const +{ + return _characteristics.size(); +} + +BLELocalCharacteristic* BLELocalService::characteristic(unsigned int index) const +{ + return _characteristics.get(index); +} + +void BLELocalService::addCharacteristic(BLELocalCharacteristic* characteristic) +{ + characteristic->retain(); + + _characteristics.add(characteristic); +} diff --git a/arduino/libraries/ArduinoBLE/src/local/BLELocalService.h b/arduino/libraries/ArduinoBLE/src/local/BLELocalService.h new file mode 100644 index 0000000..f17c610 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/local/BLELocalService.h @@ -0,0 +1,61 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LOCAL_SERVICE_H_ +#define _BLE_LOCAL_SERVICE_H_ + +#include "BLECharacteristic.h" + +#include "BLELocalAttribute.h" + +#include "utility/BLELinkedList.h" + +class BLELocalCharacteristic; + +class BLELocalService : public BLELocalAttribute { +public: + BLELocalService(const char* uuid); + virtual ~BLELocalService(); + + virtual enum BLEAttributeType type() const; + + void addCharacteristic(BLECharacteristic& characteristic); + void clear(); + +protected: + friend class ATTClass; + friend class GATTClass; + + void setHandles(uint16_t start, uint16_t end); + uint16_t startHandle() const; + uint16_t endHandle() const; + + unsigned int characteristicCount() const; + BLELocalCharacteristic* characteristic(unsigned int index) const; + + void addCharacteristic(BLELocalCharacteristic* characteristic); + +private: + uint16_t _startHandle; + uint16_t _endHandle; + + BLELinkedList _characteristics; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.cpp b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.cpp new file mode 100644 index 0000000..3018f16 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.cpp @@ -0,0 +1,51 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/BLEUuid.h" + +#include "BLERemoteAttribute.h" + +BLERemoteAttribute::BLERemoteAttribute(const uint8_t uuid[], uint8_t uuidLen) : + _uuid(BLEUuid::uuidToString(uuid, uuidLen)), + _refCount(0) +{ +} + +BLERemoteAttribute::~BLERemoteAttribute() +{ +} + +const char* BLERemoteAttribute::uuid() const +{ + return _uuid.c_str(); +} + +int BLERemoteAttribute::retain() +{ + _refCount++; + + return _refCount; +} + +int BLERemoteAttribute::release() +{ + _refCount--; + + return _refCount; +} diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.h b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.h new file mode 100644 index 0000000..2d10e5b --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteAttribute.h @@ -0,0 +1,41 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_REMOTE_ATTRIBUTE_H_ +#define _BLE_REMOTE_ATTRIBUTE_H_ + +#include + +class BLERemoteAttribute +{ +public: + BLERemoteAttribute(const uint8_t uuid[], uint8_t uuidLen); + virtual ~BLERemoteAttribute(); + + const char* uuid() const; + + int retain(); + int release(); + +private: + String _uuid; + int _refCount; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.cpp b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.cpp new file mode 100644 index 0000000..454438b --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.cpp @@ -0,0 +1,264 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEProperty.h" + +#include "utility/ATT.h" + +#include "BLERemoteCharacteristic.h" + +BLERemoteCharacteristic::BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, + uint16_t startHandle, uint16_t permissions, uint16_t valueHandle) : + BLERemoteAttribute(uuid, uuidLen), + _connectionHandle(connectionHandle), + _startHandle(startHandle), + _properties((uint8_t)(permissions & 0x00FF)), + _permissions((uint8_t)((permissions & 0xFF00)>>8)), + _valueHandle(valueHandle), + _value(NULL), + _valueLength(0), + _valueUpdated(false), + _updatedValueRead(true), + _valueUpdatedEventHandler(NULL) +{ +} + +BLERemoteCharacteristic::~BLERemoteCharacteristic() +{ + for (unsigned int i = 0; i < descriptorCount(); i++) { + BLERemoteDescriptor* d = descriptor(i); + + if (d->release() == 0) { + delete d; + } + } + + _descriptors.clear(); + + if (_value) { + free(_value); + _value = NULL; + } +} + +uint16_t BLERemoteCharacteristic::startHandle() const +{ + return _startHandle; +} + +uint8_t BLERemoteCharacteristic::properties() const +{ + return _properties; +} + +const uint8_t* BLERemoteCharacteristic::value() const +{ + return _value; +} + +int BLERemoteCharacteristic::valueLength() const +{ + return _valueLength; +} + +uint8_t BLERemoteCharacteristic::operator[] (int offset) const +{ + if (_value) { + return _value[offset]; + } + + return 0; +} + +int BLERemoteCharacteristic::writeValue(const uint8_t value[], int length, bool withResponse) +{ + if (!ATT.connected(_connectionHandle)) { + return false; + } + + uint16_t maxLength = ATT.mtu(_connectionHandle) - 3; + + if (length > (int)maxLength) { + // cap to MTU max length + length = maxLength; + } + + _value = (uint8_t*)realloc(_value, length); + if (_value == NULL) { + // realloc failed + return 0; + } + + if ((_properties & BLEWrite) && withResponse) { + uint8_t resp[4]; + int respLength = ATT.writeReq(_connectionHandle, _valueHandle, value, length, resp); + + if (!respLength) { + return 0; + } + + if (resp[0] == 0x01) { + // error + return 0; + } + + memcpy(_value, value, length); + _valueLength = length; + + return 1; + } else if (_properties & BLEWriteWithoutResponse) { + ATT.writeCmd(_connectionHandle, _valueHandle, value, length); + + memcpy(_value, value, length); + _valueLength = length; + + return 1; + } + + return 0; +} + +int BLERemoteCharacteristic::writeValue(const char* value, bool withResponse) +{ + return writeValue((uint8_t*)value, strlen(value), withResponse); +} + +bool BLERemoteCharacteristic::valueUpdated() +{ + ATT.connected(_connectionHandle); // to force a poll + + bool result = _valueUpdated; + + _valueUpdated = false; + + return result; +} + +bool BLERemoteCharacteristic::updatedValueRead() +{ + bool result = _updatedValueRead; + + _updatedValueRead = true; + + return result; +} + +bool BLERemoteCharacteristic::read() +{ + if (!ATT.connected(_connectionHandle)) { + return false; + } + + uint8_t resp[256]; + + int respLength = ATT.readReq(_connectionHandle, _valueHandle, resp); + + if (!respLength) { + _valueLength = 0; + return false; + } + + if (resp[0] == 0x01) { + // error + _valueLength = 0; + return false; + } + + _valueLength = respLength - 1; + _value = (uint8_t*)realloc(_value, _valueLength); + + if (_value == NULL) { + _valueLength = 0; + return false; + } + + memcpy(_value, &resp[1], _valueLength); + + return true; +} + +bool BLERemoteCharacteristic::writeCccd(uint16_t value) +{ + int numDescriptors = descriptorCount(); + + for (int i = 0; i < numDescriptors; i++) { + BLERemoteDescriptor* d = descriptor(i); + + if (strcmp(d->uuid(), "2902") == 0) { + return d->writeValue((uint8_t*)&value, sizeof(value)); + } + } + + if (_properties & (BLENotify | BLEIndicate)) { + // no CCCD descriptor found, fallback to _valueHandle + 1 + BLERemoteDescriptor cccd(NULL, 0, _connectionHandle, _valueHandle + 1); + + return cccd.writeValue((uint8_t*)&value, sizeof(value)); + } + + return false; +} + +uint16_t BLERemoteCharacteristic::valueHandle() const +{ + return _valueHandle; +} + +unsigned int BLERemoteCharacteristic::descriptorCount() const +{ + return _descriptors.size(); +} + +BLERemoteDescriptor* BLERemoteCharacteristic::descriptor(unsigned int index) const +{ + return _descriptors.get(index); +} + +void BLERemoteCharacteristic::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler) +{ + if (event == BLEUpdated) { + _valueUpdatedEventHandler = eventHandler; + } +} + +void BLERemoteCharacteristic::addDescriptor(BLERemoteDescriptor* descriptor) +{ + descriptor->retain(); + + _descriptors.add(descriptor); +} + +void BLERemoteCharacteristic::writeValue(BLEDevice device, const uint8_t value[], int length) +{ + _valueLength = length; + _value = (uint8_t*)realloc(_value, _valueLength); + + if (_value == NULL) { + _valueLength = 0; + return; + } + + _valueUpdated = true; + _updatedValueRead = false; + memcpy(_value, value, _valueLength); + + if (_valueUpdatedEventHandler) { + _valueUpdatedEventHandler(device, BLECharacteristic(this)); + } +} diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.h b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.h new file mode 100644 index 0000000..b53ab03 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteCharacteristic.h @@ -0,0 +1,84 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_REMOTE_CHARACTERISTIC_H_ +#define _BLE_REMOTE_CHARACTERISTIC_H_ + +#include "BLECharacteristic.h" + +#include "BLERemoteAttribute.h" +#include "BLERemoteDescriptor.h" + +#include "utility/BLELinkedList.h" + +class BLERemoteCharacteristic : public BLERemoteAttribute { +public: + BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t startHandle, uint16_t permissions, uint16_t valueHandle); + virtual ~BLERemoteCharacteristic(); + + uint8_t properties() const; + uint8_t permissions() const; + + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int writeValue(const uint8_t value[], int length, bool withResponse = true); + int writeValue(const char* value, bool withResponse = true); + + bool valueUpdated(); + bool updatedValueRead(); + + bool read(); + bool writeCccd(uint16_t value); + + unsigned int descriptorCount() const; + BLERemoteDescriptor* descriptor(unsigned int index) const; + + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler eventHandler); + +protected: + friend class ATTClass; + + uint16_t startHandle() const; + uint16_t valueHandle() const; + + void addDescriptor(BLERemoteDescriptor* descriptor); + + void writeValue(BLEDevice device, const uint8_t value[], int length); + +private: + uint16_t _connectionHandle; + uint16_t _startHandle; + uint8_t _properties; + uint8_t _permissions; + uint16_t _valueHandle; + + uint8_t* _value; + int _valueLength; + + bool _valueUpdated; + bool _updatedValueRead; + + BLELinkedList _descriptors; + + BLECharacteristicEventHandler _valueUpdatedEventHandler; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.cpp b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.cpp new file mode 100644 index 0000000..bf68675 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.cpp @@ -0,0 +1,134 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utility/ATT.h" + +#include "BLERemoteDescriptor.h" + +BLERemoteDescriptor::BLERemoteDescriptor(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t handle) : + BLERemoteAttribute(uuid, uuidLen), + _connectionHandle(connectionHandle), + _handle(handle), + _value(NULL), + _valueLength(0) +{ +} + +BLERemoteDescriptor::~BLERemoteDescriptor() +{ + if (_value) { + free(_value); + _value = NULL; + } +} + +const uint8_t* BLERemoteDescriptor::value() const +{ + return _value; +} + +int BLERemoteDescriptor::valueLength() const +{ + return _valueLength; +} + +uint8_t BLERemoteDescriptor::operator[] (int offset) const +{ + if (_value) { + return _value[offset]; + } + + return 0; +} + +int BLERemoteDescriptor::writeValue(const uint8_t value[], int length) +{ + if (!ATT.connected(_connectionHandle)) { + return false; + } + + uint16_t maxLength = ATT.mtu(_connectionHandle) - 3; + + if (length > (int)maxLength) { + // cap to MTU max length + length = maxLength; + } + + _value = (uint8_t*)realloc(_value, length); + if (_value == NULL) { + // realloc failed + return 0; + } + + uint8_t resp[4]; + int respLength = ATT.writeReq(_connectionHandle, _handle, value, length, resp); + + if (!respLength) { + return 0; + } + + if (resp[0] == 0x01) { + // error + return 0; + } + + memcpy(_value, value, length); + _valueLength = length; + + return 1; +} + +bool BLERemoteDescriptor::read() +{ + if (!ATT.connected(_connectionHandle)) { + return false; + } + + uint8_t resp[256]; + + int respLength = ATT.readReq(_connectionHandle, _handle, resp); + + if (!respLength) { + _valueLength = 0; + return false; + } + + if (resp[0] == 0x01) { + // error + _valueLength = 0; + return false; + } + + _valueLength = respLength - 1; + _value = (uint8_t*)realloc(_value, _valueLength); + + if (_value == NULL) { + _valueLength = 0; + return false; + } + + memcpy(_value, &resp[1], _valueLength); + + return true; +} + +uint16_t BLERemoteDescriptor::handle() const +{ + return _handle; +} diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.h b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.h new file mode 100644 index 0000000..68b428e --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDescriptor.h @@ -0,0 +1,50 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_REMOTE_DESCRIPTOR_H_ +#define _BLE_REMOTE_DESCRIPTOR_H_ + +#include "BLERemoteAttribute.h" + +class BLERemoteDescriptor : public BLERemoteAttribute { +public: + BLERemoteDescriptor(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t handle); + virtual ~BLERemoteDescriptor(); + + const uint8_t* value() const; + int valueLength() const; + uint8_t operator[] (int offset) const; + + int writeValue(const uint8_t value[], int length); + + bool read(); + +protected: + friend class ATTClass; + uint16_t handle() const; + +private: + uint16_t _connectionHandle; + uint16_t _handle; + + uint8_t* _value; + int _valueLength; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.cpp b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.cpp new file mode 100644 index 0000000..1a4a67a --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.cpp @@ -0,0 +1,59 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLERemoteDevice.h" + +BLERemoteDevice::BLERemoteDevice() +{ +} + +BLERemoteDevice::~BLERemoteDevice() +{ + clearServices(); +} + +void BLERemoteDevice::addService(BLERemoteService* service) +{ + service->retain(); + + _services.add(service); +} + +unsigned int BLERemoteDevice::serviceCount() const +{ + return _services.size(); +} + +BLERemoteService* BLERemoteDevice::service(unsigned int index) const +{ + return _services.get(index); +} + +void BLERemoteDevice::clearServices() +{ + for (unsigned int i = 0; i < serviceCount(); i++) { + BLERemoteService* s = service(i); + + if (s->release() == 0) { + delete s; + } + } + + _services.clear(); +} diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.h b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.h new file mode 100644 index 0000000..146a241 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteDevice.h @@ -0,0 +1,43 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_REMOTE_DEVICE_H_ +#define _BLE_REMOTE_DEVICE_H_ + +#include "utility/BLELinkedList.h" + +#include "BLERemoteService.h" + +class BLERemoteDevice /*: public BLEDevice*/ { +public: + BLERemoteDevice(); + virtual ~BLERemoteDevice(); + + void addService(BLERemoteService* service); + + unsigned int serviceCount() const; + BLERemoteService* service(unsigned int index) const; + + void clearServices(); + +private: + BLELinkedList _services; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.cpp b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.cpp new file mode 100644 index 0000000..f746129 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.cpp @@ -0,0 +1,67 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLERemoteService.h" + +BLERemoteService::BLERemoteService(const uint8_t uuid[], uint8_t uuidLen, uint16_t startHandle, uint16_t endHandle) : + BLERemoteAttribute(uuid, uuidLen), + _startHandle(startHandle), + _endHandle(endHandle) +{ +} + +BLERemoteService::~BLERemoteService() +{ + for (unsigned int i = 0; i < characteristicCount(); i++) { + BLERemoteCharacteristic* c = characteristic(i); + + if (c->release() == 0) { + delete c; + } + } + + _characteristics.clear(); +} + +uint16_t BLERemoteService::startHandle() const +{ + return _startHandle; +} + +uint16_t BLERemoteService::endHandle() const +{ + return _endHandle; +} + +unsigned int BLERemoteService::characteristicCount() const +{ + return _characteristics.size(); +} + +BLERemoteCharacteristic* BLERemoteService::characteristic(unsigned int index) const +{ + return _characteristics.get(index); +} + +void BLERemoteService::addCharacteristic(BLERemoteCharacteristic* characteristic) +{ + characteristic-> retain(); + + _characteristics.add(characteristic); +} diff --git a/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.h b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.h new file mode 100644 index 0000000..c7987ee --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/remote/BLERemoteService.h @@ -0,0 +1,53 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_REMOTE_SERVICE_H_ +#define _BLE_REMOTE_SERVICE_H_ + +#include "BLERemoteAttribute.h" +#include "BLERemoteCharacteristic.h" + +#include "utility/BLELinkedList.h" + +class BLERemoteService : public BLERemoteAttribute { +public: + BLERemoteService(const uint8_t uuid[], uint8_t uuidLen, uint16_t startHandle, uint16_t endHandle); + virtual ~BLERemoteService(); + + unsigned int characteristicCount() const; + BLERemoteCharacteristic* characteristic(unsigned int index) const; + +protected: + friend class ATTClass; + + uint16_t startHandle() const; + uint16_t endHandle() const; + + void addCharacteristic(BLERemoteCharacteristic* characteristic); + +private: + uint16_t _startHandle; + uint16_t _endHandle; + + String _uuid; + + BLELinkedList _characteristics; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/ATT.cpp b/arduino/libraries/ArduinoBLE/src/utility/ATT.cpp new file mode 100644 index 0000000..8a46e37 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/ATT.cpp @@ -0,0 +1,1967 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "HCI.h" +#include "GATT.h" + +#include "local/BLELocalAttribute.h" +#include "local/BLELocalCharacteristic.h" +#include "local/BLELocalDescriptor.h" +#include "local/BLELocalService.h" + +#include "remote/BLERemoteDevice.h" +#include "remote/BLERemoteService.h" + +#include "BLEProperty.h" + +#include "ATT.h" + +extern "C" int strcasecmp(char const *a, char const *b); + +#define ATT_OP_ERROR 0x01 +#define ATT_OP_MTU_REQ 0x02 +#define ATT_OP_MTU_RESP 0x03 +#define ATT_OP_FIND_INFO_REQ 0x04 +#define ATT_OP_FIND_INFO_RESP 0x05 +#define ATT_OP_FIND_BY_TYPE_REQ 0x06 +#define ATT_OP_FIND_BY_TYPE_RESP 0x07 +#define ATT_OP_READ_BY_TYPE_REQ 0x08 +#define ATT_OP_READ_BY_TYPE_RESP 0x09 +#define ATT_OP_READ_REQ 0x0a +#define ATT_OP_READ_RESP 0x0b +#define ATT_OP_READ_BLOB_REQ 0x0c +#define ATT_OP_READ_BLOB_RESP 0x0d +#define ATT_OP_READ_MULTI_REQ 0x0e +#define ATT_OP_READ_MULTI_RESP 0x0f +#define ATT_OP_READ_BY_GROUP_REQ 0x10 +#define ATT_OP_READ_BY_GROUP_RESP 0x11 +#define ATT_OP_WRITE_REQ 0x12 +#define ATT_OP_WRITE_RESP 0x13 +#define ATT_OP_WRITE_CMD 0x52 +#define ATT_OP_PREP_WRITE_REQ 0x16 +#define ATT_OP_PREP_WRITE_RESP 0x17 +#define ATT_OP_EXEC_WRITE_REQ 0x18 +#define ATT_OP_EXEC_WRITE_RESP 0x19 +#define ATT_OP_HANDLE_NOTIFY 0x1b +#define ATT_OP_HANDLE_IND 0x1d +#define ATT_OP_HANDLE_CNF 0x1e +#define ATT_OP_SIGNED_WRITE_CMD 0xd2 + +#define ATT_ECODE_INVALID_HANDLE 0x01 +#define ATT_ECODE_READ_NOT_PERM 0x02 +#define ATT_ECODE_WRITE_NOT_PERM 0x03 +#define ATT_ECODE_INVALID_PDU 0x04 +#define ATT_ECODE_AUTHENTICATION 0x05 +#define ATT_ECODE_REQ_NOT_SUPP 0x06 +#define ATT_ECODE_INVALID_OFFSET 0x07 +#define ATT_ECODE_AUTHORIZATION 0x08 +#define ATT_ECODE_PREP_QUEUE_FULL 0x09 +#define ATT_ECODE_ATTR_NOT_FOUND 0x0a +#define ATT_ECODE_ATTR_NOT_LONG 0x0b +#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0c +#define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0d +#define ATT_ECODE_UNLIKELY 0x0e +#define ATT_ECODE_INSUFF_ENC 0x0f +#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10 +#define ATT_ECODE_INSUFF_RESOURCES 0x11 + +// #define _BLE_TRACE_ + +ATTClass::ATTClass() : + _maxMtu(23), + _timeout(5000), + _longWriteHandle(0x0000), + _longWriteValue(NULL), + _longWriteValueLength(0) +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + _peers[i].connectionHandle = 0xffff; + _peers[i].role = 0x00; + _peers[i].addressType = 0x00; + memset(_peers[i].address, 0x00, sizeof(_peers[i].address)); + _peers[i].mtu = 23; + _peers[i].device = NULL; + _peers[i].encryption = 0x0; + } + + memset(_eventHandlers, 0x00, sizeof(_eventHandlers)); +} + +ATTClass::~ATTClass() +{ + if (_longWriteValue) { + free(_longWriteValue); + } +} + +bool ATTClass::connect(uint8_t peerBdaddrType, uint8_t peerBdaddr[6]) +{ + if (HCI.leCreateConn(0x0060, 0x0030, 0x00, peerBdaddrType, peerBdaddr, 0x00, + 0x0006, 0x000c, 0x0000, 0x00c8, 0x0004, 0x0006) != 0) { + return false; + } + + bool isConnected = false; + + for (unsigned long start = millis(); (millis() - start) < _timeout;) { + HCI.poll(); + + isConnected = connected(peerBdaddrType, peerBdaddr); + + if (isConnected) { + break; + } + } + + if (!isConnected) { + HCI.leCancelConn(); + } + + return isConnected; +} + +bool ATTClass::disconnect(uint8_t peerBdaddrType, uint8_t peerBdaddr[6]) +{ + uint16_t connHandle = connectionHandle(peerBdaddrType, peerBdaddr); + if (connHandle == 0xffff) { + return false; + } + + HCI.disconnect(connHandle); + + for (unsigned long start = millis(); (millis() - start) < _timeout;) { + HCI.poll(); + + if (!connected(connHandle)) { + return true; + } + } + + return false; +} + +bool ATTClass::discoverAttributes(uint8_t peerBdaddrType, uint8_t peerBdaddr[6], const char* serviceUuidFilter) +{ + uint16_t connHandle = connectionHandle(peerBdaddrType, peerBdaddr); + if (connHandle == 0xffff) { + return false; + } + + // send MTU request + if (!exchangeMtu(connHandle)) { + return false; + } + + // find the device entry for the peeer + BLERemoteDevice* device = NULL; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connHandle) { + if (_peers[i].device == NULL) { + _peers[i].device = new BLERemoteDevice(); + } + + device = _peers[i].device; + + break; + } + } + + if (device == NULL) { + return false; + } + + if (serviceUuidFilter == NULL) { + // clear existing services + device->clearServices(); + } else { + int serviceCount = device->serviceCount(); + + for (int i = 0; i < serviceCount; i++) { + BLERemoteService* service = device->service(i); + + if (strcasecmp(service->uuid(), serviceUuidFilter) == 0) { + // found an existing service with same UUID + return true; + } + } + } + + // discover services + if (!discoverServices(connHandle, device, serviceUuidFilter)) { + return false; + } + + // discover characteristics + if (!discoverCharacteristics(connHandle, device)) { + return false; + } + + // discover descriptors + if (!discoverDescriptors(connHandle, device)) { + return false; + } + + return true; +} + +void ATTClass::setMaxMtu(uint16_t maxMtu) +{ + _maxMtu = maxMtu; +} + +void ATTClass::setTimeout(unsigned long timeout) +{ + _timeout = timeout; +} + +void ATTClass::addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrType, + uint8_t peerBdaddr[6], uint16_t /*interval*/, + uint16_t /*latency*/, uint16_t /*supervisionTimeout*/, + uint8_t /*masterClockAccuracy*/) +{ + int peerIndex = -1; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == 0xffff) { + peerIndex = i; + break; + } + } + + if (peerIndex == -1) { + // bail, no space + return; + } + + _peers[peerIndex].connectionHandle = handle; + _peers[peerIndex].role = role; + _peers[peerIndex].mtu = 23; + _peers[peerIndex].addressType = peerBdaddrType; + memcpy(_peers[peerIndex].address, peerBdaddr, sizeof(_peers[peerIndex].address)); + uint8_t BDADDr[6]; + for(int i=0; i<6; i++) BDADDr[5-i] = peerBdaddr[i]; + if(HCI.tryResolveAddress(BDADDr,_peers[peerIndex].resolvedAddress)){ +#ifdef _BLE_TRACE_ + Serial.println("Found match."); +#endif + }else{ +#ifdef _BLE_TRACE_ + Serial.println("No matching MAC"); +#endif + memset(&_peers[peerIndex].resolvedAddress, 0, 6); + } + + if (_eventHandlers[BLEConnected]) { + _eventHandlers[BLEConnected](BLEDevice(peerBdaddrType, peerBdaddr)); + } +} + +void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + uint8_t opcode = data[0]; + + dlen--; + data++; + + uint16_t mtu = this->mtu(connectionHandle); + +#ifdef _BLE_TRACE_ + Serial.print("data opcode: 0x"); + Serial.println(opcode, HEX); +#endif + switch (opcode) { + case ATT_OP_ERROR: +#ifdef _BLE_TRACE_ + Serial.println("[Info] data error"); + // Serial.print("Error: "); + // btct.printBytes(data, dlen); +#endif + error(connectionHandle, dlen, data); + break; + + case ATT_OP_MTU_REQ: +#ifdef _BLE_TRACE_ + Serial.println("MTU"); +#endif + mtuReq(connectionHandle, dlen, data); + break; + + case ATT_OP_MTU_RESP: + mtuResp(connectionHandle, dlen, data); + break; + + case ATT_OP_FIND_INFO_REQ: +#ifdef _BLE_TRACE_ + Serial.println("Find info"); +#endif + findInfoReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_FIND_INFO_RESP: + findInfoResp(connectionHandle, dlen, data); + break; + + case ATT_OP_FIND_BY_TYPE_REQ: + findByTypeReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_READ_BY_TYPE_REQ: +#ifdef _BLE_TRACE_ + Serial.println("By type"); +#endif + readByTypeReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_READ_BY_TYPE_RESP: + readByTypeResp(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_BY_GROUP_REQ: + readByGroupReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_READ_BY_GROUP_RESP: + readByGroupResp(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_REQ: + case ATT_OP_READ_BLOB_REQ: + readOrReadBlobReq(connectionHandle, mtu, opcode, dlen, data); + break; + + case ATT_OP_READ_RESP: + readResp(connectionHandle, dlen, data); + break; + + case ATT_OP_WRITE_REQ: + case ATT_OP_WRITE_CMD: +#ifdef _BLE_TRACE_ + Serial.println("Write req"); +#endif + writeReqOrCmd(connectionHandle, mtu, opcode, dlen, data); + break; + + case ATT_OP_WRITE_RESP: + writeResp(connectionHandle, dlen, data); + break; + + case ATT_OP_PREP_WRITE_REQ: + prepWriteReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_EXEC_WRITE_REQ: + execWriteReq(connectionHandle, mtu, dlen, data); + break; + + case ATT_OP_HANDLE_NOTIFY: + case ATT_OP_HANDLE_IND: + handleNotifyOrInd(connectionHandle, opcode, dlen, data); + break; + + case ATT_OP_HANDLE_CNF: + handleCnf(connectionHandle, dlen, data); + break; + + case ATT_OP_READ_MULTI_REQ: + case ATT_OP_SIGNED_WRITE_CMD: + default: +#ifdef _BLE_TRACE_ + Serial.println("[Info] Unhandled dara"); +#endif + sendError(connectionHandle, opcode, 0x00, ATT_ECODE_REQ_NOT_SUPP); + break; + } +} + +void ATTClass::removeConnection(uint16_t handle, uint8_t /*reason*/) +{ + int peerIndex = -1; + int peerCount = 0; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == handle) { + peerIndex = i; + } + + if (_peers[i].connectionHandle != 0xffff) { + peerCount++; + } + } + + if (peerIndex == -1) { + // bail not found + return; + } + + BLEDevice bleDevice(_peers[peerIndex].addressType, _peers[peerIndex].address); + + if (peerCount == 1) { + // clear CCCD values on disconnect + for (uint16_t i = 0; i < GATT.attributeCount(); i++) { + BLELocalAttribute* attribute = GATT.attribute(i); + + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + characteristic->writeCccdValue(bleDevice, 0x0000); + } + } + + _longWriteHandle = 0x0000; + _longWriteValueLength = 0; + } + + if (_eventHandlers[BLEDisconnected]) { + _eventHandlers[BLEDisconnected](bleDevice); + } + + _peers[peerIndex].connectionHandle = 0xffff; + _peers[peerIndex].role = 0x00; + _peers[peerIndex].addressType = 0x00; + memset(_peers[peerIndex].address, 0x00, sizeof(_peers[peerIndex].address)); + _peers[peerIndex].mtu = 23; + _peers[peerIndex].encryption = PEER_ENCRYPTION::NO_ENCRYPTION; + _peers[peerIndex].IOCap[0] = 0; + _peers[peerIndex].IOCap[1] = 0; + _peers[peerIndex].IOCap[2] = 0; + + if (_peers[peerIndex].device) { + delete _peers[peerIndex].device; + } + _peers[peerIndex].device = NULL; +} + +uint16_t ATTClass::connectionHandle(uint8_t addressType, const uint8_t address[6]) const +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].addressType == addressType && memcmp(_peers[i].address, address, 6) == 0) { + return _peers[i].connectionHandle; + } + } + + return 0xffff; +} + +BLERemoteDevice* ATTClass::device(uint8_t addressType, const uint8_t address[6]) const +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].addressType == addressType && memcmp(_peers[i].address, address, 6) == 0) { + return _peers[i].device; + } + } + + return NULL; +} + +bool ATTClass::connected() const +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle != 0xffff) { + return true; + } + } + + return false; +} + +bool ATTClass::connected(uint8_t addressType, const uint8_t address[6]) const +{ + return (connectionHandle(addressType, address) != 0xffff); +} + +bool ATTClass::connected(uint16_t handle) const +{ + HCI.poll(); + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == handle) { + return true; + } + } + + return false; +} + +/* + * Return true if any of the known devices is paired (peer encrypted) + * Does not check if the paired device is also connected + */ +bool ATTClass::paired() const +{ + for(int i=0; i 0){ + return true; + } + } + return false; +} + +/* + * Return true if the specified device is paired (peer encrypted) + */ +bool ATTClass::paired(uint16_t handle) const +{ + for(int i=0; i 0; + } + return false; // unknown handle +} + +uint16_t ATTClass::mtu(uint16_t handle) const +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == handle) { + return _peers[i].mtu; + } + } + + return 23; +} + +bool ATTClass::disconnect() +{ + int numDisconnects = 0; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == 0xffff) { + continue; + } + + if (HCI.disconnect(_peers[i].connectionHandle) != 0) { + continue; + } + + numDisconnects++; + + BLEDevice bleDevice(_peers[i].addressType, _peers[i].address); + + // clear CCCD values on disconnect + for (uint16_t att = 0; att < GATT.attributeCount(); att++) { + BLELocalAttribute* attribute = GATT.attribute(att); + + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + characteristic->writeCccdValue(bleDevice, 0x0000); + } + } + + _longWriteHandle = 0x0000; + _longWriteValueLength = 0; + + _peers[i].connectionHandle = 0xffff; + _peers[i].role = 0x00; + _peers[i].addressType = 0x00; + memset(_peers[i].address, 0x00, sizeof(_peers[i].address)); + memset(_peers[i].resolvedAddress, 0x00, sizeof(_peers[i].resolvedAddress)); + _peers[i].mtu = 23; + + if (_peers[i].device) { + delete _peers[i].device; + } + _peers[i].device = NULL; + } + + return (numDisconnects > 0); +} + +BLEDevice ATTClass::central() +{ + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == 0xffff || _peers[i].role != 0x01) { + continue; + } + + return BLEDevice(_peers[i].addressType, _peers[i].address); + } + + return BLEDevice(); +} + +int ATTClass::handleNotify(uint16_t handle, const uint8_t* value, int length) +{ + int numNotifications = 0; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == 0xffff) { + continue; + } + + uint8_t notification[_peers[i].mtu]; + uint16_t notificationLength = 0; + + notification[0] = ATT_OP_HANDLE_NOTIFY; + notificationLength++; + + memcpy(¬ification[1], &handle, sizeof(handle)); + notificationLength += sizeof(handle); + + length = min((uint16_t)(_peers[i].mtu - notificationLength), (uint16_t)length); + memcpy(¬ification[notificationLength], value, length); + notificationLength += length; + + /// TODO: Set encryption requirement on notify. + HCI.sendAclPkt(_peers[i].connectionHandle, ATT_CID, notificationLength, notification); + + numNotifications++; + } + + return (numNotifications > 0) ? length : 0; +} + +int ATTClass::handleInd(uint16_t handle, const uint8_t* value, int length) +{ + int numIndications = 0; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == 0xffff) { + continue; + } + + uint8_t indication[_peers[i].mtu]; + uint16_t indicationLength = 0; + + indication[0] = ATT_OP_HANDLE_IND; + indicationLength++; + + memcpy(&indication[1], &handle, sizeof(handle)); + indicationLength += sizeof(handle); + + length = min((uint16_t)(_peers[i].mtu - indicationLength), (uint16_t)length); + memcpy(&indication[indicationLength], value, length); + indicationLength += length; + + _cnf = false; + + HCI.sendAclPkt(_peers[i].connectionHandle, ATT_CID, indicationLength, indication); + + while (!_cnf) { + HCI.poll(); + + if (!connected(_peers[i].addressType, _peers[i].address)) { + break; + } + } + + numIndications++; + } + + return (numIndications > 0) ? length : 0; +} + +void ATTClass::error(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen != 4) { + // drop + return; + } + + struct __attribute__ ((packed)) AttError { + uint8_t opcode; + uint16_t handle; + uint8_t code; + } *attError = (AttError*)data; + + if (_pendingResp.connectionHandle == connectionHandle && (_pendingResp.op - 1) == attError->opcode) { + _pendingResp.buffer[0] = ATT_OP_ERROR; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + uint16_t mtu = *(uint16_t*)data; + + if (dlen != 2) { + sendError(connectionHandle, ATT_OP_MTU_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + if (mtu > _maxMtu) { + mtu = _maxMtu; + } + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + _peers[i].mtu = mtu; + break; + } + } + + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t mtu; + } mtuResp = { ATT_OP_MTU_RESP, mtu }; + + HCI.sendAclPkt(connectionHandle, ATT_CID, sizeof(mtuResp), &mtuResp); +} + +int ATTClass::mtuReq(uint16_t connectionHandle, uint16_t mtu, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t mtu; + } mtuReq = { ATT_OP_MTU_REQ, mtu }; + + return sendReq(connectionHandle, &mtuReq, sizeof(mtuReq), responseBuffer); +} + +void ATTClass::mtuResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + uint16_t mtu = *(uint16_t*)data; + + if (dlen != 2) { + return; + } + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + _peers[i].mtu = mtu; + break; + } + } + + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_MTU_RESP) { + _pendingResp.buffer[0] = ATT_OP_MTU_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::findInfoReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) FindInfoReq { + uint16_t startHandle; + uint16_t endHandle; + } *findInfoReq = (FindInfoReq*)data; + + if (dlen != sizeof(FindInfoReq)) { + sendError(connectionHandle, ATT_OP_FIND_INFO_REQ, findInfoReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_FIND_INFO_RESP; + response[1] = 0x00; + responseLength = 2; + + for (uint16_t i = (findInfoReq->startHandle - 1); i < GATT.attributeCount() && i <= (findInfoReq->endHandle - 1); i++) { + BLELocalAttribute* attribute = GATT.attribute(i); + uint16_t handle = (i + 1); + bool isValueHandle = (attribute->type() == BLETypeCharacteristic) && (((BLELocalCharacteristic*)attribute)->valueHandle() == handle); + bool isDescriptor = attribute->type() == BLETypeDescriptor; + int uuidLen = (isValueHandle || isDescriptor) ? attribute->uuidLength() : BLE_ATTRIBUTE_TYPE_SIZE; + int infoType = (uuidLen == 2) ? 0x01 : 0x02; + + if (response[1] == 0) { + response[1] = infoType; + } + + if (response[1] != infoType) { + // different type + break; + } + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + if (isValueHandle || isDescriptor) { + // add the UUID + memcpy(&response[responseLength], attribute->uuidData(), uuidLen); + responseLength += uuidLen; + } else { + // add the type + uint16_t type = attribute->type(); + + memcpy(&response[responseLength], &type, sizeof(type)); + responseLength += sizeof(type); + } + + if ((responseLength + (2 + uuidLen)) > mtu) { + break; + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_FIND_INFO_REQ, findInfoReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +int ATTClass::findInfoReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t startHandle; + uint16_t endHandle; + } findInfoReq = { ATT_OP_FIND_INFO_REQ, startHandle, endHandle }; + + return sendReq(connectionHandle, &findInfoReq, sizeof(findInfoReq), responseBuffer); +} + +void ATTClass::findInfoResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen < 2) { + return; // invalid, drop + } + + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_FIND_INFO_RESP) { + _pendingResp.buffer[0] = ATT_OP_FIND_INFO_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::findByTypeReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) FindByTypeReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t type; + } *findByTypeReq = (FindByTypeReq*)data; + + if (dlen < sizeof(FindByTypeReq)) { + sendError(connectionHandle, ATT_OP_FIND_BY_TYPE_REQ, findByTypeReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint16_t valueLength = dlen - sizeof(*findByTypeReq); + uint8_t* value = &data[sizeof(*findByTypeReq)]; + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_FIND_BY_TYPE_RESP; + responseLength = 1; + + if (findByTypeReq->type == BLETypeService) { + for (uint16_t i = (findByTypeReq->startHandle - 1); i < GATT.attributeCount() && i <= (findByTypeReq->endHandle - 1); i++) { + BLELocalAttribute* attribute = GATT.attribute(i); + + if ((attribute->type() == findByTypeReq->type) && (attribute->uuidLength() == valueLength) && memcmp(attribute->uuidData(), value, valueLength) == 0) { + BLELocalService* service = (BLELocalService*)attribute; + + // add the start handle + uint16_t startHandle = service->startHandle(); + memcpy(&response[responseLength], &startHandle, sizeof(startHandle)); + responseLength += sizeof(startHandle); + + // add the end handle + uint16_t endHandle = service->endHandle(); + memcpy(&response[responseLength], &endHandle, sizeof(endHandle)); + responseLength += sizeof(endHandle); + } + + if ((responseLength + 4) > mtu) { + break; + } + } + } + + if (responseLength == 1) { + sendError(connectionHandle, ATT_OP_FIND_BY_TYPE_REQ, findByTypeReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::readByGroupReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) ReadByGroupReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t uuid; + } *readByGroupReq = (ReadByGroupReq*)data; +#ifdef _BLE_TRACE_ + Serial.print("readByGroupReq: start: 0x"); + Serial.println(readByGroupReq->startHandle,HEX); + Serial.print("readByGroupReq: end: 0x"); + Serial.println(readByGroupReq->endHandle,HEX); + Serial.print("readByGroupReq: UUID: 0x"); + Serial.println(readByGroupReq->uuid,HEX); +#endif + + if (dlen != sizeof(ReadByGroupReq) || (readByGroupReq->uuid != BLETypeService && readByGroupReq->uuid != 0x2801)) { + sendError(connectionHandle, ATT_OP_READ_BY_GROUP_REQ, readByGroupReq->startHandle, ATT_ECODE_UNSUPP_GRP_TYPE); + return; + } + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_READ_BY_GROUP_RESP; + response[1] = 0x00; + responseLength = 2; +#ifdef _BLE_TRACE_ + Serial.print("readByGroupReq: attrcount: "); + Serial.println(GATT.attributeCount()); +#endif + for (uint16_t i = (readByGroupReq->startHandle - 1); i < GATT.attributeCount() && i <= (readByGroupReq->endHandle - 1); i++) { + BLELocalAttribute* attribute = GATT.attribute(i); + + if (readByGroupReq->uuid != attribute->type()) { + // not the type + continue; + } + + int uuidLen = attribute->uuidLength(); + int infoSize = (uuidLen == 2) ? 6 : 20; + + if (response[1] == 0) { + response[1] = infoSize; + } + + if (response[1] != infoSize) { + // different size + break; + } + + BLELocalService* service = (BLELocalService*)attribute; + + // add the start handle + uint16_t startHandle = service->startHandle(); + memcpy(&response[responseLength], &startHandle, sizeof(startHandle)); + responseLength += sizeof(startHandle); + + // add the end handle + uint16_t endHandle = service->endHandle(); + memcpy(&response[responseLength], &endHandle, sizeof(endHandle)); + responseLength += sizeof(endHandle); + + // add the UUID + memcpy(&response[responseLength], service->uuidData(), uuidLen); + responseLength += uuidLen; + + if ((responseLength + infoSize) > mtu) { + break; + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_READ_BY_GROUP_REQ, readByGroupReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +int ATTClass::readByGroupReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint16_t uuid, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t startHandle; + uint16_t endHandle; + uint16_t uuid; + } readByGroupReq = { ATT_OP_READ_BY_GROUP_REQ, startHandle, endHandle, uuid }; + + return sendReq(connectionHandle, &readByGroupReq, sizeof(readByGroupReq), responseBuffer); +} + +void ATTClass::readByGroupResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen < 2) { + return; // invalid, drop + } + + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_READ_BY_GROUP_RESP) { + _pendingResp.buffer[0] = ATT_OP_READ_BY_GROUP_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::readOrReadBlobReq(uint16_t connectionHandle, uint16_t mtu, uint8_t opcode, uint8_t dlen, uint8_t data[]) +{ + if (opcode == ATT_OP_READ_REQ) { + if (dlen != sizeof(uint16_t)) { + sendError(connectionHandle, ATT_OP_READ_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + } else { + if (dlen != (sizeof(uint16_t) + sizeof(uint16_t))) { + sendError(connectionHandle, ATT_OP_READ_BLOB_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + } + /// if auth error, hold the response in a buffer. + bool holdResponse = false; + uint16_t handle; + memcpy(&handle, data, sizeof(handle)); + uint16_t offset = (opcode == ATT_OP_READ_REQ) ? 0 : *(uint16_t*)&data[sizeof(handle)]; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_ATTR_NOT_FOUND); + return; + } + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = (opcode == ATT_OP_READ_REQ) ? ATT_OP_READ_RESP : ATT_OP_READ_BLOB_RESP; + responseLength = 1; + + BLELocalAttribute* attribute = GATT.attribute(handle - 1); + enum BLEAttributeType attributeType = attribute->type(); + + if (attributeType == BLETypeService) { + if (offset) { + sendError(connectionHandle, ATT_ECODE_ATTR_NOT_LONG, handle, ATT_ECODE_INVALID_PDU); + return; + } + + BLELocalService* service = (BLELocalService*)attribute; + + // add the UUID + uint8_t uuidLen = service->uuidLength(); + memcpy(&response[responseLength], service->uuidData(), uuidLen); + responseLength += uuidLen; + } else if (attributeType == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (characteristic->handle() == handle) { + if (offset) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + // add the properties + response[responseLength++] = characteristic->properties(); + + // add the value handle + uint16_t valueHandle = characteristic->valueHandle(); + memcpy(&response[responseLength], &valueHandle, sizeof(valueHandle)); + responseLength += sizeof(valueHandle); + + // add the UUID + uint8_t uuidLen = characteristic->uuidLength(); + memcpy(&response[responseLength], characteristic->uuidData(), uuidLen); + responseLength += uuidLen; + } else { + if ((characteristic->properties() & BLERead) == 0) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_READ_NOT_PERM); + return; + } + // If characteristic requires encryption send error & hold response until encrypted + if ((characteristic->permissions() & (BLEPermission::BLEEncryption >> 8)) > 0 && + (getPeerEncryption(connectionHandle) & PEER_ENCRYPTION::ENCRYPTED_AES)==0 ) { + holdResponse = true; + sendError(connectionHandle, opcode, handle, ATT_ECODE_INSUFF_ENC); + } + + uint16_t valueLength = characteristic->valueLength(); + + if (offset >= valueLength) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + valueLength = min(mtu - responseLength, valueLength - offset); + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + characteristic->readValue(BLEDevice(_peers[i].addressType, _peers[i].address), offset, &response[responseLength], valueLength); + responseLength += valueLength; + } + } + } + } else if (attributeType == BLETypeDescriptor) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + uint16_t valueLength = descriptor->valueSize(); + + if (offset >= valueLength) { + sendError(connectionHandle, opcode, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + valueLength = min(mtu - responseLength, valueLength - offset); + + memcpy(&response[responseLength], descriptor->value() + offset, valueLength); + responseLength += valueLength; + } + if(holdResponse){ + memcpy(holdBuffer, response, responseLength); + holdBufferSize = responseLength; + }else{ + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +void ATTClass::readResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_READ_RESP) { + _pendingResp.buffer[0] = ATT_OP_READ_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::readByTypeReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) ReadByTypeReq { + uint16_t startHandle; + uint16_t endHandle; + uint16_t uuid; + } *readByTypeReq = (ReadByTypeReq*)data; + + if (dlen != sizeof(ReadByTypeReq)) { + sendError(connectionHandle, ATT_OP_READ_BY_TYPE_REQ, readByTypeReq->startHandle, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_READ_BY_TYPE_RESP; + response[1] = 0x00; + responseLength = 2; + + for (uint16_t i = (readByTypeReq->startHandle - 1); i < GATT.attributeCount() && i <= (readByTypeReq->endHandle - 1); i++) { + BLELocalAttribute* attribute = GATT.attribute(i); + uint16_t handle = (i + 1); + + if (attribute->type() == readByTypeReq->uuid) { + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (characteristic->valueHandle() == handle) { + // value handle, skip + continue; + } + + int uuidLen = attribute->uuidLength(); + int typeSize = (uuidLen == 2) ? 7 : 21; + + if (response[1] == 0) { + response[1] = typeSize; + } + + if (response[1] != typeSize) { + // all done, wrong size + break; + } + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the properties + response[responseLength++] = characteristic->properties(); + + // add the value handle + uint16_t valueHandle = (handle + 1); + memcpy(&response[responseLength], &valueHandle, sizeof(valueHandle)); + responseLength += sizeof(valueHandle); + + // add the UUID + memcpy(&response[responseLength], characteristic->uuidData(), uuidLen); + responseLength += uuidLen; + + // skip the next handle, it's a value handle + i++; + + if ((responseLength + typeSize) > mtu) { + break; + } + } else if (attribute->type() == 0x2902) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the value + int valueSize = min((uint16_t)(mtu - responseLength), (uint16_t)descriptor->valueSize()); + memcpy(&response[responseLength], descriptor->value(), valueSize); + responseLength += valueSize; + + response[1] = 2 + valueSize; + + break; // all done + } + } else if (attribute->type() == BLETypeCharacteristic && attribute->uuidLength() == 2 && memcmp(&readByTypeReq->uuid, attribute->uuidData(), 2) == 0) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + // add the handle + memcpy(&response[responseLength], &handle, sizeof(handle)); + responseLength += sizeof(handle); + + // add the value + int valueLength = min((uint16_t)(mtu - responseLength), (uint16_t)characteristic->valueLength()); + memcpy(&response[responseLength], characteristic->value(), valueLength); + responseLength += valueLength; + + response[1] = 2 + valueLength; + + break; // all done + } + } + + if (responseLength == 2) { + sendError(connectionHandle, ATT_OP_READ_BY_TYPE_REQ, readByTypeReq->startHandle, ATT_ECODE_ATTR_NOT_FOUND); + } else { + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } +} + +int ATTClass::readByTypeReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint16_t type, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t startHandle; + uint16_t endHandle; + uint16_t type; + } readByTypeReq = { ATT_OP_READ_BY_TYPE_REQ, startHandle, endHandle, type }; + + return sendReq(connectionHandle, &readByTypeReq, sizeof(readByTypeReq), responseBuffer); +} + +void ATTClass::readByTypeResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen < 1) { + return; // invalid, drop + } + + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_READ_BY_TYPE_RESP) { + _pendingResp.buffer[0] = ATT_OP_READ_BY_TYPE_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::writeReqOrCmd(uint16_t connectionHandle, uint16_t mtu, uint8_t op, uint8_t dlen, uint8_t data[]) +{ + bool withResponse = (op == ATT_OP_WRITE_REQ); + + if (dlen < sizeof(uint16_t)) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + } + return; + } + + uint16_t handle; + memcpy(&handle, data, sizeof(handle)); + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_FOUND); + } + return; + } + + uint8_t valueLength = dlen - sizeof(handle); + uint8_t* value = &data[sizeof(handle)]; + + BLELocalAttribute* attribute = GATT.attribute(handle - 1); + bool holdResponse = false; + + if (attribute->type() == BLETypeCharacteristic) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (handle != characteristic->valueHandle() || + withResponse ? ((characteristic->properties() & BLEWrite) == 0) : + ((characteristic->properties() & BLEWriteWithoutResponse) == 0)) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + // Check permission + if((characteristic->permissions() &( BLEPermission::BLEEncryption >> 8)) > 0 && + (getPeerEncryption(connectionHandle) & PEER_ENCRYPTION::ENCRYPTED_AES) == 0){ + holdResponse = true; + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_INSUFF_ENC); + } + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + if(holdResponse){ + + writeBufferSize = 0; + memcpy(writeBuffer, &handle, 2); + writeBufferSize+=2; + + writeBuffer[writeBufferSize++] = _peers[i].addressType; + + memcpy(&writeBuffer[writeBufferSize], _peers[i].address, sizeof(_peers[i].address)); + writeBufferSize += sizeof(_peers[i].address); + + writeBuffer[writeBufferSize] = valueLength; + writeBufferSize += sizeof(valueLength); + + if (writeBufferSize + valueLength > sizeof(writeBuffer)) { + sendError(connectionHandle, op, handle, ATT_ECODE_INSUFF_RESOURCES); + return; + } + memcpy(&writeBuffer[writeBufferSize], value, valueLength); + writeBufferSize += valueLength; + }else{ + characteristic->writeValue(BLEDevice(_peers[i].addressType, _peers[i].address), value, valueLength); + } + break; + } + } + } else if (attribute->type() == BLETypeDescriptor) { + BLELocalDescriptor* descriptor = (BLELocalDescriptor*)attribute; + + // only CCCD's are writable + if (descriptor->uuidLength() != 2 || *((uint16_t*)(descriptor->uuidData())) != 0x2902) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + // get the previous handle, should be the characteristic for the CCCD + attribute = GATT.attribute(handle - 2); + + if (attribute->type() != BLETypeCharacteristic) { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + characteristic->writeCccdValue(BLEDevice(_peers[i].addressType, _peers[i].address), *((uint16_t*)value)); + break; + } + } + } else { + if (withResponse) { + sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + } + return; + } + + if (withResponse) { + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_WRITE_RESP; + responseLength = 1; + + if(holdResponse){ + memcpy(holdBuffer, response, responseLength); + holdBufferSize = responseLength; + }else{ + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); + } + } +} +int ATTClass::processWriteBuffer(){ + if(writeBufferSize==0){ + return 0; + } + + struct __attribute__ ((packed)) WriteBuffer { + uint16_t handle; + uint8_t addressType; + uint8_t address[6]; + uint8_t valueLength; + uint8_t value[]; + } *writeBufferStruct = (WriteBuffer*)&ATT.writeBuffer; + // uint8_t value[writeBufferStruct->valueLength]; + // memcpy(value, writeBufferStruct->value, writeBufferStruct->valueLength); + BLELocalAttribute* attribute = GATT.attribute(writeBufferStruct->handle-1); + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; +#ifdef _BLE_TRACE_ + Serial.println("Writing value"); +#endif + characteristic->writeValue(BLEDevice(writeBufferStruct->addressType, writeBufferStruct->address), writeBufferStruct->value, writeBufferStruct->valueLength); + writeBufferSize = 0; + return 1; +} + +void ATTClass::writeResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + if (dlen != 0) { + return; // drop + } + + if (connectionHandle == _pendingResp.connectionHandle && _pendingResp.op == ATT_OP_WRITE_RESP) { + _pendingResp.buffer[0] = ATT_OP_WRITE_RESP; + memcpy(&_pendingResp.buffer[1], data, dlen); + _pendingResp.length = dlen + 1; + } +} + +void ATTClass::prepWriteReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) PrepWriteReq { + uint16_t handle; + uint16_t offset; + } *prepWriteReq = (PrepWriteReq*)data; + + if (dlen < sizeof(PrepWriteReq)) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + uint16_t handle = prepWriteReq->handle; + uint16_t offset = prepWriteReq->offset; + + if ((uint16_t)(handle - 1) > GATT.attributeCount()) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_FOUND); + return; + } + + BLELocalAttribute* attribute = GATT.attribute(handle - 1); + + if (attribute->type() != BLETypeCharacteristic) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute; + + if (handle != characteristic->valueHandle()) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_ATTR_NOT_LONG); + return; + } + + if ((characteristic->properties() & BLEWrite) == 0) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_WRITE_NOT_PERM); + return; + } + + if (_longWriteHandle == 0) { + int valueSize = characteristic->valueSize(); + + _longWriteValue = (uint8_t*)realloc(_longWriteValue, valueSize); + _longWriteValueLength = 0; + _longWriteHandle = handle; + + memset(_longWriteValue, 0x00, valueSize); + } else if (_longWriteHandle != handle) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_UNLIKELY); + return; + } + + uint8_t valueLength = dlen - sizeof(PrepWriteReq); + uint8_t* value = &data[sizeof(PrepWriteReq)]; + + if ((offset != _longWriteValueLength) || ((offset + valueLength) > (uint16_t)characteristic->valueSize())) { + sendError(connectionHandle, ATT_OP_PREP_WRITE_REQ, handle, ATT_ECODE_INVALID_OFFSET); + return; + } + + memcpy(_longWriteValue + offset, value, valueLength); + _longWriteValueLength += valueLength; + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_PREP_WRITE_RESP; + memcpy(&response[1], data, dlen); + responseLength = dlen + 1; + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); +} + +void ATTClass::execWriteReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]) +{ + if (dlen != sizeof(uint8_t)) { + sendError(connectionHandle, ATT_OP_EXEC_WRITE_REQ, 0x0000, ATT_ECODE_INVALID_PDU); + return; + } + + uint8_t flag = data[0]; + + if (_longWriteHandle && (flag & 0x01)) { + BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)GATT.attribute(_longWriteHandle - 1); + + for (int i = 0; i < ATT_MAX_PEERS; i++) { + if (_peers[i].connectionHandle == connectionHandle) { + characteristic->writeValue(BLEDevice(_peers[i].addressType, _peers[i].address), _longWriteValue, _longWriteValueLength); + break; + } + } + } + + _longWriteHandle = 0x0000; + _longWriteValueLength = 0; + + uint8_t response[mtu]; + uint16_t responseLength; + + response[0] = ATT_OP_EXEC_WRITE_RESP; + responseLength = 1; + + HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response); +} + +void ATTClass::handleNotifyOrInd(uint16_t connectionHandle, uint8_t opcode, uint8_t dlen, uint8_t data[]) +{ + if (dlen < 2) { + return; // drop + } + + struct __attribute__ ((packed)) HandleNotifyOrInd { + uint16_t handle; + } *handleNotifyOrInd = (HandleNotifyOrInd*)data; + + uint16_t handle = handleNotifyOrInd->handle; + + for (int peer = 0; peer < ATT_MAX_PEERS; peer++) { + if (_peers[peer].connectionHandle != connectionHandle) { + continue; + } + + BLERemoteDevice* device = _peers[peer].device; + + if (!device) { + break; + } + + int serviceCount = device->serviceCount(); + + for (int i = 0; i < serviceCount; i++) { + BLERemoteService* s = device->service(i); + + if (s->startHandle() < handle && s->endHandle() >= handle) { + int characteristicCount = s->characteristicCount(); + + for (int j = 0; j < characteristicCount; j++) { + BLERemoteCharacteristic* c = s->characteristic(j); + + if (c->valueHandle() == handle) { + c->writeValue(BLEDevice(_peers[peer].addressType, _peers[peer].address), &data[2], dlen - 2); + } + } + + break; + } + } + } + + if (opcode == ATT_OP_HANDLE_IND) { + // send CNF for IND + + uint8_t cnf = ATT_OP_HANDLE_CNF; + + HCI.sendAclPkt(connectionHandle, ATT_CID, sizeof(cnf), &cnf); + } +} + +void ATTClass::handleCnf(uint16_t /*connectionHandle*/, uint8_t /*dlen*/, uint8_t /*data*/[]) +{ + _cnf = true; +} + +void ATTClass::sendError(uint16_t connectionHandle, uint8_t opcode, uint16_t handle, uint8_t code) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint8_t opcode; + uint16_t handle; + uint8_t code; + } attError = { ATT_OP_ERROR, opcode, handle, code }; + + HCI.sendAclPkt(connectionHandle, ATT_CID, sizeof(attError), &attError); +} + + +bool ATTClass::exchangeMtu(uint16_t connectionHandle) +{ + uint8_t responseBuffer[_maxMtu]; + + if (!mtuReq(connectionHandle, _maxMtu, responseBuffer)) { + return false; + } + + return true; +} + +bool ATTClass::discoverServices(uint16_t connectionHandle, BLERemoteDevice* device, const char* serviceUuidFilter) +{ + uint16_t reqStartHandle = 0x0001; + uint16_t reqEndHandle = 0xffff; + + uint8_t responseBuffer[_maxMtu]; + + BLEUuid serviceUuid(serviceUuidFilter); + + while (reqEndHandle == 0xffff) { + int respLength = readByGroupReq(connectionHandle, reqStartHandle, reqEndHandle, BLETypeService, responseBuffer); + + if (respLength == 0) { + return false; + } + + if (responseBuffer[0] == ATT_OP_READ_BY_GROUP_RESP) { + uint16_t lengthPerService = responseBuffer[1]; + uint8_t uuidLen = lengthPerService - 4; + + for (int i = 2; i < respLength; i += lengthPerService) { + struct __attribute__ ((packed)) RawService { + uint16_t startHandle; + uint16_t endHandle; + uint8_t uuid[16]; + } *rawService = (RawService*)&responseBuffer[i]; + + if (serviceUuidFilter == NULL || + (uuidLen == serviceUuid.length() && memcmp(rawService->uuid, serviceUuid.data(), uuidLen) == 0)) { + + BLERemoteService* service = new BLERemoteService(rawService->uuid, uuidLen, + rawService->startHandle, + rawService->endHandle); + + if (service == NULL) { + return false; + } + + device->addService(service); + + } + + reqStartHandle = rawService->endHandle + 1; + + if (reqStartHandle == 0x0000) { + reqEndHandle = 0x0000; + } + } + } else { + reqEndHandle = 0x0000; + } + } + + return true; +} + +bool ATTClass::discoverCharacteristics(uint16_t connectionHandle, BLERemoteDevice* device) +{ + uint16_t reqStartHandle = 0x0001; + uint16_t reqEndHandle = 0xffff; + + uint8_t responseBuffer[_maxMtu]; + + int serviceCount = device->serviceCount(); + + for (int i = 0; i < serviceCount; i++) { + BLERemoteService* service = device->service(i); + + reqStartHandle = service->startHandle(); + reqEndHandle = service->endHandle(); + + while (1) { + int respLength = readByTypeReq(connectionHandle, reqStartHandle, reqEndHandle, BLETypeCharacteristic, responseBuffer); + + if (respLength == 0) { + return false; + } + + if (responseBuffer[0] == ATT_OP_READ_BY_TYPE_RESP) { + uint16_t lengthPerCharacteristic = responseBuffer[1]; + uint8_t uuidLen = lengthPerCharacteristic - 5; + + for (int i = 2; i < respLength; i += lengthPerCharacteristic) { + struct __attribute__ ((packed)) RawCharacteristic { + uint16_t startHandle; + uint8_t properties; + uint16_t valueHandle; + uint8_t uuid[16]; + } *rawCharacteristic = (RawCharacteristic*)&responseBuffer[i]; + + BLERemoteCharacteristic* characteristic = new BLERemoteCharacteristic(rawCharacteristic->uuid, uuidLen, + connectionHandle, + rawCharacteristic->startHandle, + rawCharacteristic->properties, + rawCharacteristic->valueHandle); + + if (characteristic == NULL) { + return false; + } + + service->addCharacteristic(characteristic); + + reqStartHandle = rawCharacteristic->valueHandle + 1; + } + } else { + break; + } + } + } + + return true; +} + +bool ATTClass::discoverDescriptors(uint16_t connectionHandle, BLERemoteDevice* device) +{ + uint16_t reqStartHandle = 0x0001; + uint16_t reqEndHandle = 0xffff; + + uint8_t responseBuffer[_maxMtu]; + + int serviceCount = device->serviceCount(); + + for (int i = 0; i < serviceCount; i++) { + BLERemoteService* service = device->service(i); + + uint16_t serviceEndHandle = service->endHandle(); + + int characteristicCount = service->characteristicCount(); + + for (int j = 0; j < characteristicCount; j++) { + BLERemoteCharacteristic* characteristic = service->characteristic(j); + BLERemoteCharacteristic* nextCharacteristic = (j == (characteristicCount - 1)) ? NULL : service->characteristic(j + 1); + + reqStartHandle = characteristic->valueHandle() + 1; + reqEndHandle = nextCharacteristic ? nextCharacteristic->valueHandle() : serviceEndHandle; + + if (reqStartHandle > reqEndHandle) { + continue; + } + + while (1) { + int respLength = findInfoReq(connectionHandle, reqStartHandle, reqEndHandle, responseBuffer); + + if (respLength == 0) { + return false; + } + + if (responseBuffer[0] == ATT_OP_FIND_INFO_RESP) { + uint16_t lengthPerDescriptor = responseBuffer[1] * 4; + uint8_t uuidLen = 2; + + for (int i = 2; i < respLength; i += lengthPerDescriptor) { + struct __attribute__ ((packed)) RawDescriptor { + uint16_t handle; + uint8_t uuid[16]; + } *rawDescriptor = (RawDescriptor*)&responseBuffer[i]; + + BLERemoteDescriptor* descriptor = new BLERemoteDescriptor(rawDescriptor->uuid, uuidLen, + connectionHandle, + rawDescriptor->handle); + + if (descriptor == NULL) { + return false; + } + + characteristic->addDescriptor(descriptor); + + reqStartHandle = rawDescriptor->handle + 1; + } + } else { + break; + } + } + } + } + + return true; +} + +int ATTClass::sendReq(uint16_t connectionHandle, void* requestBuffer, int requestLength, uint8_t responseBuffer[]) +{ + _pendingResp.connectionHandle = connectionHandle; + _pendingResp.op = ((uint8_t*)requestBuffer)[0] + 1; + _pendingResp.buffer = responseBuffer; + _pendingResp.length = 0; + + HCI.sendAclPkt(connectionHandle, ATT_CID, requestLength, requestBuffer); + + if (responseBuffer == NULL) { + // not waiting response + return 0; + } + + for (unsigned long start = millis(); (millis() - start) < _timeout;) { + HCI.poll(); + + if (!connected(connectionHandle)) { + break; + } + + if (_pendingResp.length != 0) { + _pendingResp.connectionHandle = 0xffff; + return _pendingResp.length; + } + } + + _pendingResp.connectionHandle = 0xffff; + return 0; +} + +void ATTClass::setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler) +{ + if (event < (sizeof(_eventHandlers) / (sizeof(_eventHandlers[0])))) { + _eventHandlers[event] = eventHandler; + } +} + +int ATTClass::readReq(uint16_t connectionHandle, uint16_t handle, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t handle; + } readReq = { ATT_OP_READ_REQ, handle }; + + return sendReq(connectionHandle, &readReq, sizeof(readReq), responseBuffer); +} + +int ATTClass::writeReq(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen, uint8_t responseBuffer[]) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t handle; + uint8_t data[255]; + } writeReq; + + writeReq.op = ATT_OP_WRITE_REQ; + writeReq.handle = handle; + memcpy(writeReq.data, data, dataLen); + + return sendReq(connectionHandle, &writeReq, 3 + dataLen, responseBuffer); +} + +void ATTClass::writeCmd(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen) +{ + struct __attribute__ ((packed)) { + uint8_t op; + uint16_t handle; + uint8_t data[255]; + } writeReq; + + writeReq.op = ATT_OP_WRITE_CMD; + writeReq.handle = handle; + memcpy(writeReq.data, data, dataLen); + + sendReq(connectionHandle, &writeReq, 3 + dataLen, NULL); +} + +// Set encryption state for a peer +int ATTClass::setPeerEncryption(uint16_t connectionHandle, uint8_t encryption){ + for(int i=0; i 0){ + return _peers[i].connectionHandle; + } + } + return ATT_MAX_PEERS + 1; +} +// Get the encryption state for a particular peer / connection handle +uint8_t ATTClass::getPeerEncryption(uint16_t connectionHandle) { + for(int i=0; i + +#include "BLEDevice.h" +#include "keyDistribution.h" + +#define ATT_CID 0x0004 +#define BLE_CTL 0x0008 + +#if DM_CONN_MAX +#define ATT_MAX_PEERS DM_CONN_MAX // Mbed + Cordio +#elif __AVR__ +#define ATT_MAX_PEERS 3 +#else +#define ATT_MAX_PEERS 8 +#endif + +enum PEER_ENCRYPTION { + NO_ENCRYPTION = 0, + PAIRING_REQUEST = 1 << 0, + REQUESTED_ENCRYPTION = 1 << 1, + SENT_PUBKEY = 1 << 2, + DH_KEY_CALULATED = 1 << 3, + RECEIVED_DH_CHECK = 1 << 4, + SENT_DH_CHECK = 1 << 5, + ENCRYPTED_AES = 1 << 7 +}; + +class BLERemoteDevice; + +class ATTClass { +public: + ATTClass(); + virtual ~ATTClass(); + + virtual void setMaxMtu(uint16_t maxMtu); + virtual void setTimeout(unsigned long timeout); + + virtual bool connect(uint8_t peerBdaddrType, uint8_t peerBdaddr[6]); + virtual bool disconnect(uint8_t peerBdaddrType, uint8_t peerBdaddr[6]); + virtual bool discoverAttributes(uint8_t peerBdaddrType, uint8_t peerBdaddr[6], const char* serviceUuidFilter); + + virtual void addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrType, + uint8_t peerBdaddr[6], uint16_t interval, + uint16_t latency, uint16_t supervisionTimeout, + uint8_t masterClockAccuracy); + + virtual void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + + virtual void removeConnection(uint16_t handle, uint8_t reason); + + virtual uint16_t connectionHandle(uint8_t addressType, const uint8_t address[6]) const; + virtual BLERemoteDevice* device(uint8_t addressType, const uint8_t address[6]) const; + virtual bool connected() const; + virtual bool connected(uint8_t addressType, const uint8_t address[6]) const; + virtual bool connected(uint16_t handle) const; + virtual bool paired() const; + virtual bool paired(uint16_t handle) const; + virtual uint16_t mtu(uint16_t handle) const; + + virtual bool disconnect(); + + virtual BLEDevice central(); + + virtual int handleNotify(uint16_t handle, const uint8_t* value, int length); + virtual int handleInd(uint16_t handle, const uint8_t* value, int length); + + virtual void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); + + virtual int readReq(uint16_t connectionHandle, uint16_t handle, uint8_t responseBuffer[]); + virtual int writeReq(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen, uint8_t responseBuffer[]); + virtual void writeCmd(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen); + virtual int setPeerEncryption(uint16_t connectionHandle, uint8_t encryption); + uint8_t getPeerEncryption(uint16_t connectionHandle); + uint16_t getPeerEncrptingConnectionHandle(); + virtual int getPeerAddr(uint16_t connectionHandle, uint8_t peerAddr[]); + virtual int getPeerAddrWithType(uint16_t connectionHandle, uint8_t peerAddr[]); + virtual int setPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]); + virtual int getPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]); + virtual int getPeerResolvedAddress(uint16_t connectionHandle, uint8_t* resolvedAddress); + uint8_t holdBuffer[64]; + uint8_t writeBuffer[64]; + uint8_t holdBufferSize; + uint8_t writeBufferSize; + virtual int processWriteBuffer(); + KeyDistribution remoteKeyDistribution; + KeyDistribution localKeyDistribution; + uint8_t peerIRK[16]; + /// This is just a random number... Not sure it has use unless privacy mode is active. + uint8_t localIRK[16] = {0x54,0x83,0x63,0x7c,0xc5,0x1e,0xf7,0xec,0x32,0xdd,0xad,0x51,0x89,0x4b,0x9e,0x07}; +private: + virtual void error(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual int mtuReq(uint16_t connectionHandle, uint16_t mtu, uint8_t responseBuffer[]); + virtual void mtuResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void findInfoReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual int findInfoReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint8_t responseBuffer[]); + virtual void findInfoResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void findByTypeReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual void readByTypeReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual int readByTypeReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint16_t type, uint8_t responseBuffer[]); + virtual void readByTypeResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void readOrReadBlobReq(uint16_t connectionHandle, uint16_t mtu, uint8_t opcode, uint8_t dlen, uint8_t data[]); + virtual void readResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void readByGroupReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual int readByGroupReq(uint16_t connectionHandle, uint16_t startHandle, uint16_t endHandle, uint16_t uuid, uint8_t responseBuffer[]); + virtual void readByGroupResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void writeReqOrCmd(uint16_t connectionHandle, uint16_t mtu, uint8_t op, uint8_t dlen, uint8_t data[]); + virtual void writeResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void prepWriteReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual void execWriteReq(uint16_t connectionHandle, uint16_t mtu, uint8_t dlen, uint8_t data[]); + virtual void handleNotifyOrInd(uint16_t connectionHandle, uint8_t opcode, uint8_t dlen, uint8_t data[]); + virtual void handleCnf(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + virtual void sendError(uint16_t connectionHandle, uint8_t opcode, uint16_t handle, uint8_t code); + + virtual bool exchangeMtu(uint16_t connectionHandle); + virtual bool discoverServices(uint16_t connectionHandle, BLERemoteDevice* device, const char* serviceUuidFilter); + virtual bool discoverCharacteristics(uint16_t connectionHandle, BLERemoteDevice* device); + virtual bool discoverDescriptors(uint16_t connectionHandle, BLERemoteDevice* device); + + virtual int sendReq(uint16_t connectionHandle, void* requestBuffer, int requestLength, uint8_t responseBuffer[]); + +private: + uint16_t _maxMtu; + unsigned long _timeout; + struct { + uint16_t connectionHandle; + uint8_t role; + uint8_t addressType; + uint8_t address[6]; + uint8_t resolvedAddress[6]; + uint16_t mtu; + BLERemoteDevice* device; + uint8_t encryption; + uint8_t IOCap[3]; + } _peers[ATT_MAX_PEERS]; + + volatile bool _cnf; + + uint16_t _longWriteHandle; + uint8_t* _longWriteValue; + uint16_t _longWriteValueLength; + + struct { + uint16_t connectionHandle; + uint8_t op; + uint8_t* buffer; + uint8_t length; + } _pendingResp; + + BLEDeviceEventHandler _eventHandlers[2]; +}; + +extern ATTClass& ATT; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/BLELinkedList.h b/arduino/libraries/ArduinoBLE/src/utility/BLELinkedList.h new file mode 100644 index 0000000..1583850 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/BLELinkedList.h @@ -0,0 +1,147 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_LINKED_LIST_ +#define _BLE_LINKED_LIST_ + +#include + +template struct BLELinkedListNode +{ + T data; + BLELinkedListNode* next; +}; + +template class BLELinkedList { +public: + BLELinkedList(); + ~BLELinkedList(); + + void add(T); + T get(unsigned int index) const; + void clear(); + T remove(unsigned int index); + + unsigned int size() const; + +private: + unsigned int _size; + BLELinkedListNode* _root; + BLELinkedListNode* _last; +}; + +template BLELinkedList::BLELinkedList() : + _size(0), + _root(NULL), + _last(NULL) +{ +} + +template BLELinkedList::~BLELinkedList() +{ + clear(); +} + +template void BLELinkedList::add(T item) +{ + BLELinkedListNode* itemNode = new BLELinkedListNode(); + + itemNode->data = item; + itemNode->next = NULL; + + if (_root == NULL) { + _root = itemNode; + } else { + _last->next = itemNode; + } + _last = itemNode; + + _size++; +} + +template T BLELinkedList::get(unsigned int index) const +{ + if (index >= _size) { + return T(); + } + + BLELinkedListNode* itemNode = _root; + + for (unsigned int i = 0; i < index; i++) { + itemNode = itemNode->next; + } + + return itemNode->data; +} + +template void BLELinkedList::clear() +{ + BLELinkedListNode* itemNode = _root; + + for (unsigned int i = 0; i < _size; i++) { + BLELinkedListNode* n = itemNode; + + itemNode = itemNode->next; + + delete n; + } + + _size = 0; + _root = NULL; + _last = NULL; +} + +template unsigned int BLELinkedList::size() const +{ + return _size; +} + +template T BLELinkedList::remove(unsigned int index) +{ + if (index >= _size) { + return T(); + } + + BLELinkedListNode* previousItemNode = NULL; + BLELinkedListNode* itemNode = _root; + + for (unsigned int i = 0; i < index; i++) { + previousItemNode = itemNode; + itemNode = itemNode->next; + } + + T result = itemNode->data; + + if (previousItemNode == NULL) { + _root = itemNode->next; + } else { + previousItemNode->next = itemNode->next; + } + + if (_last == itemNode) { + _last = previousItemNode; + } + + delete itemNode; + _size--; + + return result; +} + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.cpp b/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.cpp new file mode 100644 index 0000000..0465ea9 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.cpp @@ -0,0 +1,93 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "BLEUuid.h" + +BLEUuid::BLEUuid(const char * str) : + _str(str) +{ + char temp[] = {0, 0, 0}; + + memset(_data, 0x00, sizeof(_data)); + + _length = 0; + + if (str == NULL) { + return; + } + + for (int i = strlen(str) - 1; i >= 0 && _length < BLE_UUID_MAX_LENGTH; i -= 2) { + if (str[i] == '-') { + i++; + continue; + } + + temp[0] = str[i - 1]; + temp[1] = str[i]; + + _data[_length] = strtoul(temp, NULL, 16); + + _length++; + } + + if (_length <= 2) { + _length = 2; + } else { + _length = 16; + } +} + +const char* BLEUuid::str() const +{ + return _str; +} + +const uint8_t* BLEUuid::data() const +{ + return _data; +} + +uint8_t BLEUuid::length() const +{ + return _length; +} + +const char* BLEUuid::uuidToString(const uint8_t* data, uint8_t length) +{ + static char uuid[36 + 1]; + char* c = uuid; + + for (int i = length - 1; i >= 0; i--) { + uint8_t b = data[i]; + + utoa(b >> 4, c++, 16); + utoa(b & 0x0f, c++, 16); + + if (i == 6 || i == 8 || i == 10 || i == 12) { + *c++ = '-'; + } + } + + *c = '\0'; + + return uuid; +} diff --git a/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.h b/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.h new file mode 100644 index 0000000..f6836bf --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/BLEUuid.h @@ -0,0 +1,44 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _BLE_UUID_H_ +#define _BLE_UUID_H_ + +#include + +#define BLE_UUID_MAX_LENGTH 16 + +class BLEUuid +{ +public: + BLEUuid(const char * str); + + const char* str() const; + const uint8_t * data() const; + uint8_t length() const; + + static const char* uuidToString(const uint8_t* data, uint8_t length); + +private: + const char* _str; + uint8_t _data[BLE_UUID_MAX_LENGTH]; + uint8_t _length; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/CordioHCICustomDriver.h b/arduino/libraries/ArduinoBLE/src/utility/CordioHCICustomDriver.h new file mode 100644 index 0000000..fc062ee --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/CordioHCICustomDriver.h @@ -0,0 +1,17 @@ +#if defined(CORE_CM4) + +#include "CyH4TransportDriver.h" + +ble::vendor::cypress_ble::CyH4TransportDriver& ble_cordio_get_h4_transport_driver() +{ + static ble::vendor::cypress_ble::CyH4TransportDriver s_transport_driver( + /* TX */ CYBSP_BT_UART_TX, /* RX */ CYBSP_BT_UART_RX, + /* cts */ CYBSP_BT_UART_CTS, /* rts */ CYBSP_BT_UART_RTS, NC, DEF_BT_BAUD_RATE, + CYBSP_BT_HOST_WAKE, CYBSP_BT_DEVICE_WAKE + ); + return s_transport_driver; +} + +#define CUSTOM_HCI_DRIVER + +#endif \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/GAP.cpp b/arduino/libraries/ArduinoBLE/src/utility/GAP.cpp new file mode 100644 index 0000000..f1bf02f --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/GAP.cpp @@ -0,0 +1,272 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "BLEUuid.h" +#include "HCI.h" + +#include "GAP.h" + +#define GAP_MAX_DISCOVERED_QUEUE_SIZE 32 + +#define GAP_ADV_IND (0x00) +#define GAP_ADV_SCAN_IND (0x02) +#define GAP_ADV_NONCONN_IND (0x03) + +GAPClass::GAPClass() : + _advertising(false), + _scanning(false), + _advertisingInterval(160), + _connectable(true), + _discoverEventHandler(NULL) +{ +} + +GAPClass::~GAPClass() +{ +} + +bool GAPClass::advertising() +{ + return _advertising; +} + +int GAPClass::advertise(uint8_t* advData, uint8_t advDataLen, uint8_t* scanData, uint8_t scanDataLen) +{ + uint8_t directBdaddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t type = (_connectable) ? GAP_ADV_IND : (scanDataLen ? GAP_ADV_SCAN_IND : GAP_ADV_NONCONN_IND); + + stopAdvertise(); + + if (HCI.leSetAdvertisingParameters(_advertisingInterval, _advertisingInterval, type, 0x00, 0x00, directBdaddr, 0x07, 0) != 0) { + return 0; + } + + if (HCI.leSetAdvertisingData(advDataLen, advData) != 0) { + return 0; + } + + if (HCI.leSetScanResponseData(scanDataLen, scanData) != 0) { + return 0; + } + + if (HCI.leSetAdvertiseEnable(0x01) != 0) { + return 0; + } + + _advertising = true; + + return 1; +} + +void GAPClass::stopAdvertise() +{ + _advertising = false; + + HCI.leSetAdvertiseEnable(0x00); +} + +int GAPClass::scan(bool withDuplicates) +{ + HCI.leSetScanEnable(false, true); + + // active scan, 20 ms scan interval (N * 0.625), 20 ms scan window (N * 0.625), public own address type, no filter + /* + Warning (from BLUETOOTH SPECIFICATION 5.x): + - scan interval: mandatory range from 0x0012 to 0x1000; only even values are valid + - scan window: mandatory range from 0x0011 to 0x1000 + - The scan window can only be less than or equal to the scan interval + */ + if (HCI.leSetScanParameters(0x01, 0x0020, 0x0020, 0x00, 0x00) != 0) { + return false; + } + + _scanning = true; + + if (HCI.leSetScanEnable(true, !withDuplicates) != 0) { + return 0; + } + + return 1; +} + +int GAPClass::scanForName(String name, bool withDuplicates) +{ + _scanNameFilter = name; + _scanUuidFilter = ""; + _scanAddressFilter = ""; + + return scan(withDuplicates); +} + +int GAPClass::scanForUuid(String uuid, bool withDuplicates) +{ + _scanNameFilter = ""; + _scanUuidFilter = uuid; + _scanAddressFilter = ""; + + return scan(withDuplicates); +} + +int GAPClass::scanForAddress(String address, bool withDuplicates) +{ + _scanNameFilter = ""; + _scanUuidFilter = ""; + _scanAddressFilter = address; + + return scan(withDuplicates); +} + +void GAPClass::stopScan() +{ + HCI.leSetScanEnable(false, false); + + _scanning = false; + + for (unsigned int i = 0; i < _discoveredDevices.size(); i++) { + BLEDevice* device = _discoveredDevices.get(i); + + delete device; + } + + _discoveredDevices.clear(); +} + +BLEDevice GAPClass::available() +{ + for (unsigned int i = 0; i < _discoveredDevices.size(); i++) { + BLEDevice* device = _discoveredDevices.get(i); + + if (device->discovered()) { + BLEDevice result = *device; + + _discoveredDevices.remove(i); + delete device; + + if (matchesScanFilter(result)) { + return result; + } else { + continue; + } + } + } + + return BLEDevice(); +} + +void GAPClass::setAdvertisingInterval(uint16_t advertisingInterval) +{ + _advertisingInterval = advertisingInterval; +} + +void GAPClass::setConnectable(bool connectable) +{ + _connectable = connectable; +} + +void GAPClass::setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler) +{ + if (event == BLEDiscovered) { + _discoverEventHandler = eventHandler; + } +} + +void GAPClass::handleLeAdvertisingReport(uint8_t type, uint8_t addressType, uint8_t address[6], + uint8_t eirLength, uint8_t eirData[], int8_t rssi) +{ + if (!_scanning) { + return; + } + + if (_discoverEventHandler && type == 0x03) { + // call event handler and skip adding to discover list + BLEDevice device(addressType, address); + + device.setAdvertisementData(type, eirLength, eirData, rssi); + + if (matchesScanFilter(device)) { + _discoverEventHandler(device); + } + return; + } + + BLEDevice* discoveredDevice = NULL; + int discoveredIndex = -1; + + for (unsigned int i = 0; i < _discoveredDevices.size(); i++) { + BLEDevice* device = _discoveredDevices.get(i); + + if (device->hasAddress(addressType, address)) { + discoveredDevice = device; + discoveredIndex = i; + + break; + } + } + + if (discoveredDevice == NULL) { + if (_discoveredDevices.size() >= GAP_MAX_DISCOVERED_QUEUE_SIZE) { + BLEDevice* device_first = _discoveredDevices.remove(0); + if (device_first != NULL) { + delete device_first; + } + } + + discoveredDevice = new BLEDevice(addressType, address); + + _discoveredDevices.add(discoveredDevice); + discoveredIndex = _discoveredDevices.size() - 1; + } + + if (type != 0x04) { + discoveredDevice->setAdvertisementData(type, eirLength, eirData, rssi); + } else { + discoveredDevice->setScanResponseData(eirLength, eirData, rssi); + } + + if (discoveredDevice->discovered() && _discoverEventHandler) { + // remove from list and report as discovered + BLEDevice device = *discoveredDevice; + + _discoveredDevices.remove(discoveredIndex); + delete discoveredDevice; + + if (matchesScanFilter(device)) { + _discoverEventHandler(device); + } + } +} + +bool GAPClass::matchesScanFilter(const BLEDevice& device) +{ + if (_scanAddressFilter.length() > 0 && !(_scanAddressFilter.equalsIgnoreCase(device.address()))) { + return false; // drop doesn't match + } else if (_scanNameFilter.length() > 0 && _scanNameFilter != device.localName()) { + return false; // drop doesn't match + } else if (_scanUuidFilter.length() > 0 && !(_scanUuidFilter.equalsIgnoreCase(device.advertisedServiceUuid()))) { + return false; // drop doesn't match + } + + return true; +} + +#if !defined(FAKE_GAP) +GAPClass GAPObj; +GAPClass& GAP = GAPObj; +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/GAP.h b/arduino/libraries/ArduinoBLE/src/utility/GAP.h new file mode 100644 index 0000000..2ea2293 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/GAP.h @@ -0,0 +1,74 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _GAP_H_ +#define _GAP_H_ + +#include "utility/BLELinkedList.h" + +#include "BLEDevice.h" + +class GAPClass { +public: + GAPClass(); + virtual ~GAPClass(); + + virtual bool advertising(); + virtual int advertise(uint8_t* advData, uint8_t advDataLength, uint8_t* scanData, uint8_t scanDataLength); + virtual void stopAdvertise(); + + virtual int scan(bool withDuplicates); + virtual int scanForName(String name, bool withDuplicates); + virtual int scanForUuid(String uuid, bool withDuplicates); + virtual int scanForAddress(String address, bool withDuplicates); + virtual void stopScan(); + virtual BLEDevice available(); + + virtual void setAdvertisingInterval(uint16_t advertisingInterval); + virtual void setConnectable(bool connectable); + + virtual void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); + +protected: + friend class HCIClass; + + virtual void handleLeAdvertisingReport(uint8_t type, uint8_t addressType, uint8_t address[6], + uint8_t eirLength, uint8_t eirData[], int8_t rssi); + +private: + virtual bool matchesScanFilter(const BLEDevice& device); + +private: + bool _advertising; + bool _scanning; + + uint16_t _advertisingInterval; + bool _connectable; + + BLEDeviceEventHandler _discoverEventHandler; + BLELinkedList _discoveredDevices; + + String _scanNameFilter; + String _scanUuidFilter; + String _scanAddressFilter; +}; + +extern GAPClass& GAP; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/GATT.cpp b/arduino/libraries/ArduinoBLE/src/utility/GATT.cpp new file mode 100644 index 0000000..3550fd9 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/GATT.cpp @@ -0,0 +1,210 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "local/BLELocalCharacteristic.h" +#include "local/BLELocalDescriptor.h" +#include "local/BLELocalService.h" + +#include "BLEProperty.h" + +#include "GATT.h" + +GATTClass::GATTClass() : + _genericAccessService(NULL), + _deviceNameCharacteristic(NULL), + _appearanceCharacteristic(NULL), + _genericAttributeService(NULL), + _servicesChangedCharacteristic(NULL) +{ +} + +GATTClass::~GATTClass() +{ + end(); +} + +void GATTClass::begin() +{ + _genericAccessService = new BLELocalService("1800"); + _deviceNameCharacteristic = new BLELocalCharacteristic("2a00", BLERead, 20); + _appearanceCharacteristic = new BLELocalCharacteristic("2a01", BLERead, 2); + _genericAttributeService = new BLELocalService("1801"); + _servicesChangedCharacteristic = new BLELocalCharacteristic("2a05", BLEIndicate, 4); + + _genericAccessService->retain(); + _deviceNameCharacteristic->retain(); + _appearanceCharacteristic->retain(); + _genericAttributeService->retain(); + _servicesChangedCharacteristic->retain(); + + _genericAccessService->addCharacteristic(_deviceNameCharacteristic); + _genericAccessService->addCharacteristic(_appearanceCharacteristic); + _genericAttributeService->addCharacteristic(_servicesChangedCharacteristic); + + setDeviceName("Arduino"); + setAppearance(0x000); + + clearAttributes(); + + addService(_genericAccessService); + addService(_genericAttributeService); +} + +void GATTClass::end() +{ + if (_genericAccessService && _genericAccessService->release() == 0) { + delete(_genericAccessService); + _genericAccessService = NULL; + } + + if (_deviceNameCharacteristic && _deviceNameCharacteristic->release() == 0) { + delete(_deviceNameCharacteristic); + _deviceNameCharacteristic = NULL; + } + + if (_appearanceCharacteristic && _appearanceCharacteristic->release() == 0) { + delete(_appearanceCharacteristic); + _appearanceCharacteristic = NULL; + } + + if (_genericAttributeService && _genericAttributeService->release() == 0) { + delete(_genericAttributeService); + _genericAttributeService = NULL; + } + + if (_servicesChangedCharacteristic && _servicesChangedCharacteristic->release() == 0) { + delete(_servicesChangedCharacteristic); + _servicesChangedCharacteristic = NULL; + } + + clearAttributes(); +} + +void GATTClass::setDeviceName(const char* deviceName) +{ + _deviceNameCharacteristic->writeValue(deviceName); +} + +void GATTClass::setAppearance(uint16_t appearance) +{ + _appearanceCharacteristic->writeValue((uint8_t*)&appearance, sizeof(appearance)); +} + +void GATTClass::addService(BLEService& service) +{ + BLELocalService* localService = service.local(); + + if (localService) { + addService(localService); + } +} + +unsigned int GATTClass::attributeCount() const +{ + return _attributes.size(); +} + +BLELocalAttribute* GATTClass::attribute(unsigned int index) const +{ + return _attributes.get(index); +} + +uint16_t GATTClass::serviceUuidForCharacteristic(BLELocalCharacteristic* characteristic) const +{ + uint16_t serviceUuid = 0x0000; + + BLELocalService* lastService = NULL; + + for (unsigned int i = 0; i < attributeCount(); i++) { + BLELocalAttribute* a = attribute(i); + uint16_t attributeType = a->type(); + + if (attributeType == BLETypeService) { + lastService = (BLELocalService*)a; + } else if (a == characteristic) { + break; + } + } + + if (lastService) { + if (lastService->uuidLength() == 2) { + serviceUuid = *(uint16_t*)(lastService->uuidData()); + } else { + serviceUuid = *(uint16_t*)(lastService->uuidData() + 10); + } + } + + return serviceUuid; +} + +void GATTClass::addService(BLELocalService* service) +{ + service->retain(); + _attributes.add(service); + _services.add(service); + + uint16_t startHandle = attributeCount(); + + for (unsigned int i = 0; i < service->characteristicCount(); i++) { + BLELocalCharacteristic* characteristic = service->characteristic(i); + + characteristic->retain(); + _attributes.add(characteristic); + characteristic->setHandle(attributeCount()); + + // add the characteristic again to make space of the characteristic value handle + characteristic->retain(); + _attributes.add(characteristic); + + for (unsigned int j = 0; j < characteristic->descriptorCount(); j++) { + BLELocalDescriptor* descriptor = characteristic->descriptor(j); + + descriptor->retain(); + _attributes.add(descriptor); + descriptor->setHandle(attributeCount()); + } + } + + service->setHandles(startHandle, attributeCount()); +} + +void GATTClass::clearAttributes() +{ + for (unsigned int i = 0; i < attributeCount(); i++) { + BLELocalAttribute* a = attribute(i); + + if (a->release() == 0) { + delete a; + } + } + _attributes.clear(); + + for (unsigned int i = 0; i < _services.size(); i++) { + _services.get(i)->clear(); + } + _services.clear(); + +} + +#if !defined(FAKE_GATT) +GATTClass GATTObj; +GATTClass& GATT = GATTObj; +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/GATT.h b/arduino/libraries/ArduinoBLE/src/utility/GATT.h new file mode 100644 index 0000000..51d1537 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/GATT.h @@ -0,0 +1,73 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _GATT_H_ +#define _GATT_H_ + +#include "utility/BLELinkedList.h" + +#include "local/BLELocalAttribute.h" +#include "local/BLELocalCharacteristic.h" +#include "local/BLELocalService.h" + +#include "BLEService.h" + +class GATTClass { +public: + GATTClass(); + virtual ~GATTClass(); + + virtual void begin(); + virtual void end(); + + virtual void setDeviceName(const char* deviceName); + virtual void setAppearance(uint16_t appearance); + + virtual void addService(BLEService& service); + +protected: + friend class ATTClass; + + virtual unsigned int attributeCount() const; + virtual BLELocalAttribute* attribute(unsigned int index) const; + +protected: + friend class BLELocalCharacteristic; + + virtual uint16_t serviceUuidForCharacteristic(BLELocalCharacteristic* characteristic) const; + +private: + virtual void addService(BLELocalService* service); + + virtual void clearAttributes(); + +private: + BLELinkedList _attributes; + BLELinkedList _services; + + BLELocalService* _genericAccessService; + BLELocalCharacteristic* _deviceNameCharacteristic; + BLELocalCharacteristic* _appearanceCharacteristic; + BLELocalService* _genericAttributeService; + BLELocalCharacteristic* _servicesChangedCharacteristic; +}; + +extern GATTClass& GATT; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCI.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCI.cpp new file mode 100644 index 0000000..40afd7c --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCI.cpp @@ -0,0 +1,1559 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ATT.h" +#include "GAP.h" +#include "HCITransport.h" +#include "L2CAPSignaling.h" +#include "btct.h" +#include "HCI.h" +#include "bitDescriptions.h" +// #define _BLE_TRACE_ + + +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_EVENT_PKT 0x04 +#define HCI_SECURITY_PKT 0x06 + +#define EVT_DISCONN_COMPLETE 0x05 +#define EVT_ENCRYPTION_CHANGE 0x08 +#define EVT_CMD_COMPLETE 0x0e +#define EVT_CMD_STATUS 0x0f +#define EVT_NUM_COMP_PKTS 0x13 +#define EVT_RETURN_LINK_KEYS 0x15 +#define EVT_UNKNOWN 0x10 +#define EVT_LE_META_EVENT 0x3e + +#define EVT_LE_CONN_COMPLETE 0x01 +#define EVT_LE_ADVERTISING_REPORT 0x02 + + +// OGF_LINK_CTL +#define OCF_DISCONNECT 0x0006 + +// OGF_HOST_CTL +#define OCF_SET_EVENT_MASK 0x0001 +#define OCF_RESET 0x0003 + +// OGF_INFO_PARAM +#define OCF_READ_LOCAL_VERSION 0x0001 +#define OCF_READ_BD_ADDR 0x0009 + +// OGF_STATUS_PARAM +#define OCF_READ_RSSI 0x0005 + +// OGF_LE_CTL +#define OCF_LE_READ_BUFFER_SIZE 0x0002 +#define OCF_LE_SET_RANDOM_ADDRESS 0x0005 +#define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006 +#define OCF_LE_SET_ADVERTISING_DATA 0x0008 +#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009 +#define OCF_LE_SET_ADVERTISE_ENABLE 0x000a +#define OCF_LE_SET_SCAN_PARAMETERS 0x000b +#define OCF_LE_SET_SCAN_ENABLE 0x000c +#define OCF_LE_CREATE_CONN 0x000d +#define OCF_LE_CANCEL_CONN 0x000e +#define OCF_LE_CONN_UPDATE 0x0013 + +#define HCI_OE_USER_ENDED_CONNECTION 0x13 + +String metaEventToString(LE_META_EVENT event) +{ + switch(event){ + case CONN_COMPLETE: return F("CONN_COMPLETE"); + case ADVERTISING_REPORT: return F("ADVERTISING_REPORT"); + case LONG_TERM_KEY_REQUEST: return F("LE_LONG_TERM_KEY_REQUEST"); + case READ_LOCAL_P256_COMPLETE: return F("READ_LOCAL_P256_COMPLETE"); + case GENERATE_DH_KEY_COMPLETE: return F("GENERATE_DH_KEY_COMPLETE"); + case ENHANCED_CONN_COMPLETE: return F("ENHANCED_CONN_COMPLETE"); + default: return "event unknown"; + } +} +String commandToString(LE_COMMAND command){ + switch (command) + { + case ENCRYPT: return F("ENCRYPT"); + case LONG_TERM_KEY_REPLY: return F("LONG_TERM_KEY_REPLY"); + case READ_LOCAL_P256: return F("READ_LOCAL_P256"); + case GENERATE_DH_KEY_V1: return F("GENERATE_DH_KEY_V1"); + case GENERATE_DH_KEY_V2: return F("GENERATE_DH_KEY_V2"); + default: return "UNKNOWN"; + } +} + +HCIClass::HCIClass() : + _debug(NULL), + _recvIndex(0), + _pendingPkt(0), + _l2CapPduBufferSize(0) +{ +} + +HCIClass::~HCIClass() +{ +} + +int HCIClass::begin() +{ + _recvIndex = 0; + + return HCITransport.begin(); +} + +void HCIClass::end() +{ + HCITransport.end(); +} + +void HCIClass::poll() +{ + poll(0); +} + +void HCIClass::poll(unsigned long timeout) +{ +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + + if (timeout) { + HCITransport.wait(timeout); + } + + HCITransport.lockForRead(); + while (HCITransport.available()) { + byte b = HCITransport.read(); + + if (_recvIndex >= (int)sizeof(_recvBuffer)) { + _recvIndex = 0; + if (_debug) { + HCITransport.unlockForRead(); + _debug->println("_recvBuffer overflow"); + HCITransport.lockForRead(); + } + } + + _recvBuffer[_recvIndex++] = b; + + if (_recvBuffer[0] == HCI_ACLDATA_PKT) { + if (_recvIndex > 5 && _recvIndex >= (5 + (_recvBuffer[3] + (_recvBuffer[4] << 8)))) { + HCITransport.unlockForRead(); + if (_debug) { + dumpPkt("HCI ACLDATA RX <- ", _recvIndex, _recvBuffer); + } +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif + int pktLen = _recvIndex - 1; + _recvIndex = 0; + + handleAclDataPkt(pktLen, &_recvBuffer[1]); + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + HCITransport.lockForRead(); + } + } else if (_recvBuffer[0] == HCI_EVENT_PKT) { + if (_recvIndex > 3 && _recvIndex >= (3 + _recvBuffer[2])) { + HCITransport.unlockForRead(); + if (_debug) { + dumpPkt("HCI EVENT RX <- ", _recvIndex, _recvBuffer); + } +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif + // received full event + int pktLen = _recvIndex - 1; + _recvIndex = 0; + + handleEventPkt(pktLen, &_recvBuffer[1]); + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, LOW); +#endif + HCITransport.lockForRead(); + } + } else { + _recvIndex = 0; + + if (_debug) { + HCITransport.unlockForRead(); + _debug->println(b, HEX); + HCITransport.lockForRead(); + } + } + } + +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + digitalWrite(NINA_RTS, HIGH); +#endif + HCITransport.unlockForRead(); +} + +int HCIClass::reset() +{ + return sendCommand(OGF_HOST_CTL << 10 | OCF_RESET); +} + +int HCIClass::readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer, uint16_t& manufacturer, uint16_t& lmpSubVer) +{ + int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_LOCAL_VERSION); + + if (result == 0) { + struct __attribute__ ((packed)) HCILocalVersion { + uint8_t hciVer; + uint16_t hciRev; + uint8_t lmpVer; + uint16_t manufacturer; + uint16_t lmpSubVer; + } *localVersion = (HCILocalVersion*)_cmdResponse; + + hciVer = localVersion->hciVer; + hciRev = localVersion->hciRev; + lmpVer = localVersion->lmpVer; + manufacturer = localVersion->manufacturer; + lmpSubVer = localVersion->lmpSubVer; + } + + return result; +} + +int HCIClass::readBdAddr(uint8_t addr[6]) +{ + int result = sendCommand(OGF_INFO_PARAM << 10 | OCF_READ_BD_ADDR); + + if (result == 0) { + memcpy(addr, _cmdResponse, 6); + } + + return result; +} + +int HCIClass::readBdAddr(){ + uint8_t response[6]; + int result = readBdAddr(response); + if(result==0){ + for(int i=0; i<6; i++){ + localAddr[5-i] = _cmdResponse[i]; + } + } + return result; +} + +int HCIClass::readRssi(uint16_t handle) +{ + int result = sendCommand(OGF_STATUS_PARAM << 10 | OCF_READ_RSSI, sizeof(handle), &handle); + int rssi = 127; + + if (result == 0) { + struct __attribute__ ((packed)) HCIReadRssi { + uint16_t handle; + int8_t rssi; + } *readRssi = (HCIReadRssi*)_cmdResponse; + + if (readRssi->handle == handle) { + rssi = readRssi->rssi; + } + } + + return rssi; +} + +int HCIClass::setEventMask(uint64_t eventMask) +{ + return sendCommand(OGF_HOST_CTL << 10 | OCF_SET_EVENT_MASK, sizeof(eventMask), &eventMask); +} +// Set LE Event mask +int HCIClass::setLeEventMask(uint64_t leEventMask) +{ + return sendCommand(OGF_LE_CTL << 10 | 0x01, sizeof(leEventMask), &leEventMask); +} + +int HCIClass::readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt) +{ + int result = sendCommand(OGF_LE_CTL << 10 | OCF_LE_READ_BUFFER_SIZE); + + if (result == 0) { + struct __attribute__ ((packed)) HCILeBufferSize { + uint16_t pktLen; + uint8_t maxPkt; + } *leBufferSize = (HCILeBufferSize*)_cmdResponse; + + pktLen = leBufferSize->pktLen; + _maxPkt = maxPkt = leBufferSize->maxPkt; + +#ifndef __AVR__ + ATT.setMaxMtu(pktLen - 9); // max pkt len - ACL header size +#endif + } + + return result; +} + +int HCIClass::leSetRandomAddress(uint8_t addr[6]) +{ + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_RANDOM_ADDRESS, 6, addr); +} + +int HCIClass::leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval, + uint8_t advType, uint8_t ownBdaddrType, + uint8_t directBdaddrType, uint8_t directBdaddr[6], + uint8_t chanMap, + uint8_t filter) +{ + struct __attribute__ ((packed)) HCILeAdvertisingParameters { + uint16_t minInterval; + uint16_t maxInterval; + uint8_t advType; + uint8_t ownBdaddrType; + uint8_t directBdaddrType; + uint8_t directBdaddr[6]; + uint8_t chanMap; + uint8_t filter; + } leAdvertisingParamters; + + leAdvertisingParamters.minInterval = minInterval; + leAdvertisingParamters.maxInterval = maxInterval; + leAdvertisingParamters.advType = advType; + leAdvertisingParamters.ownBdaddrType = ownBdaddrType; + leAdvertisingParamters.directBdaddrType = directBdaddrType; + memcpy(leAdvertisingParamters.directBdaddr, directBdaddr, 6); + leAdvertisingParamters.chanMap = chanMap; + leAdvertisingParamters.filter = filter; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_PARAMETERS, sizeof(leAdvertisingParamters), &leAdvertisingParamters); +} + +int HCIClass::leSetAdvertisingData(uint8_t length, uint8_t data[]) +{ + struct __attribute__ ((packed)) HCILeAdvertisingData { + uint8_t length; + uint8_t data[31]; + } leAdvertisingData; + + memset(&leAdvertisingData, 0, sizeof(leAdvertisingData)); + leAdvertisingData.length = length; + memcpy(leAdvertisingData.data, data, length); + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISING_DATA, sizeof(leAdvertisingData), &leAdvertisingData); +} + +int HCIClass::leSetScanResponseData(uint8_t length, uint8_t data[]) +{ + struct __attribute__ ((packed)) HCILeScanResponseData { + uint8_t length; + uint8_t data[31]; + } leScanResponseData; + + memset(&leScanResponseData, 0, sizeof(leScanResponseData)); + leScanResponseData.length = length; + memcpy(leScanResponseData.data, data, length); + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_RESPONSE_DATA, sizeof(leScanResponseData), &leScanResponseData); +} + +int HCIClass::leSetAdvertiseEnable(uint8_t enable) +{ + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_ADVERTISE_ENABLE, sizeof(enable), &enable); +} + +int HCIClass::leSetScanParameters(uint8_t type, uint16_t interval, uint16_t window, + uint8_t ownBdaddrType, uint8_t filter) +{ + struct __attribute__ ((packed)) HCILeSetScanParameters { + uint8_t type; + uint16_t interval; + uint16_t window; + uint8_t ownBdaddrType; + uint8_t filter; + } leScanParameters; + + leScanParameters.type = type; + leScanParameters.interval = interval; + leScanParameters.window = window; + leScanParameters.ownBdaddrType = ownBdaddrType; + leScanParameters.filter = filter; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_PARAMETERS, sizeof(leScanParameters), &leScanParameters); +} + +int HCIClass::leSetScanEnable(uint8_t enabled, uint8_t duplicates) +{ + struct __attribute__ ((packed)) HCILeSetScanEnableData { + uint8_t enabled; + uint8_t duplicates; + } leScanEnableData; + + leScanEnableData.enabled = enabled; + leScanEnableData.duplicates = duplicates; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_SET_SCAN_ENABLE, sizeof(leScanEnableData), &leScanEnableData); +} + +int HCIClass::leCreateConn(uint16_t interval, uint16_t window, uint8_t initiatorFilter, + uint8_t peerBdaddrType, uint8_t peerBdaddr[6], uint8_t ownBdaddrType, + uint16_t minInterval, uint16_t maxInterval, uint16_t latency, + uint16_t supervisionTimeout, uint16_t minCeLength, uint16_t maxCeLength) +{ + struct __attribute__ ((packed)) HCILeCreateConnData { + uint16_t interval; + uint16_t window; + uint8_t initiatorFilter; + uint8_t peerBdaddrType; + uint8_t peerBdaddr[6]; + uint8_t ownBdaddrType; + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + uint16_t minCeLength; + uint16_t maxCeLength; + } leCreateConnData; + + leCreateConnData.interval = interval; + leCreateConnData.window = window; + leCreateConnData.initiatorFilter = initiatorFilter; + leCreateConnData.peerBdaddrType = peerBdaddrType; + memcpy(leCreateConnData.peerBdaddr, peerBdaddr, sizeof(leCreateConnData.peerBdaddr)); + leCreateConnData.ownBdaddrType = ownBdaddrType; + leCreateConnData.minInterval = minInterval; + leCreateConnData.maxInterval = maxInterval; + leCreateConnData.latency = latency; + leCreateConnData.supervisionTimeout = supervisionTimeout; + leCreateConnData.minCeLength = minCeLength; + leCreateConnData.maxCeLength = maxCeLength; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CREATE_CONN, sizeof(leCreateConnData), &leCreateConnData); +} + +int HCIClass::leCancelConn() +{ + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CANCEL_CONN, 0, NULL); +} + +int HCIClass::leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t supervisionTimeout) +{ + struct __attribute__ ((packed)) HCILeConnUpdateData { + uint16_t handle; + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + uint16_t minCeLength; + uint16_t maxCeLength; + } leConnUpdateData; + + leConnUpdateData.handle = handle; + leConnUpdateData.minInterval = minInterval; + leConnUpdateData.maxInterval = maxInterval; + leConnUpdateData.latency = latency; + leConnUpdateData.supervisionTimeout = supervisionTimeout; + leConnUpdateData.minCeLength = 0x0004; + leConnUpdateData.maxCeLength = 0x0006; + + return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CONN_UPDATE, sizeof(leConnUpdateData), &leConnUpdateData); +} +void HCIClass::saveNewAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* localIrk){ + (void)addressType; + (void)localIrk; + if(_storeIRK!=0){ + _storeIRK(address, peerIrk); + } + // Again... this should work + // leAddResolvingAddress(addressType, address, peerIrk, localIrk); +} +void HCIClass::leAddResolvingAddress(uint8_t addressType, uint8_t* peerAddress, uint8_t* peerIrk, uint8_t* localIrk){ + leStopResolvingAddresses(); + + struct __attribute__ ((packed)) AddDevice { + uint8_t peerAddressType; + uint8_t peerAddress[6]; + uint8_t peerIRK[16]; + uint8_t localIRK[16]; + } addDevice; + addDevice.peerAddressType = addressType; + for(int i=0; i<6; i++) addDevice.peerAddress[5-i] = peerAddress[i]; + for(int i=0; i<16; i++) { + addDevice.peerIRK[15-i] = peerIrk[i]; + addDevice.localIRK[15-i] = localIrk[i]; + } +#ifdef _BLE_TRACE_ + Serial.print("ADDTYPE :"); + btct.printBytes(&addDevice.peerAddressType,1); + Serial.print("adddddd :"); + btct.printBytes(addDevice.peerAddress,6); + Serial.print("Peer IRK :"); + btct.printBytes(addDevice.peerIRK,16); + Serial.print("localIRK :"); + btct.printBytes(addDevice.localIRK,16); +#endif + sendCommand(OGF_LE_CTL << 10 | 0x27, sizeof(addDevice), &addDevice); + + leStartResolvingAddresses(); +} +int HCIClass::leStopResolvingAddresses(){ + uint8_t enable = 0; + return HCI.sendCommand(OGF_LE_CTL << 10 | 0x2D, 1,&enable); // Disable address resolution +} +int HCIClass::leStartResolvingAddresses(){ + uint8_t enable = 1; + return HCI.sendCommand(OGF_LE_CTL << 10 | 0x2D, 1,&enable); // Disable address resolution +} +int HCIClass::leReadPeerResolvableAddress(uint8_t peerAddressType, uint8_t* peerIdentityAddress, uint8_t* peerResolvableAddress){ + (void)peerResolvableAddress; + struct __attribute__ ((packed)) Request { + uint8_t addressType; + uint8_t identityAddress[6]; + } request; + request.addressType = peerAddressType; + for(int i=0; i<6; i++) request.identityAddress[5-i] = peerIdentityAddress[i]; + + + int res = sendCommand(OGF_LE_CTL << 10 | 0x2B, sizeof(request), &request); +#ifdef _BLE_TRACE_ + Serial.print("res: 0x"); + Serial.println(res, HEX); + if(res==0){ + struct __attribute__ ((packed)) Response { + uint8_t status; + uint8_t peerResolvableAddress[6]; + } *response = (Response*)_cmdResponse; + Serial.print("Address resolution status: 0x"); + Serial.println(response->status, HEX); + Serial.print("peer resolvable address: "); + btct.printBytes(response->peerResolvableAddress,6); + } + #endif + return res; +} + +void HCIClass::writeLK(uint8_t peerAddress[], uint8_t LK[]){ + struct __attribute__ ((packed)) StoreLK { + uint8_t nKeys; + uint8_t BD_ADDR[6]; + uint8_t LTK[16]; + } storeLK; + storeLK.nKeys = 1; + memcpy(storeLK.BD_ADDR, peerAddress, 6); + for(int i=0; i<16; i++) storeLK.LTK[15-i] = LK[i]; + HCI.sendCommand(OGF_HOST_CTL << 10 | 0x11, sizeof(storeLK), &storeLK); +} +void HCIClass::readStoredLKs(){ + uint8_t BD_ADDR[6]; + readStoredLK(BD_ADDR, 1); +} +int HCIClass::readStoredLK(uint8_t BD_ADDR[], uint8_t read_all ){ + struct __attribute__ ((packed)) Request { + uint8_t BD_ADDR[6]; + uint8_t read_a; + } request = {{0},0}; + for(int i=0; i<6; i++) request.BD_ADDR[5-i] = BD_ADDR[i]; + request.read_a = read_all; + return sendCommand(OGF_HOST_CTL << 10 | 0xD, sizeof(request), &request); +} + +int HCIClass::tryResolveAddress(uint8_t* BDAddr, uint8_t* address){ + bool foundMatch = false; + if(HCI._getIRKs!=0){ + uint8_t nIRKs = 0; + uint8_t** BDAddrType = new uint8_t*; + uint8_t*** BADDRs = new uint8_t**; + uint8_t*** IRKs = new uint8_t**; + + + if(!HCI._getIRKs(&nIRKs, BDAddrType, BADDRs, IRKs)){ +#ifdef _BLE_TRACE_ + Serial.println("error getting IRKs."); +#endif + } + for(int i=0; i= _maxPkt) { + poll(); + } + + struct __attribute__ ((packed)) HCIACLHdr { + uint8_t pktType; + uint16_t handle; + uint16_t dlen; + uint16_t plen; + uint16_t cid; + } aclHdr = { HCI_ACLDATA_PKT, handle, uint8_t(plen + 4), plen, cid }; + + uint8_t txBuffer[sizeof(aclHdr) + plen]; + memcpy(txBuffer, &aclHdr, sizeof(aclHdr)); + memcpy(&txBuffer[sizeof(aclHdr)], data, plen); + + if (_debug) { + dumpPkt("HCI ACLDATA TX -> ", sizeof(aclHdr) + plen, txBuffer); + } +#ifdef _BLE_TRACE_ + Serial.print("Data tx -> "); + for(int i=0; i< sizeof(aclHdr) + plen;i++){ + Serial.print(" 0x"); + Serial.print(txBuffer[i],HEX); + } + Serial.println("."); +#endif + + _pendingPkt++; + HCITransport.write(txBuffer, sizeof(aclHdr) + plen); + + return 0; +} + +int HCIClass::disconnect(uint16_t handle) +{ + struct __attribute__ ((packed)) HCIDisconnectData { + uint16_t handle; + uint8_t reason; + } disconnectData = { handle, HCI_OE_USER_ENDED_CONNECTION }; + + return sendCommand(OGF_LINK_CTL << 10 | OCF_DISCONNECT, sizeof(disconnectData), &disconnectData); +} + +void HCIClass::debug(Stream& stream) +{ + _debug = &stream; +} + +void HCIClass::noDebug() +{ + _debug = NULL; +} + +int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters) +{ + struct __attribute__ ((packed)) { + uint8_t pktType; + uint16_t opcode; + uint8_t plen; + } pktHdr = {HCI_COMMAND_PKT, opcode, plen}; + + uint8_t txBuffer[sizeof(pktHdr) + plen]; + memcpy(txBuffer, &pktHdr, sizeof(pktHdr)); + memcpy(&txBuffer[sizeof(pktHdr)], parameters, plen); + + if (_debug) { + dumpPkt("HCI COMMAND TX -> ", sizeof(pktHdr) + plen, txBuffer); + } +#ifdef _BLE_TRACE_ + Serial.print("Command tx -> "); + for(int i=0; i< sizeof(pktHdr) + plen;i++){ + Serial.print(" 0x"); + Serial.print(txBuffer[i],HEX); + } + Serial.println(""); +#endif + HCITransport.write(txBuffer, sizeof(pktHdr) + plen); + + _cmdCompleteOpcode = 0xffff; + _cmdCompleteStatus = -1; + + for (unsigned long start = millis(); _cmdCompleteOpcode != opcode && millis() < (start + 1000);) { + poll(); + } + + return _cmdCompleteStatus; +} + +void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[]) +{ + struct __attribute__ ((packed)) HCIACLHdr { + uint16_t connectionHandleWithFlags; + uint16_t dlen; // dlen + 4 = plen (dlen is the size of the ACL SDU) + } *aclHeader = (HCIACLHdr*)pdata; + + uint8_t pbFlag = (aclHeader->connectionHandleWithFlags & 0x3000) >> 12; + uint16_t connectionHandle = aclHeader->connectionHandleWithFlags & 0x0fff; + + uint8_t *aclSdu = &pdata[sizeof(HCIACLHdr)]; + +#ifdef _BLE_TRACE_ + uint8_t bcFlag = (aclHeader->connectionHandleWithFlags & 0xc000) >> 14; + Serial.print("Acl packet bcFlag = "); + Serial.print(bcFlag, BIN); + Serial.print(" pbFlag = "); + Serial.print(pbFlag, BIN); + Serial.print(" connectionHandle = "); + Serial.print(connectionHandle, HEX); + Serial.print(" dlen = "); + Serial.println(aclHeader->dlen, DEC); +#endif + + // Pointer to the L2CAP PDU (might be reconstructed from multiple fragments) + uint8_t *l2CapPdu; + uint8_t l2CapPduSize; + + if (pbFlag == 0b10) { + // "First automatically flushable packet" = Start of our L2CAP PDU + + l2CapPdu = aclSdu; + l2CapPduSize = aclHeader->dlen; + } else if (pbFlag == 0b01) { + // "Continuing Fragment" = Continued L2CAP PDU +#ifdef _BLE_TRACE_ + Serial.print("Continued packet. Appending to L2CAP PDU buffer (previously "); + Serial.print(_l2CapPduBufferSize, DEC); + Serial.println(" bytes in buffer)"); +#endif + // If we receive a fragment, we always need to append it to the L2CAP PDU buffer + memcpy(&_l2CapPduBuffer[_l2CapPduBufferSize], aclSdu, aclHeader->dlen); + _l2CapPduBufferSize += aclHeader->dlen; + + l2CapPdu = _l2CapPduBuffer; + l2CapPduSize = _l2CapPduBufferSize; + } else { + // I don't think other values are allowed for BLE +#ifdef _BLE_TRACE_ + Serial.println("Invalid pbFlag, discarding packet"); +#endif + return; + } + + // We now have a valid L2CAP header in l2CapPdu and can parse the headers + struct __attribute__ ((packed)) HCIL2CapHdr { + uint16_t len; // size of the L2CAP SDU + uint16_t cid; + } *l2CapHeader = (HCIL2CapHdr*)l2CapPdu; + +#ifdef _BLE_TRACE_ + Serial.print("Received "); + Serial.print(l2CapPduSize - 4, DEC); + Serial.print("B/"); + Serial.print(l2CapHeader->len, DEC); + Serial.print("B of the L2CAP SDU. CID = "); + Serial.println(l2CapHeader->cid, HEX); +#endif + + // -4 because the buffer is the L2CAP PDU (with L2CAP header). The len field is only the L2CAP SDU (without L2CAP header). + if (l2CapPduSize - 4 != l2CapHeader->len) { +#ifdef _BLE_TRACE_ + Serial.println("L2CAP SDU incomplete"); +#endif + + // If this is a first packet, we have not copied it into the buffer yet + if (pbFlag == 0b10) { +#ifdef _BLE_TRACE_ + Serial.println("Storing first packet to L2CAP PDU buffer"); + if (_l2CapPduBufferSize != 0) { + Serial.print("Warning: Discarding "); + Serial.print(_l2CapPduBufferSize, DEC); + Serial.println(" bytes from buffer"); + } +#endif + + memcpy(_l2CapPduBuffer, l2CapPdu, l2CapPduSize); + _l2CapPduBufferSize = l2CapPduSize; + } + + // We need to wait for the missing parts of the L2CAP SDU + return; + } + +#ifdef _BLE_TRACE_ + Serial.println("L2CAP SDU complete"); +#endif + + if (l2CapHeader->cid == ATT_CID) { +#ifdef _BLE_TRACE_ + Serial.println("CID: ATT"); +#endif + ATT.handleData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]); + } else if (l2CapHeader->cid == SIGNALING_CID) { +#ifdef _BLE_TRACE_ + Serial.println("CID: SIGNALING"); +#endif + L2CAPSignaling.handleData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]); + } else if (l2CapHeader->cid == SECURITY_CID) { + // Security manager +#ifdef _BLE_TRACE_ + Serial.println("CID: SECURITY"); +#endif + L2CAPSignaling.handleSecurityData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]); + } else { + struct __attribute__ ((packed)) { + uint8_t op; + uint8_t id; + uint16_t length; + uint16_t reason; + uint16_t localCid; + uint16_t remoteCid; + } l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, l2CapHeader->cid, 0x0000 }; +#ifdef _BLE_TRACE_ + Serial.println("Rejecting packet cid"); +#endif + + sendAclPkt(connectionHandle, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid); + } + + // We have processed everything in the buffer. Discard the contents. + _l2CapPduBufferSize = 0; +} + +void HCIClass::handleNumCompPkts(uint16_t /*handle*/, uint16_t numPkts) +{ + if (numPkts && _pendingPkt > numPkts) { + _pendingPkt -= numPkts; + } else { + _pendingPkt = 0; + } +} + +void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[]) +{ + struct __attribute__ ((packed)) HCIEventHdr { + uint8_t evt; + uint8_t plen; + } *eventHdr = (HCIEventHdr*)pdata; +#ifdef _BLE_TRACE_ + Serial.print("HCI event: "); + Serial.println(eventHdr->evt, HEX); +#endif + + if (eventHdr->evt == EVT_DISCONN_COMPLETE) + { + struct __attribute__ ((packed)) DisconnComplete { + uint8_t status; + uint16_t handle; + uint8_t reason; + } *disconnComplete = (DisconnComplete*)&pdata[sizeof(HCIEventHdr)]; + + ATT.removeConnection(disconnComplete->handle, disconnComplete->reason); + L2CAPSignaling.removeConnection(disconnComplete->handle, disconnComplete->reason); + + if (GAP.advertising()) + { + HCI.leSetAdvertiseEnable(0x01); + } + } + else if (eventHdr->evt == EVT_ENCRYPTION_CHANGE) + { + struct __attribute__ ((packed)) EncryptionChange { + uint8_t status; + uint16_t connectionHandle; + uint8_t enabled; + } *encryptionChange = (EncryptionChange*)&pdata[sizeof(HCIEventHdr)]; +#ifdef _BLE_TRACE_ + Serial.println("[Info] Encryption changed"); + Serial.print("status : "); + btct.printBytes(&encryptionChange->status,1); + Serial.print("handle : "); + btct.printBytes((uint8_t*)&encryptionChange->connectionHandle,2); + Serial.print("enabled: "); + btct.printBytes(&encryptionChange->enabled,1); +#endif + if(encryptionChange->enabled>0){ + // 0001 1110 + if((ATT.getPeerEncryption(encryptionChange->connectionHandle)&PEER_ENCRYPTION::PAIRING_REQUEST)>0){ + if(ATT.localKeyDistribution.EncKey()){ +#ifdef _BLE_TRACE_ + Serial.println("Enc key set but should be ignored"); +#endif + }else{ +#ifdef _BLE_TRACE_ + Serial.println("No enc key distribution"); +#endif + } + // From page 1681 bluetooth standard - order matters + if(ATT.localKeyDistribution.IdKey()){ + /// We shall distribute IRK and address using identity information + { + uint8_t response[17]; + response[0] = CONNECTION_IDENTITY_INFORMATION; // Identity information. + for(int i=0; i<16; i++) response[16-i] = ATT.localIRK[i]; + HCI.sendAclPkt(encryptionChange->connectionHandle, SECURITY_CID, sizeof(response), response); +#ifdef _BLE_TRACE_ + Serial.println("Distribute ID Key"); +#endif + } + { + uint8_t response[8]; + response[0] = CONNECTION_IDENTITY_ADDRESS; // Identity address information + response[1] = 0x00; // Static local address + for(int i=0; i<6; i++) response[7-i] = HCI.localAddr[i]; + HCI.sendAclPkt(encryptionChange->connectionHandle, SECURITY_CID, sizeof(response), response); + } + } + if(ATT.localKeyDistribution.SignKey()){ + /// We shall distribut CSRK +#ifdef _BLE_TRACE_ + Serial.println("We shall distribute CSRK // not implemented"); +#endif + + }else{ + // Serial.println("We don't want to distribute CSRK"); + } + if(ATT.localKeyDistribution.LinkKey()){ +#ifdef _BLE_TRACE_ + Serial.println("We would like to use LTK to generate BR/EDR // not implemented"); +#endif + } + }else{ +#ifdef _BLE_TRACE_ + Serial.println("Reconnection, not pairing so no keys"); + Serial.println(ATT.getPeerEncryption(encryptionChange->connectionHandle),HEX); +#endif + } + + ATT.setPeerEncryption(encryptionChange->connectionHandle, PEER_ENCRYPTION::ENCRYPTED_AES); + if(ATT.writeBufferSize > 0){ + ATT.processWriteBuffer(); + } + if(ATT.holdBufferSize>0){ +#ifdef _BLE_TRACE_ + Serial.print("Sending queued response size: "); + Serial.println(ATT.holdBufferSize); +#endif + HCI.sendAclPkt(encryptionChange->connectionHandle, ATT_CID, ATT.holdBufferSize, ATT.holdBuffer); + ATT.holdBufferSize = 0; + } + }else{ + ATT.setPeerEncryption(encryptionChange->connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION); + } + } + else if (eventHdr->evt == EVT_CMD_COMPLETE) + { + struct __attribute__ ((packed)) CmdComplete { + uint8_t ncmd; + uint16_t opcode; + uint8_t status; + } *cmdCompleteHeader = (CmdComplete*)&pdata[sizeof(HCIEventHdr)]; +#ifdef _BLE_TRACE_ + Serial.print("E ncmd: 0x"); + Serial.println(cmdCompleteHeader->ncmd,HEX); + Serial.print("E opcode: 0x"); + Serial.println(cmdCompleteHeader->opcode, HEX); + Serial.print("E status: 0x"); + Serial.println(cmdCompleteHeader->status, HEX); +#endif + _cmdCompleteOpcode = cmdCompleteHeader->opcode; + _cmdCompleteStatus = cmdCompleteHeader->status; + _cmdResponseLen = pdata[1] - sizeof(CmdComplete); + _cmdResponse = &pdata[sizeof(HCIEventHdr) + sizeof(CmdComplete)]; + + } + else if (eventHdr->evt == EVT_CMD_STATUS) + { + struct __attribute__ ((packed)) CmdStatus { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; + } *cmdStatusHeader = (CmdStatus*)&pdata[sizeof(HCIEventHdr)]; + +#ifdef _BLE_TRACE_ + Serial.print("F n cmd: 0x"); + Serial.println(cmdStatusHeader->ncmd, HEX); + Serial.print("F status: 0x"); + Serial.println(cmdStatusHeader->status, HEX); + Serial.print("F opcode: 0x"); + Serial.println(cmdStatusHeader->opcode, HEX); +#endif + _cmdCompleteOpcode = cmdStatusHeader->opcode; + _cmdCompleteStatus = cmdStatusHeader->status; + _cmdResponseLen = 0; + } + else if (eventHdr->evt == EVT_NUM_COMP_PKTS) + { + uint8_t numHandles = pdata[sizeof(HCIEventHdr)]; + uint16_t* data = (uint16_t*)&pdata[sizeof(HCIEventHdr) + sizeof(numHandles)]; + + for (uint8_t i = 0; i < numHandles; i++) { + handleNumCompPkts(data[0], data[1]); +#ifdef _BLE_TRACE_ + Serial.print("Outstanding packets: "); + Serial.println(_pendingPkt); + Serial.print("Data[0]: 0x"); + Serial.println(data[0]); + Serial.print("Data[1]: 0x"); + Serial.println(data[1]); +#endif + data += 2; + } + } + else if(eventHdr->evt == 0x10) + { +#ifdef _BLE_TRACE_ + struct __attribute__ ((packed)) CmdHardwareError { + uint8_t hardwareCode; + } *cmdHardwareError = (CmdHardwareError*)&pdata[sizeof(HCIEventHdr)]; + Serial.print("Bluetooth hardware error."); + Serial.print(" Code: 0x"); + Serial.println(cmdHardwareError->hardwareCode, HEX); +#endif + } + else if (eventHdr->evt == EVT_LE_META_EVENT) + { + struct __attribute__ ((packed)) LeMetaEventHeader { + uint8_t subevent; + } *leMetaHeader = (LeMetaEventHeader*)&pdata[sizeof(HCIEventHdr)]; +#ifdef _BLE_TRACE_ + Serial.print("\tSubEvent: 0x"); + Serial.println(leMetaHeader->subevent,HEX); +#endif + switch((LE_META_EVENT)leMetaHeader->subevent){ + case ENHANCED_CONN_COMPLETE:{ + struct __attribute__ ((packed)) EvtLeConnectionComplete { + uint8_t status; + uint16_t handle; + uint8_t role; + uint8_t peerBdaddrType; + uint8_t peerBdaddr[6]; + uint8_t localResolvablePrivateAddress[6]; + uint8_t peerResolvablePrivateAddress[6]; + uint16_t interval; + uint16_t latency; + uint16_t supervisionTimeout; + uint8_t masterClockAccuracy; + } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)]; + + if (leConnectionComplete->status == 0x00) { + ATT.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + + L2CAPSignaling.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + } + // uint8_t address[6]; + // uint8_t BDAddr[6]; + // for(int i=0; i<6; i++) BDAddr[5-i] = leConnectionComplete->peerBdaddr[i]; + // leReadPeerResolvableAddress(leConnectionComplete->peerBdaddrType,BDAddr,address); + // Serial.print("Resolving address: "); + // btct.printBytes(BDAddr, 6); + // Serial.print("BT answer : "); + // btct.printBytes(address, 6); + +#ifdef _BLE_TRACE_ + Serial.print("Resolved peer : "); + btct.printBytes(leConnectionComplete->peerResolvablePrivateAddress,6); + Serial.print("Resolved local : "); + btct.printBytes(leConnectionComplete->localResolvablePrivateAddress,6); +#endif + break; + } + case CONN_COMPLETE:{ + struct __attribute__ ((packed)) EvtLeConnectionComplete { + uint8_t status; + uint16_t handle; + uint8_t role; + uint8_t peerBdaddrType; + uint8_t peerBdaddr[6]; + uint16_t interval; + uint16_t latency; + uint16_t supervisionTimeout; + uint8_t masterClockAccuracy; + } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)]; + + if (leConnectionComplete->status == 0x00) { + ATT.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + + L2CAPSignaling.addConnection(leConnectionComplete->handle, + leConnectionComplete->role, + leConnectionComplete->peerBdaddrType, + leConnectionComplete->peerBdaddr, + leConnectionComplete->interval, + leConnectionComplete->latency, + leConnectionComplete->supervisionTimeout, + leConnectionComplete->masterClockAccuracy); + } + // leReadPeerResolvableAddress(leConnectionComplete->peerBdaddrType,BDAddr,address); + // Serial.print("Resolving address: "); + // btct.printBytes(BDAddr, 6); + // Serial.print("BT answer : "); + // btct.printBytes(address, 6); + break; + } + case ADVERTISING_REPORT:{ + struct __attribute__ ((packed)) EvtLeAdvertisingReport { + uint8_t status; + uint8_t type; + uint8_t peerBdaddrType; + uint8_t peerBdaddr[6]; + uint8_t eirLength; + uint8_t eirData[31]; + } *leAdvertisingReport = (EvtLeAdvertisingReport*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)]; + + if(leAdvertisingReport->eirLength > sizeof(leAdvertisingReport->eirData)){ + return ; + } + + if (leAdvertisingReport->status == 0x01) { + // last byte is RSSI + int8_t rssi = leAdvertisingReport->eirData[leAdvertisingReport->eirLength]; + + GAP.handleLeAdvertisingReport(leAdvertisingReport->type, + leAdvertisingReport->peerBdaddrType, + leAdvertisingReport->peerBdaddr, + leAdvertisingReport->eirLength, + leAdvertisingReport->eirData, + rssi); + } + break; + } + case LONG_TERM_KEY_REQUEST:{ + struct __attribute__ ((packed)) LTKRequest + { + uint8_t subEventCode; + uint16_t connectionHandle; + uint8_t randomNumber[8]; + uint8_t encryptedDiversifier[2]; + } *ltkRequest = (LTKRequest*)&pdata[sizeof(HCIEventHdr)]; +#ifdef _BLE_TRACE_ + Serial.println("LTK request received"); + Serial.print("Connection Handle: "); + btct.printBytes((uint8_t*)<kRequest->connectionHandle,2); + Serial.print("Random Number : "); + btct.printBytes(ltkRequest->randomNumber,8); + Serial.print("EDIV : "); + btct.printBytes(ltkRequest->encryptedDiversifier,2); +#endif + // Load our LTK for this connection. + uint8_t peerAddr[7]; + uint8_t resolvableAddr[6]; + uint8_t foundLTK; + ATT.getPeerAddrWithType(ltkRequest->connectionHandle, peerAddr); + + if((ATT.getPeerEncryption(ltkRequest->connectionHandle) & PEER_ENCRYPTION::PAIRING_REQUEST)>0){ + // Pairing request - LTK is one in buffer already + foundLTK = 1; + }else{ + if(ATT.getPeerResolvedAddress(ltkRequest->connectionHandle, resolvableAddr)){ + foundLTK = getLTK(resolvableAddr, HCI.LTK); + }else{ + foundLTK = getLTK(&peerAddr[1], HCI.LTK); + } + } + // } //2d + // Send our LTK back + if(foundLTK){ + struct __attribute__ ((packed)) LTKReply + { + uint16_t connectionHandle; + uint8_t LTK[16]; + } ltkReply = {0,0}; + ltkReply.connectionHandle = ltkRequest->connectionHandle; + for(int i=0; i<16; i++) ltkReply.LTK[15-i] = HCI.LTK[i]; + int result = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::LONG_TERM_KEY_REPLY,sizeof(ltkReply), <kReply); + + #ifdef _BLE_TRACE_ + Serial.println("Sending LTK as: "); + btct.printBytes(ltkReply.LTK,16); + #endif + + if(result == 0){ + struct __attribute__ ((packed)) LTKReplyResult + { + uint8_t status; + uint16_t connectionHandle; + } ltkReplyResult = {0,0}; + memcpy(<kReplyResult, _cmdResponse, 3); + + #ifdef _BLE_TRACE_ + Serial.println("LTK send success"); + Serial.print("status : "); + btct.printBytes(<kReplyResult.status,1); + Serial.print("Conn Handle: "); + btct.printBytes((uint8_t*)<kReplyResult.connectionHandle,2); + #endif + }else{ + #ifdef _BLE_TRACE_ + Serial.print("Failed to send LTK...: "); + btct.printBytes((uint8_t*)&result,2); + #endif + } + }else{ + /// do LTK rejection +#ifdef _BLE_TRACE_ + Serial.println("LTK not found, rejecting"); +#endif + sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::LONG_TERM_KEY_NEGATIVE_REPLY,2, <kRequest->connectionHandle); + } + break; + } + case REMOTE_CONN_PARAM_REQ:{ + struct __attribute__ ((packed)) RemoteConnParamReq { + uint8_t subEventCode; + uint16_t connectionHandle; + uint16_t intervalMin; + uint16_t intervalMax; + uint16_t latency; + uint16_t timeOut; + } *remoteConnParamReq = (RemoteConnParamReq*)&pdata[sizeof(HCIEventHdr)]; +#ifdef _BLE_TRACE_ + Serial.println("--- Remtoe conn param req"); + Serial.print("Handle : "); + btct.printBytes((uint8_t*)&remoteConnParamReq->connectionHandle,2); + Serial.print("Interval min: "); + btct.printBytes((uint8_t*)&remoteConnParamReq->intervalMin,2); + Serial.print("Interval max: "); + btct.printBytes((uint8_t*)&remoteConnParamReq->intervalMax,2); + Serial.print("Latency : "); + btct.printBytes((uint8_t*)&remoteConnParamReq->latency,2); + Serial.print("Timeout : "); + btct.printBytes((uint8_t*)&remoteConnParamReq->timeOut,2); +#endif + + struct __attribute__ ((packed)) RemoteConnParamReqReply { + uint16_t connectionHandle; + uint16_t intervalMin; + uint16_t intervalMax; + uint16_t latency; + uint16_t timeOut; + uint16_t minLength; + uint16_t maxLength; + } remoteConnParamReqReply; + memcpy(&remoteConnParamReqReply, &remoteConnParamReq->connectionHandle, sizeof(RemoteConnParamReq)-1); + + remoteConnParamReqReply.minLength = 0x000F; + remoteConnParamReqReply.maxLength = 0x0FFF; + sendCommand(OGF_LE_CTL << 10 | 0x20, sizeof(RemoteConnParamReqReply), &remoteConnParamReqReply); + break; + } + case READ_LOCAL_P256_COMPLETE:{ + struct __attribute__ ((packed)) EvtReadLocalP256Complete{ + uint8_t subEventCode; + uint8_t status; + uint8_t localPublicKey[64]; + } *evtReadLocalP256Complete = (EvtReadLocalP256Complete*)&pdata[sizeof(HCIEventHdr)]; + if(evtReadLocalP256Complete->status == 0x0){ +#ifdef _BLE_TRACE_ + Serial.println("Key read success"); +#endif + struct __attribute__ ((packed)) PairingPublicKey + { + uint8_t code; + uint8_t publicKey[64]; + } pairingPublicKey = {CONNECTION_PAIRING_PUBLIC_KEY,0}; + memcpy(pairingPublicKey.publicKey,evtReadLocalP256Complete->localPublicKey,64); + memcpy(localPublicKeyBuffer, evtReadLocalP256Complete->localPublicKey,64); + + // Send the local public key to the remote + uint16_t connectionHandle = ATT.getPeerEncrptingConnectionHandle(); + if(connectionHandle>ATT_MAX_PEERS){ +#ifdef _BLE_TRACE_ + Serial.println("failed to find connection handle"); +#endif + break; + } + HCI.sendAclPkt(connectionHandle,SECURITY_CID,sizeof(PairingPublicKey),&pairingPublicKey); + uint8_t encryption = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::SENT_PUBKEY; + ATT.setPeerEncryption(connectionHandle, encryption); + + + uint8_t Z = 0; + + HCI.leRand(Nb); + HCI.leRand(&Nb[8]); + +#ifdef _BLE_TRACE_ + Serial.print("nb: "); + btct.printBytes(Nb, 16); +#endif + struct __attribute__ ((packed)) F4Params + { + uint8_t U[32]; + uint8_t V[32]; + uint8_t Z; + } f4Params = {{0},{0},Z}; + for(int i=0; i<32; i++){ + f4Params.U[31-i] = pairingPublicKey.publicKey[i]; + f4Params.V[31-i] = HCI.remotePublicKeyBuffer[i]; + } + + struct __attribute__ ((packed)) PairingConfirm + { + uint8_t code; + uint8_t cb[16]; + } pairingConfirm = {CONNECTION_PAIRING_CONFIRM,0}; + + btct.AES_CMAC(Nb,(unsigned char *)&f4Params,sizeof(f4Params),pairingConfirm.cb); + +#ifdef _BLE_TRACE_ + Serial.print("cb: "); + btct.printBytes(pairingConfirm.cb, 16); +#endif + + uint8_t cb_temp[sizeof(pairingConfirm.cb)]; + for(unsigned int i=0; istatus,HEX); + for(int i=0; i<64; i++){ + Serial.print(" 0x"); + Serial.print(evtReadLocalP256Complete->localPublicKey[i],HEX); + } + Serial.println("."); +#endif + } + break; + } + case GENERATE_DH_KEY_COMPLETE:{ + struct __attribute__ ((packed)) EvtLeDHKeyComplete{ + uint8_t subEventCode; + uint8_t status; + uint8_t DHKey[32]; + } *evtLeDHKeyComplete = (EvtLeDHKeyComplete*)&pdata[sizeof(HCIEventHdr)]; + if(evtLeDHKeyComplete->status == 0x0){ +#ifdef _BLE_TRACE_ + Serial.println("DH key generated"); +#endif + uint16_t connectionHandle = ATT.getPeerEncrptingConnectionHandle(); + if(connectionHandle>ATT_MAX_PEERS){ +#ifdef _BLE_TRACE_ + Serial.println("Failed to find connection handle DH key check"); +#endif + break; + } + + + for(int i=0; i<32; i++) DHKey[31-i] = evtLeDHKeyComplete->DHKey[i]; + +#ifdef _BLE_TRACE_ + Serial.println("Stored our DHKey:"); + btct.printBytes(DHKey,32); +#endif + uint8_t encryption = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::DH_KEY_CALULATED; + ATT.setPeerEncryption(connectionHandle, encryption); + + if((encryption & PEER_ENCRYPTION::RECEIVED_DH_CHECK) > 0){ +#ifdef _BLE_TRACE_ + Serial.println("Received DHKey check already so calculate f5, f6 now."); +#endif + L2CAPSignaling.smCalculateLTKandConfirm(connectionHandle, HCI.remoteDHKeyCheckBuffer); + + }else{ +#ifdef _BLE_TRACE_ + Serial.println("Waiting on other DHKey check before calculating."); +#endif + } + }else{ +#ifdef _BLE_TRACE_ + Serial.print("Key generation error: 0x"); + Serial.println(evtLeDHKeyComplete->status, HEX); +#endif + } + break; + } + default: + { +#ifdef _BLE_TRACE_ + Serial.println("[Info] Unhandled meta event"); +#endif + } + } + }else{ +#ifdef _BLE_TRACE_ + Serial.println("[Info] Unhandled event"); +#endif + } +} +int HCIClass::leEncrypt(uint8_t* key, uint8_t* plaintext, uint8_t* status, uint8_t* ciphertext){ + (void)status; + struct __attribute__ ((packed)) LeEncryptCommand + { + uint8_t key[16]; + uint8_t plaintext[16]; + } leEncryptCommand = {{0},{0}}; + for(int i=0; i<16; i++){ + leEncryptCommand.key[15-i] = key[i]; + leEncryptCommand.plaintext[15-i] = plaintext[i]; + } + + int res = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::ENCRYPT, 32, &leEncryptCommand); + if(res == 0){ +#ifdef _BLE_TRACE_ + Serial.print("Copying from command Response length: "); + Serial.println(_cmdResponseLen); + Serial.println("."); + for(int i=0; i<20; i++){ + Serial.print(" 0x"); + Serial.print(_cmdResponse[i],HEX); + } + Serial.println("."); +#endif + for(int i=0; i<16; i++){ + ciphertext[15-i] = _cmdResponse[i]; + } + return 1; + } +#ifdef _BLE_TRACE_ + Serial.print("Error with AES: 0x"); + Serial.println(res, HEX); +#endif + return res; +} +int HCIClass::leRand(uint8_t rand[]){ + int res = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::RANDOM); + if(res == 0){ + memcpy(rand,_cmdResponse, 8); /// backwards but it's a random number + } + return res; +} +int HCIClass::getLTK(uint8_t* address, uint8_t* LTK){ + if(_getLTK!=0){ + return _getLTK(address, LTK); + }else{ + return 0; + } +} +int HCIClass::storeIRK(uint8_t* address, uint8_t* IRK){ + if(_storeIRK!=0){ + return _storeIRK(address, IRK); + }else{ + return 0; + } +} +int HCIClass::storeLTK(uint8_t* address, uint8_t* LTK){ + if(_storeLTK!=0){ + return _storeLTK(address, LTK); + }else{ + return 0; + } +} +uint8_t HCIClass::localIOCap(){ + if(_displayCode!=0){ + /// We have a display + if(_binaryConfirmPairing!=0){ + return IOCAP_DISPLAY_YES_NO; + }else{ + return IOCAP_DISPLAY_ONLY; + } + }else{ + // We have no display + return IOCAP_NO_INPUT_NO_OUTPUT; + } +} + +/// Stub function to generate parameters for local authreq +AuthReq HCIClass::localAuthreq(){ + // If get, set, IRK, LTK all set then we can bond. + AuthReq local = AuthReq(); + if(_storeIRK!=0 && _storeLTK!=0 && _getLTK!=0 && _getIRKs!=0){ + local.setBonding(true); + } + local.setSC(true); + local.setMITM(true); + local.setCT2(true); + return LOCAL_AUTHREQ; +} + +void HCIClass::dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[]) +{ + if (_debug) { + _debug->print(prefix); + + for (uint8_t i = 0; i < plen; i++) { + byte b = pdata[i]; + + if (b < 16) { + _debug->print("0"); + } + + _debug->print(b, HEX); + } + + _debug->println(); + _debug->flush(); + } +} + +#if !defined(FAKE_HCI) +HCIClass HCIObj; +HCIClass& HCI = HCIObj; +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCI.h b/arduino/libraries/ArduinoBLE/src/utility/HCI.h new file mode 100644 index 0000000..532cf5d --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCI.h @@ -0,0 +1,168 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_H_ +#define _HCI_H_ + +#include +#include "bitDescriptions.h" + +#include "L2CAPSignaling.h" + +#define OGF_LINK_CTL 0x01 +#define OGF_HOST_CTL 0x03 +#define OGF_INFO_PARAM 0x04 +#define OGF_STATUS_PARAM 0x05 +#define OGF_LE_CTL 0x08 + +enum LE_COMMAND { + ENCRYPT = 0x0017, + RANDOM = 0x0018, + LONG_TERM_KEY_REPLY = 0x001A, + LONG_TERM_KEY_NEGATIVE_REPLY = 0x001B, + READ_LOCAL_P256 = 0x0025, + GENERATE_DH_KEY_V1 = 0x0026, + GENERATE_DH_KEY_V2 = 0x005E +}; +enum LE_META_EVENT { + CONN_COMPLETE = 0x01, + ADVERTISING_REPORT = 0x02, + LONG_TERM_KEY_REQUEST = 0x05, + REMOTE_CONN_PARAM_REQ = 0x06, + READ_LOCAL_P256_COMPLETE = 0x08, + GENERATE_DH_KEY_COMPLETE = 0x09, + ENHANCED_CONN_COMPLETE = 0x0A, +}; +String metaEventToString(LE_META_EVENT event); +String commandToString(LE_COMMAND command); + +class HCIClass { +public: + HCIClass(); + virtual ~HCIClass(); + + virtual int begin(); + virtual void end(); + + virtual void poll(); + virtual void poll(unsigned long timeout); + + virtual int reset(); + virtual int readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer, + uint16_t& manufacturer, uint16_t& lmpSubVer); + + virtual int readBdAddr(uint8_t addr[6]); + virtual int readBdAddr(); + + virtual int readRssi(uint16_t handle); + + virtual int setEventMask(uint64_t eventMask); + virtual int setLeEventMask(uint64_t leEventMask); + virtual int readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt); + virtual int leSetRandomAddress(uint8_t addr[6]); + virtual int leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval, + uint8_t advType, uint8_t ownBdaddrType, + uint8_t directBdaddrType, uint8_t directBdaddr[6], + uint8_t chanMap, + uint8_t filter); + virtual int leSetAdvertisingData(uint8_t length, uint8_t data[]); + virtual int leSetScanResponseData(uint8_t length, uint8_t data[]); + virtual int leSetAdvertiseEnable(uint8_t enable); + virtual int leSetScanParameters(uint8_t type, uint16_t interval, uint16_t window, + uint8_t ownBdaddrType, uint8_t filter); + virtual int leSetScanEnable(uint8_t enabled, uint8_t duplicates); + virtual int leCreateConn(uint16_t interval, uint16_t window, uint8_t initiatorFilter, + uint8_t peerBdaddrType, uint8_t peerBdaddr[6], uint8_t ownBdaddrType, + uint16_t minInterval, uint16_t maxInterval, uint16_t latency, + uint16_t supervisionTimeout, uint16_t minCeLength, uint16_t maxCeLength); + virtual int leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t supervisionTimeout); + virtual int leCancelConn(); + virtual int leEncrypt(uint8_t* Key, uint8_t* plaintext, uint8_t* status, uint8_t* ciphertext); + // Generate a 64 bit random number + virtual int leRand(uint8_t rand[]); + virtual AuthReq localAuthreq(); + virtual uint8_t localIOCap(); + + virtual void saveNewAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk); + virtual void leAddResolvingAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk); + virtual int leStopResolvingAddresses(); + virtual int leStartResolvingAddresses(); + virtual int leReadPeerResolvableAddress(uint8_t peerAddressType, uint8_t* peerIdentityAddress, uint8_t* peerResolvableAddress); + + virtual void readStoredLKs(); + virtual int readStoredLK(uint8_t BD_ADDR[], uint8_t read_all = 0); + virtual void writeLK(uint8_t peerAddress[], uint8_t LK[]); + virtual int tryResolveAddress(uint8_t* BDAddr, uint8_t* address); + + virtual int sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data); + + virtual int disconnect(uint16_t handle); + + virtual void debug(Stream& stream); + virtual void noDebug(); + + // TODO: Send command be private again & use ATT implementation of send command within ATT. + virtual int sendCommand(uint16_t opcode, uint8_t plen = 0, void* parameters = NULL); + uint8_t remotePublicKeyBuffer[64]; + uint8_t localPublicKeyBuffer[64]; + uint8_t remoteDHKeyCheckBuffer[16]; + uint8_t Na[16]; + uint8_t Nb[16]; + uint8_t DHKey[32]; + uint8_t localAddr[6]; + uint8_t LTK[16]; + virtual int getLTK(uint8_t* address, uint8_t* LTK); + virtual int storeLTK(uint8_t* address, uint8_t* LTK); + virtual int storeIRK(uint8_t* address, uint8_t* IRK); + int (*_storeIRK)(uint8_t* address, uint8_t* peerIrk) = 0; + int (*_getIRKs)(uint8_t* nIRKs,uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs) = 0; + int (*_storeLTK)(uint8_t*, uint8_t*) = 0; + int (*_getLTK)(uint8_t*, uint8_t*) = 0; + void (*_displayCode)(uint32_t confirmationCode) = 0; + bool (*_binaryConfirmPairing)() = 0; + +private: + + virtual void handleAclDataPkt(uint8_t plen, uint8_t pdata[]); + virtual void handleNumCompPkts(uint16_t handle, uint16_t numPkts); + virtual void handleEventPkt(uint8_t plen, uint8_t pdata[]); + + virtual void dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[]); + + Stream* _debug; + + int _recvIndex; + uint8_t _recvBuffer[3 + 255]; + + uint16_t _cmdCompleteOpcode; + int _cmdCompleteStatus; + uint8_t _cmdResponseLen; + uint8_t* _cmdResponse; + + uint8_t _maxPkt; + uint8_t _pendingPkt; + + uint8_t _l2CapPduBuffer[255]; + uint8_t _l2CapPduBufferSize; +}; + +extern HCIClass& HCI; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.cpp new file mode 100644 index 0000000..8af0808 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.cpp @@ -0,0 +1,337 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ARDUINO_ARCH_MBED) && !defined(TARGET_NANO_RP2040_CONNECT) // && !defined(CORE_CM4) +#include +#include + +#include +#include + +#if defined(ARDUINO_PORTENTA_H7_M4) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_GIGA) || defined(ARDUINO_OPTA) +#include "ble/BLE.h" +#include +#endif + +// Parts of this file are based on: https://github.com/ARMmbed/mbed-os-cordio-hci-passthrough/pull/2 +// With permission from the Arm Mbed team to re-license + +#if CORDIO_ZERO_COPY_HCI +#include +#include +#include +#include +#include +#include + +/* avoid many small allocs (and WSF doesn't have smaller buffers) */ +#define MIN_WSF_ALLOC (16) +#endif //CORDIO_ZERO_COPY_HCI + +#include "HCICordioTransport.h" + +#if (MBED_VERSION > MBED_ENCODE_VERSION(6, 2, 0)) +#define BLE_NAMESPACE ble +#else +#define BLE_NAMESPACE ble::vendor::cordio +#endif + +#include "CordioHCICustomDriver.h" + +extern BLE_NAMESPACE::CordioHCIDriver& ble_cordio_get_hci_driver(); +extern "C" void hciTrSerialRxIncoming(uint8_t *pBuf, uint8_t len); + +namespace BLE_NAMESPACE { + struct CordioHCIHook { + static CordioHCIDriver& getDriver() { + return ble_cordio_get_hci_driver(); + } + + static CordioHCITransportDriver& getTransportDriver() { + return getDriver()._transport_driver; + } + + static void setDataReceivedHandler(void (*handler)(uint8_t*, uint8_t)) { + getTransportDriver().set_data_received_handler(handler); + } + }; +} + +using BLE_NAMESPACE::CordioHCIHook; + +#if CORDIO_ZERO_COPY_HCI +extern uint8_t *SystemHeapStart; +extern uint32_t SystemHeapSize; + +void init_wsf(BLE_NAMESPACE::buf_pool_desc_t& buf_pool_desc) { + static bool init = false; + + if (init) { + return; + } + init = true; + + // use the buffer for the WSF heap + SystemHeapStart = buf_pool_desc.buffer_memory; + SystemHeapSize = buf_pool_desc.buffer_size; + + // Initialize buffers with the ones provided by the HCI driver + uint16_t bytes_used = WsfBufInit( + buf_pool_desc.pool_count, + (wsfBufPoolDesc_t*)buf_pool_desc.pool_description + ); + + // Raise assert if not enough memory was allocated + MBED_ASSERT(bytes_used != 0); + + SystemHeapStart += bytes_used; + SystemHeapSize -= bytes_used; + + WsfTimerInit(); +} + + +extern "C" void wsf_mbed_ble_signal_event(void) +{ + // do nothing +} +#endif //CORDIO_ZERO_COPY_HCI + +static void bleLoop() +{ +#if CORDIO_ZERO_COPY_HCI + uint64_t last_update_us = 0; + mbed::LowPowerTimer timer; + + timer.start(); + + while (true) { + last_update_us += (uint64_t) timer.read_high_resolution_us(); + timer.reset(); + + uint64_t last_update_ms = (last_update_us / 1000); + wsfTimerTicks_t wsf_ticks = (last_update_ms / WSF_MS_PER_TICK); + + if (wsf_ticks > 0) { + WsfTimerUpdate(wsf_ticks); + last_update_us -= (last_update_ms * 1000); + } + + wsfOsDispatcher(); + + bool sleep = false; + { + /* call needs interrupts disabled */ + mbed::CriticalSectionLock critical_section; + if (wsfOsReadyToSleep()) { + sleep = true; + } + } + + uint64_t time_spent = (uint64_t) timer.read_high_resolution_us(); + + /* don't bother sleeping if we're already past tick */ + if (sleep && (WSF_MS_PER_TICK * 1000 > time_spent)) { + /* sleep to maintain constant tick rate */ + uint64_t wait_time_us = WSF_MS_PER_TICK * 1000 - time_spent; + uint64_t wait_time_ms = wait_time_us / 1000; + + wait_time_us = wait_time_us % 1000; + + if (wait_time_ms) { + rtos::ThisThread::sleep_for(wait_time_ms); + } + + if (wait_time_us) { + wait_us(wait_time_us); + } + } + } +#else + while(true) { + rtos::ThisThread::sleep_for(osWaitForever); + } +#endif // CORDIO_ZERO_COPY_HCI +} + +static rtos::EventFlags bleEventFlags; +static rtos::Thread* bleLoopThread = NULL; + + +HCICordioTransportClass::HCICordioTransportClass() : + _begun(false) +{ +} + +HCICordioTransportClass::~HCICordioTransportClass() +{ +} + +#if (defined(ARDUINO_PORTENTA_H7_M4) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_GIGA) || defined(ARDUINO_OPTA)) && !defined(CUSTOM_HCI_DRIVER) +events::EventQueue eventQueue(10 * EVENTS_EVENT_SIZE); +void scheduleMbedBleEvents(BLE::OnEventsToProcessCallbackContext *context) { + eventQueue.call(mbed::Callback(&context->ble, &BLE::processEvents)); +} + +void completeCallback(BLE::InitializationCompleteCallbackContext *context) { + eventQueue.break_dispatch(); +} +#endif + +int HCICordioTransportClass::begin() +{ + _rxBuf.clear(); + +#if CORDIO_ZERO_COPY_HCI + BLE_NAMESPACE::buf_pool_desc_t bufPoolDesc = CordioHCIHook::getDriver().get_buffer_pool_description(); + init_wsf(bufPoolDesc); +#endif + +#if (defined(ARDUINO_PORTENTA_H7_M4) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_GIGA) || defined(ARDUINO_OPTA)) && !defined(CUSTOM_HCI_DRIVER) + + BLE &ble = BLE::Instance(); + ble.onEventsToProcess(scheduleMbedBleEvents); + + ble.init(completeCallback); + eventQueue.dispatch(10000); + + if (!ble.hasInitialized()){ + return 0; + } +#else + CordioHCIHook::getDriver().initialize(); +#endif + + if (bleLoopThread == NULL) { + bleLoopThread = new rtos::Thread(); + bleLoopThread->start(bleLoop); + } + + CordioHCIHook::setDataReceivedHandler(HCICordioTransportClass::onDataReceived); + + _begun = true; + + return 1; +} + +void HCICordioTransportClass::end() +{ + if (bleLoopThread != NULL) { + bleLoopThread->terminate(); + delete bleLoopThread; + bleLoopThread = NULL; + } + // Reset the callback with the mbed-os default handler to properly handle the following CYW43xxx chip initializations and begins + CordioHCIHook::setDataReceivedHandler(hciTrSerialRxIncoming); + +#if (defined(ARDUINO_PORTENTA_H7_M4) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_GIGA) || defined(ARDUINO_OPTA)) && !defined(CUSTOM_HCI_DRIVER) + BLE &ble = BLE::Instance(); + ble.shutdown(); +#endif + +#if !defined(TARGET_STM32H7) + CordioHCIHook::getDriver().terminate(); +#endif + + _begun = false; +} + +void HCICordioTransportClass::wait(unsigned long timeout) +{ + { + mbed::CriticalSectionLock critical_section; + if (available()) { + return; + } + } + + // wait for handleRxData to signal + bleEventFlags.wait_all(0x01, timeout, true); +} + +int HCICordioTransportClass::available() +{ + return _rxBuf.available(); +} + +int HCICordioTransportClass::peek() +{ + return _rxBuf.peek(); +} + +int HCICordioTransportClass::read() +{ + return _rxBuf.read_char(); +} + +void HCICordioTransportClass::lockForRead() { + mbed::CriticalSectionLock::enable(); +} + +void HCICordioTransportClass::unlockForRead() { + mbed::CriticalSectionLock::disable(); +} + +size_t HCICordioTransportClass::write(const uint8_t* data, size_t length) +{ + if (!_begun) { + return 0; + } + + uint8_t packetLength = length - 1; + uint8_t packetType = data[0]; + +#if CORDIO_ZERO_COPY_HCI + uint8_t* packet = (uint8_t*)WsfMsgAlloc(max(packetLength, MIN_WSF_ALLOC)); + + memcpy(packet, &data[1], packetLength); + + return CordioHCIHook::getTransportDriver().write(packetType, packetLength, packet); +#else + return CordioHCIHook::getTransportDriver().write(packetType, packetLength, (uint8_t*)&data[1]); +#endif +} + +void HCICordioTransportClass::handleRxData(uint8_t* data, uint8_t len) +{ + { + #if defined(ARDUINO_ARCH_NRF52840) + mbed::CriticalSectionLock critical_section; + #endif + if (_rxBuf.availableForStore() < len) { + // drop! + return; + } + + for (int i = 0; i < len; i++) { + _rxBuf.store_char(data[i]); + } + } + bleEventFlags.set(0x01); +} + +HCICordioTransportClass HCICordioTransport; +HCITransportInterface& HCITransport = HCICordioTransport; + +void HCICordioTransportClass::onDataReceived(uint8_t* data, uint8_t len) +{ + HCICordioTransport.handleRxData(data, len); +} + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.h new file mode 100644 index 0000000..38f8e24 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCICordioTransport.h @@ -0,0 +1,57 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2019 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_CORDIO_TRANSPORT_H_ +#define _HCI_CORDIO_TRANSPORT_H_ + +#include + +#include "api/RingBuffer.h" + +#include "HCITransport.h" + +class HCICordioTransportClass : public HCITransportInterface { +public: + HCICordioTransportClass(); + virtual ~HCICordioTransportClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual void lockForRead() override; + virtual void unlockForRead() override; + + virtual size_t write(const uint8_t* data, size_t length); + +private: + static void onDataReceived(uint8_t* data, uint8_t len); + void handleRxData(uint8_t* data, uint8_t len); + +private: + bool _begun; + RingBufferN<256> _rxBuf; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.cpp new file mode 100644 index 0000000..3f63847 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.cpp @@ -0,0 +1,209 @@ +/* + This file is part of the ArduinoBLE library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_SAMD_MKRVIDOR4000) // STRIX: Vidor via FPGA pass-through +#include "HCINinaSpiTransport.h" +#include + +enum { + BLE_BEGIN = 0x4A, + BLE_END = 0x4B, + BLE_AVAILABLE = 0x4C, + BLE_PEEK = 0x4D, + BLE_READ = 0x4E, + BLE_WRITE = 0x4F, +}; + +int BleDrv::bleBegin() { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(BLE_BEGIN, PARAM_NUMS_0); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t len = 1; + uint8_t result = 0; + SpiDrv::waitResponseCmd(BLE_BEGIN, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); + + return result == 0; +} + +void BleDrv::bleEnd() { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(BLE_END, PARAM_NUMS_0); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t len = 1; + uint8_t result = 0; + SpiDrv::waitResponseCmd(BLE_END, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); +} + +int BleDrv::bleAvailable() { + WAIT_FOR_SLAVE_SELECT(); + uint16_t result = 0; + + SpiDrv::sendCmd(BLE_AVAILABLE, PARAM_NUMS_0); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t len = 2; + SpiDrv::waitResponseCmd(BLE_AVAILABLE, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); + + return result; +} + +int BleDrv::bleRead(uint8_t data[], size_t length) { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(BLE_READ, PARAM_NUMS_1); + + int commandSize = 7; // 4 for the normal command length + 3 for the parameter + uint16_t param = length; // TODO check length doesn't exceed 2^16 + SpiDrv::sendParam((uint8_t*)¶m, sizeof(param), LAST_PARAM); + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint16_t res_len = 0; + SpiDrv::waitResponseData16(BLE_READ, data, (uint16_t*)&res_len); + + SpiDrv::spiSlaveDeselect(); + + return res_len; +} + +int BleDrv::blePeek(uint8_t data[], size_t length) { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(BLE_PEEK, PARAM_NUMS_1); + + int commandSize = 7; // 4 for the normal command length + 3 for the parameter + uint16_t param = length; // TODO check length doesn't exceed 2^16 + SpiDrv::sendParam((uint8_t*)¶m, sizeof(param), LAST_PARAM); + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint16_t res_len = 0; + SpiDrv::waitResponseData16(BLE_READ, data, (uint16_t*)&res_len); + + SpiDrv::spiSlaveDeselect(); + + return res_len; +} + +size_t BleDrv::bleWrite(const uint8_t* data, size_t len) { + WAIT_FOR_SLAVE_SELECT(); + + int commandSize = 4; + SpiDrv::sendCmd(BLE_WRITE, PARAM_NUMS_1); + + SpiDrv::sendBuffer((uint8_t*)data, len, LAST_PARAM); + commandSize += len+2; + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t res_len = 1; + uint16_t res = 0; + SpiDrv::waitResponseCmd(BLE_WRITE, PARAM_NUMS_1, (uint8_t*)&res, &res_len); + SpiDrv::spiSlaveDeselect(); + + return res; +} + +int HCINinaSpiTransportClass::begin() +{ + SpiDrv::begin(); + return BleDrv::bleBegin(); +} + +void HCINinaSpiTransportClass::end() +{ + BleDrv::bleEnd(); +} + +void HCINinaSpiTransportClass::wait(unsigned long timeout) +{ + for (unsigned long start = millis(); (millis() - start) < timeout;) { + if (available()) { + break; + } + } +} + +int HCINinaSpiTransportClass::available() +{ + return BleDrv::bleAvailable(); +} + +int HCINinaSpiTransportClass::peek() +{ + int res=-1; + BleDrv::blePeek((uint8_t*)&res, 1); // read a single byte, if nothing is returned we return -1 + + return res; +} + +int HCINinaSpiTransportClass::read() +{ + int res=-1; + BleDrv::bleRead((uint8_t*)&res, 1); // read a single byte, if nothing is returned we return -1 + + return res; +} + +size_t HCINinaSpiTransportClass::write(const uint8_t* data, size_t length) +{ + return BleDrv::bleWrite(data, length); +} + +HCINinaSpiTransportClass HCINinaSpiTransport; +HCITransportInterface& HCITransport = HCINinaSpiTransport; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.h new file mode 100644 index 0000000..d02a97c --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCINinaSpiTransport.h @@ -0,0 +1,43 @@ +/* + This file is part of the ArduinoBLE library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _HCI_NINA_SPI_TRANSPORT_H_ +#define _HCI_NINA_SPI_TRANSPORT_H_ + +#include "HCITransport.h" + +class BleDrv +{ +public: + static int bleBegin(); + static void bleEnd(); + static int bleAvailable(); + static int bleRead(uint8_t data[], size_t length); + static int blePeek(uint8_t data[], size_t length); + static size_t bleWrite(const uint8_t* data, size_t length); +}; + +class HCINinaSpiTransportClass : public HCITransportInterface { +public: + HCINinaSpiTransportClass() {} + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.cpp new file mode 100644 index 0000000..135fd02 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.cpp @@ -0,0 +1,130 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2024 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ARDUINO_SILABS) + +#include "HCISilabsTransport.h" +#include "sl_string.h" + +extern "C" { +#include "sl_btctrl_linklayer.h" +#include "sl_hci_common_transport.h" +} + +extern "C" int strcasecmp(char const *a, char const *b) { + return sl_strcasecmp(a, b); +} + +static RingBufferN<512> buf; + +HCISilabsTransportClass::HCISilabsTransportClass() +{ +} + +HCISilabsTransportClass::~HCISilabsTransportClass() +{ +} + +int HCISilabsTransportClass::begin() +{ + if(!sl_btctrl_is_initialized()) { + sl_bt_controller_init(); + } + + /* Initialize adv & scan components */ + sl_btctrl_init_adv(); + sl_btctrl_init_scan(); + sl_btctrl_init_conn(); + sl_btctrl_init_adv_ext(); + sl_btctrl_init_scan_ext(); + + /* Initialize HCI controller */ + sl_bthci_init_upper(); + sl_btctrl_hci_parser_init_default(); + sl_btctrl_hci_parser_init_conn(); + sl_btctrl_hci_parser_init_adv(); + sl_btctrl_hci_parser_init_phy(); + + return 1; +} + +void HCISilabsTransportClass::end() +{ + sl_bt_controller_deinit(); +} + +void HCISilabsTransportClass::wait(unsigned long timeout) +{ + for (unsigned long start = millis(); (millis() - start) < timeout;) { + if (available()) { + break; + } + } +} + +int HCISilabsTransportClass::available() +{ + return buf.available(); +} + +int HCISilabsTransportClass::peek() +{ + return buf.peek(); +} + +int HCISilabsTransportClass::read() +{ + return buf.read_char(); +} + +size_t HCISilabsTransportClass::write(const uint8_t* data, size_t len) +{ + int ret = 0; + ret = hci_common_transport_receive((uint8_t *)data, len, true); + + if (ret == 0) return len; + + return 0; +} + +extern "C" { + /** + * @brief Transmit HCI message using the currently used transport layer. + * The HCI calls this function to transmit a full HCI message. + * @param[in] data Packet type followed by HCI packet data. + * @param[in] len Length of the `data` parameter + * @return 0 - on success, or non-zero on failure. + */ + uint32_t hci_common_transport_transmit(uint8_t *data, int16_t len) + { + for (int i = 0; i < len; i++) { + buf.store_char(data[i]); + if (buf.isFull()) return SL_STATUS_FAIL; + } + + sl_btctrl_hci_transmit_complete(0); + return 0; + } +} + +HCISilabsTransportClass HCISilabsTransport; + +HCITransportInterface& HCITransport = HCISilabsTransport; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.h new file mode 100644 index 0000000..2061e78 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCISilabsTransport.h @@ -0,0 +1,42 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_SILABS_TRANSPORT_H_ +#define _HCI_SILABS_TRANSPORT_H_ + +#include "HCITransport.h" + +class HCISilabsTransportClass : public HCITransportInterface { +public: + HCISilabsTransportClass(); + virtual ~HCISilabsTransportClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); +}; + +#endif \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCITransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCITransport.h new file mode 100644 index 0000000..0b99e68 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCITransport.h @@ -0,0 +1,47 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_TRANSPORT_INTERFACE_H_ +#define _HCI_TRANSPORT_INTERFACE_H_ + +#include + +class HCITransportInterface { +public: + virtual int begin() = 0; + virtual void end() = 0; + + virtual void wait(unsigned long timeout) = 0; + + virtual int available() = 0; + virtual int peek() = 0; + virtual int read() = 0; + + // Some transports require a lock to use available/peek/read + // These methods allow to keep the lock while reading an unknown number of bytes + // These methods might disable interrupts. Only keep the lock as long as necessary. + virtual void lockForRead() {} + virtual void unlockForRead() {} + + virtual size_t write(const uint8_t* data, size_t length) = 0; +}; + +extern HCITransportInterface& HCITransport; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp new file mode 100644 index 0000000..bbf09bf --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp @@ -0,0 +1,93 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ARDUINO_PORTENTA_C33) && !defined(__ZEPHYR__) + +#include "HCIUartTransport.h" + +#if defined(ARDUINO_PORTENTA_C33) +#define SerialHCI Serial5 +#else +#error "Unsupported board selected!" +#endif + +HCIUartTransportClass::HCIUartTransportClass(HardwareSerial& uart, unsigned long baudrate) : + _uart(&uart), + _baudrate(baudrate) +{ +} + +HCIUartTransportClass::~HCIUartTransportClass() +{ +} + +int HCIUartTransportClass::begin() +{ + _uart->begin(_baudrate); + + return 1; +} + +void HCIUartTransportClass::end() +{ + _uart->end(); +} + +void HCIUartTransportClass::wait(unsigned long timeout) +{ + for (unsigned long start = millis(); (millis() - start) < timeout;) { + if (available()) { + break; + } + } +} + +int HCIUartTransportClass::available() +{ + return _uart->available(); +} + +int HCIUartTransportClass::peek() +{ + return _uart->peek(); +} + +int HCIUartTransportClass::read() +{ + return _uart->read(); +} + +size_t HCIUartTransportClass::write(const uint8_t* data, size_t length) +{ +#ifdef ARDUINO_AVR_UNO_WIFI_REV2 + // wait while the CTS pin is low + while (digitalRead(NINA_CTS) == HIGH); +#endif + + size_t result = _uart->write(data, length); + + _uart->flush(); + + return result; +} + +HCIUartTransportClass HCIUartTransport(SerialHCI, 912600); +HCITransportInterface& HCITransport = HCIUartTransport; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.h new file mode 100644 index 0000000..ba70dff --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.h @@ -0,0 +1,46 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_UART_TRANSPORT_H_ +#define _HCI_UART_TRANSPORT_H_ + +#include "HCITransport.h" + +class HCIUartTransportClass : public HCITransportInterface { +public: + HCIUartTransportClass(HardwareSerial& uart, unsigned long baudrate); + virtual ~HCIUartTransportClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); + +private: + HardwareSerial* _uart; + unsigned long _baudrate; +}; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.cpp new file mode 100644 index 0000000..3959d73 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.cpp @@ -0,0 +1,144 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ESP32) + +#include "HCIVirtualTransport.h" + +StreamBufferHandle_t rec_buffer; +StreamBufferHandle_t send_buffer; +TaskHandle_t bleHandle; + + +static void notify_host_send_available(void) +{ +} + +static int notify_host_recv(uint8_t *data, uint16_t length) +{ + xStreamBufferSend(rec_buffer,data,length,portMAX_DELAY); // !!!potentially waiting forever + return 0; +} + +static esp_vhci_host_callback_t vhci_host_cb = { + notify_host_send_available, + notify_host_recv +}; + +void bleTask(void *pvParameters) +{ + esp_vhci_host_register_callback(&vhci_host_cb); + size_t length; + uint8_t mybuf[258]; + + while(true){ + length = xStreamBufferReceive(send_buffer,mybuf,258,portMAX_DELAY); + while (!esp_vhci_host_check_send_available()) {} + esp_vhci_host_send_packet(mybuf, length); + } +} + + +HCIVirtualTransportClass::HCIVirtualTransportClass() +{ +} + +HCIVirtualTransportClass::~HCIVirtualTransportClass() +{ +} + +int HCIVirtualTransportClass::begin() +{ + btStarted(); // this somehow stops the arduino ide from initializing bluedroid + + rec_buffer = xStreamBufferCreate(258, 1); + send_buffer = xStreamBufferCreate(258, 1); + + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + +#if CONFIG_IDF_TARGET_ESP32 + bt_cfg.mode = ESP_BT_MODE_BLE; //original esp32 chip +#else +#if !(CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2) + bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; //different api for newer models +#endif +#endif + + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + xTaskCreatePinnedToCore(&bleTask, "bleTask", 2048, NULL, 5, &bleHandle, 0); + return 1; +} + +void HCIVirtualTransportClass::end() +{ + vStreamBufferDelete(rec_buffer); + vStreamBufferDelete(send_buffer); + esp_bt_controller_disable(); + esp_bt_controller_deinit(); + vTaskDelete(bleHandle); +} + +void HCIVirtualTransportClass::wait(unsigned long timeout) +{ + for (unsigned long start = (esp_timer_get_time() / 1000ULL); ((esp_timer_get_time() / 1000ULL) - start) < timeout;) { + if (available()) { + break; + } + } +} + +int HCIVirtualTransportClass::available() +{ + size_t bytes = xStreamBufferBytesAvailable(rec_buffer); + return bytes; +} + +// never called +int HCIVirtualTransportClass::peek() +{ + return -1; +} + +int HCIVirtualTransportClass::read() +{ + uint8_t c; + if(xStreamBufferReceive(rec_buffer, &c, 1, portMAX_DELAY)) { + return c; + } + return -1; +} + +size_t HCIVirtualTransportClass::write(const uint8_t* data, size_t length) +{ + size_t result = xStreamBufferSend(send_buffer,data,length,portMAX_DELAY); + return result; +} + +HCIVirtualTransportClass HCIVirtualTransport; + +HCITransportInterface& HCITransport = HCIVirtualTransport; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.h b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.h new file mode 100644 index 0000000..0da43ca --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransport.h @@ -0,0 +1,50 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCITransport.h" +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/stream_buffer.h" + +#include "esp_bt.h" +#include "nvs_flash.h" + +#include "esp32-hal-bt.h" // this is needed to disable bluedroid + + +class HCIVirtualTransportClass : public HCITransportInterface { +public: + HCIVirtualTransportClass(); + virtual ~HCIVirtualTransportClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); +}; \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.cpp new file mode 100644 index 0000000..7b3a24a --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.cpp @@ -0,0 +1,112 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ARDUINO_UNOR4_WIFI) + +#include "HCIVirtualTransportAT.h" + +extern ModemClass modem; + +HCIVirtualTransportATClass::HCIVirtualTransportATClass() +{ +} + +HCIVirtualTransportATClass::~HCIVirtualTransportATClass() +{ +} + +static RingBufferN<258> buf; + +int HCIVirtualTransportATClass::begin() +{ + // TODO: add this helper + //modem.debug(Serial); + buf.clear(); + //modem.debug(true); + std::string res = ""; + modem.begin(); + if (modem.write(std::string(PROMPT(_HCI_BEGIN)), res, CMD(_HCI_BEGIN))) { + return 1; + } + return 0; +} + +void HCIVirtualTransportATClass::end() +{ +} + +void HCIVirtualTransportATClass::wait(unsigned long timeout) +{ + std::string res = ""; + modem.write(std::string(PROMPT(_HCI_WAIT)), res, "%d\n\r", CMD_WRITE(_HCI_WAIT), timeout); +} + +int HCIVirtualTransportATClass::available() +{ + std::string res = ""; + if (buf.available()) { + return buf.available(); + } + if (modem.write(std::string(PROMPT(_HCI_AVAILABLE)), res, CMD_READ(_HCI_AVAILABLE))) { + return atoi(res.c_str()); + } + + return 0; +} + +// never called +int HCIVirtualTransportATClass::peek() +{ + return -1; +} + +int HCIVirtualTransportATClass::read() +{ + uint8_t c; + std::string res = ""; + if (buf.available()) { + return buf.read_char(); + } + modem.avoid_trim_results(); + modem.read_using_size(); + if (modem.write(std::string(PROMPT(_HCI_READ)), res, CMD(_HCI_READ))) { + for(int i = 0; i < res.size(); i++) { + buf.store_char((uint8_t)res[i]); + } + return buf.read_char(); + } + + return -1; +} + +size_t HCIVirtualTransportATClass::write(const uint8_t* data, size_t length) +{ + std::string res = ""; + modem.write_nowait(std::string(PROMPT(_HCI_WRITE)), res, "%s%d\r\n" , CMD_WRITE(_HCI_WRITE), length); + if(modem.passthrough(data, length)) { + return length; + } + return 0; +} + +HCIVirtualTransportATClass HCIVirtualTransportAT; + +HCITransportInterface& HCITransport = HCIVirtualTransportAT; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.h b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.h new file mode 100644 index 0000000..8e29801 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportAT.h @@ -0,0 +1,42 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCITransport.h" +#include +#include +#include + +#include "WiFiS3.h" + +class HCIVirtualTransportATClass : public HCITransportInterface { +public: + HCIVirtualTransportATClass(); + virtual ~HCIVirtualTransportATClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); +}; \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.cpp new file mode 100644 index 0000000..7d71f01 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.cpp @@ -0,0 +1,118 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018-2025 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(ARDUINO_UNO_Q) + +#include "HCIVirtualTransportRPC.h" + +#include + +using RouterBridge::HCI; + +HCIVirtualTransportRPCClass::HCIVirtualTransportRPCClass() : initialized(false) { +} + +HCIVirtualTransportRPCClass::~HCIVirtualTransportRPCClass() { +} + +int HCIVirtualTransportRPCClass::begin() { + // Initialize the Bridge if not already started + if (!Bridge) { + if (!Bridge.begin()) { + return 0; + } + } + + // Open HCI device through Bridge RPC + if (!HCI.begin("hci0")) { + return 0; + } + + rxbuf.clear(); + initialized = true; + + return 1; +} + +void HCIVirtualTransportRPCClass::end() { + if (initialized) { + HCI.end(); + initialized = false; + } +} + +void HCIVirtualTransportRPCClass::wait(unsigned long timeout) { + delay(timeout); +} + +int HCIVirtualTransportRPCClass::available() { + if (!initialized) { + return 0; + } + + // Return buffered data if available + if (rxbuf.available()) { + return rxbuf.available(); + } + + // Check if data is available from Bridge HCI and buffer it + if (HCI.available()) { + uint8_t packet[258]; + int received = HCI.recv(packet, sizeof(packet)); + + if (received > 0) { + // Store received data in ring buffer + for (int i = 0; i < received; i++) { + rxbuf.store_char(packet[i]); + } + } + } + + return rxbuf.available(); +} + +int HCIVirtualTransportRPCClass::peek() { + return -1; +} + +int HCIVirtualTransportRPCClass::read() { + if (rxbuf.available()) { + return rxbuf.read_char(); + } + + return -1; +} + +size_t HCIVirtualTransportRPCClass::write(const uint8_t* data, size_t length) { + if (!initialized) { + return 0; + } + + int sent = HCI.send(data, length); + if (sent < 0) { + return 0; + } + + return sent; +} + +HCIVirtualTransportRPCClass HCIVirtualTransportRPC; +HCITransportInterface& HCITransport = HCIVirtualTransportRPC; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.h b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.h new file mode 100644 index 0000000..3ae2fd8 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportRPC.h @@ -0,0 +1,52 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018-2025 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _HCI_VIRTUAL_TRANSPORT_RPC_H_ +#define _HCI_VIRTUAL_TRANSPORT_RPC_H_ + +#include "HCITransport.h" +#include "api/RingBuffer.h" +#include +#include +#include + +class HCIVirtualTransportRPCClass : public HCITransportInterface { + public: + HCIVirtualTransportRPCClass(); + virtual ~HCIVirtualTransportRPCClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); + + private: + RingBufferN<258> rxbuf; + bool initialized; +}; + +extern HCIVirtualTransportRPCClass HCIVirtualTransportRPC; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.cpp b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.cpp new file mode 100644 index 0000000..6740016 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.cpp @@ -0,0 +1,231 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018-2025 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(__ZEPHYR__) && !defined(ARDUINO_UNO_Q) && !defined(ARDUINO_NANO_RP2040_CONNECT) + +#include "HCIVirtualTransportZephyr.h" +#include "HCI.h" + +#include +#include +#include +#include +#include +#include + +static K_FIFO_DEFINE(rx_queue); +struct k_fifo* __rx_queue = &rx_queue; +extern "C" int bt_h4_vnd_setup(const struct device *dev); + +#if CONFIG_CYW4343W_MURATA_1DX +// External firmware defines. +extern const uint8_t brcm_patchram_buf[]; +extern const int brcm_patch_ram_length; + +enum { + HCI_VND_OP_FW_DOWNLOAD = 0xFC2E, + HCI_VND_OP_WRITE_RAM = 0xFC4C, + HCI_VND_OP_LAUNCH_RAM = 0xFC4E, + HCI_VND_OP_SET_BAUDRATE = 0xFC18, +}; + +static int cyw4343_set_baudrate(const struct device *uart, uint32_t baudrate) { + struct __attribute__ ((packed)) { + uint16_t zero; + uint32_t baud; + } param = { 0, baudrate }; + + struct uart_config uart_cfg; + if (uart_config_get(uart, &uart_cfg)) { + return -1; + } + + if (HCI.sendCommand(HCI_VND_OP_SET_BAUDRATE, sizeof(param), (void *) ¶m)) { + return -1; + } + + uart_cfg.baudrate = baudrate; + if (uart_configure(uart, &uart_cfg)) { + return -1; + } + + uart_irq_rx_enable(uart); + return 0; +} + +static int cyw4343_download_firmware(const struct device *uart) { + #define MURATA_NODE DT_CHILD(DT_CHOSEN(zephyr_bt_hci), murata_1dx) + uint32_t operational_speed = DT_PROP_OR(MURATA_NODE, hci_operation_speed, 115200); + uint32_t fw_download_speed = DT_PROP_OR(MURATA_NODE, fw_download_speed, 115200); + + // Reset controller + if (HCI.sendCommand(0x0C03, 0, NULL)) { + return -1; + } + + // Switch to fast baudrate + if ((operational_speed != fw_download_speed) && + cyw4343_set_baudrate(uart, fw_download_speed)) { + return -1; + } + + // Start firmware downloader. + if (HCI.sendCommand(HCI_VND_OP_FW_DOWNLOAD, 0, NULL)) { + return -1; + } + + // Load the firmware image. + for (int offset=0; offset < brcm_patch_ram_length;) { + uint8_t length = brcm_patchram_buf[offset + 2]; + uint16_t opcode = (brcm_patchram_buf[offset + 0]) | + (brcm_patchram_buf[offset + 1] << 8); + + // Opcode should be write RAM or launch RAM. + if (opcode != HCI_VND_OP_WRITE_RAM && opcode != HCI_VND_OP_LAUNCH_RAM) { + return -1; + } + + if (HCI.sendCommand(opcode, length, (void *) &brcm_patchram_buf[offset + 3])) { + return -1; + } + + offset += length + 3; + } + + // Delay after firmware loading. + delay(250); + + // Switch back to the default baudrate. + if ((operational_speed != fw_download_speed) && + cyw4343_set_baudrate(uart, operational_speed)) { + return -1; + } + + return 0; +} +#endif + +HCIVirtualTransportZephyrClass::HCIVirtualTransportZephyrClass() { + +} + +HCIVirtualTransportZephyrClass::~HCIVirtualTransportZephyrClass() { + +} + +int HCIVirtualTransportZephyrClass::begin() { + k_fifo_init(&rx_queue); + bt_enable_raw(__rx_queue); + +#if CONFIG_BT_HCI_SETUP + const struct device *uart = DEVICE_DT_GET(DT_PARENT(DT_CHOSEN(zephyr_bt_hci))); + if (!uart) { + return 0; + } + + if (bt_h4_vnd_setup(uart)) { + return 0; + } + +#if CONFIG_CYW4343W_MURATA_1DX + if (cyw4343_download_firmware(uart)) { + return 0; + } +#endif /* CONFIG_CYW4343W_MURATA_1DX */ +#endif /* CONFIG_BT_HCI_SETUP */ + +#if CONFIG_BT_LL_SW_SPLIT + // Use unique device id for BD addr. + uint8_t bd_addr[6] = { 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA }; + +#if CONFIG_HWINFO + hwinfo_get_device_id(bd_addr, sizeof(bd_addr)); +#endif /* CONFIG_HWINFO */ + + // Set public address for controller. + bt_ctlr_set_public_addr(bd_addr); +#endif /* CONFIG_BT_LL_SW_SPLIT */ + + rxbuf.clear(); + + return 1; +} + +void HCIVirtualTransportZephyrClass::end() { + +} + +void HCIVirtualTransportZephyrClass::wait(unsigned long timeout) { + delay(timeout); +} + +int HCIVirtualTransportZephyrClass::available() { + static struct net_buf *buf = NULL; + + if (rxbuf.available()) { + return rxbuf.available(); + } + + buf = (struct net_buf *) k_fifo_get(__rx_queue, K_MSEC(10)); + if (!buf) { + return 0; + } + + for (int i=0; ilen; i++) { + rxbuf.store_char((uint8_t)buf->data[i]); + } + + net_buf_pull(buf, buf->len); + if (!buf->len) { + net_buf_unref(buf); + buf = NULL; + } + + return rxbuf.available(); +} + +int HCIVirtualTransportZephyrClass::peek() { + return -1; +} + +int HCIVirtualTransportZephyrClass::read() { + if (rxbuf.available()) { + return rxbuf.read_char(); + } + + return -1; +} + +size_t HCIVirtualTransportZephyrClass::write(const uint8_t* data, size_t length) { + enum bt_buf_type type = bt_buf_type_from_h4(data[0], BT_BUF_OUT); + struct net_buf *buf = bt_buf_get_tx(type, K_FOREVER, &data[1], length - 1); + + if (buf) { + auto err = bt_send(buf); + if (err) { + net_buf_unref(buf); + } + return length; + } + return 0; +} + +HCIVirtualTransportZephyrClass HCIVirtualTransportZephyr; +HCITransportInterface& HCITransport = HCIVirtualTransportZephyr; +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.h b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.h new file mode 100644 index 0000000..a8affb6 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/HCIVirtualTransportZephyr.h @@ -0,0 +1,44 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018-2025 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCITransport.h" +#include "api/RingBuffer.h" +#include +#include +#include + +class HCIVirtualTransportZephyrClass : public HCITransportInterface { + public: + HCIVirtualTransportZephyrClass(); + virtual ~HCIVirtualTransportZephyrClass(); + + virtual int begin(); + virtual void end(); + + virtual void wait(unsigned long timeout); + + virtual int available(); + virtual int peek(); + virtual int read(); + + virtual size_t write(const uint8_t* data, size_t length); + + private: + RingBufferN<258> rxbuf; +}; diff --git a/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.cpp b/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.cpp new file mode 100644 index 0000000..a758268 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.cpp @@ -0,0 +1,510 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "HCI.h" +#include "ATT.h" +#include "btct.h" +#include "L2CAPSignaling.h" +#include "keyDistribution.h" +#include "bitDescriptions.h" +#define CONNECTION_PARAMETER_UPDATE_REQUEST 0x12 +#define CONNECTION_PARAMETER_UPDATE_RESPONSE 0x13 + +//#define _BLE_TRACE_ + +L2CAPSignalingClass::L2CAPSignalingClass() : + _minInterval(0), + _maxInterval(0), + _supervisionTimeout(0), + _pairing_enabled(1) +{ +} + +L2CAPSignalingClass::~L2CAPSignalingClass() +{ +} + +void L2CAPSignalingClass::addConnection(uint16_t handle, uint8_t role, uint8_t /*peerBdaddrType*/, + uint8_t /*peerBdaddr*/[6], uint16_t interval, + uint16_t /*latency*/, uint16_t supervisionTimeout, + uint8_t /*masterClockAccuracy*/) +{ + if (role != 1) { + // ignore + return; + } + + bool updateParameters = false; + uint16_t updatedMinInterval = interval; + uint16_t updatedMaxInterval = interval; + uint16_t updatedSupervisionTimeout = supervisionTimeout; + + if (_minInterval && _maxInterval) { + if (interval < _minInterval || interval > _maxInterval) { + updatedMinInterval = _minInterval; + updatedMaxInterval = _maxInterval; + updateParameters = true; + } + } + + if (_supervisionTimeout && supervisionTimeout != _supervisionTimeout) { + updatedSupervisionTimeout = _supervisionTimeout; + updateParameters = true; + } + + if (updateParameters) { + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest { + uint8_t code; + uint8_t identifier; + uint16_t length; + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + } request = { CONNECTION_PARAMETER_UPDATE_REQUEST, 0x01, 8, + updatedMinInterval, updatedMaxInterval, 0x0000, updatedSupervisionTimeout }; + + HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(request), &request); + } +} + +void L2CAPSignalingClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) L2CAPSignalingHdr { + uint8_t code; + uint8_t identifier; + uint16_t length; + } *l2capSignalingHdr = (L2CAPSignalingHdr*)data; + + if (dlen < sizeof(L2CAPSignalingHdr)) { + // too short, ignore + return; + } + + if (dlen != (sizeof(L2CAPSignalingHdr) + l2capSignalingHdr->length)) { + // invalid length, ignore + return; + } + + uint8_t code = l2capSignalingHdr->code; + uint8_t identifier = l2capSignalingHdr->identifier; + uint16_t length = l2capSignalingHdr->length; + data = &data[sizeof(L2CAPSignalingHdr)]; + + if (code == CONNECTION_PARAMETER_UPDATE_REQUEST) { + connectionParameterUpdateRequest(connectionHandle, identifier, length, data); + } else if (code == CONNECTION_PARAMETER_UPDATE_RESPONSE) { + connectionParameterUpdateResponse(connectionHandle, identifier, length, data); + } +} +void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) L2CAPSignalingHdr { + uint8_t code; + uint8_t data[64]; + } *l2capSignalingHdr = (L2CAPSignalingHdr*)data; +#ifdef _BLE_TRACE_ + Serial.print("dlen: "); + Serial.println(dlen); +#else + (void)dlen; +#endif + uint8_t code = l2capSignalingHdr->code; + +#ifdef _BLE_TRACE_ + Serial.print("handleSecurityData: code: 0x"); + Serial.println(code, HEX); + Serial.print("rx security:"); + btct.printBytes(data,dlen); +#endif + if (code == CONNECTION_PAIRING_REQUEST) { + + if (isPairingEnabled()){ + if (_pairing_enabled >= 2) _pairing_enabled = 0; // 2 = pair once only + + // 0x1 + struct __attribute__ ((packed)) PairingRequest { + uint8_t ioCapability; + uint8_t oobDataFlag; + uint8_t authReq; + uint8_t maxEncSize; + uint8_t initiatorKeyDistribution; + uint8_t responderKeyDistribution; + } *pairingRequest = (PairingRequest*)l2capSignalingHdr->data; + + KeyDistribution responseKD = KeyDistribution(); + responseKD.setIdKey(true); + + ATT.remoteKeyDistribution = responseKD;// KeyDistribution(pairingRequest->initiatorKeyDistribution); + ATT.localKeyDistribution = responseKD; //KeyDistribution(pairingRequest->responderKeyDistribution); + // KeyDistribution rkd(pairingRequest->responderKeyDistribution); + AuthReq req(pairingRequest->authReq); +#ifdef _BLE_TRACE_ + Serial.print("Req has properties: "); + Serial.print(req.Bonding()?"bonding, ":"no bonding, "); + Serial.print(req.CT2()?"CT2, ":"no CT2, "); + Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, "); + Serial.print(req.MITM()?"MITM, ":"no MITM, "); + Serial.print(req.SC()?"SC, ":"no SC, "); +#endif + + uint8_t peerIOCap[3]; + peerIOCap[0] = pairingRequest->authReq; + peerIOCap[1] = pairingRequest->oobDataFlag; + peerIOCap[2] = pairingRequest->ioCapability; + ATT.setPeerIOCap(connectionHandle, peerIOCap); + ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST); +#ifdef _BLE_TRACE_ + Serial.print("Peer encryption : 0b"); + Serial.println(ATT.getPeerEncryption(connectionHandle), BIN); +#endif + struct __attribute__ ((packed)) PairingResponse { + uint8_t code; + uint8_t ioCapability; + uint8_t oobDataFlag; + uint8_t authReq; + uint8_t maxEncSize; + uint8_t initiatorKeyDistribution; + uint8_t responderKeyDistribution; + } response = { CONNECTION_PAIRING_RESPONSE, HCI.localIOCap(), 0, HCI.localAuthreq().getOctet(), 0x10, responseKD.getOctet(), responseKD.getOctet()}; + + HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response); + + } else { + // Pairing not enabled + uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing + HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret); + ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION); + } + } + else if (code == CONNECTION_PAIRING_RANDOM) + { + struct __attribute__ ((packed)) PairingRandom { + uint8_t Na[16]; + } *pairingRandom = (PairingRandom*)l2capSignalingHdr->data; + for(int i=0; i<16; i++){ + HCI.Na[15-i] = pairingRandom->Na[i]; + } +#ifdef _BLE_TRACE_ + Serial.println("[Info] Pairing random."); +#endif + struct __attribute__ ((packed)) PairingResponse { + uint8_t code; + uint8_t Nb[16]; + } response = { CONNECTION_PAIRING_RANDOM, 0}; + for(int i=0; i< 16; i++) response.Nb[15-i] = HCI.Nb[i]; + + HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response); + + // We now have all needed for compare value + uint8_t g2Result[4]; + uint8_t U[32]; + uint8_t V[32]; + + for(int i=0; i<32; i++){ + U[31-i] = HCI.remotePublicKeyBuffer[i]; + V[31-i] = HCI.localPublicKeyBuffer[i]; + } + + btct.g2(U,V,HCI.Na,HCI.Nb, g2Result); + uint32_t result = 0; + for(int i=0; i<4; i++) result += g2Result[3-i] << 8*i; + +#ifdef _BLE_TRACE_ + Serial.print("U : "); + btct.printBytes(U,32); + Serial.print("V : "); + btct.printBytes(V,32); + Serial.print("X : "); + btct.printBytes(HCI.Na,16); + Serial.print("Y : "); + btct.printBytes(HCI.Nb,16); + Serial.print("g2res : "); + btct.printBytes(g2Result,4); + Serial.print("Result : "); + Serial.println(result); +#endif + + if(HCI._displayCode!=0){ + HCI._displayCode(result%1000000); + } + if(HCI._binaryConfirmPairing!=0){ + if(!HCI._binaryConfirmPairing()){ +#ifdef _BLE_TRACE_ + Serial.println("User rejection"); +#endif + uint8_t rejection[2]; + rejection[0] = CONNECTION_PAIRING_FAILED; + rejection[1] = 0x0C; // Numeric comparison failed + HCI.sendAclPkt(connectionHandle, SECURITY_CID, 2, rejection); + ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION); + }else{ +#ifdef _BLE_TRACE_ + Serial.println("User did confirm"); +#endif + } + } + } + else if (code == CONNECTION_PAIRING_RESPONSE) + { + } + else if(code == CONNECTION_PAIRING_FAILED) + { +#ifdef _BLE_TRACE_ + struct __attribute__ ((packed)) PairingFailed + { + uint8_t code; + uint8_t reason; + } *pairingFailed = (PairingFailed*)data; + Serial.print("Pairing failed with code: 0x"); + Serial.println(pairingFailed->reason,HEX); +#endif + ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION); + } + else if (code == CONNECTION_IDENTITY_INFORMATION){ + struct __attribute__ ((packed)) IdentityInformation { + uint8_t code; + uint8_t PeerIRK[16]; + } *identityInformation = (IdentityInformation*)data; + for(int i=0; i<16; i++) ATT.peerIRK[15-i] = identityInformation->PeerIRK[i]; +#ifdef _BLE_TRACE_ + Serial.println("Saved peer IRK"); +#endif + } + else if (code == CONNECTION_IDENTITY_ADDRESS){ + struct __attribute__ ((packed)) IdentityAddress { + uint8_t code; + uint8_t addressType; + uint8_t address[6]; + } *identityAddress = (IdentityAddress*)data; + // we can save this information now. + uint8_t peerAddress[6]; + for(int i=0; i<6; i++) peerAddress[5-i] = identityAddress->address[i]; + + HCI.saveNewAddress(identityAddress->addressType, peerAddress, ATT.peerIRK, ATT.localIRK); + if(HCI._storeLTK!=0){ + HCI._storeLTK(peerAddress, HCI.LTK); + } + } + else if (code == CONNECTION_PAIRING_PUBLIC_KEY){ + /// Received a public key + struct __attribute__ ((packed)) ConnectionPairingPublicKey { + uint8_t x[32]; + uint8_t y[32]; + } *connectionPairingPublicKey = (ConnectionPairingPublicKey*)l2capSignalingHdr->data; + struct __attribute__ ((packed)) GenerateDHKeyCommand { + uint8_t x[32]; + uint8_t y[32]; + } generateDHKeyCommand = { + {0x00}, + {0x00}, + }; + memcpy(generateDHKeyCommand.x,connectionPairingPublicKey->x,32); + memcpy(generateDHKeyCommand.y,connectionPairingPublicKey->y,32); + + if(ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::REQUESTED_ENCRYPTION)){ +#ifdef _BLE_TRACE_ + Serial.println("[Info] Pairing public key"); + Serial.println("Requested encryption stored."); +#endif + }else{ +#ifdef _BLE_TRACE_ + Serial.println("[Info] Pairing public key"); + Serial.print("Failed to store encryption request with handle: 0x"); + Serial.println(connectionHandle,HEX); +#endif + } + + memcpy(HCI.remotePublicKeyBuffer,&generateDHKeyCommand,sizeof(generateDHKeyCommand)); + HCI.sendCommand( (OGF_LE_CTL << 10 )| LE_COMMAND::READ_LOCAL_P256, 0); + } + else if(code == CONNECTION_PAIRING_DHKEY_CHECK) + { + uint8_t RemoteDHKeyCheck[16]; + for(int i=0; i<16; i++) RemoteDHKeyCheck[15-i] = l2capSignalingHdr->data[i]; + + +#ifdef _BLE_TRACE_ + Serial.println("[Info] DH Key check"); + Serial.print("Remote DHKey Check: "); + btct.printBytes(RemoteDHKeyCheck, 16); +#endif + + + + uint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK; + ATT.setPeerEncryption(connectionHandle, encryptionState); + if((encryptionState & PEER_ENCRYPTION::DH_KEY_CALULATED) == 0){ +#ifdef _BLE_TRACE_ + Serial.println("DHKey not yet ready, will calculate f5, f6 later"); +#endif + // store RemoteDHKeyCheck for later check + memcpy(HCI.remoteDHKeyCheckBuffer,RemoteDHKeyCheck,16); + + } else { + // We've already calculated the DHKey so we can calculate our check and send it. + smCalculateLTKandConfirm(connectionHandle, RemoteDHKeyCheck); + + } + } +} + +void L2CAPSignalingClass::smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]) +{ // Authentication stage 2: LTK Calculation + + uint8_t localAddress[7]; + uint8_t remoteAddress[7]; + ATT.getPeerAddrWithType(handle, remoteAddress); + + HCI.readBdAddr(); + memcpy(&localAddress[1],HCI.localAddr,6); + localAddress[0] = 0; // IOT 33 uses a static address // TODO: confirm for Nano BLE + + // Compute the LTK and MacKey + uint8_t MacKey[16]; + btct.f5(HCI.DHKey, HCI.Na, HCI.Nb, remoteAddress, localAddress, MacKey, HCI.LTK); + + // Compute Ea and Eb + uint8_t Ea[16]; + uint8_t Eb[16]; + uint8_t R[16]; + uint8_t MasterIOCap[3]; + uint8_t SlaveIOCap[3] = {HCI.localAuthreq().getOctet(), 0x0, HCI.localIOCap()}; + + ATT.getPeerIOCap(handle, MasterIOCap); + for(int i=0; i<16; i++) R[i] = 0; + + btct.f6(MacKey, HCI.Na,HCI.Nb,R, MasterIOCap, remoteAddress, localAddress, Ea); + btct.f6(MacKey, HCI.Nb,HCI.Na,R, SlaveIOCap, localAddress, remoteAddress, Eb); + +#ifdef _BLE_TRACE_ + Serial.println("Calculate and confirm LTK via f5, f6:"); + Serial.print("DHKey : "); btct.printBytes(HCI.DHKey,32); + Serial.print("Na : "); btct.printBytes(HCI.Na,16); + Serial.print("Nb : "); btct.printBytes(HCI.Nb,16); + Serial.print("MacKey : "); btct.printBytes(MacKey,16); + Serial.print("LTK : "); btct.printBytes(HCI.LTK,16); + Serial.print("Expected Ea: "); btct.printBytes(expectedEa, 16); + Serial.print("Ea : "); btct.printBytes(Ea, 16); + Serial.print("Eb : "); btct.printBytes(Eb,16); + Serial.print("Local Addr : "); btct.printBytes(localAddress, 7); + Serial.print("LocalIOCap : "); btct.printBytes(SlaveIOCap, 3); + Serial.print("MasterAddr : "); btct.printBytes(remoteAddress, 7); + Serial.print("MasterIOCAP: "); btct.printBytes(MasterIOCap, 3); +#endif + + // Check if Ea = expectedEa + if (memcmp(Ea, expectedEa, 16) == 0){ + // Check ok + // Send our confirmation value to complete authentication stage 2 + uint8_t ret[17]; + ret[0] = CONNECTION_PAIRING_DHKEY_CHECK; + for(uint32_t i=0; i 0; +} + +void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]) +{ + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest { + uint16_t minInterval; + uint16_t maxInterval; + uint16_t latency; + uint16_t supervisionTimeout; + } *request = (L2CAPConnectionParameterUpdateRequest*)data; + + if (dlen < sizeof(L2CAPConnectionParameterUpdateRequest)) { + // too short, ignore + return; + } + + struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateResponse { + uint8_t code; + uint8_t identifier; + uint16_t length; + uint16_t value; + } response = { CONNECTION_PARAMETER_UPDATE_RESPONSE, identifier, 2, 0x0000 }; + + if (_minInterval && _maxInterval) { + if (request->minInterval < _minInterval || request->maxInterval > _maxInterval) { + response.value = 0x0001; // reject + } + } + + if (_supervisionTimeout) { + if (request->supervisionTimeout != _supervisionTimeout) { + response.value = 0x0001; // reject + } + } + + HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(response), &response); + + if (response.value == 0x0000) { + HCI.leConnUpdate(handle, request->minInterval, request->maxInterval, request->latency, request->supervisionTimeout); + } +} + +void L2CAPSignalingClass::connectionParameterUpdateResponse(uint16_t /*handle*/, uint8_t /*identifier*/, uint8_t /*dlen*/, uint8_t /*data*/[]) +{ +} + +#if !defined(FAKE_L2CAP) +L2CAPSignalingClass L2CAPSignalingObj; +L2CAPSignalingClass& L2CAPSignaling = L2CAPSignalingObj; +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.h b/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.h new file mode 100644 index 0000000..2604216 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/L2CAPSignaling.h @@ -0,0 +1,96 @@ +/* + This file is part of the ArduinoBLE library. + Copyright (c) 2018 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _L2CAP_SIGNALING_H_ +#define _L2CAP_SIGNALING_H_ + +#include + +#define SIGNALING_CID 0x0005 +#define SECURITY_CID 0x0006 + + +#define CONNECTION_PAIRING_REQUEST 0x01 +#define CONNECTION_PAIRING_RESPONSE 0x02 +#define CONNECTION_PAIRING_CONFIRM 0x03 +#define CONNECTION_PAIRING_RANDOM 0x04 +#define CONNECTION_PAIRING_FAILED 0x05 +#define CONNECTION_ENCRYPTION_INFORMATION 0x06 +#define CONNECTION_MASTER_IDENTIFICATION 0x07 +#define CONNECTION_IDENTITY_INFORMATION 0x08 +#define CONNECTION_IDENTITY_ADDRESS 0x09 +#define CONNECTION_SIGNING_INFORMATION 0x0A +#define CONNECTION_SECURITY_REQUEST 0x0B +#define CONNECTION_PAIRING_PUBLIC_KEY 0x0C +#define CONNECTION_PAIRING_DHKEY_CHECK 0x0D +#define CONNECTION_PAIRING_KEYPRESS 0x0E + +#define IOCAP_DISPLAY_ONLY 0x00 +#define IOCAP_DISPLAY_YES_NO 0x01 +#define IOCAP_KEYBOARD_ONLY 0x02 +#define IOCAP_NO_INPUT_NO_OUTPUT 0x03 +#define IOCAP_KEYBOARD_DISPLAY 0x04 + + +#define LOCAL_AUTHREQ 0b00101101 +// #define LOCAL_IOCAP IOCAP_DISPLAY_ONLY // will use JustWorks pairing + +class L2CAPSignalingClass { +public: + L2CAPSignalingClass(); + virtual ~L2CAPSignalingClass(); + + virtual void addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrType, + uint8_t peerBdaddr[6], uint16_t interval, + uint16_t latency, uint16_t supervisionTimeout, + uint8_t masterClockAccuracy); + + virtual void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + + virtual void handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]); + + virtual void removeConnection(uint8_t handle, uint16_t reason); + + virtual void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval); + + virtual void setSupervisionTimeout(uint16_t supervisionTimeout); + + virtual void setPairingEnabled(uint8_t enabled); + virtual bool isPairingEnabled(); + + + + virtual void smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]); + + +private: + virtual void connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); + virtual void connectionParameterUpdateResponse(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); + + +private: + uint16_t _minInterval; + uint16_t _maxInterval; + uint16_t _supervisionTimeout; + uint8_t _pairing_enabled; +}; + +extern L2CAPSignalingClass& L2CAPSignaling; + +#endif diff --git a/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.cpp b/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.cpp new file mode 100644 index 0000000..bf896bc --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.cpp @@ -0,0 +1,30 @@ +#include "bitDescriptions.h" + + +#define BONDING_BIT 0b00000001 +#define MITM_BIT 0b00000100 +#define SC_BIT 0b00001000 +#define KEYPRESS_BIT 0b00010000 +#define CT2_BIT 0b00100000 + + +AuthReq::AuthReq(){} +AuthReq::AuthReq(uint8_t octet):_octet(octet){} +bool AuthReq::Bonding(){ return (_octet & BONDING_BIT)>0;} +bool AuthReq::MITM(){ return (_octet & MITM_BIT)>0;} +bool AuthReq::SC(){ return (_octet & SC_BIT)>0;} +bool AuthReq::KeyPress(){ return (_octet & KEYPRESS_BIT)>0;} +bool AuthReq::CT2(){ return (_octet & CT2_BIT)>0;} + + +void AuthReq::setBonding(bool state) { _octet= state? _octet|BONDING_BIT : _octet&~BONDING_BIT;} +void AuthReq::setMITM(bool state) { _octet= state? _octet|MITM_BIT : _octet&~MITM_BIT;} +void AuthReq::setSC(bool state){ _octet= state? _octet|SC_BIT : _octet&~SC_BIT;} +void AuthReq::setKeyPress(bool state){ _octet= state? _octet|KEYPRESS_BIT : _octet&~KEYPRESS_BIT;} +void AuthReq::setCT2(bool state){ _octet= state? _octet|CT2_BIT : _octet&~CT2_BIT;} + +uint8_t _octet; + + +void AuthReq::setOctet( uint8_t octet){_octet = octet;} +uint8_t AuthReq::getOctet() {return _octet;} diff --git a/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.h b/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.h new file mode 100644 index 0000000..6d32c52 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/bitDescriptions.h @@ -0,0 +1,41 @@ +#ifndef _BIT_DESCRIPTIONS_H_ +#define _BIT_DESCRIPTIONS_H_ +#include + +class AuthReq{ +public: + AuthReq(); + AuthReq(uint8_t octet); + void setOctet( uint8_t octet); + uint8_t getOctet(); + + + // The Bonding_Flags field is a 2-bit field that indicates the type of bonding being requested by the initiating device + bool Bonding(); + // The MITM field is a 1-bit flag that is set to one if the device is requesting MITM protection + bool MITM(); + // The SC field is a 1 bit flag. If LE Secure Connections pairing is supported by the device, then the SC field shall be set to 1, otherwise it shall be set to 0. + bool SC(); + // The keypress field is a 1-bit flag that is used only in the Passkey Entry protocol and shall be ignored in other protocols. + bool KeyPress(); + // The CT2 field is a 1-bit flag that shall be set to 1 upon transmission to indicate support for the h7 function. + bool CT2(); + + void setBonding(bool state); + void setMITM(bool state); + void setSC(bool state); + void setKeyPress(bool state); + void setCT2(bool state); +private: + uint8_t _octet; +}; + +enum IOCap { + DisplayOnly, + DisplayYesNo, + KeyboardOnly, + NoInputNoOutput, + KeyboardDisplay +}; + +#endif \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/btct.cpp b/arduino/libraries/ArduinoBLE/src/utility/btct.cpp new file mode 100644 index 0000000..0a84dd5 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/btct.cpp @@ -0,0 +1,387 @@ +#include "btct.h" +#include +#include "HCI.h" +#include "ArduinoBLE.h" +BluetoothCryptoToolbox::BluetoothCryptoToolbox(){} +// In step 1, AES-128 with key K is applied to an all-zero input block. +// In step 2, K1 is derived through the following operation: +// If the most significant bit of L is equal to 0, K1 is the left-shift +// of L by 1 bit. +// Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L +// by 1 bit. +// In step 3, K2 is derived through the following operation: +// If the most significant bit of K1 is equal to 0, K2 is the left-shift +// of K1 by 1 bit. +// Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of +// K1 by 1 bit. +// In step 4, (K1,K2) := Generate_Subkey(K) is returned. +unsigned char const_Rb[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 + }; + +#define DHKEY_LENGTH 32 +#define N_LEN 16 +#define ADDR_LEN 6 +#define LEN_LTK 16 +#define LEN_MAC_KEY 16 + +void BluetoothCryptoToolbox::printBytes(uint8_t bytes[], uint8_t length){ +#ifdef _BLE_TRACE_ + for(int i=0; i0){ + Serial.print(", 0x"); + }else{ + Serial.print("0x"); + } + Serial.print(bytes[i],HEX); + } + Serial.print('\n'); +#else + (void)bytes; + (void)length; +#endif +} + + +int BluetoothCryptoToolbox::f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[], + uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[]) +{ + uint8_t SALT[16] = {0x6C, 0x88, 0x83, 0x91, 0xAA, 0xF5, 0xA5, 0x38, 0x60, 0x37, 0x0B, 0xDB, 0x5A, 0x60, 0x83, 0xBE}; + uint8_t keyID[4] = {0x62, 0x74, 0x6c, 0x65}; + uint8_t length[2]; + length[0] = 0x01; + length[1] = 0x00; +#ifdef _BLE_TRACE_ + Serial.print("Starting f5 calculation"); + Serial.print("Using DHKey: "); + printBytes(DHKey, DHKEY_LENGTH); + Serial.print("Using N_Master: "); + printBytes(N_master, N_LEN); + Serial.print("Using N_Slave: "); + printBytes(N_slave, N_LEN); + + Serial.println("Using BD_ADDR_MASTER: "); + printBytes(BD_ADDR_master, ADDR_LEN); + Serial.println("Using BD_ADDR_SLAVE: "); + printBytes(BD_ADDR_slave, ADDR_LEN); +#endif + + uint8_t T[16]; + + struct __attribute__ ((packed)) CmacInput + { + uint8_t counter; + uint8_t keyID[4]; + uint8_t N1[16]; + uint8_t N2[16]; + uint8_t A1[7]; + uint8_t A2[7]; + uint8_t length[2]; + } cmacInput = {0,{0},{0},{0},{0},{0},{0}}; + cmacInput.counter = 0; + memcpy(cmacInput.keyID, keyID, 4); + memcpy(cmacInput.N1,N_master,16); + memcpy(cmacInput.N2,N_slave,16); + memcpy(cmacInput.A1,BD_ADDR_master,7); + memcpy(cmacInput.A2,BD_ADDR_slave,7); + memcpy(cmacInput.length,length,2); + AES_CMAC(SALT, DHKey, 32, T); + + AES_CMAC(T, (uint8_t*)&cmacInput,sizeof(cmacInput), MacKey); + cmacInput.counter=1; + AES_CMAC(T, (uint8_t*)&cmacInput, sizeof(cmacInput), LTK); + + return 1; +} +int BluetoothCryptoToolbox::f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[]) +{ + struct __attribute__ ((packed)) F6Input + { + uint8_t N1[16]; + uint8_t N2[16]; + uint8_t R[16]; + uint8_t IOCap[3]; + uint8_t A1[7]; + uint8_t A2[7]; + } f6Input = {{0},{0},{0},{0},{0},{0}}; + + memcpy(f6Input.N1, N1, 16); + memcpy(f6Input.N2, N2, 16); + memcpy(f6Input.R, R, 16); + memcpy(f6Input.IOCap, IOCap, 3); + memcpy(f6Input.A1, A1, 7); + memcpy(f6Input.A2, A2, 7); + + + AES_CMAC(W, (uint8_t*)&f6Input, sizeof(f6Input),Ex); + return 1; +} +// AES_CMAC from RFC +int BluetoothCryptoToolbox::ah(uint8_t k[16], uint8_t r[3], uint8_t* result) +{ + uint8_t r_[16]; + int i=0; + for(i=0; i<16; i++) r_[i] = 0; + for(i=0; i<3; i++) r_[i+13] = r[i]; + uint8_t intermediate[16]; + AES_128(k,r_,intermediate); + for(i=0; i<3; i++){ + result[i] = intermediate[i+13]; + } + return 1; +} +void BluetoothCryptoToolbox::testAh() +{ + uint8_t irk[16] = {0xec,0x02,0x34,0xa3,0x57,0xc8,0xad,0x05,0x34,0x10,0x10,0xa6,0x0a,0x39,0x7d,0x9b}; + uint8_t expected_final[3] = {0x0d,0xfb,0xaa}; + uint8_t ourResult[3]; + ah(irk, expected_final, ourResult); + +#ifdef _BLE_TRACE_ + Serial.print("Expected : "); + printBytes(&expected_final[3], 3); + Serial.print("Actual : "); + printBytes(ourResult, 3); +#endif +} + +int BluetoothCryptoToolbox::g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4]) +{ + struct __attribute__ ((packed)) CmacInput { + uint8_t U[32]; + uint8_t V[32]; + uint8_t Y[16]; + } cmacInput= {{0},{0},{0}}; + memcpy(cmacInput.U,U,32); + memcpy(cmacInput.V,V,32); + memcpy(cmacInput.Y,Y,16); + uint8_t intermediate[16]; + AES_CMAC(X,(uint8_t*)&cmacInput,sizeof(CmacInput),intermediate); + memcpy(out,&intermediate[12],4); + return 1; +} +void BluetoothCryptoToolbox::testg2(){ + uint8_t U[32] = {0x20,0xb0,0x03,0xd2,0xf2,0x97,0xbe,0x2c,0x5e,0x2c,0x83,0xa7,0xe9,0xf9,0xa5,0xb9,0xef,0xf4,0x91,0x11,0xac,0xf4,0xfd,0xdb,0xcc,0x03,0x01,0x48,0x0e,0x35,0x9d,0xe6}; + uint8_t V[32] = {0x55,0x18,0x8b,0x3d,0x32,0xf6,0xbb,0x9a,0x90,0x0a,0xfc,0xfb,0xee,0xd4,0xe7,0x2a,0x59,0xcb,0x9a,0xc2,0xf1,0x9d,0x7c,0xfb,0x6b,0x4f,0xdd,0x49,0xf4,0x7f,0xc5,0xfd}; + uint8_t X[16] = {0xd5,0xcb,0x84,0x54,0xd1,0x77,0x73,0x3e,0xff,0xff,0xb2,0xec,0x71,0x2b,0xae,0xab}; + uint8_t Y[16] = {0xa6,0xe8,0xe7,0xcc,0x25,0xa7,0x5f,0x6e,0x21,0x65,0x83,0xf7,0xff,0x3d,0xc4,0xcf}; + uint8_t out[4]; + + g2(U,V,X,Y,out); + uint32_t result = 0; + for(int i=0; i<4; i++) result += out[i] << 8*i; + +#ifdef _BLE_TRACE_ + Serial.println("Expected : 0"); + Serial.print("Result : "); + Serial.println(result); + Serial.println(); +#endif +} + +void BluetoothCryptoToolbox::AES_CMAC ( unsigned char *key, unsigned char *input, int length, + unsigned char *mac ) +{ + unsigned char X[16],Y[16], M_last[16], padded[16]; + unsigned char K1[16], K2[16]; + int n, i, flag; + generateSubkey(key,K1,K2); + + n = (length+15) / 16; /* n is number of rounds */ + + if ( n == 0 ) { + n = 1; + flag = 0; + } else { + if ( (length%16) == 0 ) { /* last block is a complete block */ + flag = 1; + } else { /* last block is not complete block */ + flag = 0; + } + } + + if ( flag ) { /* last block is complete block */ + xor_128(&input[16*(n-1)],K1,M_last); + } else { + padding(&input[16*(n-1)],padded,length%16); + xor_128(padded,K2,M_last); + } + + for ( i=0; i<16; i++ ) X[i] = 0; + for ( i=0; i=0; i-- ) { + output[i] = input[i] << 1; + output[i] |= overflow; + overflow = (input[i] & 0x80)?1:0; + } + return; +} +// From RFC +void BluetoothCryptoToolbox::xor_128(unsigned char *a, unsigned char *b, unsigned char *out) +{ + int i; + for (i=0;i<16; i++) + { + out[i] = a[i] ^ b[i]; + } +} +BluetoothCryptoToolbox btct; diff --git a/arduino/libraries/ArduinoBLE/src/utility/btct.h b/arduino/libraries/ArduinoBLE/src/utility/btct.h new file mode 100644 index 0000000..08f8f19 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/btct.h @@ -0,0 +1,30 @@ +#ifndef _BTCT_H_ +#define _BTCT_H_ +#include + +// Implementation of functions defined in BTLE standard +class BluetoothCryptoToolbox{ +public: + BluetoothCryptoToolbox(); + void printBytes(uint8_t bytes[], uint8_t length); + void generateSubkey(uint8_t* K, uint8_t* K1, uint8_t* K2); + void AES_CMAC ( unsigned char *key, unsigned char *input, int length, + unsigned char *mac ); + int f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[], + uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[]); + int f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[]); + int g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4]); + int ah(uint8_t k[16], uint8_t r[3], uint8_t result[3]); + void test(); + void testF5(); + void testF6(); + void testAh(); + void testg2(); +private: + int AES_128(uint8_t key[], uint8_t data_in[], uint8_t data_out[]); + void leftshift_onebit(unsigned char *input,unsigned char *output); + void xor_128(unsigned char *a, unsigned char *b, unsigned char *out); + void padding ( unsigned char *lastb, unsigned char *pad, int length ); +}; +extern BluetoothCryptoToolbox btct; +#endif \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.cpp b/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.cpp new file mode 100644 index 0000000..f754366 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.cpp @@ -0,0 +1,24 @@ +#include "keyDistribution.h" + +KeyDistribution::KeyDistribution():_octet(0){} +KeyDistribution::KeyDistribution(uint8_t octet):_octet(octet){} + +#define ENCKEY 0b00000001 +#define IDKEY 0b00000010 +#define SIGNKEY 0b00000100 +#define LINKKEY 0b00001000 +void KeyDistribution::setOctet( uint8_t octet){_octet = octet;} +uint8_t KeyDistribution::getOctet() {return _octet;} +// Ignored when SMP is on LE transport +bool KeyDistribution::EncKey(){ return (_octet & ENCKEY)>0;} +// Device shall distribute IRK using Identity information command followed by its address using Identity address information +bool KeyDistribution::IdKey(){ return (_octet & IDKEY)>0;} +// Device shall distribute CSRK using signing information command +bool KeyDistribution::SignKey(){ return (_octet & SIGNKEY)>0;} +// Device would like to derive BR/EDR from LTK +bool KeyDistribution::LinkKey(){ return (_octet & LINKKEY)>0;} + +void KeyDistribution::setEncKey(bool state) { _octet= state? _octet|ENCKEY : _octet&~ENCKEY;} +void KeyDistribution::setIdKey(bool state) { _octet= state? _octet|IDKEY : _octet&~IDKEY;} +void KeyDistribution::setSignKey(bool state){ _octet= state? _octet|SIGNKEY : _octet&~SIGNKEY;} +void KeyDistribution::setLinkKey(bool state){ _octet= state? _octet|LINKKEY : _octet&~LINKKEY;} \ No newline at end of file diff --git a/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.h b/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.h new file mode 100644 index 0000000..d78fcc1 --- /dev/null +++ b/arduino/libraries/ArduinoBLE/src/utility/keyDistribution.h @@ -0,0 +1,29 @@ +#ifndef _KEY_DISTRIBUTION_H_ +#define _KEY_DISTRIBUTION_H_ +#include + +class KeyDistribution{ +public: + KeyDistribution(); + KeyDistribution(uint8_t octet); + void setOctet( uint8_t octet); + uint8_t getOctet(); + // Ignored when SMP is on LE transport + bool EncKey(); + // Device shall distribute IRK using Identity information command followed by its address using Identity address information + bool IdKey(); + // Device shall distribute CSRK using signing information command + bool SignKey(); + // Device would like to derive BR/EDR from LTK + bool LinkKey(); + + void setEncKey(bool state); + void setIdKey(bool state); + void setSignKey(bool state); + void setLinkKey(bool state); +private: + uint8_t _octet; + // 1. IRK by the slave2. BD ADDR by the slave3. CSRK by the slave4. IRK by the master5. BD_ADDR by the master6. CSRK by the master +}; + +#endif \ No newline at end of file diff --git a/arduino/libraries/Arduino_SpiNINA/LICENSE b/arduino/libraries/Arduino_SpiNINA/LICENSE new file mode 100644 index 0000000..5f7a7c4 --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/arduino/libraries/Arduino_SpiNINA/README.md b/arduino/libraries/Arduino_SpiNINA/README.md new file mode 100644 index 0000000..edd0cfd --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/README.md @@ -0,0 +1,6 @@ +`Arduino_SpiNINA` +================= + +[![Check Arduino](https://github.com/arduino-libraries/Arduino_SpiNINA/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_SpiNINA/actions/workflows/check-arduino.yml) +[![Compile Examples](https://github.com/arduino-libraries/Arduino_SpiNINA/workflows/Compile%20Examples/badge.svg)](https://github.com/arduino-libraries/Arduino_SpiNINA/actions?workflow=Compile+Examples) +[![Spell Check](https://github.com/arduino-libraries/Arduino_SpiNINA/workflows/Spell%20Check/badge.svg)](https://github.com/arduino-libraries/Arduino_SpiNINA/actions?workflow=Spell+Check) \ No newline at end of file diff --git a/arduino/libraries/Arduino_SpiNINA/library.properties b/arduino/libraries/Arduino_SpiNINA/library.properties new file mode 100644 index 0000000..34b941d --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/library.properties @@ -0,0 +1,10 @@ +name=Arduino_SpiNINA +version=0.0.2 +author=Arduino +maintainer=Arduino +sentence=Spi driver for NINA modules +paragraph=With this library you can send and receive messages through the SPI interface. +category=Communication +url=http://www.arduino.cc/en/Reference/Arduino_SpiNINA +architectures=samd,mbed_nano,megaavr +includes=Arduino_SpiNINA.h diff --git a/arduino/libraries/Arduino_SpiNINA/src/Arduino_SpiNINA.h b/arduino/libraries/Arduino_SpiNINA/src/Arduino_SpiNINA.h new file mode 100644 index 0000000..fb67647 --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/src/Arduino_SpiNINA.h @@ -0,0 +1,13 @@ +/* + This file is part of the Arduino_SpiNINA library. + + Copyright (C) Arduino s.r.l. and/or its affiliated companies + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include \ No newline at end of file diff --git a/arduino/libraries/Arduino_SpiNINA/src/spi_drv.cpp b/arduino/libraries/Arduino_SpiNINA/src/spi_drv.cpp new file mode 100644 index 0000000..5731483 --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/src/spi_drv.cpp @@ -0,0 +1,457 @@ +/* + This file is part of the Arduino_SpiNINA library. + + Copyright (C) Arduino s.r.l. and/or its affiliated companies + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "Arduino.h" +#include +#include "spi_drv.h" +#include "pins_arduino.h" + +#ifdef ARDUINO_SAMD_MKRVIDOR4000 +// STRIX: the STRIX FPGA image passes the NINA module's pins through to SAMD21 pins, so the +// module is driven directly (SPI on D8-D10) instead of through Arduino's FPGA peripherals. +// GPIO0 is bidirectional through the FPGA; the firmware switches its direction around begin(). +#define NINA_GPIO0 6 +#define SPIWIFI_SS 5 +#define SPIWIFI_ACK 4 +#define SPIWIFI_RESET 7 +#endif + +// #define _DEBUG_ +#include "utility/debug.h" + +static uint8_t SLAVESELECT = 10; // ss +static uint8_t SLAVEREADY = 7; // handshake pin +static uint8_t SLAVERESET = 5; // reset pin + +static bool inverted_reset = false; + +#define DELAY_TRANSFER() + +#ifndef SPIWIFI +#define SPIWIFI SPI +#endif + +#ifndef NINA_GPIOIRQ +#define NINA_GPIOIRQ NINA_GPIO0 +#endif + +bool SpiDrv::initialized = false; + +void SpiDrv::begin(bool force) { + if (initialized && !force) { + return; + } + + +#ifdef SPIWIFI_SS + SLAVESELECT = SPIWIFI_SS; +#endif + +#ifdef SPIWIFI_ACK + SLAVEREADY = SPIWIFI_ACK; +#endif + +#ifdef SPIWIFI_RESET + SLAVERESET = (uint8_t)SPIWIFI_RESET; +#endif + +#ifdef ARDUINO_SAMD_MKRVIDOR4000 + inverted_reset = false; +#else + if (SLAVERESET > PINS_COUNT) { + inverted_reset = true; + SLAVERESET = ~SLAVERESET; + } +#endif + + pinMode(SLAVESELECT, OUTPUT); + pinMode(SLAVEREADY, INPUT); + pinMode(SLAVERESET, OUTPUT); + pinMode(NINA_GPIO0, OUTPUT); + + digitalWrite(NINA_GPIO0, HIGH); + digitalWrite(SLAVESELECT, HIGH); + digitalWrite(SLAVERESET, inverted_reset ? HIGH : LOW); + delay(10); + digitalWrite(SLAVERESET, inverted_reset ? LOW : HIGH); + delay(750); + + digitalWrite(NINA_GPIO0, LOW); + pinMode(NINA_GPIOIRQ, INPUT); + + SPIWIFI.begin(); + +#ifdef _DEBUG_ + INIT_TRIGGER() +#endif + + initialized = true; +} + +void SpiDrv::end() { + digitalWrite(SLAVERESET, inverted_reset ? HIGH : LOW); + + pinMode(SLAVESELECT, INPUT); + + SPIWIFI.end(); + + initialized = false; +} + +void SpiDrv::spiSlaveSelect() { + SPIWIFI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + digitalWrite(SLAVESELECT, LOW); + + // wait for up to 5 ms for the NINA to indicate it is not ready for transfer + // the timeout is only needed for the case when the shield or module is not present + for (unsigned long start = millis(); (digitalRead(SLAVEREADY) != HIGH) && (millis() - start) < 5;); +} + + +void SpiDrv::spiSlaveDeselect() { + digitalWrite(SLAVESELECT, HIGH); + SPIWIFI.endTransaction(); +} + + +char SpiDrv::spiTransfer(volatile char data) { + char result = SPIWIFI.transfer(data); + DELAY_TRANSFER(); + + return result; // return the received byte +} + +int SpiDrv::waitSpiChar(unsigned char waitChar) { + int timeout = TIMEOUT_CHAR; + unsigned char _readChar = 0; + do { + _readChar = readChar(); //get data byte + if (_readChar == ERR_CMD) { + WARN("Err cmd received\n"); + return -1; + } + } while ((timeout-- > 0) && (_readChar != waitChar)); + return (_readChar == waitChar); +} + +int SpiDrv::readAndCheckChar(char checkChar, char* readChar) { + getParam((uint8_t*)readChar); + + return (*readChar == checkChar); +} + +char SpiDrv::readChar() { + uint8_t readChar = 0; + getParam(&readChar); + return readChar; +} + +#define WAIT_START_CMD(x) waitSpiChar(START_CMD) + +#define IF_CHECK_START_CMD(x) \ + if (!WAIT_START_CMD(_data)) { \ + TOGGLE_TRIGGER() \ + WARN("Error waiting START_CMD"); \ + return 0; \ + } else + +#define CHECK_DATA(check, x) \ + if (!readAndCheckChar(check, &x)) { \ + TOGGLE_TRIGGER() \ + WARN("Reply error"); \ + INFO2(check, (uint8_t)x); \ + return 0; \ + } else + +#define waitSlaveReady() (digitalRead(SLAVEREADY) == LOW) +#define waitSlaveSign() (digitalRead(SLAVEREADY) == HIGH) +#define waitSlaveSignalH() \ + while (digitalRead(SLAVEREADY) != HIGH) {} +#define waitSlaveSignalL() \ + while (digitalRead(SLAVEREADY) != LOW) {} + +void SpiDrv::waitForSlaveSign() { + while (!waitSlaveSign()); +} + +void SpiDrv::waitForSlaveReady(bool const feed_watchdog) { + unsigned long const start = millis(); + while (!waitSlaveReady()) { + if (feed_watchdog) { + if ((millis() - start) < 10000) { + //TODO: Feed watchdog in a proper way + //WiFi.feedWatchdog(); + } + } + } +} + +void SpiDrv::getParam(uint8_t* param) { + // Get Params data + *param = spiTransfer(DUMMY_DATA); + DELAY_TRANSFER(); +} + +int SpiDrv::waitResponseCmd(uint8_t cmd, uint8_t numParam, uint8_t* param, uint8_t* param_len) { + char _data = 0; + int ii = 0; + + IF_CHECK_START_CMD(_data) { + CHECK_DATA(cmd | REPLY_FLAG, _data){}; + + CHECK_DATA(numParam, _data) { + readParamLen8(param_len); + for (ii = 0; ii < (*param_len); ++ii) { + // Get Params data + //param[ii] = spiTransfer(DUMMY_DATA); + getParam(¶m[ii]); + } + } + + readAndCheckChar(END_CMD, &_data); + } + + return 1; +} + +int SpiDrv::waitResponseData16(uint8_t cmd, uint8_t* param, uint16_t* param_len) { + char _data = 0; + uint16_t ii = 0; + + IF_CHECK_START_CMD(_data) { + CHECK_DATA(cmd | REPLY_FLAG, _data){}; + + uint8_t numParam = readChar(); + if (numParam != 0) { + readParamLen16(param_len); + for (ii = 0; ii < (*param_len); ++ii) { + // Get Params data + param[ii] = spiTransfer(DUMMY_DATA); + } + } + + readAndCheckChar(END_CMD, &_data); + } + + return 1; +} + +int SpiDrv::waitResponseData8(uint8_t cmd, uint8_t* param, uint8_t* param_len) { + char _data = 0; + int ii = 0; + + IF_CHECK_START_CMD(_data) { + CHECK_DATA(cmd | REPLY_FLAG, _data){}; + + uint8_t numParam = readChar(); + if (numParam != 0) { + readParamLen8(param_len); + for (ii = 0; ii < (*param_len); ++ii) { + // Get Params data + param[ii] = spiTransfer(DUMMY_DATA); + } + } + + readAndCheckChar(END_CMD, &_data); + } + + return 1; +} + +int SpiDrv::waitResponseParams(uint8_t cmd, uint8_t numParam, tParam* params) { + char _data = 0; + int i = 0, ii = 0; + + + IF_CHECK_START_CMD(_data) { + CHECK_DATA(cmd | REPLY_FLAG, _data){}; + + uint8_t _numParam = readChar(); + if (_numParam != 0) { + for (i = 0; i < _numParam; ++i) { + params[i].paramLen = readParamLen8(); + for (ii = 0; ii < params[i].paramLen; ++ii) { + // Get Params data + params[i].param[ii] = spiTransfer(DUMMY_DATA); + } + } + } else { + WARN("Error numParam == 0"); + return 0; + } + + if (numParam != _numParam) { + WARN("Mismatch numParam"); + return 0; + } + + readAndCheckChar(END_CMD, &_data); + } + return 1; +} + +#define SPI_RESPONSE_MAX_LENGTH 32 +#define SPI_MAX_RESPONSE_NUMBER 10 + +int SpiDrv::waitResponse(uint8_t cmd, uint8_t* numParamRead, uint8_t** params, uint8_t maxNumParams) { + char _data = 0; + int i = 0, ii = 0; + + char* index[SPI_RESPONSE_MAX_LENGTH]; + + for (i = 0; i < SPI_MAX_RESPONSE_NUMBER; i++) + index[i] = (char*)params + SPI_RESPONSE_MAX_LENGTH * i; + + IF_CHECK_START_CMD(_data) { + CHECK_DATA(cmd | REPLY_FLAG, _data){}; + + uint8_t numParam = readChar(); + + if (numParam > maxNumParams) { + numParam = maxNumParams; + } + *numParamRead = numParam; + if (numParam != 0) { + for (i = 0; i < numParam; ++i) { + uint8_t paramLen = readParamLen8(); + for (ii = 0; ii < paramLen; ++ii) { + //ssid[ii] = spiTransfer(DUMMY_DATA); + // Get Params data + index[i][ii] = (uint8_t)spiTransfer(DUMMY_DATA); + } + index[i][ii] = 0; + } + } else { + WARN("Error numParams == 0"); + readAndCheckChar(END_CMD, &_data); + return 0; + } + readAndCheckChar(END_CMD, &_data); + } + return 1; +} + +void SpiDrv::sendParamNoLen(const uint8_t* param, size_t param_len, uint8_t lastParam) { + size_t i = 0; + // Send SPI paramLen + sendParamLen8(0); + + // Send SPI param data + for (i = 0; i < param_len; ++i) { + spiTransfer(param[i]); + } + + // if lastParam==1 Send SPI END CMD + if (lastParam == 1) + spiTransfer(END_CMD); +} + +void SpiDrv::sendParam(const uint8_t* param, uint8_t param_len, uint8_t lastParam) { + int i = 0; + // Send SPI paramLen + sendParamLen8(param_len); + + // Send SPI param data + for (i = 0; i < param_len; ++i) { + spiTransfer(param[i]); + } + + // if lastParam==1 Send SPI END CMD + if (lastParam == 1) + spiTransfer(END_CMD); +} + +void SpiDrv::sendParamLen8(uint8_t param_len) { + // Send SPI paramLen + spiTransfer(param_len); +} + +void SpiDrv::sendParamLen16(uint16_t param_len) { + // Send SPI paramLen + spiTransfer((uint8_t)((param_len & 0xff00) >> 8)); + spiTransfer((uint8_t)(param_len & 0xff)); +} + +uint8_t SpiDrv::readParamLen8(uint8_t* param_len) { + uint8_t _param_len = spiTransfer(DUMMY_DATA); + if (param_len != NULL) { + *param_len = _param_len; + } + return _param_len; +} + +uint16_t SpiDrv::readParamLen16(uint16_t* param_len) { + uint16_t _param_len = spiTransfer(DUMMY_DATA) << 8 | (spiTransfer(DUMMY_DATA) & 0xff); + if (param_len != NULL) { + *param_len = _param_len; + } + return _param_len; +} + + +void SpiDrv::sendBuffer(const uint8_t* param, uint16_t param_len, uint8_t lastParam) { + uint16_t i = 0; + + // Send SPI paramLen + sendParamLen16(param_len); + + // Send SPI param data + for (i = 0; i < param_len; ++i) { + spiTransfer(param[i]); + } + + // if lastParam==1 Send SPI END CMD + if (lastParam == 1) + spiTransfer(END_CMD); +} + + +void SpiDrv::sendParam(uint16_t param, uint8_t lastParam) { + // Send SPI paramLen + sendParamLen8(2); + + spiTransfer((uint8_t)((param & 0xff00) >> 8)); + spiTransfer((uint8_t)(param & 0xff)); + + // if lastParam==1 Send SPI END CMD + if (lastParam == 1) + spiTransfer(END_CMD); +} + +/* Cmd Struct Message */ +/* _________________________________________________________________________________ */ +/*| START CMD | C/R | CMD |[TOT LEN]| N.PARAM | PARAM LEN | PARAM | .. | END CMD | */ +/*|___________|______|______|_________|_________|___________|________|____|_________| */ +/*| 8 bit | 1bit | 7bit | 8bit | 8bit | 8bit | nbytes | .. | 8bit | */ +/*|___________|______|______|_________|_________|___________|________|____|_________| */ + +void SpiDrv::sendCmd(uint8_t cmd, uint8_t numParam) { + // Send SPI START CMD + spiTransfer(START_CMD); + + // Send SPI C + cmd + spiTransfer(cmd & ~(REPLY_FLAG)); + + // Send SPI totLen + //spiTransfer(totLen); + + // Send SPI numParam + spiTransfer(numParam); + + // If numParam == 0 send END CMD + if (numParam == 0) + spiTransfer(END_CMD); +} + +int SpiDrv::available() { + return (digitalRead(NINA_GPIOIRQ) != LOW); +} + +SpiDrv spiDrv; diff --git a/arduino/libraries/Arduino_SpiNINA/src/spi_drv.h b/arduino/libraries/Arduino_SpiNINA/src/spi_drv.h new file mode 100644 index 0000000..4a934be --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/src/spi_drv.h @@ -0,0 +1,67 @@ +/* + This file is part of the Arduino_SpiNINA library. + + Copyright (C) Arduino s.r.l. and/or its affiliated companies + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef SPI_Drv_h +#define SPI_Drv_h + +#include +#include "spi_types.h" + +#define SPI_START_CMD_DELAY 10 + +#define NO_LAST_PARAM 0 +#define LAST_PARAM 1 + +#define DUMMY_DATA 0xFF + +#define WAIT_FOR_SLAVE_SELECT() \ + if (!SpiDrv::initialized) { \ + SpiDrv::begin(); \ + } \ + SpiDrv::waitForSlaveReady(); \ + SpiDrv::spiSlaveSelect(); + +class SpiDrv { +private: + static void waitForSlaveSign(); + static void getParam(uint8_t* param); +public: + static bool initialized; + + static void begin(bool force = false); + static void end(); + static void spiDriverInit(); + static void spiSlaveSelect(); + static void spiSlaveDeselect(); + static char spiTransfer(volatile char data); + static void waitForSlaveReady(bool const feed_watchdog = false); + static int waitSpiChar(unsigned char waitChar); + static int readAndCheckChar(char checkChar, char* readChar); + static char readChar(); + static int waitResponseParams(uint8_t cmd, uint8_t numParam, tParam* params); + static int waitResponseCmd(uint8_t cmd, uint8_t numParam, uint8_t* param, uint8_t* param_len); + static int waitResponseData8(uint8_t cmd, uint8_t* param, uint8_t* param_len); + static int waitResponseData16(uint8_t cmd, uint8_t* param, uint16_t* param_len); + static int waitResponse(uint8_t cmd, uint8_t* numParamRead, uint8_t** params, uint8_t maxNumParams); + static void sendParam(const uint8_t* param, uint8_t param_len, uint8_t lastParam = NO_LAST_PARAM); + static void sendParamNoLen(const uint8_t* param, size_t param_len, uint8_t lastParam = NO_LAST_PARAM); + static void sendParamLen8(const uint8_t param_len); + static void sendParamLen16(const uint16_t param_len); + static uint8_t readParamLen8(uint8_t* param_len = NULL); + static uint16_t readParamLen16(uint16_t* param_len = NULL); + static void sendBuffer(const uint8_t* param, uint16_t param_len, uint8_t lastParam = NO_LAST_PARAM); + static void sendParam(uint16_t param, uint8_t lastParam = NO_LAST_PARAM); + static void sendCmd(uint8_t cmd, uint8_t numParam); + static int available(); +}; + +extern SpiDrv spiDrv; + +#endif diff --git a/arduino/libraries/Arduino_SpiNINA/src/spi_types.h b/arduino/libraries/Arduino_SpiNINA/src/spi_types.h new file mode 100644 index 0000000..33fa43b --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/src/spi_types.h @@ -0,0 +1,88 @@ +/* + This file is part of the Arduino_SpiNINA library. + + Copyright (C) Arduino s.r.l. and/or its affiliated companies + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include + +#define CMD_FLAG 0 +#define REPLY_FLAG 1 << 7 +#define DATA_FLAG 0x40 + +#define WIFI_SPI_ACK 1 +#define WIFI_SPI_ERR 0xFF + +#define TIMEOUT_CHAR 1000 + +#define START_CMD 0xE0 +#define END_CMD 0xEE +#define ERR_CMD 0xEF +#define CMD_POS 1 // Position of Command OpCode on SPI stream +#define PARAM_LEN_POS 2 // Position of Param len on SPI stream + + +enum numParams { + PARAM_NUMS_0, + PARAM_NUMS_1, + PARAM_NUMS_2, + PARAM_NUMS_3, + PARAM_NUMS_4, + PARAM_NUMS_5, + PARAM_NUMS_6, + MAX_PARAM_NUMS +}; + +#define MAX_PARAMS MAX_PARAM_NUMS - 1 +#define PARAM_LEN_SIZE 1 + +typedef struct __attribute__((__packed__)) { + uint8_t paramLen; + char* param; +} tParam; + +typedef struct __attribute__((__packed__)) { + uint16_t dataLen; + char* data; +} tDataParam; + +typedef struct __attribute__((__packed__)) { + unsigned char cmd; + unsigned char tcmd; + unsigned char nParam; + tParam params[MAX_PARAMS]; +} tSpiMsg; + +typedef struct __attribute__((__packed__)) { + unsigned char cmd; + unsigned char tcmd; + unsigned char nParam; + tDataParam params[MAX_PARAMS]; +} tSpiMsgData; + +typedef struct __attribute__((__packed__)) { + unsigned char cmd; + unsigned char tcmd; + unsigned char nParam; +} tSpiHdr; + +typedef struct __attribute__((__packed__)) { + uint8_t paramLen; + uint32_t param; +} tLongParam; + +typedef struct __attribute__((__packed__)) { + uint8_t paramLen; + uint16_t param; +} tIntParam; + +typedef struct __attribute__((__packed__)) { + uint8_t paramLen; + uint8_t param; +} tByteParam; diff --git a/arduino/libraries/Arduino_SpiNINA/src/utility/debug.h b/arduino/libraries/Arduino_SpiNINA/src/utility/debug.h new file mode 100644 index 0000000..9968f36 --- /dev/null +++ b/arduino/libraries/Arduino_SpiNINA/src/utility/debug.h @@ -0,0 +1,104 @@ +/* + This file is part of the Arduino_SpiNINA library. + + Copyright (C) Arduino s.r.l. and/or its affiliated companies + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef Debug_H +#define Debug_H + +#include +#include + +#define PRINT_FILE_LINE() \ + do { \ + Serial.print("["); \ + Serial.print(__FILE__); \ + Serial.print("::"); \ + Serial.print(__LINE__); \ + Serial.print("]"); \ + } while (0); + +#ifdef _DEBUG_ + +#define INFO(format, args...) \ + do { \ + char buf[250]; \ + sprintf(buf, format, args); \ + Serial.println(buf); \ + } while (0); + +#define INFO1(x) \ + do { \ + PRINT_FILE_LINE() \ + Serial.print("-I-"); \ + Serial.println(x); \ + } while (0); + + +#define INFO2(x, y) \ + do { \ + PRINT_FILE_LINE() \ + Serial.print("-I-"); \ + Serial.print(x, 16); \ + Serial.print(","); \ + Serial.println(y, 16); \ + } while (0); + + +#else +#define INFO1(x) \ + do { \ + } while (0); +#define INFO2(x, y) \ + do { \ + } while (0); +#define INFO(format, args...) \ + do { \ + } while (0); +#endif + +#if 0 +#define WARN(args) \ + do { \ + PRINT_FILE_LINE() \ + Serial.print("-W-"); \ + Serial.println(args); \ + } while (0); +#else +#define WARN(args) \ + do { \ + } while (0); +#endif + +#if _DEBUG_SPI_ +#define DBG_PIN2 5 +#define DBG_PIN 4 + +#define START() digitalWrite(DBG_PIN2, HIGH); +#define END() digitalWrite(DBG_PIN2, LOW); +#define SET_TRIGGER() digitalWrite(DBG_PIN, HIGH); +#define RST_TRIGGER() digitalWrite(DBG_PIN, LOW); + +#define INIT_TRIGGER() \ + pinMode(DBG_PIN, OUTPUT); \ + pinMode(DBG_PIN2, OUTPUT); \ + RST_TRIGGER() +#define TOGGLE_TRIGGER() \ + SET_TRIGGER() \ + delayMicroseconds(2); \ + RST_TRIGGER() +#else +#define START() +#define END() +#define SET_TRIGGER() +#define RST_TRIGGER() +#define INIT_TRIGGER() +#define TOGGLE_TRIGGER() +#endif + +#endif diff --git a/arduino/libraries/STRIX-NINA-LIBRARIES.md b/arduino/libraries/STRIX-NINA-LIBRARIES.md new file mode 100644 index 0000000..a7091a6 --- /dev/null +++ b/arduino/libraries/STRIX-NINA-LIBRARIES.md @@ -0,0 +1,9 @@ +# NINA libraries for STRIX + +Vendored from Arduino (GNU LGPL 2.1 or later, see each library's license file), with these changes so they drive the MKR Vidor 4000's NINA-W102 module through the STRIX FPGA image's pin pass-through instead of Arduino's FPGA peripherals bitstream: + +- `Arduino_SpiNINA` 0.0.2: on the Vidor, uses SAMD21 pins directly (SPI on D8–D10, SS D5, ACK D4, GPIO0 D6, RESETN D7) and no longer calls `FPGA.begin()`. +- `ArduinoBLE` 2.1.0: the NINA SPI transport (BLE over SPI, NINA firmware 3.0.0 or newer) is also enabled for the Vidor. +- `WiFiNINA` 2.1.1: unchanged; it uses `Arduino_SpiNINA`. + +The NINA module runs nina-fw 3.0.1, flashed with `arduino/strix_nina_passthrough` and esptool (see the README). diff --git a/arduino/libraries/WiFiNINA/CHANGELOG b/arduino/libraries/WiFiNINA/CHANGELOG index ad6e784..562f93c 100644 --- a/arduino/libraries/WiFiNINA/CHANGELOG +++ b/arduino/libraries/WiFiNINA/CHANGELOG @@ -16,7 +16,7 @@ WiFiNINA 1.7.0 - 2020.08.06 WiFiNINA 1.6.0 - 2020.07.13 -* Adding API to make use of file storage/retieval capability available since nina-fw 1.5.0 (#74) +* Adding API to make use of file storage/retrieval capability available since nina-fw 1.5.0 (#74) * Speed up of duration of function ServerDrv::getDataBuf (#84) WiFiNINA 1.5.0 - 2019.12.30 diff --git a/arduino/libraries/WiFiNINA/LICENSE.txt b/arduino/libraries/WiFiNINA/LICENSE.txt new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/arduino/libraries/WiFiNINA/LICENSE.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/arduino/libraries/WiFiNINA/README.adoc b/arduino/libraries/WiFiNINA/README.adoc index 783984f..b066555 100644 --- a/arduino/libraries/WiFiNINA/README.adoc +++ b/arduino/libraries/WiFiNINA/README.adoc @@ -4,15 +4,16 @@ = {repository-name} library for Arduino = -image:https://github.com/{repository-owner}/{repository-name}/workflows/Compile%20Examples/badge.svg["Compile Examples Status", link="https://github.com/{repository-owner}/{repository-name}/actions?workflow=Compile+Examples"] -image:https://github.com/{repository-owner}/{repository-name}/workflows/Spell%20Check/badge.svg["Spell Check Status", link="https://github.com/{repository-owner}/{repository-name}/actions?workflow=Spell+Check"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] -Enables network connection (local and Internet) with the Arduino MKR WiFi 1010, Arduino MKR VIDOR 4000 and Arduino UNO WiFi Rev.2. +Enables network connection (local and Internet) with the Arduino MKR WiFi 1010, Arduino MKR VIDOR 4000 and Arduino Uno WiFi Rev.2. With this library you can instantiate Servers, Clients and send/receive UDP packets through WiFi. The board can connect either to open or encrypted networks (WEP, WPA). The IP address can be assigned statically or through a DHCP. The library can also manage DNS. For more information about this library please visit us at -https://www.arduino.cc/en/Reference/{repository-name} +https://www.arduino.cc/reference/en/libraries/wifinina/ == License == diff --git a/arduino/libraries/WiFiNINA/STRIX.md b/arduino/libraries/WiFiNINA/STRIX.md deleted file mode 100644 index 93edc94..0000000 --- a/arduino/libraries/WiFiNINA/STRIX.md +++ /dev/null @@ -1,3 +0,0 @@ -# WiFiNINA for STRIX - -Arduino's [WiFiNINA](https://github.com/arduino-libraries/WiFiNINA) 1.8.5 (GNU LGPL 2.1 or later, see the source headers), vendored with one change in `src/utility/spi_drv.cpp`: on the MKR Vidor 4000 it drives the NINA module directly through the STRIX FPGA image's pin pass-through (SPI on D8–D10, SS D5, ACK D4, GPIO0 D6, RESETN D7) instead of through Arduino's FPGA peripherals bitstream. diff --git a/arduino/libraries/WiFiNINA/keywords.txt b/arduino/libraries/WiFiNINA/keywords.txt deleted file mode 100644 index 2e1f9c6..0000000 --- a/arduino/libraries/WiFiNINA/keywords.txt +++ /dev/null @@ -1,65 +0,0 @@ -####################################### -# Syntax Coloring Map For WiFiNINA -####################################### - -####################################### -# Library (KEYWORD1) -####################################### - -WiFi KEYWORD1 -WiFiNINA KEYWORD1 -WiFiUdp KEYWORD1 -WiFiClient KEYWORD1 -WiFiSSLClient KEYWORD1 -WiFiServer KEYWORD1 -WiFiUDP KEYWORD1 - - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -firmwareVersion KEYWORD2 -status KEYWORD2 -reasonCode KEYWORD2 -connect KEYWORD2 -write KEYWORD2 -available KEYWORD2 -config KEYWORD2 -setDNS KEYWORD2 -read KEYWORD2 -flush KEYWORD2 -stop KEYWORD2 -connected KEYWORD2 -begin KEYWORD2 -disconnect KEYWORD2 -macAddress KEYWORD2 -localIP KEYWORD2 -subnetMask KEYWORD2 -gatewayIP KEYWORD2 -SSID KEYWORD2 -BSSID KEYWORD2 -RSSI KEYWORD2 -encryptionType KEYWORD2 -beginPacket KEYWORD2 -endPacket KEYWORD2 -parsePacket KEYWORD2 -remoteIP KEYWORD2 -remotePort KEYWORD2 - -beginAP KEYWORD2 -beginEnterprise KEYWORD2 -setHostname KEYWORD2 -end KEYWORD2 -getTime KEYWORD2 -lowPowerMode KEYWORD2 -noLowPowerMode KEYWORD2 -ping KEYWORD2 -beginMulticast KEYWORD2 -setTimeout KEYWORD2 - - -####################################### -# Constants (LITERAL1) -####################################### - diff --git a/arduino/libraries/WiFiNINA/library.properties b/arduino/libraries/WiFiNINA/library.properties index e11fb11..2dd2321 100644 --- a/arduino/libraries/WiFiNINA/library.properties +++ b/arduino/libraries/WiFiNINA/library.properties @@ -1,10 +1,11 @@ name=WiFiNINA -version=1.8.5 +version=2.1.1 author=Arduino maintainer=Arduino -sentence=Enables network connection (local and Internet) with the Arduino MKR WiFi 1010, Arduino MKR VIDOR 4000, Arduino UNO WiFi Rev.2 and Nano 33 IoT. +sentence=Enables network connection (local and Internet) with the Arduino MKR WiFi 1010, Arduino MKR VIDOR 4000, Arduino Uno WiFi Rev.2 and Nano 33 IoT. paragraph=With this library you can instantiate Servers, Clients and send/receive UDP packets through WiFi. The board can connect either to open or encrypted networks (WEP, WPA). The IP address can be assigned statically or through a DHCP. The library can also manage DNS. category=Communication url=http://www.arduino.cc/en/Reference/WiFiNINA -architectures=* +architectures=samd,mbed_nano,megaavr,zephyr_main includes=WiFiNINA.h +depends=Arduino_SpiNINA diff --git a/arduino/libraries/WiFiNINA/src/WiFi.cpp b/arduino/libraries/WiFiNINA/src/WiFi.cpp index ed5bae0..0ecf831 100644 --- a/arduino/libraries/WiFiNINA/src/WiFi.cpp +++ b/arduino/libraries/WiFiNINA/src/WiFi.cpp @@ -1,5 +1,5 @@ /* - WiFi.cpp - Library for Arduino Wifi shield. + WiFi.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -24,10 +24,10 @@ extern "C" { #include "utility/wl_definitions.h" #include "utility/wl_types.h" - #include "utility/debug.h" + //#include "utility/debug.h" } -WiFiClass::WiFiClass() : _timeout(50000) +WiFiClass::WiFiClass() : _timeout(50000), _feed_watchdog_func(0) { } @@ -38,191 +38,205 @@ void WiFiClass::init() const char* WiFiClass::firmwareVersion() { - return WiFiDrv::getFwVersion(); + return WiFiDrv::getFwVersion(); +} + +uint32_t WiFiClass::firmwareVersionU32() +{ + return WiFiDrv::getFwVersionU32(); } int WiFiClass::begin(const char* ssid) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; - if (WiFiDrv::wifiSetNetwork(ssid, strlen(ssid)) != WL_FAILURE) - { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } - }else - { - status = WL_CONNECT_FAILED; - } - return status; + if (WiFiDrv::wifiSetNetwork(ssid, strlen(ssid)) != WL_FAILURE) + { + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + feedWatchdog(); + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } + }else + { + status = WL_CONNECT_FAILED; + } + return status; } int WiFiClass::begin(const char* ssid, uint8_t key_idx, const char *key) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; - // set encryption key - if (WiFiDrv::wifiSetKey(ssid, strlen(ssid), key_idx, key, strlen(key)) != WL_FAILURE) - { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } - }else{ - status = WL_CONNECT_FAILED; - } - return status; + // set encryption key + if (WiFiDrv::wifiSetKey(ssid, strlen(ssid), key_idx, key, strlen(key)) != WL_FAILURE) + { + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + feedWatchdog(); + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } + }else{ + status = WL_CONNECT_FAILED; + } + return status; } int WiFiClass::begin(const char* ssid, const char *passphrase) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; // set passphrase if (WiFiDrv::wifiSetPassphrase(ssid, strlen(ssid), passphrase, strlen(passphrase))!= WL_FAILURE) { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + feedWatchdog(); + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } }else{ - status = WL_CONNECT_FAILED; + status = WL_CONNECT_FAILED; } return status; } uint8_t WiFiClass::beginAP(const char *ssid) { - return beginAP(ssid, 1); + return beginAP(ssid, 1); } uint8_t WiFiClass::beginAP(const char *ssid, uint8_t channel) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; - if (WiFiDrv::wifiSetApNetwork(ssid, strlen(ssid), channel) != WL_FAILURE) - { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } - }else - { - status = WL_AP_FAILED; - } - return status; + if (WiFiDrv::wifiSetApNetwork(ssid, strlen(ssid), channel) != WL_FAILURE) + { + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } + }else{ + status = WL_AP_FAILED; + } + return status; } uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) { - return beginAP(ssid, passphrase, 1); + return beginAP(ssid, passphrase, 1); } uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase, uint8_t channel) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; // set passphrase if (WiFiDrv::wifiSetApPassphrase(ssid, strlen(ssid), passphrase, strlen(passphrase), channel)!= WL_FAILURE) { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } }else{ - status = WL_AP_FAILED; + status = WL_AP_FAILED; } return status; } uint8_t WiFiClass::beginEnterprise(const char* ssid, const char* username, const char* password) { - return beginEnterprise(ssid, username, password, ""); + return beginEnterprise(ssid, username, password, ""); } uint8_t WiFiClass::beginEnterprise(const char* ssid, const char* username, const char* password, const char* identity) { - return beginEnterprise(ssid, username, password, identity, ""); + return beginEnterprise(ssid, username, password, identity, ""); } uint8_t WiFiClass::beginEnterprise(const char* ssid, const char* username, const char* password, const char* identity, const char* ca) { - uint8_t status = WL_IDLE_STATUS; + uint8_t status = WL_IDLE_STATUS; - // set passphrase - if (WiFiDrv::wifiSetEnterprise(0 /*PEAP/MSCHAPv2*/, ssid, strlen(ssid), username, strlen(username), password, strlen(password), identity, strlen(identity), ca, strlen(ca) + 1)!= WL_FAILURE) - { - for (unsigned long start = millis(); (millis() - start) < _timeout;) - { - delay(WL_DELAY_START_CONNECTION); - status = WiFiDrv::getConnectionStatus(); - if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { - break; - } - } - } else { - status = WL_CONNECT_FAILED; - } - return status; + // set passphrase + if (WiFiDrv::wifiSetEnterprise(0 /*PEAP/MSCHAPv2*/, ssid, strlen(ssid), username, strlen(username), password, strlen(password), identity, strlen(identity), ca, strlen(ca) + 1)!= WL_FAILURE) + { + for (unsigned long start = millis(); (millis() - start) < _timeout;) + { + delay(WL_DELAY_START_CONNECTION); + status = WiFiDrv::getConnectionStatus(); + if ((status != WL_IDLE_STATUS) && (status != WL_NO_SSID_AVAIL) && (status != WL_SCAN_COMPLETED)) { + break; + } + } + } else { + status = WL_CONNECT_FAILED; + } + return status; } void WiFiClass::config(IPAddress local_ip) { - WiFiDrv::config(1, (uint32_t)local_ip, 0, 0); + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns = local_ip; + dns[3] = 1; + config(local_ip, dns); } void WiFiClass::config(IPAddress local_ip, IPAddress dns_server) { - WiFiDrv::config(1, (uint32_t)local_ip, 0, 0); - WiFiDrv::setDNS(1, (uint32_t)dns_server, 0); + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + config(local_ip, dns_server, gateway); } void WiFiClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) { - WiFiDrv::config(2, (uint32_t)local_ip, (uint32_t)gateway, 0); - WiFiDrv::setDNS(1, (uint32_t)dns_server, 0); + WiFiDrv::config(2, (uint32_t)local_ip, (uint32_t)gateway, 0); + WiFiDrv::setDNS(1, (uint32_t)dns_server, 0); } void WiFiClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) { - WiFiDrv::config(3, (uint32_t)local_ip, (uint32_t)gateway, (uint32_t)subnet); - WiFiDrv::setDNS(1, (uint32_t)dns_server, 0); + WiFiDrv::config(3, (uint32_t)local_ip, (uint32_t)gateway, (uint32_t)subnet); + WiFiDrv::setDNS(1, (uint32_t)dns_server, 0); } void WiFiClass::setDNS(IPAddress dns_server1) { - WiFiDrv::setDNS(1, (uint32_t)dns_server1, 0); + WiFiDrv::setDNS(1, (uint32_t)dns_server1, 0); } void WiFiClass::setDNS(IPAddress dns_server1, IPAddress dns_server2) { - WiFiDrv::setDNS(2, (uint32_t)dns_server1, (uint32_t)dns_server2); + WiFiDrv::setDNS(2, (uint32_t)dns_server1, (uint32_t)dns_server2); } void WiFiClass::setHostname(const char* name) { - WiFiDrv::setHostname(name); + WiFiDrv::setHostname(name); } int WiFiClass::disconnect() @@ -232,35 +246,45 @@ int WiFiClass::disconnect() void WiFiClass::end(void) { - WiFiDrv::wifiDriverDeinit(); + WiFiDrv::wifiDriverDeinit(); } uint8_t* WiFiClass::macAddress(uint8_t* mac) { - uint8_t* _mac = WiFiDrv::getMacAddress(); - memcpy(mac, _mac, WL_MAC_ADDR_LENGTH); + uint8_t* _mac = WiFiDrv::getMacAddress(); + memcpy(mac, _mac, WL_MAC_ADDR_LENGTH); return mac; } - + IPAddress WiFiClass::localIP() { - IPAddress ret; - WiFiDrv::getIpAddress(ret); - return ret; + IPAddress ret; + WiFiDrv::getIpAddress(ret); + return ret; } IPAddress WiFiClass::subnetMask() { - IPAddress ret; - WiFiDrv::getSubnetMask(ret); - return ret; + IPAddress ret; + WiFiDrv::getSubnetMask(ret); + return ret; } IPAddress WiFiClass::gatewayIP() { - IPAddress ret; - WiFiDrv::getGatewayIP(ret); - return ret; + IPAddress ret; + WiFiDrv::getGatewayIP(ret); + return ret; +} + +IPAddress WiFiClass::dnsIP(int n) +{ + if (n > 1) + return IPAddress(0,0,0,0); + IPAddress dnsip0; + IPAddress dnsip1; + WiFiDrv::getDNS(dnsip0, dnsip1); + return n ? dnsip1 : dnsip0; } const char* WiFiClass::SSID() @@ -270,8 +294,8 @@ const char* WiFiClass::SSID() uint8_t* WiFiClass::BSSID(uint8_t* bssid) { - uint8_t* _bssid = WiFiDrv::getCurrentBSSID(); - memcpy(bssid, _bssid, WL_MAC_ADDR_LENGTH); + uint8_t* _bssid = WiFiDrv::getCurrentBSSID(); + memcpy(bssid, _bssid, WL_MAC_ADDR_LENGTH); return bssid; } @@ -288,28 +312,26 @@ uint8_t WiFiClass::encryptionType() int8_t WiFiClass::scanNetworks() { - uint8_t attempts = 10; - uint8_t numOfNetworks = 0; + uint8_t attempts = 10; + uint8_t numOfNetworks = 0; - if (WiFiDrv::startScanNetworks() == WL_FAILURE) - return WL_FAILURE; - do - { - delay(2000); - numOfNetworks = WiFiDrv::getScanNetworks(); - } - while (( numOfNetworks == 0)&&(--attempts>0)); - return numOfNetworks; + if (WiFiDrv::startScanNetworks() == WL_FAILURE) + return WL_FAILURE; + do { + delay(2000); + numOfNetworks = WiFiDrv::getScanNetworks(); + }while (( numOfNetworks == 0)&&(--attempts>0)); + return numOfNetworks; } const char* WiFiClass::SSID(uint8_t networkItem) { - return WiFiDrv::getSSIDNetoworks(networkItem); + return WiFiDrv::getSSIDNetoworks(networkItem); } int32_t WiFiClass::RSSI(uint8_t networkItem) { - return WiFiDrv::getRSSINetoworks(networkItem); + return WiFiDrv::getRSSINetoworks(networkItem); } uint8_t WiFiClass::encryptionType(uint8_t networkItem) @@ -319,12 +341,12 @@ uint8_t WiFiClass::encryptionType(uint8_t networkItem) uint8_t* WiFiClass::BSSID(uint8_t networkItem, uint8_t* bssid) { - return WiFiDrv::getBSSIDNetowrks(networkItem, bssid); + return WiFiDrv::getBSSIDNetowrks(networkItem, bssid); } uint8_t WiFiClass::channel(uint8_t networkItem) { - return WiFiDrv::getChannelNetowrks(networkItem); + return WiFiDrv::getChannelNetowrks(networkItem); } uint8_t WiFiClass::status() @@ -334,52 +356,69 @@ uint8_t WiFiClass::status() uint8_t WiFiClass::reasonCode() { - return WiFiDrv::getReasonCode(); + return WiFiDrv::getReasonCode(); } int WiFiClass::hostByName(const char* aHostname, IPAddress& aResult) { - return WiFiDrv::getHostByName(aHostname, aResult); + return WiFiDrv::getHostByName(aHostname, aResult); } unsigned long WiFiClass::getTime() { - return WiFiDrv::getTime(); + return WiFiDrv::getTime(); +} + +int WiFiClass::setTime(unsigned long unixTime) +{ + return WiFiDrv::setTime(unixTime); } void WiFiClass::lowPowerMode() { - WiFiDrv::setPowerMode(1); + WiFiDrv::setPowerMode(1); } void WiFiClass::noLowPowerMode() { - WiFiDrv::setPowerMode(0); + WiFiDrv::setPowerMode(0); } int WiFiClass::ping(const char* hostname, uint8_t ttl) { - IPAddress ip; + IPAddress ip; - if (!hostByName(hostname, ip)) { - return WL_PING_UNKNOWN_HOST; - } + if (!hostByName(hostname, ip)) { + return WL_PING_UNKNOWN_HOST; + } - return ping(ip, ttl); + return ping(ip, ttl); } int WiFiClass::ping(const String &hostname, uint8_t ttl) { - return ping(hostname.c_str(), ttl); + return ping(hostname.c_str(), ttl); } int WiFiClass::ping(IPAddress host, uint8_t ttl) { - return WiFiDrv::ping(host, ttl); + return WiFiDrv::ping(host, ttl); } void WiFiClass::setTimeout(unsigned long timeout) { - _timeout = timeout; + _timeout = timeout; } + +void WiFiClass::setFeedWatchdogFunc(FeedHostProcessorWatchdogFuncPointer func) +{ + _feed_watchdog_func = func; +} + +void WiFiClass::feedWatchdog() +{ + if (_feed_watchdog_func) + _feed_watchdog_func(); +} + WiFiClass WiFi; diff --git a/arduino/libraries/WiFiNINA/src/WiFi.h b/arduino/libraries/WiFiNINA/src/WiFi.h index 6f92706..49c9b1e 100644 --- a/arduino/libraries/WiFiNINA/src/WiFi.h +++ b/arduino/libraries/WiFiNINA/src/WiFi.h @@ -1,5 +1,5 @@ /* - WiFi.h - Library for Arduino Wifi shield. + WiFi.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -21,7 +21,8 @@ #ifndef WiFi_h #define WiFi_h -#define WIFI_FIRMWARE_LATEST_VERSION "1.4.3" +#define WIFI_FIRMWARE_LATEST_VERSION "3.0.1" +#define WIFI_HAS_FEED_WATCHDOG_FUNC #include @@ -36,12 +37,15 @@ extern "C" { #include "WiFiServer.h" #include "WiFiStorage.h" +typedef void(*FeedHostProcessorWatchdogFuncPointer)(); + class WiFiClass { private: static void init(); unsigned long _timeout; + FeedHostProcessorWatchdogFuncPointer _feed_watchdog_func; public: WiFiClass(); @@ -49,6 +53,7 @@ public: * Get firmware version */ static const char* firmwareVersion(); + uint32_t firmwareVersionU32(); /* Start WiFi connection for OPEN networks @@ -85,46 +90,46 @@ public: uint8_t beginEnterprise(const char* ssid, const char* username, const char* password, const char* identity); uint8_t beginEnterprise(const char* ssid, const char* username, const char* password, const char* identity, const char* ca); - /* Change Ip configuration settings disabling the dhcp client + /* Change IP configuration settings disabling the DHCP client * - * param local_ip: Static ip configuration + * param local_ip: Static IP configuration */ void config(IPAddress local_ip); - /* Change Ip configuration settings disabling the dhcp client + /* Change IP configuration settings disabling the DHCP client * - * param local_ip: Static ip configuration + * param local_ip: Static IP configuration * param dns_server: IP configuration for DNS server 1 */ void config(IPAddress local_ip, IPAddress dns_server); - /* Change Ip configuration settings disabling the dhcp client + /* Change IP configuration settings disabling the DHCP client * - * param local_ip: Static ip configuration + * param local_ip: Static IP configuration * param dns_server: IP configuration for DNS server 1 * param gateway : Static gateway configuration */ void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway); - /* Change Ip configuration settings disabling the dhcp client + /* Change IP configuration settings disabling the DHCP client * - * param local_ip: Static ip configuration + * param local_ip: Static IP configuration * param dns_server: IP configuration for DNS server 1 * param gateway: Static gateway configuration * param subnet: Static Subnet mask */ void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); - /* Change DNS Ip configuration + /* Change DNS IP configuration * - * param dns_server1: ip configuration for DNS server 1 + * param dns_server1: IP configuration for DNS server 1 */ void setDNS(IPAddress dns_server1); - /* Change DNS Ip configuration + /* Change DNS IP configuration * - * param dns_server1: ip configuration for DNS server 1 - * param dns_server2: ip configuration for DNS server 2 + * param dns_server1: IP configuration for DNS server 1 + * param dns_server2: IP configuration for DNS server 2 * */ void setDNS(IPAddress dns_server1, IPAddress dns_server2); @@ -156,7 +161,7 @@ public: /* * Get the interface IP address. * - * return: Ip address value + * return: IP address value */ IPAddress localIP(); @@ -168,12 +173,20 @@ public: IPAddress subnetMask(); /* - * Get the gateway ip address. + * Get the gateway IP address. * - * return: gateway ip address value + * return: gateway IP address value */ IPAddress gatewayIP(); + /* + * Get the DNS server IP address. + * param n: index of the DNS server + * return: DNS server IP address value + * requires firmware version > 1.5.0 + */ + IPAddress dnsIP(int n = 0); + /* * Return the current SSID associated with the network * @@ -190,7 +203,7 @@ public: uint8_t* BSSID(uint8_t* bssid); /* - * Return the current RSSI /Received Signal Strength in dBm) + * Return the current RSSI/Received Signal Strength in dBm) * associated with the network * * return: signed value @@ -216,7 +229,7 @@ public: * * param networkItem: specify from which network item want to get the information * - * return: ssid string of the specified item on the networks scanned list + * return: SSID string of the specified item on the networks scanned list */ const char* SSID(uint8_t networkItem); @@ -265,6 +278,7 @@ public: int hostByName(const char* aHostname, IPAddress& aResult); unsigned long getTime(); + int setTime(unsigned long unixTime); void lowPowerMode(); void noLowPowerMode(); @@ -274,6 +288,9 @@ public: int ping(IPAddress host, uint8_t ttl = 128); void setTimeout(unsigned long timeout); + + void setFeedWatchdogFunc(FeedHostProcessorWatchdogFuncPointer func); + void feedWatchdog(); }; extern WiFiClass WiFi; diff --git a/arduino/libraries/WiFiNINA/src/WiFiClient.cpp b/arduino/libraries/WiFiNINA/src/WiFiClient.cpp index b8ec61d..bb72a7c 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiClient.cpp +++ b/arduino/libraries/WiFiNINA/src/WiFiClient.cpp @@ -1,5 +1,5 @@ /* - WiFiClient.cpp - Library for Arduino Wifi shield. + WiFiClient.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -22,7 +22,7 @@ extern "C" { #include "utility/wl_definitions.h" #include "utility/wl_types.h" #include "string.h" - #include "utility/debug.h" + //#include "utility/debug.h" } @@ -59,13 +59,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) { _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { - ServerDrv::startClient(uint32_t(ip), port, _sock); - - unsigned long start = millis(); - - // wait 4 second for the connection to close - while (!connected() && millis() - start < 10000) - delay(1); + ServerDrv::startClient(nullptr, 0, uint32_t(ip), port, _sock, TCP_MODE, _connTimeout); if (!connected()) { @@ -88,13 +82,7 @@ int WiFiClient::connectSSL(IPAddress ip, uint16_t port) _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { - ServerDrv::startClient(uint32_t(ip), port, _sock, TLS_MODE); - - unsigned long start = millis(); - - // wait 4 second for the connection to close - while (!connected() && millis() - start < 10000) - delay(1); + ServerDrv::startClient(nullptr, 0, uint32_t(ip), port, _sock, TLS_MODE, _connTimeout); if (!connected()) { @@ -117,13 +105,7 @@ int WiFiClient::connectSSL(const char *host, uint16_t port) _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { - ServerDrv::startClient(host, strlen(host), uint32_t(0), port, _sock, TLS_MODE); - - unsigned long start = millis(); - - // wait 4 second for the connection to close - while (!connected() && millis() - start < 10000) - delay(1); + ServerDrv::startClient(host, strlen(host), uint32_t(0), port, _sock, TLS_MODE, _connTimeout); if (!connected()) { @@ -146,13 +128,7 @@ int WiFiClient::connectBearSSL(IPAddress ip, uint16_t port) _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { - ServerDrv::startClient(uint32_t(ip), port, _sock, TLS_BEARSSL_MODE); - - unsigned long start = millis(); - - // wait 4 second for the connection to close - while (!connected() && millis() - start < 10000) - delay(1); + ServerDrv::startClient(nullptr, 0, uint32_t(ip), port, _sock, TLS_BEARSSL_MODE, _connTimeout); if (!connected()) { @@ -175,13 +151,7 @@ int WiFiClient::connectBearSSL(const char *host, uint16_t port) _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { - ServerDrv::startClient(host, strlen(host), uint32_t(0), port, _sock, TLS_BEARSSL_MODE); - - unsigned long start = millis(); - - // wait 4 second for the connection to close - while (!connected() && millis() - start < 10000) - delay(1); + ServerDrv::startClient(host, strlen(host), uint32_t(0), port, _sock, TLS_BEARSSL_MODE, _connTimeout); if (!connected()) { @@ -194,6 +164,16 @@ int WiFiClient::connectBearSSL(const char *host, uint16_t port) return 1; } +int WiFiClient::setECTrustAnchorBearSSL(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize) +{ + return ServerDrv::setECTrustAnchorBearSSL(dName, dNameSize, flags, curve, key, keySize); +} + +int WiFiClient::errorCodeBearSSL() +{ + return ServerDrv::errorCodeBearSSL(); +} + size_t WiFiClient::write(uint8_t b) { return write(&b, 1); } @@ -245,8 +225,9 @@ size_t WiFiClient::retry(const uint8_t *buf, size_t size, bool write) { return rec_bytes; } else { - //RETRY READ - // To be implemented, if needed + return rec_bytes; + //RETRY READ + // To be implemented, if needed } } diff --git a/arduino/libraries/WiFiNINA/src/WiFiClient.h b/arduino/libraries/WiFiNINA/src/WiFiClient.h index a6f43e2..e65a403 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiClient.h +++ b/arduino/libraries/WiFiNINA/src/WiFiClient.h @@ -1,5 +1,5 @@ /* - WiFiClient.cpp - Library for Arduino Wifi shield. + WiFiClient.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -32,12 +32,16 @@ public: WiFiClient(uint8_t sock); uint8_t status(); + void setConnectionTimeout(uint16_t timeout) {_connTimeout = timeout;} + virtual int connect(IPAddress ip, uint16_t port); virtual int connect(const char *host, uint16_t port); virtual int connectSSL(IPAddress ip, uint16_t port); virtual int connectSSL(const char *host, uint16_t port); virtual int connectBearSSL(IPAddress ip, uint16_t port); virtual int connectBearSSL(const char *host, uint16_t port); + virtual int setECTrustAnchorBearSSL(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize); + virtual int errorCodeBearSSL(); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buf, size_t size); virtual size_t retry(const uint8_t *buf, size_t size, bool write); @@ -61,8 +65,8 @@ public: private: static uint16_t _srcport; - uint8_t _sock; //not used - uint16_t _socket; + uint8_t _sock; + uint16_t _connTimeout = 0; bool _retrySend; }; diff --git a/arduino/libraries/WiFiNINA/src/WiFiPreferences.cpp b/arduino/libraries/WiFiNINA/src/WiFiPreferences.cpp new file mode 100644 index 0000000..31a51f5 --- /dev/null +++ b/arduino/libraries/WiFiNINA/src/WiFiPreferences.cpp @@ -0,0 +1,308 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "WiFiPreferences.h" +#include "utility/wifi_drv.h" + +using namespace std; + +Preferences::Preferences() { + +} + +Preferences::~Preferences() { + end(); +} + +bool Preferences::begin(const char * name, bool readOnly, const char* partition_label){ + WiFiDrv::wifiDriverInit(); + return WiFiDrv::prefBegin(name, readOnly, partition_label); +} + +void Preferences::end() { + WiFiDrv::prefEnd(); +} + +/* + * Clear all keys in opened preferences + * */ + +bool Preferences::clear() { + + return WiFiDrv::prefClear(); +} + +/* + * Remove a key + * */ + +bool Preferences::remove(const char * key) { + + return WiFiDrv::prefRemove(key); +} + +/* + * Put a key value + * */ + +size_t Preferences::putChar(const char* key, int8_t value) { + return WiFiDrv::prefPut(key, PT_I8, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putUChar(const char* key, uint8_t value) { + return WiFiDrv::prefPut(key, PT_U8, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putShort(const char* key, int16_t value) { + return WiFiDrv::prefPut(key, PT_I16, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putUShort(const char* key, uint16_t value) { + return WiFiDrv::prefPut(key, PT_U16, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putInt(const char* key, int32_t value) { + return WiFiDrv::prefPut(key, PT_I32, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putUInt(const char* key, uint32_t value) { + return WiFiDrv::prefPut(key, PT_U32, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putLong(const char* key, int32_t value) { + return WiFiDrv::prefPut(key, PT_I32, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putULong(const char* key, uint32_t value) { + return WiFiDrv::prefPut(key, PT_U32, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putLong64(const char* key, int64_t value) { + return WiFiDrv::prefPut(key, PT_I64, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putULong64(const char* key, uint64_t value) { + return WiFiDrv::prefPut(key, PT_U64, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putFloat(const char* key, const float_t value) { + return WiFiDrv::prefPut(key, PT_BLOB, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putDouble(const char* key, const double_t value) { + return WiFiDrv::prefPut(key, PT_BLOB, (uint8_t*) &value, sizeof(value)); +} + +size_t Preferences::putBool(const char* key, const bool value) { + return putUChar(key, (uint8_t) (value ? 1 : 0)); +} + +size_t Preferences::putString(const char* key, const char* value) { + return WiFiDrv::prefPut(key, PT_STR, (uint8_t*) value, strlen(value)+1); // we also send the \0 +} + +size_t Preferences::putString(const char* key, const String value) { + return putString(key, value.c_str()); +} + +size_t Preferences::putBytes(const char* key, const void* value, size_t len) { + return WiFiDrv::prefPut(key, PT_BLOB, (uint8_t*) value, len); +} + +PreferenceType Preferences::getType(const char* key) { + return WiFiDrv::prefGetType(key); +} + +bool Preferences::isKey(const char* key) { + return getType(key) != PT_INVALID; +} + +/* + * Get a key value + * */ + +int8_t Preferences::getChar(const char* key, const int8_t defaultValue) { + int8_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_I8, (uint8_t*)&res, sizeof(res)); + + return res; +} + +uint8_t Preferences::getUChar(const char* key, const uint8_t defaultValue) { + uint8_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_U8, (uint8_t*)&res, sizeof(res)); + + return res; +} + +int16_t Preferences::getShort(const char* key, const int16_t defaultValue) { + int16_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_I16, (uint8_t*)&res, sizeof(res)); + + return res; +} + +uint16_t Preferences::getUShort(const char* key, const uint16_t defaultValue) { + uint16_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_U16, (uint8_t*)&res, sizeof(res)); + + return res; +} + +int32_t Preferences::getInt(const char* key, const int32_t defaultValue) { + int32_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_I32, (uint8_t*)&res, sizeof(res)); + + return res; +} + +uint32_t Preferences::getUInt(const char* key, const uint32_t defaultValue) { + uint32_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_U32, (uint8_t*)&res, sizeof(res)); + + return res; +} + +int32_t Preferences::getLong(const char* key, const int32_t defaultValue) { + + if(!isKey(key)) { + return defaultValue; + } + + return getInt(key, defaultValue); +} + +uint32_t Preferences::getULong(const char* key, const uint32_t defaultValue) { + + if(!isKey(key)) { + return defaultValue; + } + + return getUInt(key, defaultValue); +} + +int64_t Preferences::getLong64(const char* key, const int64_t defaultValue) { + int64_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_I64, (uint8_t*)&res, sizeof(res)); + + return res; +} + +uint64_t Preferences::getULong64(const char* key, const uint64_t defaultValue) { + uint64_t res=0; + + if(!isKey(key)) { + return defaultValue; + } + + WiFiDrv::prefGet(key, PT_U64, (uint8_t*)&res, sizeof(res)); + + return res; +} + +float_t Preferences::getFloat(const char* key, const float_t defaultValue) { + float_t value = defaultValue; + + if(!isKey(key)) { + return defaultValue; + } + + getBytes(key, (void*) &value, sizeof(float_t)); + return value; +} + +double_t Preferences::getDouble(const char* key, const double_t defaultValue) { + double_t value = defaultValue; + + if(!isKey(key)) { + return defaultValue; + } + + getBytes(key, (void*) &value, sizeof(double_t)); + return value; +} + +bool Preferences::getBool(const char* key, const bool defaultValue) { + + if(!isKey(key)) { + return defaultValue; + } + + return getUChar(key, defaultValue ? 1 : 0) == 1; +} + +size_t Preferences::getString(const char* key, char* value, const size_t maxLen) { + return WiFiDrv::prefGet(key, PT_STR, (uint8_t*)value, maxLen); +} + +String Preferences::getString(const char* key, const String defaultValue) { + size_t len = getBytesLength(key); + char *str = new char[len+1]; + + getString(key, str, len+1); + str[len] = '\0'; + + String res(str); + delete str; + str = nullptr; + + return res; +} + +size_t Preferences::getBytesLength(const char* key) { + return WiFiDrv::prefLen(key); +} + +size_t Preferences::getBytes(const char* key, void * buf, size_t maxLen) { + return WiFiDrv::prefGet(key, PT_BLOB, (uint8_t*)buf, maxLen); +} + +size_t Preferences::freeEntries() { + return WiFiDrv::prefStat(); +} diff --git a/arduino/libraries/WiFiNINA/src/WiFiPreferences.h b/arduino/libraries/WiFiNINA/src/WiFiPreferences.h new file mode 100644 index 0000000..7d611ac --- /dev/null +++ b/arduino/libraries/WiFiNINA/src/WiFiPreferences.h @@ -0,0 +1,74 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _PREFERENCES_H_ +#define _PREFERENCES_H_ + +#include "Arduino.h" + +typedef enum { + PT_I8, PT_U8, PT_I16, PT_U16, PT_I32, PT_U32, PT_I64, PT_U64, PT_STR, PT_BLOB, PT_INVALID +} PreferenceType; + +#ifdef ARDUINO_ARCH_MEGAAVR +typedef float float_t; +typedef double double_t; +#endif + +class Preferences { +public: + Preferences(); + ~Preferences(); + bool begin(const char * name, bool readOnly=false, const char* partition_label=NULL); + void end(); + bool clear(); + bool remove(const char * key); + size_t putChar(const char* key, int8_t value); + size_t putUChar(const char* key, uint8_t value); + size_t putShort(const char* key, int16_t value); + size_t putUShort(const char* key, uint16_t value); + size_t putInt(const char* key, int32_t value); + size_t putUInt(const char* key, uint32_t value); + size_t putLong(const char* key, int32_t value); + size_t putULong(const char* key, uint32_t value); + size_t putLong64(const char* key, int64_t value); + size_t putULong64(const char* key, uint64_t value); + size_t putFloat(const char* key, float_t value); + size_t putDouble(const char* key, double_t value); + size_t putBool(const char* key, bool value); + size_t putString(const char* key, const char* value); + size_t putString(const char* key, String value); + size_t putBytes(const char* key, const void* value, size_t len); + bool isKey(const char* key); + PreferenceType getType(const char* key); + int8_t getChar(const char* key, int8_t defaultValue = 0); + uint8_t getUChar(const char* key, uint8_t defaultValue = 0); + int16_t getShort(const char* key, int16_t defaultValue = 0); + uint16_t getUShort(const char* key, uint16_t defaultValue = 0); + int32_t getInt(const char* key, int32_t defaultValue = 0); + uint32_t getUInt(const char* key, uint32_t defaultValue = 0); + int32_t getLong(const char* key, int32_t defaultValue = 0); + uint32_t getULong(const char* key, uint32_t defaultValue = 0); + int64_t getLong64(const char* key, int64_t defaultValue = 0); + uint64_t getULong64(const char* key, uint64_t defaultValue = 0); + float_t getFloat(const char* key, float_t defaultValue = NAN); + double_t getDouble(const char* key, double_t defaultValue = NAN); + bool getBool(const char* key, bool defaultValue = false); + size_t getString(const char* key, char* value, size_t maxLen); + String getString(const char* key, String defaultValue = String()); + size_t getBytesLength(const char* key); + size_t getBytes(const char* key, void * buf, size_t maxLen); + size_t freeEntries(); +}; + +#endif diff --git a/arduino/libraries/WiFiNINA/src/WiFiSSLClient.cpp b/arduino/libraries/WiFiNINA/src/WiFiSSLClient.cpp index f051144..9382223 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiSSLClient.cpp +++ b/arduino/libraries/WiFiNINA/src/WiFiSSLClient.cpp @@ -58,3 +58,13 @@ int WiFiBearSSLClient::connect(const char* host, uint16_t port) { return WiFiClient::connectBearSSL(host, port); } + +int WiFiBearSSLClient::setECTrustAnchor(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize) +{ + return WiFiClient::setECTrustAnchorBearSSL(dName, dNameSize, flags, curve, key, keySize); +} + +int WiFiBearSSLClient::errorCode() +{ + return WiFiClient::errorCodeBearSSL(); +} diff --git a/arduino/libraries/WiFiNINA/src/WiFiSSLClient.h b/arduino/libraries/WiFiNINA/src/WiFiSSLClient.h index 41c3a6a..77065fb 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiSSLClient.h +++ b/arduino/libraries/WiFiNINA/src/WiFiSSLClient.h @@ -40,6 +40,8 @@ public: virtual int connect(IPAddress ip, uint16_t port); virtual int connect(const char* host, uint16_t port); + virtual int setECTrustAnchor(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize); + virtual int errorCode(); }; #endif /* WIFISSLCLIENT_H */ diff --git a/arduino/libraries/WiFiNINA/src/WiFiServer.cpp b/arduino/libraries/WiFiNINA/src/WiFiServer.cpp index 729958b..4686e3e 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiServer.cpp +++ b/arduino/libraries/WiFiNINA/src/WiFiServer.cpp @@ -1,5 +1,5 @@ /* - WiFiServer.cpp - Library for Arduino Wifi shield. + WiFiServer.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -21,14 +21,21 @@ #include #include "utility/server_drv.h" -extern "C" { - #include "utility/debug.h" -} +//extern "C" { +// #include "utility/debug.h" +//} #include "WiFi.h" #include "WiFiClient.h" #include "WiFiServer.h" +WiFiServer::WiFiServer() : + _sock(NO_SOCKET_AVAIL), + _lastSock(NO_SOCKET_AVAIL) +{ + _port = 80; +} + WiFiServer::WiFiServer(uint16_t port) : _sock(NO_SOCKET_AVAIL), _lastSock(NO_SOCKET_AVAIL) @@ -38,6 +45,7 @@ WiFiServer::WiFiServer(uint16_t port) : void WiFiServer::begin() { + end(); _sock = ServerDrv::getSocket(); if (_sock != NO_SOCKET_AVAIL) { @@ -45,6 +53,22 @@ void WiFiServer::begin() } } +void WiFiServer::begin(uint16_t port) +{ + end(); + _port = port; + begin(); +} + +void WiFiServer::end() +{ + if (_sock != NO_SOCKET_AVAIL) { + ServerDrv::stopServer(_sock); + _sock = NO_SOCKET_AVAIL; + _lastSock = NO_SOCKET_AVAIL; + } +} + WiFiClient WiFiServer::available(byte* status) { int sock = NO_SOCKET_AVAIL; @@ -80,6 +104,12 @@ WiFiClient WiFiServer::available(byte* status) return WiFiClient(255); } +WiFiClient WiFiServer::accept() +{ + int sock = ServerDrv::availServer(_sock, true); + return WiFiClient(sock); +} + uint8_t WiFiServer::status() { if (_sock == NO_SOCKET_AVAIL) { return CLOSED; @@ -88,6 +118,9 @@ uint8_t WiFiServer::status() { } } +WiFiServer::operator bool() { + return (_sock != NO_SOCKET_AVAIL); +} size_t WiFiServer::write(uint8_t b) { diff --git a/arduino/libraries/WiFiNINA/src/WiFiServer.h b/arduino/libraries/WiFiNINA/src/WiFiServer.h index d60f558..81ced8e 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiServer.h +++ b/arduino/libraries/WiFiNINA/src/WiFiServer.h @@ -1,5 +1,5 @@ /* - WiFiServer.h - Library for Arduino Wifi shield. + WiFiServer.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -36,12 +36,17 @@ private: uint16_t _port; void* pcb; public: + WiFiServer(); WiFiServer(uint16_t); WiFiClient available(uint8_t* status = NULL); + WiFiClient accept(); void begin(); + void begin(uint16_t port); + void end(); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buf, size_t size); uint8_t status(); + explicit operator bool(); using Print::write; }; diff --git a/arduino/libraries/WiFiNINA/src/WiFiStorage.h b/arduino/libraries/WiFiNINA/src/WiFiStorage.h index 30a6276..b6f3d04 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiStorage.h +++ b/arduino/libraries/WiFiNINA/src/WiFiStorage.h @@ -59,7 +59,7 @@ public: return true; } static bool downloadOTA(const char * url, uint8_t * res_ota_download = NULL) { - /* The buffer within the nina firmware allows a maximum + /* The buffer within the NINA firmware allows a maximum * url size of 128 bytes. It's better to prevent the * transmission of over-sized URL as soon as possible. */ diff --git a/arduino/libraries/WiFiNINA/src/WiFiUdp.cpp b/arduino/libraries/WiFiNINA/src/WiFiUdp.cpp index 12f27fa..6e3fb7d 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiUdp.cpp +++ b/arduino/libraries/WiFiNINA/src/WiFiUdp.cpp @@ -1,5 +1,5 @@ /* - WiFiUdp.cpp - Library for Arduino Wifi shield. + WiFiUdp.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. @@ -21,7 +21,7 @@ #include extern "C" { - #include "utility/debug.h" + //#include "utility/debug.h" #include "utility/wifi_spi.h" } diff --git a/arduino/libraries/WiFiNINA/src/WiFiUdp.h b/arduino/libraries/WiFiNINA/src/WiFiUdp.h index 96c019e..922502d 100644 --- a/arduino/libraries/WiFiNINA/src/WiFiUdp.h +++ b/arduino/libraries/WiFiNINA/src/WiFiUdp.h @@ -1,5 +1,5 @@ /* - WiFiUdp.h - Library for Arduino Wifi shield. + WiFiUdp.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino LLC. All right reserved. diff --git a/arduino/libraries/WiFiNINA/src/utility/debug.h b/arduino/libraries/WiFiNINA/src/utility/debug.h deleted file mode 100644 index 5569e45..0000000 --- a/arduino/libraries/WiFiNINA/src/utility/debug.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - debug.h - Library for Arduino Wifi shield. - Copyright (c) 2011-2014 Arduino. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -//*********************************************/ -// -// File: debug.h -// -// Author: dlf (Metodo2 srl) -// -//********************************************/ - - -#ifndef Debug_H -#define Debug_H - -#include -#include - -#define PRINT_FILE_LINE() do { \ - Serial.print("[");Serial.print(__FILE__); \ - Serial.print("::");Serial.print(__LINE__);Serial.print("]");\ -}while (0); - -#ifdef _DEBUG_ - -#define INFO(format, args...) do { \ - char buf[250]; \ - sprintf(buf, format, args); \ - Serial.println(buf); \ -} while(0); - -#define INFO1(x) do { PRINT_FILE_LINE() Serial.print("-I-");\ - Serial.println(x); \ -}while (0); - - -#define INFO2(x,y) do { PRINT_FILE_LINE() Serial.print("-I-");\ - Serial.print(x,16);Serial.print(",");Serial.println(y,16); \ -}while (0); - - -#else -#define INFO1(x) do {} while(0); -#define INFO2(x,y) do {} while(0); -#define INFO(format, args...) do {} while(0); -#endif - -#if 0 -#define WARN(args) do { PRINT_FILE_LINE() \ - Serial.print("-W-"); Serial.println(args); \ -}while (0); -#else -#define WARN(args) do {} while (0); -#endif - -#if _DEBUG_SPI_ -#define DBG_PIN2 5 -#define DBG_PIN 4 - -#define START() digitalWrite(DBG_PIN2, HIGH); -#define END() digitalWrite(DBG_PIN2, LOW); -#define SET_TRIGGER() digitalWrite(DBG_PIN, HIGH); -#define RST_TRIGGER() digitalWrite(DBG_PIN, LOW); - -#define INIT_TRIGGER() pinMode(DBG_PIN, OUTPUT); \ - pinMode(DBG_PIN2, OUTPUT); \ - RST_TRIGGER() -#define TOGGLE_TRIGGER() SET_TRIGGER() \ - delayMicroseconds(2); \ - RST_TRIGGER() -#else -#define START() -#define END() -#define SET_TRIGGER() -#define RST_TRIGGER() -#define INIT_TRIGGER() -#define TOGGLE_TRIGGER() -#endif - -#endif diff --git a/arduino/libraries/WiFiNINA/src/utility/nano_rp2040_support.cpp b/arduino/libraries/WiFiNINA/src/utility/nano_rp2040_support.cpp new file mode 100644 index 0000000..ad2f097 --- /dev/null +++ b/arduino/libraries/WiFiNINA/src/utility/nano_rp2040_support.cpp @@ -0,0 +1,85 @@ +/* + This file is part of the WiFiNINA library. + Copyright (c) 2021 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef ARDUINO_NANO_RP2040_CONNECT + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include "nina_pins.h" /* variants/NANO_RP2040_CONNECT/ninaPins.h */ +#include "wifi_drv.h" + +/****************************************************************************** + * FUNCTION DEFINITION + ******************************************************************************/ + +#ifdef NINA_PINS_AS_CLASS +#define VAL(x) x.get() +#else +#define VAL(x) static_cast(x) +#endif + +uint8_t toAnalogPin(NinaPin pin) +{ + if (pin == A4) return 6; /* ADC1 - CH6 */ + else if (pin == A5) return 3; /* ADC1 - CH3 */ + else if (pin == A6) return 0; /* ADC1 - CH0 */ + else if (pin == A7) return 7; /* ADC1 - CH7 */ + else return 0xFF; +} + +void pinMode(NinaPin pin, PinMode mode) +{ + WiFiDrv::pinMode(VAL(pin), static_cast(mode)); +} + +PinStatus digitalRead(NinaPin pin) +{ + return WiFiDrv::digitalRead(VAL(pin)); +} + +void digitalWrite(NinaPin pin, PinStatus value) +{ + if (value == LOW) + WiFiDrv::digitalWrite(VAL(pin), 1); + else + WiFiDrv::digitalWrite(VAL(pin), 0); +} + +int analogRead(NinaPin pin) +{ + uint8_t const adc_channel = toAnalogPin(pin); + + if (adc_channel == 0xFF) + return 0; + else +#ifdef NINA_PINS_AS_CLASS + return WiFiDrv::analogRead(adc_channel) >> (12 - pin.analogReadResolution()); +#else + return WiFiDrv::analogRead(adc_channel); +#endif +} + +void analogWrite(NinaPin pin, int value) +{ + WiFiDrv::analogWrite(VAL(pin), static_cast(value)); +} + +#endif /* ARDUINO_NANO_RP2040_CONNECT */ diff --git a/arduino/libraries/WiFiNINA/src/utility/server_drv.cpp b/arduino/libraries/WiFiNINA/src/utility/server_drv.cpp index 4fc35b4..556de83 100644 --- a/arduino/libraries/WiFiNINA/src/utility/server_drv.cpp +++ b/arduino/libraries/WiFiNINA/src/utility/server_drv.cpp @@ -1,5 +1,5 @@ /* - server_drv.cpp - Library for Arduino Wifi shield. + server_drv.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. @@ -23,7 +23,7 @@ #include "utility/server_drv.h" #include "Arduino.h" -#include "utility/spi_drv.h" +#include extern "C" { #include "utility/wl_types.h" @@ -84,6 +84,28 @@ void ServerDrv::startServer(uint32_t ipAddress, uint16_t port, uint8_t sock, uin SpiDrv::spiSlaveDeselect(); } +void ServerDrv::stopServer(uint8_t sock) +{ + WAIT_FOR_SLAVE_SELECT(); + // Send Command + SpiDrv::sendCmd(STOP_SERVER_TCP_CMD, PARAM_NUMS_1); + SpiDrv::sendParam(&sock, 1, LAST_PARAM); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t _data = 0; + uint8_t _dataLen = 0; + if (!SpiDrv::waitResponseCmd(STOP_SERVER_TCP_CMD, PARAM_NUMS_1, &_data, &_dataLen)) + { + WARN("error waitResponse"); + } + SpiDrv::spiSlaveDeselect(); +} + // Start server TCP on port specified void ServerDrv::startClient(uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode) { @@ -110,19 +132,20 @@ void ServerDrv::startClient(uint32_t ipAddress, uint16_t port, uint8_t sock, uin SpiDrv::spiSlaveDeselect(); } -void ServerDrv::startClient(const char* host, uint8_t host_len, uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode) +void ServerDrv::startClient(const char* host, uint8_t host_len, uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode, uint16_t timeout) { WAIT_FOR_SLAVE_SELECT(); // Send Command - SpiDrv::sendCmd(START_CLIENT_TCP_CMD, PARAM_NUMS_5); + SpiDrv::sendCmd(START_CLIENT_TCP_CMD, PARAM_NUMS_6); SpiDrv::sendParam((uint8_t*)host, host_len); SpiDrv::sendParam((uint8_t*)&ipAddress, sizeof(ipAddress)); SpiDrv::sendParam(port); SpiDrv::sendParam(&sock, 1); - SpiDrv::sendParam(&protMode, 1, LAST_PARAM); + SpiDrv::sendParam(&protMode, 1); + SpiDrv::sendParam(timeout, LAST_PARAM); // pad to multiple of 4 - int commandSize = 17 + host_len; + int commandSize = 20 + host_len; while (commandSize % 4) { SpiDrv::readChar(); commandSize++; @@ -256,7 +279,7 @@ uint16_t ServerDrv::availData(uint8_t sock) return len; } -uint8_t ServerDrv::availServer(uint8_t sock) +uint8_t ServerDrv::availServer(uint8_t sock, uint8_t accept) { if (!SpiDrv::available()) { return 255; @@ -264,12 +287,9 @@ uint8_t ServerDrv::availServer(uint8_t sock) WAIT_FOR_SLAVE_SELECT(); // Send Command - SpiDrv::sendCmd(AVAIL_DATA_TCP_CMD, PARAM_NUMS_1); - SpiDrv::sendParam(&sock, sizeof(sock), LAST_PARAM); - - // pad to multiple of 4 - SpiDrv::readChar(); - SpiDrv::readChar(); + SpiDrv::sendCmd(AVAIL_DATA_TCP_CMD, PARAM_NUMS_2); + SpiDrv::sendParam(&sock, sizeof(sock)); + SpiDrv::sendParam(&accept, sizeof(accept), LAST_PARAM); SpiDrv::spiSlaveDeselect(); //Wait the reply elaboration @@ -517,4 +537,71 @@ uint8_t ServerDrv::getSocket() return _data; } +uint8_t ServerDrv::setECTrustAnchorBearSSL(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize) +{ + WAIT_FOR_SLAVE_SELECT(); + + int commandSize = 4; + SpiDrv::sendCmd(BRSSL_SET_EC_TA, PARAM_NUMS_4); + + /* Send distinguished name */ + SpiDrv::sendBuffer((uint8_t*)dName, dNameSize); + commandSize += dNameSize; + + /* Send flags */ + SpiDrv::sendParam(flags); + commandSize += 3; + + /* Send curve */ + SpiDrv::sendParam(curve); + commandSize += 3; + + /* Send key */ + SpiDrv::sendBuffer((uint8_t*)key, keySize, LAST_PARAM); + commandSize += keySize; + + // pad to multiple of 4 + while (commandSize % 4) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t result = 0; + uint8_t len = 1; + SpiDrv::waitResponseCmd(BRSSL_SET_EC_TA, PARAM_NUMS_1, (uint8_t*)&result, &len); + + SpiDrv::spiSlaveDeselect(); + + // if everything went ok the returned value is 0 + return result == 0; +} + +int ServerDrv::errorCodeBearSSL() +{ + WAIT_FOR_SLAVE_SELECT(); + + // Send Command + SpiDrv::sendCmd(BRSSL_ERROR_CODE, PARAM_NUMS_0); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + int _data = 0; + uint8_t _dataLen = 0; + SpiDrv::waitResponseCmd(BRSSL_ERROR_CODE, PARAM_NUMS_1, (uint8_t*)&_data, &_dataLen); + + SpiDrv::spiSlaveDeselect(); + + return _data; +} + ServerDrv serverDrv; diff --git a/arduino/libraries/WiFiNINA/src/utility/server_drv.h b/arduino/libraries/WiFiNINA/src/utility/server_drv.h index 3cac5d3..5297146 100644 --- a/arduino/libraries/WiFiNINA/src/utility/server_drv.h +++ b/arduino/libraries/WiFiNINA/src/utility/server_drv.h @@ -1,5 +1,5 @@ /* - server_drv.h - Library for Arduino Wifi shield. + server_drv.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. @@ -35,12 +35,14 @@ public: static void startServer(uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode=TCP_MODE); + static void stopServer(uint8_t sock); + static void startClient(uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode=TCP_MODE); - static void startClient(const char* host, uint8_t host_len, uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode=TCP_MODE); + static void startClient(const char* host, uint8_t host_len, uint32_t ipAddress, uint16_t port, uint8_t sock, uint8_t protMode=TCP_MODE, uint16_t timeout = 0); static void stopClient(uint8_t sock); - + static uint8_t getServerState(uint8_t sock); static uint8_t getClientState(uint8_t sock); @@ -57,11 +59,15 @@ public: static uint16_t availData(uint8_t sock); - static uint8_t availServer(uint8_t sock); + static uint8_t availServer(uint8_t sock, uint8_t accept = false); static uint8_t checkDataSent(uint8_t sock); static uint8_t getSocket(); + + static uint8_t setECTrustAnchorBearSSL(const uint8_t *dName, uint32_t dNameSize, uint16_t flags, uint16_t curve, const uint8_t *key, uint32_t keySize); + + static int errorCodeBearSSL(); }; extern ServerDrv serverDrv; diff --git a/arduino/libraries/WiFiNINA/src/utility/spi_drv.cpp b/arduino/libraries/WiFiNINA/src/utility/spi_drv.cpp deleted file mode 100644 index 744bd85..0000000 --- a/arduino/libraries/WiFiNINA/src/utility/spi_drv.cpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - spi_drv.cpp - Library for Arduino Wifi shield. - Copyright (c) 2018 Arduino SA. All rights reserved. - Copyright (c) 2011-2014 Arduino. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "Arduino.h" -#include -#include "utility/spi_drv.h" -#include "pins_arduino.h" - -#ifdef ARDUINO_SAMD_MKRVIDOR4000 -// STRIX: the STRIX FPGA image passes the NINA module's pins through to SAMD21 pins, so the -// module is driven directly (SPI on D8-D10) instead of through Arduino's FPGA peripherals. -#define NINA_GPIO0 6 -#define SPIWIFI_SS 5 -#define SPIWIFI_ACK 4 -#define SPIWIFI_RESET 7 -#endif - -//#define _DEBUG_ -extern "C" { -#include "utility/debug.h" -} - -static uint8_t SLAVESELECT = 10; // ss -static uint8_t SLAVEREADY = 7; // handshake pin -static uint8_t SLAVERESET = 5; // reset pin - -static bool inverted_reset = false; - -#define DELAY_TRANSFER() - -#ifndef SPIWIFI -#define SPIWIFI SPI -#endif - -bool SpiDrv::initialized = false; - -__attribute__((weak)) void wifi_nina_feed_watchdog() -{ - /* This function can be overwritten by a "strong" implementation - * in a higher level application, such as the ArduinoIoTCloud - * firmware stack. - */ -} - -void SpiDrv::begin() -{ -#ifdef SPIWIFI_SS - SLAVESELECT = SPIWIFI_SS; -#endif - -#ifdef SPIWIFI_ACK - SLAVEREADY = SPIWIFI_ACK; -#endif - -#ifdef SPIWIFI_RESET - SLAVERESET = (uint8_t)SPIWIFI_RESET; -#endif - -#ifdef ARDUINO_SAMD_MKRVIDOR4000 - inverted_reset = false; -#else - if (SLAVERESET > PINS_COUNT) { - inverted_reset = true; - SLAVERESET = ~SLAVERESET; - } -#endif - - SPIWIFI.begin(); - pinMode(SLAVESELECT, OUTPUT); - pinMode(SLAVEREADY, INPUT); - pinMode(SLAVERESET, OUTPUT); - pinMode(NINA_GPIO0, OUTPUT); - - digitalWrite(NINA_GPIO0, HIGH); - digitalWrite(SLAVESELECT, HIGH); - digitalWrite(SLAVERESET, inverted_reset ? HIGH : LOW); - delay(10); - digitalWrite(SLAVERESET, inverted_reset ? LOW : HIGH); - delay(750); - - digitalWrite(NINA_GPIO0, LOW); - pinMode(NINA_GPIO0, INPUT); - -#ifdef _DEBUG_ - INIT_TRIGGER() -#endif - - initialized = true; -} - -void SpiDrv::end() { - digitalWrite(SLAVERESET, inverted_reset ? HIGH : LOW); - - pinMode(SLAVESELECT, INPUT); - - SPIWIFI.end(); - - initialized = false; -} - -void SpiDrv::spiSlaveSelect() -{ - SPIWIFI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); - digitalWrite(SLAVESELECT,LOW); - - // wait for up to 5 ms for the NINA to indicate it is not ready for transfer - // the timeout is only needed for the case when the shield or module is not present - for (unsigned long start = millis(); (digitalRead(SLAVEREADY) != HIGH) && (millis() - start) < 5;); -} - - -void SpiDrv::spiSlaveDeselect() -{ - digitalWrite(SLAVESELECT,HIGH); - SPIWIFI.endTransaction(); -} - - -char SpiDrv::spiTransfer(volatile char data) -{ - char result = SPIWIFI.transfer(data); - DELAY_TRANSFER(); - - return result; // return the received byte -} - -int SpiDrv::waitSpiChar(unsigned char waitChar) -{ - int timeout = TIMEOUT_CHAR; - unsigned char _readChar = 0; - do{ - _readChar = readChar(); //get data byte - if (_readChar == ERR_CMD) - { - WARN("Err cmd received\n"); - return -1; - } - }while((timeout-- > 0) && (_readChar != waitChar)); - return (_readChar == waitChar); -} - -int SpiDrv::readAndCheckChar(char checkChar, char* readChar) -{ - getParam((uint8_t*)readChar); - - return (*readChar == checkChar); -} - -char SpiDrv::readChar() -{ - uint8_t readChar = 0; - getParam(&readChar); - return readChar; -} - -#define WAIT_START_CMD(x) waitSpiChar(START_CMD) - -#define IF_CHECK_START_CMD(x) \ - if (!WAIT_START_CMD(_data)) \ - { \ - TOGGLE_TRIGGER() \ - WARN("Error waiting START_CMD"); \ - return 0; \ - }else \ - -#define CHECK_DATA(check, x) \ - if (!readAndCheckChar(check, &x)) \ - { \ - TOGGLE_TRIGGER() \ - WARN("Reply error"); \ - INFO2(check, (uint8_t)x); \ - return 0; \ - }else \ - -#define waitSlaveReady() (digitalRead(SLAVEREADY) == LOW) -#define waitSlaveSign() (digitalRead(SLAVEREADY) == HIGH) -#define waitSlaveSignalH() while(digitalRead(SLAVEREADY) != HIGH){} -#define waitSlaveSignalL() while(digitalRead(SLAVEREADY) != LOW){} - -void SpiDrv::waitForSlaveSign() -{ - while (!waitSlaveSign()); -} - -void SpiDrv::waitForSlaveReady(bool const feed_watchdog) -{ - unsigned long const start = millis(); - while (!waitSlaveReady()) - { - if (feed_watchdog) { - if ((millis() - start) < 10000) { - wifi_nina_feed_watchdog(); - } - } - } -} - -void SpiDrv::getParam(uint8_t* param) -{ - // Get Params data - *param = spiTransfer(DUMMY_DATA); - DELAY_TRANSFER(); -} - -int SpiDrv::waitResponseCmd(uint8_t cmd, uint8_t numParam, uint8_t* param, uint8_t* param_len) -{ - char _data = 0; - int ii = 0; - - IF_CHECK_START_CMD(_data) - { - CHECK_DATA(cmd | REPLY_FLAG, _data){}; - - CHECK_DATA(numParam, _data) - { - readParamLen8(param_len); - for (ii=0; ii<(*param_len); ++ii) - { - // Get Params data - //param[ii] = spiTransfer(DUMMY_DATA); - getParam(¶m[ii]); - } - } - - readAndCheckChar(END_CMD, &_data); - } - - return 1; -} -/* -int SpiDrv::waitResponse(uint8_t cmd, uint8_t numParam, uint8_t* param, uint16_t* param_len) -{ - char _data = 0; - int i =0, ii = 0; - - IF_CHECK_START_CMD(_data) - { - CHECK_DATA(cmd | REPLY_FLAG, _data){}; - - CHECK_DATA(numParam, _data); - { - readParamLen16(param_len); - for (ii=0; ii<(*param_len); ++ii) - { - // Get Params data - param[ii] = spiTransfer(DUMMY_DATA); - } - } - - readAndCheckChar(END_CMD, &_data); - } - - return 1; -} -*/ - -int SpiDrv::waitResponseData16(uint8_t cmd, uint8_t* param, uint16_t* param_len) -{ - char _data = 0; - uint16_t ii = 0; - - IF_CHECK_START_CMD(_data) - { - CHECK_DATA(cmd | REPLY_FLAG, _data){}; - - uint8_t numParam = readChar(); - if (numParam != 0) - { - readParamLen16(param_len); - for (ii=0; ii<(*param_len); ++ii) - { - // Get Params data - param[ii] = spiTransfer(DUMMY_DATA); - } - } - - readAndCheckChar(END_CMD, &_data); - } - - return 1; -} - -int SpiDrv::waitResponseData8(uint8_t cmd, uint8_t* param, uint8_t* param_len) -{ - char _data = 0; - int ii = 0; - - IF_CHECK_START_CMD(_data) - { - CHECK_DATA(cmd | REPLY_FLAG, _data){}; - - uint8_t numParam = readChar(); - if (numParam != 0) - { - readParamLen8(param_len); - for (ii=0; ii<(*param_len); ++ii) - { - // Get Params data - param[ii] = spiTransfer(DUMMY_DATA); - } - } - - readAndCheckChar(END_CMD, &_data); - } - - return 1; -} - -int SpiDrv::waitResponseParams(uint8_t cmd, uint8_t numParam, tParam* params) -{ - char _data = 0; - int i =0, ii = 0; - - - IF_CHECK_START_CMD(_data) - { - CHECK_DATA(cmd | REPLY_FLAG, _data){}; - - uint8_t _numParam = readChar(); - if (_numParam != 0) - { - for (i=0; i<_numParam; ++i) - { - params[i].paramLen = readParamLen8(); - for (ii=0; ii maxNumParams) - { - numParam = maxNumParams; - } - *numParamRead = numParam; - if (numParam != 0) - { - for (i=0; i maxNumParams) - { - numParam = maxNumParams; - } - *numParamRead = numParam; - if (numParam != 0) - { - for (i=0; i>8)); - spiTransfer((uint8_t)(param_len & 0xff)); -} - -uint8_t SpiDrv::readParamLen8(uint8_t* param_len) -{ - uint8_t _param_len = spiTransfer(DUMMY_DATA); - if (param_len != NULL) - { - *param_len = _param_len; - } - return _param_len; -} - -uint16_t SpiDrv::readParamLen16(uint16_t* param_len) -{ - uint16_t _param_len = spiTransfer(DUMMY_DATA)<<8 | (spiTransfer(DUMMY_DATA)& 0xff); - if (param_len != NULL) - { - *param_len = _param_len; - } - return _param_len; -} - - -void SpiDrv::sendBuffer(uint8_t* param, uint16_t param_len, uint8_t lastParam) -{ - uint16_t i = 0; - - // Send Spi paramLen - sendParamLen16(param_len); - - // Send Spi param data - for (i=0; i>8)); - spiTransfer((uint8_t)(param & 0xff)); - - // if lastParam==1 Send Spi END CMD - if (lastParam == 1) - spiTransfer(END_CMD); -} - -/* Cmd Struct Message */ -/* _________________________________________________________________________________ */ -/*| START CMD | C/R | CMD |[TOT LEN]| N.PARAM | PARAM LEN | PARAM | .. | END CMD | */ -/*|___________|______|______|_________|_________|___________|________|____|_________| */ -/*| 8 bit | 1bit | 7bit | 8bit | 8bit | 8bit | nbytes | .. | 8bit | */ -/*|___________|______|______|_________|_________|___________|________|____|_________| */ - -void SpiDrv::sendCmd(uint8_t cmd, uint8_t numParam) -{ - // Send Spi START CMD - spiTransfer(START_CMD); - - // Send Spi C + cmd - spiTransfer(cmd & ~(REPLY_FLAG)); - - // Send Spi totLen - //spiTransfer(totLen); - - // Send Spi numParam - spiTransfer(numParam); - - // If numParam == 0 send END CMD - if (numParam == 0) - spiTransfer(END_CMD); - -} - -int SpiDrv::available() -{ - return (digitalRead(NINA_GPIO0) != LOW); -} - -SpiDrv spiDrv; diff --git a/arduino/libraries/WiFiNINA/src/utility/spi_drv.h b/arduino/libraries/WiFiNINA/src/utility/spi_drv.h deleted file mode 100644 index d8d1186..0000000 --- a/arduino/libraries/WiFiNINA/src/utility/spi_drv.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - spi_drv.h - Library for Arduino Wifi shield. - Copyright (c) 2018 Arduino SA. All rights reserved. - Copyright (c) 2011-2014 Arduino. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef SPI_Drv_h -#define SPI_Drv_h - -#include -#include "utility/wifi_spi.h" - -#define SPI_START_CMD_DELAY 10 - -#define NO_LAST_PARAM 0 -#define LAST_PARAM 1 - -#define DUMMY_DATA 0xFF - -#define WAIT_FOR_SLAVE_SELECT() \ - if (!SpiDrv::initialized) { \ - SpiDrv::begin(); \ - } \ - SpiDrv::waitForSlaveReady(); \ - SpiDrv::spiSlaveSelect(); - -class SpiDrv -{ -private: - //static bool waitSlaveReady(); - static void waitForSlaveSign(); - static void getParam(uint8_t* param); -public: - static bool initialized; - - static void begin(); - - static void end(); - - static void spiDriverInit(); - - static void spiSlaveSelect(); - - static void spiSlaveDeselect(); - - static char spiTransfer(volatile char data); - - static void waitForSlaveReady(bool const feed_watchdog = false); - - //static int waitSpiChar(char waitChar, char* readChar); - - static int waitSpiChar(unsigned char waitChar); - - static int readAndCheckChar(char checkChar, char* readChar); - - static char readChar(); - - static int waitResponseParams(uint8_t cmd, uint8_t numParam, tParam* params); - - static int waitResponseCmd(uint8_t cmd, uint8_t numParam, uint8_t* param, uint8_t* param_len); - - static int waitResponseData8(uint8_t cmd, uint8_t* param, uint8_t* param_len); - - static int waitResponseData16(uint8_t cmd, uint8_t* param, uint16_t* param_len); - /* - static int waitResponse(uint8_t cmd, tParam* params, uint8_t* numParamRead, uint8_t maxNumParams); - - static int waitResponse(uint8_t cmd, uint8_t numParam, uint8_t* param, uint16_t* param_len); -*/ - static int waitResponse(uint8_t cmd, uint8_t* numParamRead, uint8_t** params, uint8_t maxNumParams); - - static void sendParam(uint8_t* param, uint8_t param_len, uint8_t lastParam = NO_LAST_PARAM); - - static void sendParamNoLen(uint8_t* param, size_t param_len, uint8_t lastParam = NO_LAST_PARAM); - - static void sendParamLen8(uint8_t param_len); - - static void sendParamLen16(uint16_t param_len); - - static uint8_t readParamLen8(uint8_t* param_len = NULL); - - static uint16_t readParamLen16(uint16_t* param_len = NULL); - - static void sendBuffer(uint8_t* param, uint16_t param_len, uint8_t lastParam = NO_LAST_PARAM); - - static void sendParam(uint16_t param, uint8_t lastParam = NO_LAST_PARAM); - - static void sendCmd(uint8_t cmd, uint8_t numParam); - - static int available(); -}; - -extern SpiDrv spiDrv; - -#endif diff --git a/arduino/libraries/WiFiNINA/src/utility/wifi_drv.cpp b/arduino/libraries/WiFiNINA/src/utility/wifi_drv.cpp index 24beece..e219b62 100644 --- a/arduino/libraries/WiFiNINA/src/utility/wifi_drv.cpp +++ b/arduino/libraries/WiFiNINA/src/utility/wifi_drv.cpp @@ -1,5 +1,5 @@ /* - wifi_drv.cpp - Library for Arduino Wifi shield. + wifi_drv.cpp - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. @@ -23,7 +23,7 @@ #include #include "Arduino.h" -#include "utility/spi_drv.h" +#include #include "utility/wifi_drv.h" #define _DEBUG_ @@ -113,12 +113,11 @@ void WiFiDrv::wifiDriverInit() void WiFiDrv::wifiDriverDeinit() { - SpiDrv::end(); } int8_t WiFiDrv::wifiSetNetwork(const char* ssid, uint8_t ssid_len) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SET_NET_CMD, PARAM_NUMS_1); SpiDrv::sendParam((uint8_t*)ssid, ssid_len, LAST_PARAM); @@ -150,7 +149,7 @@ int8_t WiFiDrv::wifiSetNetwork(const char* ssid, uint8_t ssid_len) int8_t WiFiDrv::wifiSetPassphrase(const char* ssid, uint8_t ssid_len, const char *passphrase, const uint8_t len) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SET_PASSPHRASE_CMD, PARAM_NUMS_2); SpiDrv::sendParam((uint8_t*)ssid, ssid_len, NO_LAST_PARAM); @@ -183,13 +182,13 @@ int8_t WiFiDrv::wifiSetPassphrase(const char* ssid, uint8_t ssid_len, const char int8_t WiFiDrv::wifiSetKey(const char* ssid, uint8_t ssid_len, uint8_t key_idx, const void *key, const uint8_t len) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SET_KEY_CMD, PARAM_NUMS_3); SpiDrv::sendParam((uint8_t*)ssid, ssid_len, NO_LAST_PARAM); SpiDrv::sendParam(&key_idx, KEY_IDX_LEN, NO_LAST_PARAM); SpiDrv::sendParam((uint8_t*)key, len, LAST_PARAM); - + // pad to multiple of 4 int commandSize = 8 + ssid_len + len; while (commandSize % 4) { @@ -216,7 +215,7 @@ int8_t WiFiDrv::wifiSetKey(const char* ssid, uint8_t ssid_len, uint8_t key_idx, void WiFiDrv::config(uint8_t validParams, uint32_t local_ip, uint32_t gateway, uint32_t subnet) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SET_IP_CONFIG_CMD, PARAM_NUMS_4); SpiDrv::sendParam((uint8_t*)&validParams, 1, NO_LAST_PARAM); @@ -247,7 +246,7 @@ void WiFiDrv::config(uint8_t validParams, uint32_t local_ip, uint32_t gateway, u void WiFiDrv::setDNS(uint8_t validParams, uint32_t dns_server1, uint32_t dns_server2) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SET_DNS_CONFIG_CMD, PARAM_NUMS_3); SpiDrv::sendParam((uint8_t*)&validParams, 1, NO_LAST_PARAM); @@ -299,10 +298,10 @@ void WiFiDrv::setHostname(const char* hostname) } SpiDrv::spiSlaveDeselect(); } - + int8_t WiFiDrv::disconnect() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(DISCONNECT_CMD, PARAM_NUMS_1); @@ -352,7 +351,7 @@ uint8_t WiFiDrv::getReasonCode() uint8_t WiFiDrv::getConnectionStatus() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_CONN_STATUS_CMD, PARAM_NUMS_0); @@ -374,14 +373,14 @@ uint8_t WiFiDrv::getConnectionStatus() uint8_t* WiFiDrv::getMacAddress() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_MACADDR_CMD, PARAM_NUMS_1); uint8_t _dummy = DUMMY_DATA; SpiDrv::sendParam(&_dummy, 1, LAST_PARAM); - + // pad to multiple of 4 SpiDrv::readChar(); SpiDrv::readChar(); @@ -402,25 +401,58 @@ uint8_t* WiFiDrv::getMacAddress() void WiFiDrv::getIpAddress(IPAddress& ip) { - getNetworkData(_localIp, _subnetMask, _gatewayIp); - ip = _localIp; + getNetworkData(_localIp, _subnetMask, _gatewayIp); + ip = _localIp; } - void WiFiDrv::getSubnetMask(IPAddress& mask) - { - getNetworkData(_localIp, _subnetMask, _gatewayIp); - mask = _subnetMask; - } +void WiFiDrv::getSubnetMask(IPAddress& mask) +{ + getNetworkData(_localIp, _subnetMask, _gatewayIp); + mask = _subnetMask; +} - void WiFiDrv::getGatewayIP(IPAddress& ip) - { - getNetworkData(_localIp, _subnetMask, _gatewayIp); - ip = _gatewayIp; - } +void WiFiDrv::getGatewayIP(IPAddress& ip) +{ + getNetworkData(_localIp, _subnetMask, _gatewayIp); + ip = _gatewayIp; +} + +void WiFiDrv::getDNS(IPAddress& dnsip0, IPAddress& dnsip1) +{ + uint8_t ip0[WL_IPV4_LENGTH] = {0}; + uint8_t ip1[WL_IPV4_LENGTH] = {0}; + + tParam params[PARAM_NUMS_2] = { {0, (char*)ip0}, {0, (char*)ip1}}; + + WAIT_FOR_SLAVE_SELECT(); + + // Send Command + SpiDrv::sendCmd(GET_DNS_CONFIG_CMD, PARAM_NUMS_1); + + uint8_t _dummy = DUMMY_DATA; + SpiDrv::sendParam(&_dummy, sizeof(_dummy), LAST_PARAM); + + // pad to multiple of 4 + SpiDrv::readChar(); + SpiDrv::readChar(); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + SpiDrv::waitResponseParams(GET_DNS_CONFIG_CMD, PARAM_NUMS_2, params); + + SpiDrv::spiSlaveDeselect(); + + dnsip0 = ip0; + dnsip1 = ip1; +} const char* WiFiDrv::getCurrentSSID() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_CURR_SSID_CMD, PARAM_NUMS_1); @@ -450,7 +482,7 @@ const char* WiFiDrv::getCurrentSSID() uint8_t* WiFiDrv::getCurrentBSSID() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_CURR_BSSID_CMD, PARAM_NUMS_1); @@ -478,7 +510,7 @@ uint8_t* WiFiDrv::getCurrentBSSID() int32_t WiFiDrv::getCurrentRSSI() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_CURR_RSSI_CMD, PARAM_NUMS_1); @@ -507,7 +539,7 @@ int32_t WiFiDrv::getCurrentRSSI() uint8_t WiFiDrv::getCurrentEncryptionType() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_CURR_ENCT_CMD, PARAM_NUMS_1); @@ -536,7 +568,7 @@ uint8_t WiFiDrv::getCurrentEncryptionType() int8_t WiFiDrv::startScanNetworks() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(START_SCAN_NETWORKS, PARAM_NUMS_0); @@ -551,10 +583,10 @@ int8_t WiFiDrv::startScanNetworks() uint8_t _dataLen = 0; if (!SpiDrv::waitResponseCmd(START_SCAN_NETWORKS, PARAM_NUMS_1, &_data, &_dataLen)) - { - WARN("error waitResponse"); - _data = WL_FAILURE; - } + { + WARN("error waitResponse"); + _data = WL_FAILURE; + } SpiDrv::spiSlaveDeselect(); @@ -564,7 +596,7 @@ int8_t WiFiDrv::startScanNetworks() uint8_t WiFiDrv::getScanNetworks() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(SCAN_NETWORKS, PARAM_NUMS_0); @@ -585,18 +617,18 @@ uint8_t WiFiDrv::getScanNetworks() const char* WiFiDrv::getSSIDNetoworks(uint8_t networkItem) { - if (networkItem >= WL_NETWORKS_LIST_MAXNUM) - return (char*)NULL; + if (networkItem >= WL_NETWORKS_LIST_MAXNUM) + return (char*)NULL; - return _networkSsid[networkItem]; + return _networkSsid[networkItem]; } uint8_t WiFiDrv::getEncTypeNetowrks(uint8_t networkItem) { - if (networkItem >= WL_NETWORKS_LIST_MAXNUM) - return ENC_TYPE_UNKNOWN; + if (networkItem >= WL_NETWORKS_LIST_MAXNUM) + return ENC_TYPE_UNKNOWN; - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_IDX_ENCT_CMD, PARAM_NUMS_1); @@ -649,7 +681,7 @@ uint8_t* WiFiDrv::getBSSIDNetowrks(uint8_t networkItem, uint8_t* bssid) SpiDrv::spiSlaveDeselect(); - return bssid; + return bssid; } uint8_t WiFiDrv::getChannelNetowrks(uint8_t networkItem) @@ -680,16 +712,16 @@ uint8_t WiFiDrv::getChannelNetowrks(uint8_t networkItem) SpiDrv::spiSlaveDeselect(); - return channel; + return channel; } int32_t WiFiDrv::getRSSINetoworks(uint8_t networkItem) { - if (networkItem >= WL_NETWORKS_LIST_MAXNUM) - return 0; - int32_t networkRssi = 0; + if (networkItem >= WL_NETWORKS_LIST_MAXNUM) + return 0; + int32_t networkRssi = 0; - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_IDX_RSSI_CMD, PARAM_NUMS_1); @@ -711,12 +743,12 @@ int32_t WiFiDrv::getRSSINetoworks(uint8_t networkItem) SpiDrv::spiSlaveDeselect(); - return networkRssi; + return networkRssi; } uint8_t WiFiDrv::reqHostByName(const char* aHostname) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(REQ_HOST_BY_NAME_CMD, PARAM_NUMS_1); @@ -750,11 +782,11 @@ uint8_t WiFiDrv::reqHostByName(const char* aHostname) int WiFiDrv::getHostByName(IPAddress& aResult) { - uint8_t _ipAddr[WL_IPV4_LENGTH]; - IPAddress dummy(0xFF,0xFF,0xFF,0xFF); - int result = 0; + uint8_t _ipAddr[WL_IPV4_LENGTH]; + IPAddress dummy(0xFF,0xFF,0xFF,0xFF); + int result = 0; - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_HOST_BY_NAME_CMD, PARAM_NUMS_0); @@ -769,8 +801,8 @@ int WiFiDrv::getHostByName(IPAddress& aResult) { WARN("error waitResponse"); }else{ - aResult = _ipAddr; - result = (aResult != dummy); + aResult = _ipAddr; + result = (aResult != dummy); } SpiDrv::spiSlaveDeselect(); return result; @@ -778,17 +810,17 @@ int WiFiDrv::getHostByName(IPAddress& aResult) int WiFiDrv::getHostByName(const char* aHostname, IPAddress& aResult) { - if (reqHostByName(aHostname)) - { - return getHostByName(aResult); - }else{ - return 0; - } + if (reqHostByName(aHostname)) + { + return getHostByName(aResult); + }else{ + return 0; + } } const char* WiFiDrv::getFwVersion() { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(GET_FW_VERSION_CMD, PARAM_NUMS_0); @@ -807,6 +839,28 @@ const char* WiFiDrv::getFwVersion() return fwVersion; } +uint32_t WiFiDrv::getFwVersionU32() +{ + WAIT_FOR_SLAVE_SELECT(); + // Send Command + SpiDrv::sendCmd(GET_FW_VERSION_U32_CMD, PARAM_NUMS_0); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t _data[4] = {0,0,0,0}; + uint8_t _dataLen = 0; + if (!SpiDrv::waitResponseCmd(GET_FW_VERSION_U32_CMD, PARAM_NUMS_1, _data, &_dataLen)) + { + WARN("error waitResponse"); + } + SpiDrv::spiSlaveDeselect(); + return _data[0] << 16| _data[1] << 8 | _data[2]; +} + uint32_t WiFiDrv::getTime() { WAIT_FOR_SLAVE_SELECT(); @@ -829,6 +883,36 @@ uint32_t WiFiDrv::getTime() return _data; } +int WiFiDrv::setTime(uint32_t epochTime) +{ + WAIT_FOR_SLAVE_SELECT(); + // Send Command + SpiDrv::sendCmd(SET_TIME_CMD, PARAM_NUMS_1); + SpiDrv::sendParam((uint8_t*)&epochTime, sizeof(epochTime), LAST_PARAM); + + // pad to multiple of 4 + int commandSize = 5 + sizeof(epochTime); + while (commandSize % 4) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t _data = 0; + uint8_t _dataLen = 0; + if (!SpiDrv::waitResponseCmd(SET_TIME_CMD, PARAM_NUMS_1, &_data, &_dataLen)) + { + WARN("error waitResponse"); + } + SpiDrv::spiSlaveDeselect(); + return _data; +} + void WiFiDrv::setPowerMode(uint8_t mode) { WAIT_FOR_SLAVE_SELECT(); @@ -982,7 +1066,7 @@ int16_t WiFiDrv::ping(uint32_t ipAddress, uint8_t ttl) _data = WL_PING_ERROR; } SpiDrv::spiSlaveDeselect(); - return _data; + return _data; } void WiFiDrv::debug(uint8_t on) @@ -1008,7 +1092,7 @@ void WiFiDrv::debug(uint8_t on) uint8_t data = 0; SpiDrv::waitResponseCmd(SET_DEBUG_CMD, PARAM_NUMS_1, &data, &dataLen); - SpiDrv::spiSlaveDeselect(); + SpiDrv::spiSlaveDeselect(); } float WiFiDrv::getTemperature() @@ -1060,6 +1144,38 @@ void WiFiDrv::pinMode(uint8_t pin, uint8_t mode) SpiDrv::spiSlaveDeselect(); } +PinStatus WiFiDrv::digitalRead(uint8_t pin) +{ + WAIT_FOR_SLAVE_SELECT(); + // Send Command + SpiDrv::sendCmd(GET_DIGITAL_READ, PARAM_NUMS_1); + SpiDrv::sendParam((uint8_t*)&pin, 1, LAST_PARAM); + + // pad to multiple of 4 + SpiDrv::readChar(); + SpiDrv::readChar(); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t _data = 0; + uint8_t _dataLen = 0; + if (!SpiDrv::waitResponseCmd(GET_DIGITAL_READ, PARAM_NUMS_1, &_data, &_dataLen)) + { + WARN("error waitResponse"); + _data = WL_FAILURE; + } + SpiDrv::spiSlaveDeselect(); + + if (_data == 1) + return HIGH; + else + return LOW; +} + void WiFiDrv::digitalWrite(uint8_t pin, uint8_t value) { WAIT_FOR_SLAVE_SELECT(); @@ -1087,6 +1203,34 @@ void WiFiDrv::digitalWrite(uint8_t pin, uint8_t value) SpiDrv::spiSlaveDeselect(); } +uint16_t WiFiDrv::analogRead(uint8_t adc_channel) +{ + WAIT_FOR_SLAVE_SELECT(); + // Send Command + SpiDrv::sendCmd(GET_ANALOG_READ, PARAM_NUMS_1); + SpiDrv::sendParam((uint8_t*)&adc_channel, 1, LAST_PARAM); + + // pad to multiple of 4 + SpiDrv::readChar(); + SpiDrv::readChar(); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint16_t adc_raw = 0; + uint8_t adc_raw_len = 0; + if (!SpiDrv::waitResponseCmd(GET_ANALOG_READ, PARAM_NUMS_1, (uint8_t*)&adc_raw, &adc_raw_len)) + { + WARN("error waitResponse"); + } + SpiDrv::spiSlaveDeselect(); + + return adc_raw; +} + void WiFiDrv::analogWrite(uint8_t pin, uint8_t value) { WAIT_FOR_SLAVE_SELECT(); @@ -1116,7 +1260,7 @@ void WiFiDrv::analogWrite(uint8_t pin, uint8_t value) int8_t WiFiDrv::downloadFile(const char* url, uint8_t url_len, const char *filename, uint8_t filename_len) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); // Send Command SpiDrv::sendCmd(DOWNLOAD_FILE, PARAM_NUMS_2); SpiDrv::sendParam((uint8_t*)url, url_len, NO_LAST_PARAM); @@ -1179,7 +1323,7 @@ int8_t WiFiDrv::downloadOTA(const char* url, uint8_t url_len) int8_t WiFiDrv::renameFile(const char * old_file_name, uint8_t const old_file_name_len, const char * new_file_name, uint8_t const new_file_name_len) { - WAIT_FOR_SLAVE_SELECT(); + WAIT_FOR_SLAVE_SELECT(); /* Send Command */ SpiDrv::sendCmd(RENAME_FILE, PARAM_NUMS_2); SpiDrv::sendParam((uint8_t*)old_file_name, old_file_name_len, NO_LAST_PARAM); @@ -1218,16 +1362,20 @@ int8_t WiFiDrv::fileOperation(uint8_t operation, const char *filename, uint8_t f numParams = PARAM_NUMS_4; } - SpiDrv::sendCmd(operation, numParams); - SpiDrv::sendParam((uint8_t*)&offset, sizeof(offset), NO_LAST_PARAM); - SpiDrv::sendParam((uint8_t*)&len, sizeof(len), NO_LAST_PARAM); + SpiDrv::sendCmd(operation, numParams); //send 3 bytes + SpiDrv::sendParam((uint8_t*)&offset, sizeof(offset), NO_LAST_PARAM); //send sizeof(offset) + 1 bytes + SpiDrv::sendParam((uint8_t*)&len, sizeof(len), NO_LAST_PARAM); //send sizeof(len) + 1 bytes SpiDrv::sendParam((uint8_t*)filename, filename_len, (operation == WRITE_FILE) ? NO_LAST_PARAM : LAST_PARAM); + // send filename_len + 1 bytes if operation is WRITE_FILE; send filename_len + 2 bytes otherwise. if (operation == WRITE_FILE) { - SpiDrv::sendParamNoLen((uint8_t*)buffer, len, LAST_PARAM); + SpiDrv::sendParamNoLen((uint8_t*)buffer, len, LAST_PARAM); //send len + 2 bytes if operation is WRITE_FILE } // pad to multiple of 4 - int commandSize = 4 + numParams + sizeof(offset) + sizeof(len) + filename_len; + int commandSize = 7 + sizeof(offset) + sizeof(len) + filename_len; + if (operation == WRITE_FILE) { + commandSize += len + 1; + } while (commandSize % 4) { SpiDrv::readChar(); commandSize++; @@ -1259,4 +1407,422 @@ void WiFiDrv::applyOTA() { } +bool WiFiDrv::prefBegin(const char * name, bool readOnly, const char* partition_label) { + WAIT_FOR_SLAVE_SELECT(); + + // calculated by considering: 1 byte for start_cmd + 1byte CMD + 1 byte param + end command + int commandSize = 4; + bool result = false; + + SpiDrv::sendCmd(PREFERENCES_BEGIN, partition_label!=NULL ? PARAM_NUMS_3 : PARAM_NUMS_2); + SpiDrv::sendParam((uint8_t*)name, strlen(name)); + commandSize += strlen(name) + 1; // number of bytes in name + 1 byte for the length field + + SpiDrv::sendParam((uint8_t*)&readOnly, 1, partition_label!=NULL ? NO_LAST_PARAM : LAST_PARAM); + commandSize += 2; + + if(partition_label!=NULL) { + SpiDrv::sendParam((uint8_t*)partition_label, strlen(partition_label), LAST_PARAM); + commandSize += strlen(partition_label)+1; // number of bytes in partition_label + 1 byte for the length field + } + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t len = 1; + SpiDrv::waitResponseCmd(PREFERENCES_BEGIN, PARAM_NUMS_1, (uint8_t*)&result, &len); + + SpiDrv::spiSlaveDeselect(); + + // if everything went ok the returned value is 0 + return result == 0; +} + +void WiFiDrv::prefEnd() { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(PREFERENCES_END, PARAM_NUMS_0); + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t len = 1; + bool result = false; + SpiDrv::waitResponseCmd(PREFERENCES_END, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); +} + +bool WiFiDrv::prefClear() { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(PREFERENCES_CLEAR, PARAM_NUMS_0); + + SpiDrv::readChar(); + SpiDrv::readChar(); + SpiDrv::readChar(); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + + // Wait for reply + uint8_t len = 1; + bool result = false; + SpiDrv::waitResponseCmd(PREFERENCES_CLEAR, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); + + // if everything went ok the returned value is 0 + return result == 0; +} + +bool WiFiDrv::prefRemove(const char * key) { + WAIT_FOR_SLAVE_SELECT(); + + bool result = false; + int commandSize = 4; + SpiDrv::sendCmd(PREFERENCES_REMOVE, PARAM_NUMS_1); + + SpiDrv::sendParam((uint8_t*)key, strlen(key), LAST_PARAM); + commandSize += strlen(key) + 1; // number of bytes in key + 1 byte for the length field + + // pad to multiple of 4 + while (commandSize % 4) { + SpiDrv::readChar(); + commandSize++; + } + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t len = 1; + SpiDrv::waitResponseCmd(PREFERENCES_REMOVE, PARAM_NUMS_1, (uint8_t*)&result, &len); + + SpiDrv::spiSlaveDeselect(); + return result == 0; +} + +size_t WiFiDrv::prefLen(const char * key) { + WAIT_FOR_SLAVE_SELECT(); + + uint32_t result = 0; + int commandSize = 4; + SpiDrv::sendCmd(PREFERENCES_LEN, PARAM_NUMS_1); + + SpiDrv::sendParam((uint8_t*)key, strlen(key), LAST_PARAM); + commandSize += strlen(key) + 1; // number of bytes in key + 1 byte for the length field + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // Wait for reply + uint8_t len = 4; + SpiDrv::waitResponseCmd(PREFERENCES_LEN, PARAM_NUMS_1, (uint8_t*)&result, &len); + + SpiDrv::spiSlaveDeselect(); + + // if len == 1 it means that the command returned and error, in result the error code, + // as of now 255 may be returned if the number of parameters passed is wrong + return len == 1? 0 : result; +} + +size_t WiFiDrv::prefStat() { + WAIT_FOR_SLAVE_SELECT(); + + SpiDrv::sendCmd(PREFERENCES_STAT, PARAM_NUMS_0); + + SpiDrv::readChar(); + SpiDrv::readChar(); + SpiDrv::readChar(); + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + size_t result = 0; + uint8_t len = 4; + SpiDrv::waitResponseCmd(PREFERENCES_STAT, PARAM_NUMS_1, (uint8_t*)&result, &len); + SpiDrv::spiSlaveDeselect(); + + return result; +} + +size_t WiFiDrv::prefPut(const char * key, PreferenceType type, const uint8_t value[], size_t len) { + WAIT_FOR_SLAVE_SELECT(); + + int commandSize = 4; + SpiDrv::sendCmd(PREFERENCES_PUT, PARAM_NUMS_3); + + SpiDrv::sendParam((uint8_t*)key, strlen(key)); + commandSize += strlen(key) + 1; // number of bytes in key + 1 byte for the length field + + SpiDrv::sendParam((uint8_t*)&type, 1); + commandSize += 2; + + SpiDrv::sendBuffer(value, len, LAST_PARAM); + commandSize += len + 1; + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + uint8_t res_len = 1; + uint32_t res = 0; + SpiDrv::waitResponseCmd(PREFERENCES_PUT, PARAM_NUMS_1, (uint8_t*)&res, &res_len); + SpiDrv::spiSlaveDeselect(); + + // if len == 1 it means that the command returned and error, in result the error code, + // as of now 255 may be returned if the number of parameters passed is wrong + // and 254 if the type passed as parameter is wrong + return res_len == 1? 0 : res; +} + +size_t WiFiDrv::prefGet(const char * key, PreferenceType type, uint8_t value[], size_t len) { + WAIT_FOR_SLAVE_SELECT(); + + int commandSize = 4; + SpiDrv::sendCmd(PREFERENCES_GET, PARAM_NUMS_2); + + SpiDrv::sendParam((uint8_t*)key, strlen(key)); + commandSize += strlen(key) + 1; // number of bytes in key + 1 byte for the length field + + SpiDrv::sendParam((uint8_t*)&type, 1, LAST_PARAM); + commandSize += 2; + + // pad to multiple of 4 + while (commandSize % 4 != 0) { + SpiDrv::readChar(); + commandSize++; + } + + SpiDrv::spiSlaveDeselect(); + //Wait the reply elaboration + SpiDrv::waitForSlaveReady(); + SpiDrv::spiSlaveSelect(); + + // we need to account for \0 if it is a string + size_t res_len = type == PT_STR? len-1 : len; + SpiDrv::waitResponseData16(PREFERENCES_GET, value, (uint16_t*)&res_len); + + SpiDrv::spiSlaveDeselect(); + + // if res_len == 0 it means that the command returned and error + if(res_len == 0) { + return 0; + } + + // fix endianness + if(type != PT_STR && type != PT_BLOB) { + for(uint8_t i=0; i -#include "utility/wifi_spi.h" +#include "wifi_spi.h" #include "IPAddress.h" #include "WiFiUdp.h" #include "WiFiClient.h" +#include "WiFiPreferences.h" // Key index length #define KEY_IDX_LEN 1 -// 100 msecs of delay to have the connection established +// 100 ms of delay to have the connection established #define WL_DELAY_START_CONNECTION 100 // firmware version string length #define WL_FW_VER_LENGTH 6 @@ -78,10 +79,10 @@ public: * Set the desired network which the connection manager should try to * connect to. * - * The ssid of the desired network should be specified. + * The SSID of the desired network should be specified. * - * param ssid: The ssid of the desired network. - * param ssid_len: Length of ssid string. + * param ssid: The SSID of the desired network. + * param ssid_len: Length of SSID string. * return: WL_SUCCESS or WL_FAILURE */ static int8_t wifiSetNetwork(const char* ssid, uint8_t ssid_len); @@ -90,7 +91,7 @@ public: * the most secure supported mode will be automatically selected * * param ssid: Pointer to the SSID string. - * param ssid_len: Length of ssid string. + * param ssid_len: Length of SSID string. * param passphrase: Passphrase. Valid characters in a passphrase * must be between ASCII 32-126 (decimal). * param len: Length of passphrase string. @@ -103,7 +104,7 @@ public: * is determined by the size of the key (5 bytes for WEP-40, 13 bytes for WEP-104). * * param ssid: Pointer to the SSID string. - * param ssid_len: Length of ssid string. + * param ssid_len: Length of SSID string. * param key_idx: The key index to set. Valid values are 0-3. * param key: Key input buffer. * param len: Length of key string. @@ -114,18 +115,18 @@ public: static int8_t wifiSetApNetwork(const char* ssid, uint8_t ssid_len); static int8_t wifiSetApPassphrase(const char* ssid, uint8_t ssid_len, const char *passphrase, const uint8_t len); - /* Set ip configuration disabling dhcp client + /* Set IP configuration disabling DHCP client * * param validParams: set the number of parameters that we want to change - * i.e. validParams = 1 means that we'll change only ip address - * validParams = 3 means that we'll change ip address, gateway and netmask - * param local_ip: Static ip configuration + * i.e. validParams = 1 means that we'll change only IP address + * validParams = 3 means that we'll change IP address, gateway and netmask + * param local_ip: Static IP configuration * param gateway: Static gateway configuration * param subnet: Static subnet mask configuration */ static void config(uint8_t validParams, uint32_t local_ip, uint32_t gateway, uint32_t subnet); - /* Set DNS ip configuration + /* Set DNS IP configuration * * param validParams: set the number of parameters that we want to change * i.e. validParams = 1 means that we'll change only dns_server1 @@ -163,7 +164,7 @@ public: /* * Get the interface IP address. * - * return: copy the ip address value in IPAddress object + * return: copy the IP address value in IPAddress object */ static void getIpAddress(IPAddress& ip); @@ -175,16 +176,23 @@ public: static void getSubnetMask(IPAddress& mask); /* - * Get the gateway ip address. + * Get the gateway IP address. * - * return: copy the gateway ip address value in IPAddress object + * return: copy the gateway IP address value in IPAddress object */ static void getGatewayIP(IPAddress& ip); + /* + * Get the DNS servers IP addresses. + * + * return: copy the DNS servers IP addresses into IPAddress objects + */ + static void getDNS(IPAddress& dnsip0, IPAddress& dnsip1); + /* * Return the current SSID associated with the network * - * return: ssid string + * return: SSID string */ static const char* getCurrentSSID(); @@ -230,7 +238,7 @@ public: * * param networkItem: specify from which network item want to get the information * - * return: ssid string of the specified item on the networks scanned list + * return: SSID string of the specified item on the networks scanned list */ static const char* getSSIDNetoworks(uint8_t networkItem); @@ -270,8 +278,10 @@ public: * result: version as string with this format a.b.c */ static const char* getFwVersion(); + static uint32_t getFwVersionU32(); static uint32_t getTime(); + static int setTime(uint32_t epochTime); static void setPowerMode(uint8_t mode); @@ -290,7 +300,9 @@ public: static void debug(uint8_t on); static float getTemperature(); static void pinMode(uint8_t pin, uint8_t mode); + static PinStatus digitalRead(uint8_t pin); static void digitalWrite(uint8_t pin, uint8_t value); + static uint16_t analogRead(uint8_t adc_channel); static void analogWrite(uint8_t pin, uint8_t value); static int8_t downloadFile(const char* url, uint8_t url_len, const char *filename, uint8_t filename_len); @@ -315,6 +327,23 @@ public: return length >= 0; }; + static bool prefBegin(const char * name, bool readOnly=false, const char* partition_label=NULL); + static void prefEnd(); + static bool prefClear(); + static bool prefRemove(const char * key); + static size_t prefLen(const char * key); + static size_t prefStat(); + static size_t prefPut(const char * key, PreferenceType type, const uint8_t value[], size_t len); + static size_t prefGet(const char * key, PreferenceType type, uint8_t value[], size_t len); + static PreferenceType prefGetType(const char * key); + + static int bleBegin(); + static void bleEnd(); + static int bleAvailable(); + static int bleRead(uint8_t data[], size_t length); + static int blePeek(uint8_t data[], size_t length); + static size_t bleWrite(const uint8_t* data, size_t length); + static void applyOTA(); friend class WiFiUDP; diff --git a/arduino/libraries/WiFiNINA/src/utility/wifi_spi.h b/arduino/libraries/WiFiNINA/src/utility/wifi_spi.h index 0dc4681..3f9f52d 100644 --- a/arduino/libraries/WiFiNINA/src/utility/wifi_spi.h +++ b/arduino/libraries/WiFiNINA/src/utility/wifi_spi.h @@ -1,5 +1,5 @@ /* - wifi_spi.h - Library for Arduino Wifi shield. + wifi_spi.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. @@ -24,180 +24,123 @@ #include #include "utility/wl_definitions.h" -#define CMD_FLAG 0 -#define REPLY_FLAG 1<<7 -#define DATA_FLAG 0x40 - -#define WIFI_SPI_ACK 1 -#define WIFI_SPI_ERR 0xFF - -#define TIMEOUT_CHAR 1000 - -//#define MAX_SOCK_NUM 4 /**< Maxmium number of socket */ +//#define MAX_SOCK_NUM 4 /**< Maximum number of socket */ #define NO_SOCKET_AVAIL 255 -#define START_CMD 0xE0 -#define END_CMD 0xEE -#define ERR_CMD 0xEF -#define CMD_POS 1 // Position of Command OpCode on SPI stream -#define PARAM_LEN_POS 2 // Position of Param len on SPI stream - - enum { - SET_NET_CMD = 0x10, - SET_PASSPHRASE_CMD = 0x11, - SET_KEY_CMD = 0x12, -// TEST_CMD = 0x13, - SET_IP_CONFIG_CMD = 0x14, - SET_DNS_CONFIG_CMD = 0x15, - SET_HOSTNAME_CMD = 0x16, - SET_POWER_MODE_CMD = 0x17, - SET_AP_NET_CMD = 0x18, - SET_AP_PASSPHRASE_CMD = 0x19, - SET_DEBUG_CMD = 0x1A, - GET_TEMPERATURE_CMD = 0x1B, - GET_REASON_CODE_CMD = 0x1F, + SET_NET_CMD = 0x10, + SET_PASSPHRASE_CMD = 0x11, + SET_KEY_CMD = 0x12, +// TEST_CMD = 0x13, + SET_IP_CONFIG_CMD = 0x14, + SET_DNS_CONFIG_CMD = 0x15, + SET_HOSTNAME_CMD = 0x16, + SET_POWER_MODE_CMD = 0x17, + SET_AP_NET_CMD = 0x18, + SET_AP_PASSPHRASE_CMD = 0x19, + SET_DEBUG_CMD = 0x1A, + GET_TEMPERATURE_CMD = 0x1B, + GET_DNS_CONFIG_CMD = 0x1E, + GET_REASON_CODE_CMD = 0x1F, - GET_CONN_STATUS_CMD = 0x20, - GET_IPADDR_CMD = 0x21, - GET_MACADDR_CMD = 0x22, - GET_CURR_SSID_CMD = 0x23, - GET_CURR_BSSID_CMD = 0x24, - GET_CURR_RSSI_CMD = 0x25, - GET_CURR_ENCT_CMD = 0x26, - SCAN_NETWORKS = 0x27, - START_SERVER_TCP_CMD= 0x28, - GET_STATE_TCP_CMD = 0x29, - DATA_SENT_TCP_CMD = 0x2A, - AVAIL_DATA_TCP_CMD = 0x2B, - GET_DATA_TCP_CMD = 0x2C, - START_CLIENT_TCP_CMD= 0x2D, - STOP_CLIENT_TCP_CMD = 0x2E, - GET_CLIENT_STATE_TCP_CMD= 0x2F, - DISCONNECT_CMD = 0x30, -// GET_IDX_SSID_CMD = 0x31, - GET_IDX_RSSI_CMD = 0x32, - GET_IDX_ENCT_CMD = 0x33, - REQ_HOST_BY_NAME_CMD= 0x34, - GET_HOST_BY_NAME_CMD= 0x35, - START_SCAN_NETWORKS = 0x36, - GET_FW_VERSION_CMD = 0x37, -// GET_TEST_CMD = 0x38, - SEND_DATA_UDP_CMD = 0x39, - GET_REMOTE_DATA_CMD = 0x3A, - GET_TIME_CMD = 0x3B, - GET_IDX_BSSID = 0x3C, - GET_IDX_CHANNEL_CMD = 0x3D, - PING_CMD = 0x3E, - GET_SOCKET_CMD = 0x3F, + GET_CONN_STATUS_CMD = 0x20, + GET_IPADDR_CMD = 0x21, + GET_MACADDR_CMD = 0x22, + GET_CURR_SSID_CMD = 0x23, + GET_CURR_BSSID_CMD = 0x24, + GET_CURR_RSSI_CMD = 0x25, + GET_CURR_ENCT_CMD = 0x26, + SCAN_NETWORKS = 0x27, + START_SERVER_TCP_CMD = 0x28, + GET_STATE_TCP_CMD = 0x29, + DATA_SENT_TCP_CMD = 0x2A, + AVAIL_DATA_TCP_CMD = 0x2B, + GET_DATA_TCP_CMD = 0x2C, + START_CLIENT_TCP_CMD = 0x2D, + STOP_CLIENT_TCP_CMD = 0x2E, + GET_CLIENT_STATE_TCP_CMD = 0x2F, + DISCONNECT_CMD = 0x30, +// GET_IDX_SSID_CMD = 0x31, + GET_IDX_RSSI_CMD = 0x32, + GET_IDX_ENCT_CMD = 0x33, + REQ_HOST_BY_NAME_CMD = 0x34, + GET_HOST_BY_NAME_CMD = 0x35, + START_SCAN_NETWORKS = 0x36, + GET_FW_VERSION_CMD = 0x37, + STOP_SERVER_TCP_CMD = 0x38, + SEND_DATA_UDP_CMD = 0x39, + GET_REMOTE_DATA_CMD = 0x3A, + GET_TIME_CMD = 0x3B, + GET_IDX_BSSID = 0x3C, + GET_IDX_CHANNEL_CMD = 0x3D, + PING_CMD = 0x3E, + GET_SOCKET_CMD = 0x3F, // All command with DATA_FLAG 0x40 send a 16bit Len - SET_ENT_CMD = 0x40, + SET_ENT_CMD = 0x40, - SEND_DATA_TCP_CMD = 0x44, - GET_DATABUF_TCP_CMD = 0x45, - INSERT_DATABUF_CMD = 0x46, + SEND_DATA_TCP_CMD = 0x44, + GET_DATABUF_TCP_CMD = 0x45, + INSERT_DATABUF_CMD = 0x46, + + BLE_BEGIN = 0x4A, + BLE_END = 0x4B, + BLE_AVAILABLE = 0x4C, + BLE_PEEK = 0x4D, + BLE_READ = 0x4E, + BLE_WRITE = 0x4F, // regular format commands - SET_PIN_MODE = 0x50, - SET_DIGITAL_WRITE = 0x51, - SET_ANALOG_WRITE = 0x52, + SET_PIN_MODE = 0x50, + SET_DIGITAL_WRITE = 0x51, + SET_ANALOG_WRITE = 0x52, + GET_DIGITAL_READ = 0x53, + GET_ANALOG_READ = 0x54, + + // Prefrences api + PREFERENCES_BEGIN = 0x55, + PREFERENCES_END = 0x56, + PREFERENCES_CLEAR = 0x57, + PREFERENCES_REMOVE = 0x58, + PREFERENCES_LEN = 0x59, + PREFERENCES_STAT = 0x5A, + PREFERENCES_PUT = 0x5B, + PREFERENCES_GET = 0x5C, + PREFERENCES_GETTYPE = 0x5D, // regular format commands - WRITE_FILE = 0x60, - READ_FILE = 0x61, - DELETE_FILE = 0x62, - EXISTS_FILE = 0x63, - DOWNLOAD_FILE = 0x64, - APPLY_OTA_COMMAND = 0x65, - RENAME_FILE = 0x66, - DOWNLOAD_OTA = 0x67, + GET_FW_VERSION_U32_CMD = 0x5E, + SET_TIME_CMD = 0x5F, + + // regular format commands + WRITE_FILE = 0x60, + READ_FILE = 0x61, + DELETE_FILE = 0x62, + EXISTS_FILE = 0x63, + DOWNLOAD_FILE = 0x64, + APPLY_OTA_COMMAND = 0x65, + RENAME_FILE = 0x66, + DOWNLOAD_OTA = 0x67, + BRSSL_SET_EC_TA = 0x68, + BRSSL_ERROR_CODE = 0x69, + + // Low-level BSD-like sockets functions. + // From 0x70 to 0x7F }; enum wl_tcp_state { - CLOSED = 0, - LISTEN = 1, - SYN_SENT = 2, - SYN_RCVD = 3, - ESTABLISHED = 4, - FIN_WAIT_1 = 5, - FIN_WAIT_2 = 6, - CLOSE_WAIT = 7, - CLOSING = 8, - LAST_ACK = 9, - TIME_WAIT = 10 + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 }; - -enum numParams{ - PARAM_NUMS_0, - PARAM_NUMS_1, - PARAM_NUMS_2, - PARAM_NUMS_3, - PARAM_NUMS_4, - PARAM_NUMS_5, - PARAM_NUMS_6, - MAX_PARAM_NUMS -}; - -#define MAX_PARAMS MAX_PARAM_NUMS-1 -#define PARAM_LEN_SIZE 1 - -typedef struct __attribute__((__packed__)) -{ - uint8_t paramLen; - char* param; -}tParam; - -typedef struct __attribute__((__packed__)) -{ - uint16_t dataLen; - char* data; -}tDataParam; - - -typedef struct __attribute__((__packed__)) -{ - unsigned char cmd; - unsigned char tcmd; - unsigned char nParam; - tParam params[MAX_PARAMS]; -}tSpiMsg; - -typedef struct __attribute__((__packed__)) -{ - unsigned char cmd; - unsigned char tcmd; - unsigned char nParam; - tDataParam params[MAX_PARAMS]; -}tSpiMsgData; - - -typedef struct __attribute__((__packed__)) -{ - unsigned char cmd; - unsigned char tcmd; - //unsigned char totLen; - unsigned char nParam; -}tSpiHdr; - -typedef struct __attribute__((__packed__)) -{ - uint8_t paramLen; - uint32_t param; -}tLongParam; - -typedef struct __attribute__((__packed__)) -{ - uint8_t paramLen; - uint16_t param; -}tIntParam; - -typedef struct __attribute__((__packed__)) -{ - uint8_t paramLen; - uint8_t param; -}tByteParam; - #endif diff --git a/arduino/libraries/WiFiNINA/src/utility/wl_definitions.h b/arduino/libraries/WiFiNINA/src/utility/wl_definitions.h index dc14045..8e60133 100644 --- a/arduino/libraries/WiFiNINA/src/utility/wl_definitions.h +++ b/arduino/libraries/WiFiNINA/src/utility/wl_definitions.h @@ -1,5 +1,5 @@ /* - wl_definitions.h - Library for Arduino Wifi shield. + wl_definitions.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. @@ -39,7 +39,7 @@ #define WL_IPV4_LENGTH 4 // Maximum size of a SSID list #define WL_NETWORKS_LIST_MAXNUM 10 -// Maxmium number of socket +// Maximum number of socket #define WIFI_MAX_SOCK_NUM 10 // Socket not available constant #define SOCK_NOT_AVAIL 255 diff --git a/arduino/libraries/WiFiNINA/src/utility/wl_types.h b/arduino/libraries/WiFiNINA/src/utility/wl_types.h index ff0187e..03af271 100644 --- a/arduino/libraries/WiFiNINA/src/utility/wl_types.h +++ b/arduino/libraries/WiFiNINA/src/utility/wl_types.h @@ -1,5 +1,5 @@ /* - wl_types.h - Library for Arduino Wifi shield. + wl_types.h - Library for Arduino WiFi shield. Copyright (c) 2018 Arduino SA. All rights reserved. Copyright (c) 2011-2014 Arduino. All right reserved. diff --git a/arduino/strix/API.md b/arduino/strix/API.md index 039c7f6..99a610d 100644 --- a/arduino/strix/API.md +++ b/arduino/strix/API.md @@ -10,10 +10,12 @@ The firmware speaks a line protocol over the board's USB serial port (115200 bau | Command | Reply | |---|---| -| `id` | Device, firmware and FPGA version, generator and ring counts, and the reset cause of the last boot (`reset_cause`: SAMD21 PM->RCAUSE; 1 power-on, 4 brown-out, 16 reset pin, 64 software) | +| `id` | Device, firmware and FPGA version, generator and ring counts, whether external power was present at boot (`external_power`, from the battery charger chip), and the reset cause of the last boot (`reset_cause`: SAMD21 PM->RCAUSE; 1 power-on, 4 brown-out, 16 reset pin, 64 software) | | `wifi` | WiFi status: module probed, access point running, module firmware, SSID, IP | | `wifi probe` | Releases the WiFi module from reset and queries it, without starting the radio | -| `wifi start` | Starts the access point and the HTTP server | +| `wifi start` | Starts the access point and the HTTP server (refused while BLE runs) | +| `ble` | BLE status: advertising, address, connected | +| `ble start` | Starts BLE (refused while WiFi runs) | | `entropy [hex\|bin]` | `n` (1–65536) entropy bytes from the stream source. `hex` (default): one line of hex. `bin`: a line `BIN `, then `n` raw bytes. Refused if the stream source is not healthy. If the source stops delivering for 2 s, hex ends with an error line and binary ends early. | | `health` | Per generator: healthy, active, swept, test failures and event counts, dead rings. Stream FIFO level and dropped bytes. | | `health clear` | Clears latched test failures and event counters | @@ -41,12 +43,13 @@ The firmware speaks a line protocol over the board's USB serial port (115200 bau | `amp_sub_ms` | 1–255 | Sub-trial length | | `amp_subtrials` | 1–15 | Sub-trials per amplifier trial | | `amp_vn` | 0/1 | Von Neumann extractor on the amplifier path | +| `radio_generators` | 0–15 | Generators allowed while WiFi or BLE runs (default 3: generators 0 and 1). Starting a radio disables the others, and `generators` is limited to this mask while a radio runs. Radio transmission plus all ring oscillators can draw more than the board's supply delivers. | Changing an `amp_*` setting restarts the amplifier. ## WiFi -Without a USB host (wall charger or battery), the device starts WiFi by itself: an access point named `Strix` (WPA2, password `owl12345678`) at `192.168.4.1`, with an HTTP server on port 80. With a computer attached, WiFi stays off until `wifi start`. On weak USB power the radio's current bursts plus the ring oscillators can reset the board. +Without a USB host and on external power (wall charger), the device starts WiFi by itself: an access point named `Strix` (WPA2, password `owl12345678`) at `192.168.4.1`, with an HTTP server on port 80. With a computer attached, WiFi stays off until `wifi start`. On weak USB power the radio's current bursts plus the ring oscillators can reset the board. Until WiFi is started, the FPGA holds the WiFi module in reset. @@ -60,6 +63,20 @@ Until WiFi is started, the FPGA holds the WiFi module in reset. Replies have status 200 (errors are JSON `{"error":...}`), or 404 for unknown paths, and allow cross-origin requests. `watch` is USB-only. +## Bluetooth LE + +On battery (no external power and no USB host) the device advertises as `STRIX` over Bluetooth LE instead of starting WiFi. With a computer attached, `ble start` starts it; WiFi and BLE do not run at the same time. BLE needs NINA module firmware 3.0.0 or newer. + +Service `53545249-5800-4000-8000-000000000001` with three characteristics: + +| Characteristic | UUID suffix | Properties | Use | +|---|---|---|---| +| command | `…0002` | write | One USB command per write (text, up to 128 bytes) | +| reply | `…0003` | notify | The command's reply in chunks of up to 20 bytes. Every reply ends with a newline, so concatenate notifications until `\n`. | +| entropy | `…0004` | read, notify | Each read returns 64 fresh bytes from the entropy stream. Subscribers receive new bytes about every 50 ms (each notification carries up to the connection's ATT payload, 20 bytes by default). An empty value means the stream had no fresh bytes; bytes are never sent twice. | + +`tools/ble_check.py` exercises all three from a computer with Bluetooth. + ## Example ``` diff --git a/arduino/strix/strix.ino b/arduino/strix/strix.ino index c63e1c8..1ad6b0d 100644 --- a/arduino/strix/strix.ino +++ b/arduino/strix/strix.ino @@ -1,20 +1,23 @@ // STRIX firmware for the Arduino MKR Vidor 4000: boots the STRIX FPGA image and serves -// entropy, health, live statistics and bias amplifier trials over USB serial and over HTTP on -// its own WiFi access point. +// entropy, health, live statistics and bias amplifier trials over USB serial, over HTTP on its +// own WiFi access point, and over Bluetooth LE. // // USB protocol: one command per line; replies are single JSON lines, except `entropy`, which -// replies with hex text or a "BIN " header followed by n raw bytes. HTTP serves the same -// commands as URLs. See API.md. +// replies with hex text or a "BIN " header followed by n raw bytes. HTTP and BLE carry the +// same commands. See API.md. +#include #include #include +#include using namespace strix; -const char FIRMWARE_VERSION[] = "0.2.0"; +const char FIRMWARE_VERSION[] = "0.3.0"; const unsigned long POLL_MS = 10; const unsigned long ENTROPY_TIMEOUT_MS = 2000; const unsigned long USB_HOST_WAIT_MS = 1500; +const unsigned long ENTROPY_NOTIFY_MS = 50; // BLE entropy stream interval const size_t MAX_ENTROPY_BYTES = 65536; const size_t CHUNK_BYTES = 256; const uint8_t MAX_ROLLING = 50; @@ -31,6 +34,21 @@ bool binaryHeader = true; WiFiServer httpServer(80); bool wifiProbed = false; // the NINA module has been reset and queried bool wifiReady = false; // access point and HTTP server running +bool bleReady = false; // BLE service advertised +bool externalPower = true; // charger or USB power present at boot (from the charger chip) +// Generators allowed while a radio runs: radio transmission plus all ring oscillators can draw +// more than the board's supply delivers. Two generators still far exceed what WiFi or BLE carry. +uint8_t radioGenerators = 0x03; + +bool radioActive() { return wifiReady || bleReady; } + +// BLE: commands are written to `command` as text lines; replies arrive as notifications on `reply` +// in chunks, each reply ending with a newline. `entropy` holds fresh bytes for every read and +// streams them to subscribers. +BLEService bleService("53545249-5800-4000-8000-000000000001"); +BLECharacteristic bleCommand("53545249-5800-4000-8000-000000000002", BLEWrite | BLEWriteWithoutResponse, 128); +BLECharacteristic bleReply("53545249-5800-4000-8000-000000000003", BLENotify, 20); +BLECharacteristic bleEntropy("53545249-5800-4000-8000-000000000004", BLERead | BLENotify, 64); StrixFPGA fpga; bool fpgaReady = false; @@ -177,6 +195,7 @@ void restartAmplifier() { void printStats(); void printAmp(); void printWifi(); +void printBle(); void pollStats() { uint8_t raw[STATS_SNAPSHOT_BYTES]; @@ -252,6 +271,8 @@ void printId() { out->print(fpgaReady ? fpga.read(REG_RINGS) : 0); out->print(",\"reset_cause\":"); out->print(resetCause); + out->print(",\"external_power\":"); + printBool(externalPower); out->println('}'); } @@ -455,6 +476,8 @@ void printConfig() { out->print(fpga.read(REG_AMP_SUBS)); out->print(",\"amp_vn\":"); out->print(fpga.read(REG_AMP_CTRL) & AMP_VON_NEUMANN); + out->print(",\"radio_generators\":"); + out->print(radioGenerators); out->println('}'); } @@ -465,7 +488,10 @@ void setConfig(const String &key, const String &value) { if (key == "temporal" && inRange(number, 1, 8)) { fpga.write(REG_TEMPORAL, number); } else if (key == "generators" && inRange(number, 0, 15)) { - fpga.write(REG_GEN_ENABLE, number); + fpga.write(REG_GEN_ENABLE, radioActive() ? number & radioGenerators : number); + } else if (key == "radio_generators" && inRange(number, 0, 15)) { + radioGenerators = number; + if (radioActive()) fpga.write(REG_GEN_ENABLE, fpga.read(REG_GEN_ENABLE) & radioGenerators); } else if (key == "trial_ms" && inRange(number, 1, 65535)) { fpga.write(REG_TRIAL_HIGH, number >> 8); fpga.write(REG_TRIAL_LOW, number & 0xFF); @@ -547,8 +573,23 @@ void handleCommand(String line) { } else if (command == "wifi") { // The NINA module is reached through the FPGA's pin pass-through, so the FPGA must run. if (first == "probe") probeWifi(); - if (first == "start") startWifi(); + if (first == "start") { + if (bleReady) { + printError("BLE is running"); + return; + } + startWifi(); + } printWifi(); + } else if (command == "ble") { + if (first == "start") { + if (wifiReady) { + printError("WiFi is running"); + return; + } + startBle(); + } + printBle(); } else if (command == "health") { if (first == "clear") { fpga.write(REG_HEALTH_CTRL, HEALTH_CLEAR); @@ -713,20 +754,133 @@ void serveHttp() { client.stop(); } +// Releases the NINA module from reset and runs `begin`, which resets it through the library. +// GPIO0 is driven by the SAMD21 during that reset (boot mode) and by the running module +// afterwards (its data-ready signal), so its direction in the FPGA follows. +void beginModule(void (*begin)()) { + fpga.write(REG_NINA_CTRL, NINA_ENABLE); + begin(); + fpga.write(REG_NINA_CTRL, NINA_ENABLE | NINA_GPIO0_FROM_MODULE); +} + // Resets the NINA module and checks that it answers, without starting the radio. void probeWifi() { - fpga.write(REG_NINA_CTRL, 1); // the FPGA holds the module in reset until now - WiFi.status(); + if (!wifiProbed) beginModule([] { WiFi.status(); }); wifiProbed = true; } +// Limits the running generators to the radio budget before a radio starts. +void applyRadioBudget() { fpga.write(REG_GEN_ENABLE, fpga.read(REG_GEN_ENABLE) & radioGenerators); } + void startWifi() { + applyRadioBudget(); probeWifi(); if (WiFi.status() == WL_NO_MODULE) return; wifiReady = WiFi.beginAP(WIFI_SSID, WIFI_PASSWORD) == WL_AP_LISTENING; if (wifiReady) httpServer.begin(); } +// Bluetooth LE + +// Sends a reply as BLE notifications of up to 20 bytes (the default ATT payload). +class BleReply : public Print { + public: + size_t write(uint8_t value) override { + buffer_[length_++] = value; + if (length_ == sizeof(buffer_)) flush(); + return 1; + } + + void flush() override { + if (length_) bleReply.writeValue(buffer_, length_); + length_ = 0; + } + + private: + uint8_t buffer_[20]; + size_t length_ = 0; +}; + +// Puts 64 fresh stream bytes into the entropy characteristic (which also notifies subscribers). +// If the stream has too few, a refill after a read empties the value, so a read never returns +// bytes that were already handed out; a streaming refill just waits. +void refillBleEntropy(bool afterRead) { + uint8_t bytes[64]; + uint16_t available = (uint16_t)fpga.read(REG_LEVEL_HIGH) << 8 | fpga.read(REG_LEVEL_LOW); + if (available < sizeof(bytes)) { + if (afterRead) bleEntropy.writeValue(bytes, 0); + return; + } + fpga.readBurst(REG_STREAM, bytes, sizeof(bytes)); + bleEntropy.writeValue(bytes, sizeof(bytes)); +} + +void onBleEntropyRead(BLEDevice, BLECharacteristic) { refillBleEntropy(true); } + +void startBle() { + if (bleReady) return; + bool started = false; + applyRadioBudget(); + beginModule([] {}); + started = BLE.begin(); + wifiProbed = true; // the module has been reset either way + if (!started) return; + BLE.setLocalName("STRIX"); + BLE.setDeviceName("STRIX"); + BLE.setAdvertisedService(bleService); + bleService.addCharacteristic(bleCommand); + bleService.addCharacteristic(bleReply); + bleService.addCharacteristic(bleEntropy); + BLE.addService(bleService); + bleEntropy.setEventHandler(BLERead, onBleEntropyRead); + refillBleEntropy(true); + BLE.advertise(); + bleReady = true; +} + +void printBle() { + out->print("{\"ble\":"); + printBool(bleReady); + if (bleReady) { + out->print(",\"address\":\""); + out->print(BLE.address()); + out->print("\",\"connected\":"); + printBool(BLE.connected()); + } + out->println('}'); +} + +void serveBle() { + BLE.poll(); + if (bleCommand.written()) { + String command((const char *)bleCommand.value(), bleCommand.valueLength()); + BleReply reply; + out = &reply; + binaryHeader = true; + handleCommand(command); + out = &Serial; + reply.flush(); + } + // Stream entropy to subscribers, one notification per interval. + static unsigned long lastNotify = 0; + if (bleEntropy.subscribed() && millis() - lastNotify >= ENTROPY_NOTIFY_MS) { + lastNotify = millis(); + refillBleEntropy(false); + } +} + +// Power source at boot, from the battery charger chip (BQ24195, I2C 0x6B, register 0x08 bit 2 +// "power good": an input supply is present). Must run before the FPGA link takes over SERCOM0. +bool readExternalPower() { + Wire.begin(); + Wire.beginTransmission(0x6B); + Wire.write(0x08); + bool ok = Wire.endTransmission(false) == 0 && Wire.requestFrom(0x6B, 1) == 1; + uint8_t status = ok ? Wire.read() : 0x04; // unknown: assume external power + Wire.end(); + return status & 0x04; +} + // Main char lineBuffer[128]; @@ -759,19 +913,32 @@ bool usbHostPresent() { void setup() { resetCause = PM->RCAUSE.reg; + externalPower = readExternalPower(); Serial.begin(115200); // no wait for a host: the device also runs on battery pinMode(LED_BUILTIN, OUTPUT); fpgaReady = fpga.begin(); if (fpgaReady) restartStats(); - // Stationary mode: without a USB host (wall charger or battery) WiFi starts by itself. With a - // computer attached it stays off, since radio bursts plus the ring oscillators can brown out - // the board on weak USB power; `wifi start` still starts it. - if (fpgaReady && !usbHostPresent()) startWifi(); + // Radio mode: with a computer attached, USB only (radio bursts plus the ring oscillators can + // brown out the board on weak USB power; `wifi start` or `ble start` still work). Otherwise + // WiFi on external power (stationary) and BLE on battery (mobile). + // Builds with -DSTRIX_PREFER_BLE use BLE on external power too (for testing BLE on a charger). + if (fpgaReady && !usbHostPresent()) { +#ifdef STRIX_PREFER_BLE + if (false) { +#else + if (externalPower) { +#endif + startWifi(); + } else { + startBle(); + } + } } void loop() { readSerial(); if (wifiReady) serveHttp(); + if (bleReady) serveBle(); static unsigned long lastPoll = 0; if (fpgaReady && millis() - lastPoll >= POLL_MS) { lastPoll = millis(); diff --git a/arduino/strix_nina_passthrough/strix_nina_passthrough.ino b/arduino/strix_nina_passthrough/strix_nina_passthrough.ino new file mode 100644 index 0000000..b1d00c9 --- /dev/null +++ b/arduino/strix_nina_passthrough/strix_nina_passthrough.ino @@ -0,0 +1,57 @@ +// STRIX NINA passthrough: connects the USB serial port to the NINA-W102 WiFi module's UART so +// esptool can flash its firmware. The STRIX FPGA image passes the module's UART (D14 -> RX, +// D13 <- TX), GPIO0 (D6) and RESETN (D7) through to the SAMD21 without inversion; asserted USB +// RTS holds the module in reset and asserted DTR pulls GPIO0 low, as esptool's reset sequence +// expects. Based on Arduino's SerialNINAPassthrough. + +#include + +using namespace strix; + +const int NINA_GPIO0_PIN = 6; +const int NINA_RESETN_PIN = 7; + +StrixFPGA fpga; +unsigned long baud = 115200; +int rts = -1; +int dtr = -1; + +void setup() { + Serial.begin(baud); + fpga.begin(); + fpga.write(REG_GEN_ENABLE, 0); // reduce load while the module runs + fpga.write(REG_NINA_CTRL, 1); // let the module out of reset + + Serial1.begin(baud); + pinMode(NINA_GPIO0_PIN, OUTPUT); + pinMode(NINA_RESETN_PIN, OUTPUT); +} + +void loop() { + if (rts != Serial.rts()) { + digitalWrite(NINA_RESETN_PIN, Serial.rts() ? LOW : HIGH); + rts = Serial.rts(); + } + if (dtr != Serial.dtr()) { + digitalWrite(NINA_GPIO0_PIN, Serial.dtr() ? LOW : HIGH); // DTR asserted: download mode + dtr = Serial.dtr(); + } + + // Forward in chunks: one USB write per byte is too slow at esptool's rates and lets the UART's + // receive buffer overflow. + static uint8_t buffer[256]; + size_t count = 0; + while (Serial1.available() && count < sizeof(buffer)) buffer[count++] = Serial1.read(); + if (count) Serial.write(buffer, count); + count = 0; + while (Serial.available() && count < sizeof(buffer)) buffer[count++] = Serial.read(); + if (count) Serial1.write(buffer, count); + + // Follow baud rate changes requested by esptool. + if (Serial.baud() != baud) { + rts = -1; + dtr = -1; + baud = Serial.baud(); + Serial1.begin(baud); + } +} diff --git a/tools/ble_check.py b/tools/ble_check.py new file mode 100644 index 0000000..342fbcf --- /dev/null +++ b/tools/ble_check.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Checks the STRIX firmware's Bluetooth LE API (arduino/strix/API.md) from this computer: +finds the device, sends commands over the command characteristic, reads the replies from the +reply notifications, and reads and streams the entropy characteristic.""" + +import argparse +import asyncio +import json +import sys + +from bleak import BleakClient, BleakScanner + +SERVICE = "53545249-5800-4000-8000-000000000001" +COMMAND = "53545249-5800-4000-8000-000000000002" +REPLY = "53545249-5800-4000-8000-000000000003" +ENTROPY = "53545249-5800-4000-8000-000000000004" + + +class Replies: + """Reassembles reply notifications into newline-terminated lines.""" + + def __init__(self): + self.buffer = b"" + self.lines: asyncio.Queue[str] = asyncio.Queue() + + def handle(self, _sender, data: bytearray) -> None: + self.buffer += bytes(data) + while b"\n" in self.buffer: + line, self.buffer = self.buffer.split(b"\n", 1) + self.lines.put_nowait(line.decode(errors="replace").strip()) + + +async def command(client: BleakClient, replies: Replies, text: str) -> str: + await client.write_gatt_char(COMMAND, text.encode(), response=True) + return await asyncio.wait_for(replies.lines.get(), timeout=10) + + +async def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--timeout", type=float, default=15, help="scan time in seconds") + args = parser.parse_args() + + device = await BleakScanner.find_device_by_filter( + lambda d, adv: (d.name or "") == "STRIX" or SERVICE in adv.service_uuids, timeout=args.timeout) + if device is None: + sys.exit("no STRIX device found") + print(f"found {device.name} at {device.address}") + + async with BleakClient(device) as client: + replies = Replies() + await client.start_notify(REPLY, replies.handle) + + for text in ["id", "health", "ble", "entropy 32"]: + reply = await command(client, replies, text) + print(f"{text:<12} -> {reply[:160]}") + stats = json.loads(await command(client, replies, "stats")) + print(f"{'stats':<12} -> trial {stats.get('trial')}, combined total z " + f"{stats['channels'][-1]['total']['z'] if 'channels' in stats else None}") + + first = await client.read_gatt_char(ENTROPY) + second = await client.read_gatt_char(ENTROPY) + print(f"entropy read -> {len(first)} bytes, then {len(second)} bytes, " + f"{'different' if first != second else 'IDENTICAL'}") + + chunks = [] + await client.start_notify(ENTROPY, lambda _s, data: chunks.append(bytes(data))) + await asyncio.sleep(3) + await client.stop_notify(ENTROPY) + total = sum(len(c) for c in chunks) + print(f"entropy stream -> {len(chunks)} notifications, {total} bytes in 3 s " + f"({total / 3:.0f} B/s), {len(set(chunks))} distinct") + + +if __name__ == "__main__": + asyncio.run(main())