From ade23f0c2c064b733a1c3d264eb29e1a608f8745 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 17 Oct 2016 16:11:18 +0800 Subject: [PATCH 1/2] Arduino BLE new library 1. Add new library 2. Add BLEPeripheral library back compatible features 3. Fix IMU build issue 4. Fix the memset crash issue i. The FIRQ doesn't save the LP_COUNTER register. ii. Update the code to save LP related register 5. Fix the central crash issue when peripheral disconnect 6. Revert the UART changes -This change will break the BLE's communication 7. Implement the balloc to replace old one -Create memory pool -Port V3's balloc -Fix the local name display unexpected chars 8. Fix ScanCallback, LED related issue -The Serial.print called in interrupt and interrupt block the Serial interrupt. -The resource not released when call disconnect API. 9. Fix the discover blocked when connect/disconnect the link successively Block the disconnect if not receive the disconnect event 10. Return error when discover attributes if error happened 11. Update the back compatible comments 12. Fix the discover attribute failed issue -Add the -fcheck-new build option -Increase the IRQ stack size 13. Add discoverAttributesByService API - The sensorTag has many services and RAM is not enough for discover all services -This crash caused by bt_uuid_16 and bt_uuid_128 has different space and makes an address exception 14. Add keywords and fix read issue 15. Update the CurieBLE and delete BLE --- cores/arduino/UARTClass.cpp | 37 +- .../BatteryAdvChange/BatteryAdvChange.ino | 116 -- .../BatteryMonitor/BatteryMonitor.ino | 143 -- .../CurieBLE/examples/ButtonLED/ButtonLED.ino | 104 -- .../examples/CallbackLED/CallbackLED.ino | 110 -- .../examples/IMUBleCentral/IMUBleCentral.ino | 172 --- libraries/CurieBLE/examples/LED/LED.ino | 98 -- .../examples/LEDCentral/LEDCentral.ino | 179 --- .../CurieBLE/examples/MIDIBLE/MIDIBLE.ino | 161 -- .../CurieBLE/examples/Scanning/Scanning.ino | 151 -- .../UpdateConnectionInterval.ino | 206 --- .../central/IMUBleCentral/IMUBleCentral.ino | 125 ++ .../central/led_control/led_control.ino | 127 ++ .../peripheral_explorer.ino | 152 ++ .../CurieBLE/examples/central/scan/scan.ino | 70 + .../central/scan_callback/scan_callback.ino | 72 + .../sensortag_button/sensortag_button.ino | 121 ++ .../IMUBleNotification/IMUBleNotification.ino | 57 +- .../CurieBLE/examples/peripheral/led/led.ino | 84 ++ .../peripheral/led_callback/led_callback.ino | 90 ++ libraries/CurieBLE/keywords.txt | 159 +- libraries/CurieBLE/library.properties | 6 +- libraries/CurieBLE/src/BLEAttribute.cpp | 122 -- libraries/CurieBLE/src/BLEAttribute.h | 136 -- .../CurieBLE/src/BLEAttributeWithValue.cpp | 181 +++ .../CurieBLE/src/BLEAttributeWithValue.h | 63 + libraries/CurieBLE/src/BLECentral.cpp | 185 +-- libraries/CurieBLE/src/BLECentral.h | 189 +-- libraries/CurieBLE/src/BLECentralHelper.h | 58 - libraries/CurieBLE/src/BLECentralRole.cpp | 298 ---- libraries/CurieBLE/src/BLECentralRole.h | 276 ---- libraries/CurieBLE/src/BLECharacteristic.cpp | 741 ++++++---- libraries/CurieBLE/src/BLECharacteristic.h | 600 +++++--- libraries/CurieBLE/src/BLECommon.h | 38 +- libraries/CurieBLE/src/BLEDescriptor.cpp | 199 ++- libraries/CurieBLE/src/BLEDescriptor.h | 165 ++- libraries/CurieBLE/src/BLEDevice.cpp | 492 +++++++ libraries/CurieBLE/src/BLEDevice.h | 688 +++++++++ libraries/CurieBLE/src/BLEHelper.cpp | 160 -- libraries/CurieBLE/src/BLEHelper.h | 181 --- libraries/CurieBLE/src/BLEPeripheral.cpp | 350 ++--- libraries/CurieBLE/src/BLEPeripheral.h | 348 +---- .../CurieBLE/src/BLEPeripheralHelper.cpp | 117 -- libraries/CurieBLE/src/BLEPeripheralHelper.h | 166 --- libraries/CurieBLE/src/BLEPeripheralRole.cpp | 304 ---- libraries/CurieBLE/src/BLEPeripheralRole.h | 282 ---- libraries/CurieBLE/src/BLEProfile.cpp | 726 --------- libraries/CurieBLE/src/BLEProfile.h | 216 --- libraries/CurieBLE/src/BLERoleBase.cpp | 86 -- libraries/CurieBLE/src/BLERoleBase.h | 133 -- libraries/CurieBLE/src/BLEService.cpp | 263 +++- libraries/CurieBLE/src/BLEService.h | 183 ++- .../CurieBLE/src/BLETypedCharacteristic.h | 37 +- .../CurieBLE/src/BLETypedCharacteristics.cpp | 4 + .../CurieBLE/src/BLETypedCharacteristics.h | 18 + libraries/CurieBLE/src/CurieBLE.h | 59 +- .../CurieBLE/src/internal/BLEAttribute.cpp | 64 + .../CurieBLE/src/internal/BLEAttribute.h | 73 + .../CurieBLE/src/internal/BLECallbacks.cpp | 226 +++ .../CurieBLE/src/internal/BLECallbacks.h | 78 + .../src/internal/BLECharacteristicImp.cpp | 1016 +++++++++++++ .../src/internal/BLECharacteristicImp.h | 340 +++++ .../src/internal/BLEDescriptorImp.cpp | 149 ++ .../CurieBLE/src/internal/BLEDescriptorImp.h | 83 ++ .../src/internal/BLEDeviceManager.cpp | 1300 +++++++++++++++++ .../CurieBLE/src/internal/BLEDeviceManager.h | 437 ++++++ .../src/internal/BLEProfileManager.cpp | 1076 ++++++++++++++ .../CurieBLE/src/internal/BLEProfileManager.h | 220 +++ .../CurieBLE/src/internal/BLEServiceImp.cpp | 390 +++++ .../CurieBLE/src/internal/BLEServiceImp.h | 101 ++ libraries/CurieBLE/src/internal/BLEUtils.cpp | 206 +++ .../BLEUtils.h} | 41 +- libraries/CurieBLE/src/internal/LinkList.h | 100 ++ libraries/CurieBLE/src/internal/ble_client.c | 16 +- libraries/CurieBLE/src/internal/ble_client.h | 3 +- libraries/CurieIMU/src/BMI160.h | 2 +- platform.txt | 2 +- system/libarc32_arduino101/bootcode/init.S | 24 +- .../drivers/bluetooth/bluetooth.h | 1 + .../drivers/ipc_uart_ns16550.c | 296 ++-- system/libarc32_arduino101/drivers/ns16550.c | 22 +- .../framework/src/cfw/service_api.c | 5 + .../framework/src/infra/port.c | 48 +- .../framework/src/os/balloc.c | 511 +++++++ .../framework/src/os/memory_pool_list.def | 21 + .../libarc32_arduino101/framework/src/os/os.c | 7 +- .../framework/src/os/panic.c | 16 + .../framework/src/services/ble/gap.c | 11 + .../framework/src/services/ble/gatt.c | 23 +- .../framework/src/services/ble/uuid.c | 10 +- .../src/services/ble_service/ble_service.c | 6 +- variants/arduino_101/libarc32drv_arduino101.a | Bin 782810 -> 799666 bytes variants/arduino_101/linker_scripts/flash.ld | 2 +- 93 files changed, 10978 insertions(+), 6552 deletions(-) delete mode 100644 libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino delete mode 100644 libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino delete mode 100644 libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino delete mode 100644 libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino delete mode 100644 libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino delete mode 100644 libraries/CurieBLE/examples/LED/LED.ino delete mode 100644 libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino delete mode 100644 libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino delete mode 100644 libraries/CurieBLE/examples/Scanning/Scanning.ino delete mode 100644 libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino create mode 100644 libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino create mode 100644 libraries/CurieBLE/examples/central/led_control/led_control.ino create mode 100644 libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino create mode 100644 libraries/CurieBLE/examples/central/scan/scan.ino create mode 100644 libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino create mode 100644 libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino rename libraries/CurieBLE/examples/{ => peripheral}/IMUBleNotification/IMUBleNotification.ino (69%) create mode 100644 libraries/CurieBLE/examples/peripheral/led/led.ino create mode 100644 libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino delete mode 100644 libraries/CurieBLE/src/BLEAttribute.cpp delete mode 100644 libraries/CurieBLE/src/BLEAttribute.h create mode 100644 libraries/CurieBLE/src/BLEAttributeWithValue.cpp create mode 100644 libraries/CurieBLE/src/BLEAttributeWithValue.h delete mode 100644 libraries/CurieBLE/src/BLECentralHelper.h delete mode 100644 libraries/CurieBLE/src/BLECentralRole.cpp delete mode 100644 libraries/CurieBLE/src/BLECentralRole.h create mode 100644 libraries/CurieBLE/src/BLEDevice.cpp create mode 100644 libraries/CurieBLE/src/BLEDevice.h delete mode 100644 libraries/CurieBLE/src/BLEHelper.cpp delete mode 100644 libraries/CurieBLE/src/BLEHelper.h delete mode 100644 libraries/CurieBLE/src/BLEPeripheralHelper.cpp delete mode 100644 libraries/CurieBLE/src/BLEPeripheralHelper.h delete mode 100644 libraries/CurieBLE/src/BLEPeripheralRole.cpp delete mode 100644 libraries/CurieBLE/src/BLEPeripheralRole.h delete mode 100644 libraries/CurieBLE/src/BLEProfile.cpp delete mode 100644 libraries/CurieBLE/src/BLEProfile.h delete mode 100644 libraries/CurieBLE/src/BLERoleBase.cpp delete mode 100644 libraries/CurieBLE/src/BLERoleBase.h create mode 100644 libraries/CurieBLE/src/internal/BLEAttribute.cpp create mode 100644 libraries/CurieBLE/src/internal/BLEAttribute.h create mode 100644 libraries/CurieBLE/src/internal/BLECallbacks.cpp create mode 100644 libraries/CurieBLE/src/internal/BLECallbacks.h create mode 100644 libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp create mode 100644 libraries/CurieBLE/src/internal/BLECharacteristicImp.h create mode 100644 libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp create mode 100644 libraries/CurieBLE/src/internal/BLEDescriptorImp.h create mode 100644 libraries/CurieBLE/src/internal/BLEDeviceManager.cpp create mode 100644 libraries/CurieBLE/src/internal/BLEDeviceManager.h create mode 100644 libraries/CurieBLE/src/internal/BLEProfileManager.cpp create mode 100644 libraries/CurieBLE/src/internal/BLEProfileManager.h create mode 100644 libraries/CurieBLE/src/internal/BLEServiceImp.cpp create mode 100644 libraries/CurieBLE/src/internal/BLEServiceImp.h create mode 100644 libraries/CurieBLE/src/internal/BLEUtils.cpp rename libraries/CurieBLE/src/{BLECentralHelper.cpp => internal/BLEUtils.h} (56%) create mode 100644 libraries/CurieBLE/src/internal/LinkList.h create mode 100644 system/libarc32_arduino101/framework/src/os/balloc.c create mode 100644 system/libarc32_arduino101/framework/src/os/memory_pool_list.def diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index 57d3f99e..000985f2 100644 --- a/cores/arduino/UARTClass.cpp +++ b/cores/arduino/UARTClass.cpp @@ -168,7 +168,7 @@ int UARTClass::read( void ) void UARTClass::flush( void ) { - while (_tx_buffer->_iHead != *(volatile int*)&(_tx_buffer->_iTail)); //wait for transmit data to be sent + while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent // Wait for transmission to complete while(!uart_tx_complete(CONFIG_UART_CONSOLE_INDEX)); } @@ -179,11 +179,12 @@ size_t UARTClass::write( const uint8_t uc_data ) return(0); // Is the hardware currently busy? - if (_tx_buffer->_iTail != _tx_buffer->_iHead || !uart_tx_ready(CONFIG_UART_CONSOLE_INDEX)) + if (_tx_buffer->_iTail != _tx_buffer->_iHead) { // If busy we buffer int l = (_tx_buffer->_iHead + 1) % UART_BUFFER_SIZE; - while (*(volatile int*)&(_tx_buffer->_iTail) == l); // Spin locks if we're about to overwrite the buffer. This continues once the data is sent + while (_tx_buffer->_iTail == l) + ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data; _tx_buffer->_iHead = l; @@ -200,29 +201,21 @@ size_t UARTClass::write( const uint8_t uc_data ) void UARTClass::IrqHandler( void ) { - uart_irq_update(CONFIG_UART_CONSOLE_INDEX); - // if irq is Receiver Data Available - if(uart_irq_rx_ready(CONFIG_UART_CONSOLE_INDEX)) - { - uint8_t uc_data; - int ret; + uint8_t uc_data; + int ret; + ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); + + while ( ret != -1 ) { + _rx_buffer->store_char(uc_data); ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); - - while ( ret != -1 ) { - _rx_buffer->store_char(uc_data); - ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); - } } - // if irq is Transmitter Holding Register - else if(uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) + // Do we need to keep sending data? + if (!uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { - if(_tx_buffer->_iTail != _tx_buffer->_iHead) - { - int end = (_tx_buffer->_iTail < _tx_buffer->_iHead) ? _tx_buffer->_iHead:UART_BUFFER_SIZE; - int l = min(end - _tx_buffer->_iTail, UART_FIFO_SIZE); - uart_fifo_fill(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer+_tx_buffer->_iTail, l); - _tx_buffer->_iTail = (_tx_buffer->_iTail+l)%UART_BUFFER_SIZE; + if (_tx_buffer->_iTail != _tx_buffer->_iHead) { + uart_poll_out(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer[_tx_buffer->_iTail]); + _tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % UART_BUFFER_SIZE; } else { diff --git a/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino b/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino deleted file mode 100644 index 8b977a39..00000000 --- a/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino +++ /dev/null @@ -1,116 +0,0 @@ -/* Please see code copyright at the bottom of this example code */ - -/* - This example can work with phone BLE app. - - This sketch illustrates how to change the advertising data so that it is visible but not - connectable. Then after 10 seconds it changes to being connectable. - This sketch example partially implements the standard Bluetooth Low-Energy Battery service. - - This sketch is not paired with a specific central example sketch, - but to see how it works you need to use a BLE APP on your phone or central device - and try connecting when it is either a connectable or not connectable state - as displayed in the serial monitor. -*/ - -#include - -BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) -BLEService batteryService("180F"); // BLE Battery Service -int count = 0; -// BLE Battery Level Characteristic" -BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID - BLERead | BLENotify); // remote clients will be able to -// get notifications if this characteristic changes - -void setup() { - Serial.begin(9600); // initialize serial communication - pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected - while (!Serial) { - //wait for Serial to connect - } - /* Set a local name for the BLE device - This name will appear in advertising packets - and can be used by remote devices to identify this BLE device - The name can be changed but maybe be truncated based on space left in advertisement packet */ - blePeripheral.setLocalName("BatteryAdvChangeSketch"); - blePeripheral.setAdvertisedServiceUuid(batteryService.uuid()); // add the service UUID - blePeripheral.addAttribute(batteryService); // Add the BLE Battery service - blePeripheral.addAttribute(batteryLevelChar); // add the battery level characteristic - - /* Now activate the BLE device. It will start continuously transmitting BLE - advertising packets and will be visible to remote BLE central devices - until it receives a new connection */ - - blePeripheral.begin(); - Serial.println("Bluetooth device active, waiting for connections..."); - Serial.println("Starts in Connectable mode"); -} - -void loop() { - // listen for BLE peripherals to connect: - BLECentralHelper central = blePeripheral.central(); - // wait - Serial.print(". "); - if (count == 10) { - Serial.print("\nReached count "); - Serial.println(count); - - } - delay (1000); - count++; - // Switch from Connectable to Non Connectable and vice versa - if (count > 10 ) { - static bool change_discover = false; - Serial.println("Stop Adv and pausing for 10 seconds. Device should be invisible"); - // Some central devices (phones included) may cache previous scan inofrmation - // restart your central and it should not see this peripheral once stopAdvertising() is called - blePeripheral.stopAdvertising(); - delay(10000); - - if (change_discover) - { - - // Using the function setConnectable we specify that it now NOT connectable - // The loop is for 10 seconds. Your central device may timeout later than that - // and may eventually connect when we set it back to connectable mode below - blePeripheral.setConnectable(false); - Serial.println("In Non Connectable mode"); - - } - else - { - - //using the function setConnectable we specify that it now connectable - blePeripheral.setConnectable(true); - Serial.println("In Connectable mode"); - } - Serial.println("Start Adv"); - blePeripheral.startAdvertising(); - if (change_discover) { - Serial.println("Adding 5 second delay in Non Connect Mode"); - delay(5000); - } - change_discover = !change_discover; - count = 0; - } -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino deleted file mode 100644 index e3a297cf..00000000 --- a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -#include - -/* - This sketch can work with UpdateConnectionInterval. - - You can also use an android or IOS app that supports notifications. - This sketch example partially implements the standard Bluetooth Low-Energy Battery service - and connection interval paramater update. - For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx -*/ - -/* */ -BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) -BLEService batteryService("180F"); // BLE Battery Service - -// BLE Battery Level Characteristic" -BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID defined in the URL above - BLERead | BLENotify); // remote clients will be able to - // get notifications if this characteristic changes - -int oldBatteryLevel = 0; // last battery level reading from analog input -long previousMillis = 0; // last time the battery level was checked, in ms - -void setup() { - Serial.begin(9600); // initialize serial communication - pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected - - /* Set a local name for the BLE device - This name will appear in advertising packets - and can be used by remote devices to identify this BLE device - The name can be changed but maybe be truncated based on space left in advertisement packet */ - blePeripheral.setLocalName("BatteryMonitorSketch"); - blePeripheral.setAdvertisedServiceUuid(batteryService.uuid()); // add the service UUID - blePeripheral.addAttribute(batteryService); // Add the BLE Battery service - blePeripheral.addAttribute(batteryLevelChar); // add the battery level characteristic - batteryLevelChar.setValue(oldBatteryLevel); // initial value for this characteristic - - /* Now activate the BLE device. It will start continuously transmitting BLE - advertising packets and will be visible to remote BLE central devices - until it receives a new connection */ - blePeripheral.begin(); - Serial.println("Bluetooth device active, waiting for connections..."); -} - -void loop() { - // listen for BLE peripherals to connect: - BLECentralHelper central = blePeripheral.central(); - - // if a central is connected to peripheral: - if (central) { - Serial.print("Connected to central: "); - // print the central's MAC address: - Serial.println(central.address()); - // turn on the LED to indicate the connection: - digitalWrite(13, HIGH); - - // check the battery level every 200ms - // as long as the central is still connected: - while (central.connected()) { - long currentMillis = millis(); - // if 200ms have passed, check the battery level: - if (currentMillis - previousMillis >= 200) { - previousMillis = currentMillis; - updateBatteryLevel(); - - static unsigned short count = 0; - count++; - // update the connection interval - if(count%5 == 0){ - delay(1000); - updateIntervalParams(central); - } - } - } - // when the central disconnects, turn off the LED: - digitalWrite(13, LOW); - Serial.print("Disconnected from central: "); - Serial.println(central.address()); - } -} - -void updateBatteryLevel() { - /* Read the current voltage level on the A0 analog input pin. - This is used here to simulate the charge level of a battery. - */ - int battery = analogRead(A0); - int batteryLevel = map(battery, 0, 1023, 0, 100); - - if (batteryLevel != oldBatteryLevel) { // if the battery level has changed - Serial.print("Battery Level % is now: "); // print it - Serial.println(batteryLevel); - batteryLevelChar.setValue(batteryLevel); // and update the battery level characteristic - oldBatteryLevel = batteryLevel; // save the level for next comparison - } -} - -void updateIntervalParams(BLECentralHelper ¢ral) { - // read and update the connection interval that peer central device - static unsigned short interval = 0x60; - ble_conn_param_t m_conn_param; - // Get connection interval that peer central device wanted - central.getConnParams(m_conn_param); - Serial.print("min interval = " ); - Serial.println(m_conn_param.interval_min ); - Serial.print("max interval = " ); - Serial.println(m_conn_param.interval_max ); - Serial.print("latency = " ); - Serial.println(m_conn_param.latency ); - Serial.print("timeout = " ); - Serial.println(m_conn_param.timeout ); - - //Update connection interval - Serial.println("set Connection Interval"); - central.setConnectionInterval(interval,interval); - - interval++; - if(interval<0x06) - interval = 0x06; - if(interval>0x100) - interval = 0x06; -} -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino b/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino deleted file mode 100644 index 7a46c164..00000000 --- a/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - This example can work with phone BLE app. - - This examples needs a button connected similarly as described here https://www.arduino.cc/en/Tutorial/Button - The only difference is that instead of connecting to pin 2, it connects to pin 4 - After the sketch starts connect to a BLE app on a phone and set notification to the Characteristic and you should see it update - whenever the button is pressed. This sketch is not written to pair with any of the central examples. -*/ - -#include - -const int ledPin = 13; // set ledPin to on-board LED -const int buttonPin = 4; // set buttonPin to digital pin 4 - -BLEPeripheral blePeripheral; // create peripheral instance -BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service with a 128-bit UUID (32 characters exclusive of dashes). - // Long UUID denote custom user created UUID - - -// create switch characteristic and allow remote device to read and write -BLECharCharacteristic ledCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); -// create button characteristic and allow remote device to get notifications -BLECharCharacteristic buttonCharacteristic("19B10012-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify); // allows remote device to get notifications -// Note use of Typed Characteristics. These previous 2 characeristics are of the type char - -void setup() { - Serial.begin(9600); - pinMode(ledPin, OUTPUT); // use the LED on pin 13 as an output - pinMode(buttonPin, INPUT); // use button pin 4 as an input - - // set the local name peripheral advertises - blePeripheral.setLocalName("ButtonLED"); - // set the UUID for the service this peripheral advertises: - blePeripheral.setAdvertisedServiceUuid(ledService.uuid()); - - // add service and characteristics - blePeripheral.addAttribute(ledService); - blePeripheral.addAttribute(ledCharacteristic); - blePeripheral.addAttribute(buttonCharacteristic); - - // set initial values for led and button characteristic - ledCharacteristic.setValue(0); - buttonCharacteristic.setValue(0); - - // advertise the service - blePeripheral.begin(); - - Serial.println("Bluetooth device active, waiting for connections..."); -} - -void loop() { - // poll peripheral - blePeripheral.poll(); - - // read the current button pin state - char buttonValue = digitalRead(buttonPin); - - // has the value changed since the last read - boolean buttonChanged = (buttonCharacteristic.value() != buttonValue); - - if (buttonChanged) { - // button state changed, update characteristics - ledCharacteristic.setValue(buttonValue); - buttonCharacteristic.setValue(buttonValue); - } - - if (ledCharacteristic.written() || buttonChanged) { - // update LED, either central has written to characteristic or button state has changed - // if you are using a phone or a BLE central device that is aware of this characteristic, writing a value of 0x40 for example - // Will be interpreted as written - if (ledCharacteristic.value()) { - Serial.println("LED on"); - digitalWrite(ledPin, HIGH); - } else { - // If central writes a 0 value then it is interpreted as no value and turns off the LED - Serial.println("LED off"); - digitalWrite(ledPin, LOW); - } - } -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino deleted file mode 100644 index 2a661189..00000000 --- a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - - /* - This example can work with LEDCentral. - - You should see the LED blink on and off. - This example demonstrates the use of Callback or event Handlers responding to events. - BLEConnected, BLEDisconnected and BLEWritten are events. - To test interactively, use a Phone app like nrf Controller (Android) or Light Blue (iOS). - Connect to BLE device named LEDCB and explore characteristic with UUID 19B10001-E8F2-537E-4F6C-D104768A1214. - Writing a byte value such as 0x40 should turn on the LED. - Writing a byte value of 0x00 should turn off the LED. - */ - -#include - -const int ledPin = 13; // set ledPin to use on-board LED -BLEPeripheral blePeripheral; // create peripheral instance -BLECentralHelper *bleCentral1 = NULL; // peer central device - -BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service with a 128-bit UUID (32 characters exclusive of dashes). - // Long UUID denote custom user created UUID - -// create switch characteristic and allow remote device to read and write -BLECharCharacteristic switchChar("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); - -void setup() { - Serial.begin(9600); - pinMode(ledPin, OUTPUT); // use the LED on pin 13 as an output - - // set the local name peripheral advertises - blePeripheral.setLocalName("LEDCB"); - // set the UUID for the service this peripheral advertises - blePeripheral.setAdvertisedServiceUuid(ledService.uuid()); - - // add service and characteristic - blePeripheral.addAttribute(ledService); - blePeripheral.addAttribute(switchChar); - - // assign event handlers for connected, disconnected to peripheral - blePeripheral.setEventHandler(BLEConnected, blePeripheralConnectHandler); - blePeripheral.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); - - // assign event handlers for characteristic - switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten); -// set an initial value for the characteristic - switchChar.setValue(0); - - // advertise the service - blePeripheral.begin(); - Serial.println(("Bluetooth device active, waiting for connections...")); -} - -void loop() { - // poll peripheral - blePeripheral.poll(); -} - -// The function parameter (BLEHelper& central) is for peripheral devices -// This enable us to have access to the central's data like its bluetooth address - -void blePeripheralConnectHandler(BLEHelper& central) { - // central connected event handler - bleCentral1 = blePeripheral.getPeerCentralBLE(central); - Serial.print("Connected event, central: "); - Serial.println(bleCentral1->address()); -} - -void blePeripheralDisconnectHandler(BLEHelper& central) { - // central disconnected event handler - Serial.print("Disconnected event, central: "); - Serial.println(central.address()); -} - -// In addtion to the BLECentral& central parameter, we also have to have to BLECharacteristic& characteristic parameter - -void switchCharacteristicWritten(BLEHelper& central, BLECharacteristic& characteristic) { - // central wrote new value to characteristic, update LED - Serial.print("Characteristic event, written: "); - - if (switchChar.value()) { - Serial.println("LED on"); - digitalWrite(ledPin, HIGH); - } else { - Serial.println("LED off"); - digitalWrite(ledPin, LOW); - } -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino deleted file mode 100644 index a5359509..00000000 --- a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -#include - -/* - This sketch example works with IMUBleNotification.ino - - IMUBleNotification.ino will send notification to this central sketch. - This sketch will receive the notifications and output the received data in the serial monitor. - It also illustrates using a non-typed characteristic. - Set the baud rate to 115200 on the serial monitor to accomodate the speed of constant data updates from IMU subsystem. -*/ - -#define MAX_IMU_RECORD 1 - -ble_conn_param_t conn_param = {30.0, // minimum interval in ms 7.5 - 4000 - 50.0, // maximum interval in ms 7.5 - - 0, // latency - 4000 // timeout in ms 100 - 32000ms - }; -// define a structure that will serve as buffer for holding IMU data - -typedef struct { - int index; - unsigned int slot[3]; -} imuFrameType; - -imuFrameType imuBuf[MAX_IMU_RECORD]; -BLECentral bleCentral; // BLE Central Device (the board you're programming) - -BLEService bleImuService("F7580001-153E-D4F6-F26D-43D8D98EEB13"); -BLECharacteristic bleImuChar("F7580003-153E-D4F6-F26D-43D8D98EEB13", // standard 128-bit characteristic UUID - BLERead | BLENotify, sizeof(imuBuf)); // remote clients will be able to - // get notifications if this characteristic changes - // We have a third parameter which is the size of imyBuffer. This is because it is a non-typed characteristic - // If we are only writing to this characteristic we can set this buffer to 512 bytes - // But because of the limitation of the Nordic FW, please do not set this to more than 128 if you intend to read it. - // MAX_IMU_RECORD value is 1 so we are safe -// function prototype for function that determines if the advertising data is found -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr); - -void setup() -{ - // This is set to higher baud rate because accelerometer data changes very quickly - Serial.begin(115200); // initialize serial communication - pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected - // set the event handeler function for the bleImuChar characteristic - bleImuChar.setEventHandler(BLEWritten, bleImuCharacteristicWritten); - - bleCentral.addAttribute(bleImuService); // Add the BLE IMU service - bleCentral.addAttribute(bleImuChar); // Add the BLE IMU characteristic - - // Setup callback whenever a Peripheral advertising data is found) - bleCentral.setAdvertiseHandler(adv_found); - bleCentral.setEventHandler(BLEConnected, ble_connected); - - /* Now activate the BLE device. It will start continuously transmitting BLE - advertising packets and will be visible to remote BLE central devices - until it receives a new connection */ - bleCentral.begin(); -} - - -void loop() -{ - // we put a 2 second delay - // Even though this looks empty, since we setup 2 callbacks by setting the advertising handler adv_found - // and event handler for BLEConnected, we basically are lsitening for advertising data and connected events. - - delay(2000); -} - -void ble_connected(BLEHelper &role) -{ - // since we are a central device we create a BLEPeripheralHelper peripheral - BLEPeripheralHelper *peripheral = bleCentral.getPeerPeripheralBLE(role); - Serial.println("Connected"); - - // Start discovery the profiles in peripheral device - peripheral->discover(); -} - -void bleImuCharacteristicWritten(BLEHelper& peripheral, BLECharacteristic& characteristic) -{ - // Peripheral wrote new value to characteristic by Notification/Indication - // We have to use pointers because we are NOT using a type characteristic - // In other examples our charcteristics are typed so we not have to use pointers and can access the value directly - // The parent non typde characteristic class, the value method gives a pointer to the characteristic value - - const unsigned char *cvalue = characteristic.value(); - const imuFrameType *value = (const imuFrameType *)cvalue; - Serial.print("\r\nCharacteristic event, written: "); - Serial.print(value->index); - Serial.print("\t"); - Serial.print(value->slot[0]); - Serial.print("\t"); - Serial.print(value->slot[1]); - Serial.print("\t"); - Serial.println(value->slot[2]); -} - -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr) -{ - int i; - - Serial.print("[AD]:"); - Serial.print(type); - Serial.print(" data_len "); - Serial.println(data_len); - // Please see https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile - // To decode the data the central device cares. - // This example use UUID as identity. - switch (type) - { - case BT_DATA_UUID128_SOME: - case BT_DATA_UUID128_ALL: - { - if (data_len % UUID_SIZE_128 != 0) - { - Serial.println("AD malformed"); - return true; - } - for (i = 0; i < data_len; i += UUID_SIZE_128) - { - if (bleImuService.uuidCompare(dataPtr + i, UUID_SIZE_128) == false) - { - continue; - } - - // Accept the advertisement - if (!bleCentral.stopScan()) - { - Serial.println("Stop LE scan failed"); - continue; - } - Serial.println("Connecting"); - // Connect to peripheral - bleCentral.connect(addrPtr, &conn_param); - return false; - } - } - } - - return true; -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/LED/LED.ino b/libraries/CurieBLE/examples/LED/LED.ino deleted file mode 100644 index 34c26a8c..00000000 --- a/libraries/CurieBLE/examples/LED/LED.ino +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - - /* - This example can work with LEDCentral - - This example is similar to CallbackLED example in functionality. - It does not use callbacks. In the loop it interogates the connection state with central. - Checks if the characteristic is written and turns the LED on or off accordingly. - To test interactively, use a phone app like nrf Controller (Android) or Light Blue (iOS). - Connect to BLE device named LEDCB and explore characteristic with UUID 19B10001-E8F2-537E-4F6C-D104768A1214. - Writing a byte value such as 0x40 should turn on the LED. - Writing a byte value of 0x00 should turn off the LED. - */ - -#include - -BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) -BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service - -// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central -BLEUnsignedCharCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); - -const int ledPin = 13; // pin to use for the LED - -void setup() { - Serial.begin(9600); - - // set LED pin to output mode - pinMode(ledPin, OUTPUT); - - // set advertised local name and service UUID: - blePeripheral.setLocalName("LED"); - blePeripheral.setAdvertisedServiceUuid(ledService.uuid()); - - // add service and characteristic: - blePeripheral.addAttribute(ledService); - blePeripheral.addAttribute(switchCharacteristic); - - // set the initial value for the characeristic: - switchCharacteristic.setValue(0); - - // begin advertising BLE service: - blePeripheral.begin(); - - Serial.println("BLE LED Peripheral"); -} - -void loop() { - // listen for BLE peripherals to connect: - BLECentralHelper central = blePeripheral.central(); - - // if a central is connected to peripheral: - if (central) { - Serial.print("Connected to central: "); - // print the central's MAC address: - Serial.println(central.address()); - - // while the central is still connected to peripheral: - while (central.connected()) { - // if the remote device wrote to the characteristic, - // use the value to control the LED: - if (switchCharacteristic.written()) { - if (switchCharacteristic.value()) { // any value other than 0 - Serial.println("LED on"); - digitalWrite(ledPin, HIGH); // will turn the LED on - } else { // a 0 value - Serial.println(F("LED off")); - digitalWrite(ledPin, LOW); // will turn the LED off - } - } - } - - // when the central disconnects, print it out: - Serial.print(F("Disconnected from central: ")); - Serial.println(central.address()); - } -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino deleted file mode 100644 index e26e9451..00000000 --- a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -#include - -/* - This example can work with CallbackLED and LED sketches. - - To show how a central device can do charcteristic read and write operations. - A third party serial terminal is recommended to see outputs from central and peripheral device. -*/ - -// set up connection params - -ble_conn_param_t conn_param = {30.0, // minimum interval in ms 7.5 - 4000 - 50.0, // maximum interval in ms 7.5 - - 0, // latency - 4000 // timeout in ms 100 - 32000ms - }; - -const int ledPin = 13; // set ledPin to use on-board LED -BLECentral bleCentral; // create central instance -BLEPeripheralHelper *blePeripheral1 = NULL; // peer peripheral device - -BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service with a 128-bit UUID (32 characters exclusive of dashes). - // Long UUID denote custom user created UUID -BLECharCharacteristic switchChar("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);// create switch characteristic and allow remote device to read and write - -// function prototype for function that determines if the advertising data is found -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr); - -void setup() -{ - Serial.begin(9600); - pinMode(ledPin, OUTPUT); // use the LED on pin 13 as an output - - // add service and characteristic - bleCentral.addAttribute(ledService); - bleCentral.addAttribute(switchChar); - - // assign event handlers for connected, disconnected to central - bleCentral.setEventHandler(BLEConnected, bleCentralConnectHandler); - bleCentral.setEventHandler(BLEDisconnected, bleCentralDisconnectHandler); - - // advertise the service - bleCentral.setAdvertiseHandler(adv_found); - - // assign event handlers for characteristic - switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten); - - bleCentral.begin(); - Serial.println(("Bluetooth device active, waiting for connections...")); -} - -void loop() -{ - static unsigned int counter = 0; - static char ledstate = 0; - delay(2000); - - if (blePeripheral1) - { - counter++; - if (counter % 3) - { - switchChar.read(*blePeripheral1); - } - else - { - ledstate = !ledstate; - switchChar.write(*blePeripheral1, ledstate); - } - } -} - -void bleCentralConnectHandler(BLEHelper& peripheral) -{ - // peripheral connected event handler - blePeripheral1 = bleCentral.getPeerPeripheralBLE(peripheral); - Serial.print("Connected event, peripheral: "); - Serial.println(blePeripheral1->address()); - // Start discovery the profiles in peripheral device - blePeripheral1->discover(); -} - -void bleCentralDisconnectHandler(BLEHelper& peripheral) -{ - // peripheral disconnected event handler - blePeripheral1 = NULL; - Serial.print("Disconnected event, peripheral: "); - Serial.println(peripheral.address()); - bleCentral.startScan(); -} - -void switchCharacteristicWritten(BLEHelper& peripheral, BLECharacteristic& characteristic) -{ - // Read response/Notification wrote new value to characteristic, update LED - Serial.print("Characteristic event, written: "); - - if (switchChar.value()) - { - Serial.println("LED on"); - digitalWrite(ledPin, HIGH); - } - else - { - Serial.println("LED off"); - digitalWrite(ledPin, LOW); - } -} - -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr) -{ - int i; - - Serial.print("[AD]:"); - Serial.print(type); - Serial.print(" data_len "); - Serial.println(data_len); - - switch (type) - { - case BT_DATA_UUID128_SOME: - case BT_DATA_UUID128_ALL: - { - if (data_len % UUID_SIZE_128 != 0) - { - Serial.println("AD malformed"); - return true; - } - for (i = 0; i < data_len; i += UUID_SIZE_128) - { - if (ledService.uuidCompare(dataPtr + i, UUID_SIZE_128) == false) - { - continue; - } - - // Accept the advertisement - if (!bleCentral.stopScan()) - { - Serial.println("Stop LE scan failed"); - continue; - } - Serial.println("Connecting"); - // Connect to peripheral - bleCentral.connect(addrPtr, &conn_param); - return false; - } - } - } - - return true; -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino b/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino deleted file mode 100644 index 7d78df27..00000000 --- a/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino +++ /dev/null @@ -1,161 +0,0 @@ -/* Written by Oren Levy (auxren.com; @auxren) while competing on - America's Greatest Makers with help from Intel. - MIDI over BLE info from: https://developer.apple.com/bluetooth/Apple-Bluetooth-Low-Energy-MIDI-Specification.pdf - This sketch is not written to pair with any of the central examples. - - This sketch plays a random MIDI note (between 0 and 127) every 400ms. - For a 'smarter' sketch, check out my Airpeggiator example. - The Airpeggiator uses the Curie's IMU to allow you to play - an imaginary harp in the air. I included a quantizer so you can - select a key and scale so you can jam along with friends. - https://github.com/auxren/MIDIBLE101/tree/master/Airpeggiator - - I have only tested MIDI over BLE using Apple devices. Android doesn't - support native MIDI over BLE yet and I haven't had much of a chance - to test with Windows machines. - - To connect on a Mac, search for Audio MIDI Setup. - Click 'Window' on the top menu and choose 'Show MIDI Studio'. - Double click 'Bluetooth' and the bluetooth configuration window - will pop up. After loading the MIDIBLE sketch on your Arduino 101 - you should see it advertising as Auxren. Click connect and the device - will be available as MIDI device in all your audio software like Garageband. - - There are a few ways to connect using an iOS device. One way to to open - up Garageband. Click on the wrench icon in the upper right and choose 'Advanced' - Towards the bottom of advanced, you will see 'Bluetooth MIDI devices'. - You should see your Arduino 101 advertising in the list. Connect to - your device and it should be available to all other iOS MIDI apps you have. - - If you do not have iOS, you can still use a BLE app on Android and just subscribe - to the midiChar charcteristic and see the updates. - - To send data, you use the following line: char.setValue(d, n); where char is - the BLE characteristic (in our case, midiCha), d is the data, and n is the - number of bytes of data. - The first 2 bytes of data are the header byte and timestamp byte. If you want, - you can figure out the timestamping scheme, but I just left it with a generic value - since I haven't worked on anything timeing sensitive yet (like a sequencer). - The third, fourth, and fifth bytes are standard MIDI bytes. You can send more bytes - if you would like as long as it is complies to the standard MIDI spec. - - The MIT License (MIT) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - -*/ -#include - -#define TXRX_BUF_LEN 20 //max number of bytes -#define RX_BUF_LEN 20 //max number of bytes -uint8_t rx_buf[RX_BUF_LEN]; -int rx_buf_num, rx_state = 0; -uint8_t rx_temp_buf[20]; -uint8_t outBufMidi[128]; - -//Buffer to hold 5 bytes of MIDI data. Note the timestamp is forced -uint8_t midiData[] = {0x80, 0x80, 0x00, 0x00, 0x00}; - -//Loads up buffer with values for note On -void noteOn(char chan, char note, char vel) //channel 1 -{ - midiData[2] = 0x90 + chan; - midiData[3] = note; - midiData[4] = vel; -} - -//Loads up buffer with values for note Off -void noteOff(char chan, char note) //channel 1 -{ - midiData[2] = 0x80 + chan; - midiData[3] = note; - midiData[4] = 0; -} - -BLEPeripheral midiDevice; // create peripheral instance - -BLEService midiSvc("03B80E5A-EDE8-4B33-A751-6CE34EC4C700"); // create service - -// create switch characteristic and allow remote device to read and write -BLECharacteristic midiChar("7772E5DB-3868-4112-A1A9-F2669D106BF3", BLEWrite | BLEWriteWithoutResponse | BLENotify | BLERead, 5); - -void setup() { - Serial.begin(9600); - - BLESetup(); - Serial.println(("Bluetooth device active, waiting for connections...")); -} - -void loop() { - - /*Simple randome note player to test MIDI output - Plays random note every 400ms - */ - int note = random(0, 127); - //readMIDI(); - noteOn(0, note, 127); //loads up midiData buffer - midiChar.setValue(midiData, 5);//midiData); //posts 5 bytes - delay(200); - noteOff(0, note); - midiChar.setValue(midiData, 5);//midiData); //posts 5 bytes - delay(200); -} - -void BLESetup() -{ - // set the local name peripheral advertises - midiDevice.setLocalName("Auxren"); - midiDevice.setDeviceName("Auxren"); - - // set the UUID for the service this peripheral advertises - midiDevice.setAdvertisedServiceUuid(midiSvc.uuid()); - - // add service and characteristic - midiDevice.addAttribute(midiSvc); - midiDevice.addAttribute(midiChar); - - // assign event handlers for connected, disconnected to peripheral - midiDevice.setEventHandler(BLEConnected, midiDeviceConnectHandler); - midiDevice.setEventHandler(BLEDisconnected, midiDeviceDisconnectHandler); - - // assign event handlers for characteristic - midiChar.setEventHandler(BLEWritten, midiCharacteristicWritten); - // set an initial value for the characteristic - midiChar.setValue(midiData, 5); - - // advertise the service - midiDevice.begin(); -} - -void midiDeviceConnectHandler(BLEHelper& central) { - // central connected event handler - Serial.print("Connected event, central: "); - Serial.println(central.address()); -} - -void midiDeviceDisconnectHandler(BLEHelper& central) { - // central disconnected event handler - Serial.print("Disconnected event, central: "); - Serial.println(central.address()); -} - -void midiCharacteristicWritten(BLEHelper& central, BLECharacteristic& characteristic) { - // central wrote new value to characteristic, update LED - Serial.print("Characteristic event, written: "); -} diff --git a/libraries/CurieBLE/examples/Scanning/Scanning.ino b/libraries/CurieBLE/examples/Scanning/Scanning.ino deleted file mode 100644 index 966bd3b4..00000000 --- a/libraries/CurieBLE/examples/Scanning/Scanning.ino +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -#include - -/* - This sketch is meaningful if one or more BLE peripheral devices (any of the peripheral examples will do) - are present. - - This sketch try to show the scan function. - The sketch will list the device's MAC address and device name to the console. - The list will refresh every 3s. -*/ - - -const int bleScanMaxCnt = 5; - -typedef struct{ - char macaddr[32]; // BLE MAC address. - char loacalname[22]; // Device's name -}ble_device_info_t; - -ble_device_info_t device_list[bleScanMaxCnt]; -uint8_t list_index = 0; - -BLECentral bleCentral; // BLE Central Device (the board you're programming) - -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr); - -void setup() -{ - Serial.begin(115200); // initialize serial communication - - /* Setup callback */ - bleCentral.setAdvertiseHandler(adv_found); - - /* Now activate the BLE device. - It will start continuously scanning BLE advertising - */ - bleCentral.begin(); - Serial.println("Bluetooth device active, start scanning..."); -} - -void loop() -{ - // Output the scanned device per 3s - delay(3000); - Serial.print("\r\n\r\n\t\t\tScaning result\r\n \tMAC\t\t\t\tLocal Name\r\n"); - Serial.print("-------------------------------------------------------------\r\n"); - - for (int i = 0; i < list_index; i++) - { - - Serial.print(device_list[i].macaddr); - Serial.print(" | "); - Serial.println(device_list[i].loacalname); - } - if (list_index == 0) - { - Serial.print("No device found\r\n"); - } - Serial.print("-------------------------------------------------------------\r\n"); - adv_list_clear(); -} - -// Add the scanned BLE device into the global variables. -bool adv_list_add(ble_device_info_t &device) -{ - if (list_index >= bleScanMaxCnt) - { - return false; - } - for (int i = 0; i < list_index; i++) - { - if (0 == memcmp(device.macaddr, device_list[i].macaddr, sizeof (device.macaddr))) - { - // Found and update the item - return false; - } - } - // Add the device - memcpy(&device_list[list_index], &device, sizeof (ble_device_info_t)); - list_index++; - return true; -} - - -bool adv_list_update(ble_device_info_t &device) -{ - for (int i = 0; i < list_index; i++) - { - if (0 == memcmp(device.macaddr, device_list[i].macaddr, sizeof (device.macaddr))) - { - // Found and update the item - memcpy(device_list[i].loacalname, device.loacalname, sizeof(device.loacalname)); - return true; - } - } - return false; -} - -void adv_list_clear() -{ - list_index = 0; - memset(device_list, 0x00, sizeof(device_list)); -} - -// Process the Advertisement data -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr) -{ - ble_device_info_t device; - bt_addr_le_to_str (addrPtr, device.macaddr, sizeof (device.macaddr)); - memcpy(device.loacalname, " -NA-", sizeof(" -NA-")); - // Please see https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile - switch (type) { - case BT_DATA_NAME_SHORTENED: - case BT_DATA_NAME_COMPLETE: - memcpy(device.loacalname, dataPtr, data_len); - device.loacalname[data_len] = '\0'; - adv_list_update(device); - break; - } - adv_list_add(device); - return true; -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino b/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino deleted file mode 100644 index 852b8e3b..00000000 --- a/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -#include - -/* - This example can work with BatteryMonitor. - - Show how to control and response the connection interval request. -*/ - -// set up connection params - -ble_conn_param_t conn_param = {30.0, // minimum interval in ms 7.5 - 4000 - 50.0, // maximum interval in ms 7.5 - - 0, // latency - 4000 // timeout in ms 100 - 32000ms - }; - -const int ledPin = 13; // set ledPin to use on-board LED -BLECentral bleCentral; // create central instance -BLEPeripheralHelper *blePeripheral1 = NULL; // // peer peripheral device - -BLEService batteryService("180F"); // create service with a 16-bit UUID -BLECharCharacteristic batteryLevelChar("2A19", BLERead | BLENotify);// create switch characteristic -//and allow remote device to read and notify - -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr); - -void setup() -{ - Serial.begin(9600); - - // add service and characteristic - bleCentral.addAttribute(batteryService); - bleCentral.addAttribute(batteryLevelChar); - - // assign event handlers for connected, disconnected to central - bleCentral.setEventHandler(BLEConnected, bleCentralConnectHandler); - bleCentral.setEventHandler(BLEDisconnected, bleCentralDisconnectHandler); - bleCentral.setEventHandler(BLEUpdateParam, bleCentralUpdateParam); - - // advertise the service - bleCentral.setAdvertiseHandler(adv_found); - - // assign event handlers for characteristic - batteryLevelChar.setEventHandler(BLEWritten, switchCharacteristicWritten); - - bleCentral.begin(); - Serial.println(("Bluetooth device active, waiting for connections...")); -} - -void loop() -{ - static unsigned int counter = 0; - static char ledstate = 0; - delay(2000); - if (blePeripheral1) - { - counter++; - - if (counter % 3) - { - batteryLevelChar.read(*blePeripheral1); - } - else - { - ledstate = !ledstate; - batteryLevelChar.write(*blePeripheral1, ledstate); - } - } - -} - -void bleCentralConnectHandler(BLEHelper& peripheral) -{ - // peripheral connected event handler - blePeripheral1 = bleCentral.getPeerPeripheralBLE(peripheral); - Serial.print("Connected event, peripheral: "); - Serial.println(peripheral.address()); - // Start discovery the profiles in peripheral device - blePeripheral1->discover(); -} - -void bleCentralDisconnectHandler(BLEHelper& peripheral) -{ - // peripheral disconnected event handler - blePeripheral1 = NULL; - Serial.print("Disconnected event, peripheral: "); - Serial.println(peripheral.address()); - bleCentral.startScan(); -} - -void bleCentralUpdateParam(BLEHelper& peripheral) -{ - // peripheral update the connection interval event handler - Serial.print("UpdateParam event, peripheral: "); - blePeripheral1 = bleCentral.getPeerPeripheralBLE(peripheral);; - Serial.println(peripheral.address()); - - // Get connection interval that peer peripheral device wanted - ble_conn_param_t m_conn_param; - blePeripheral1->getConnParams(m_conn_param); - Serial.print("min interval = " ); - Serial.println(m_conn_param.interval_min ); - Serial.print("max interval = " ); - Serial.println(m_conn_param.interval_max ); - Serial.print("latency = " ); - Serial.println(m_conn_param.latency ); - Serial.print("timeout = " ); - Serial.println(m_conn_param.timeout ); - - //Update the connection interval - blePeripheral1->setConnectionInterval(m_conn_param.interval_min,m_conn_param.interval_max); -} - -void switchCharacteristicWritten(BLEHelper& peripheral, BLECharacteristic& characteristic) -{ - // Read response/Notification wrote new value to characteristic, update LED - Serial.print("Characteristic event, notify: "); - - int battery = batteryLevelChar.value(); - if (battery) - { - Serial.print("Battery Level % is now: "); // print it - Serial.println(battery); - delay(100); - - Serial.println("LED on"); - digitalWrite(ledPin, HIGH); - } - else - { - Serial.println("LED off"); - digitalWrite(ledPin, LOW); - } -} - -bool adv_found(uint8_t type, - const uint8_t *dataPtr, - uint8_t data_len, - const bt_addr_le_t *addrPtr) -{ - int i; - - Serial.print("[AD]:"); - Serial.print(type); - Serial.print(" data_len "); - Serial.println(data_len); - - switch (type) - { - case BT_DATA_UUID16_SOME: - case BT_DATA_UUID16_ALL: - { - if (data_len % UUID_SIZE_16 != 0) - { - Serial.println("AD malformed"); - return true; - } - for (i = 0; i < data_len; i += UUID_SIZE_16) - { - if (batteryService.uuidCompare(dataPtr + i, UUID_SIZE_16) == false) - { - continue; - } - - // Accept the advertisement - if (!bleCentral.stopScan()) - { - Serial.println("Stop LE scan failed"); - continue; - } - Serial.println("Connecting"); - // Connect to peripheral - bleCentral.connect(addrPtr, &conn_param); - return false; - } - } - } - - return true; -} - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino new file mode 100644 index 00000000..60748578 --- /dev/null +++ b/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino @@ -0,0 +1,125 @@ +/* + Copyright (c) 2016 Arduino LLC. 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 + +/* + This sketch example works with IMUBleNotification.ino + + IMUBleNotification.ino will send notification to this central sketch. + This sketch will receive the notifications and output the received data in the serial monitor. + It also illustrates using a non-typed characteristic. + Set the baud rate to 115200 on the serial monitor to accomodate the speed of constant data updates from IMU subsystem. +*/ + +#define LED_PIN 13 +#define MAX_IMU_RECORD 1 + +// define a structure that will serve as buffer for holding IMU data + +typedef struct { + int index; + unsigned int slot[3]; +} imuFrameType; + +imuFrameType imuBuf[MAX_IMU_RECORD]; + +void setup() +{ + // This is set to higher baud rate because accelerometer data changes very quickly + Serial.begin(9600); // initialize serial communication + while (!Serial); + pinMode(LED_PIN, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected + + /* Now activate the BLE device. It will start continuously transmitting BLE + advertising packets and will be visible to remote BLE central devices + until it receives a new connection */ + BLE.begin(); + Serial.println(BLE.address()); + + BLE.scanForName("Imu"); +} + + +void loop() +{ + BLEDevice peripheral = BLE.available(); + //pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (peripheral) + { + Serial.println(peripheral.address()); + BLE.stopScan(); + delay (1000); + // central connected to peripheral + controlImu(peripheral); + delay (4000); + BLE.scanForName("Imu"); + } +} + +void controlImu(BLEDevice peripheral) +{ + static bool discovered = false; + // connect to the peripheral + Serial.print("Connecting ... "); + Serial.println(peripheral.address()); + + if (peripheral.connect()) + { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + } + else + { + Serial.println("Failed to connect!"); + return; + } + + peripheral.discoverAttributes(); + + BLECharacteristic bleImuChar = peripheral.characteristic("F7580003-153E-D4F6-F26D-43D8D98EEB13"); + + if (!bleImuChar) + { + peripheral.disconnect(); + Serial.println("Peripheral does not have IMU characteristic!"); + delay(5000); + return; + } + bleImuChar.subscribe(); + + + discovered = false; + while (peripheral.connected()) + { + if (bleImuChar.valueUpdated()) + { + const unsigned char *cvalue = bleImuChar.value(); + const imuFrameType *value = (const imuFrameType *)cvalue; + Serial.print("\r\nCharacteristic event, written: "); + Serial.print(value->index); + Serial.print("\t"); + Serial.print(value->slot[0]); + Serial.print("\t"); + Serial.print(value->slot[1]); + Serial.print("\t"); + Serial.println(value->slot[2]); + } + } + Serial.print("Disconnected"); + Serial.println(peripheral.address()); +} diff --git a/libraries/CurieBLE/examples/central/led_control/led_control.ino b/libraries/CurieBLE/examples/central/led_control/led_control.ino new file mode 100644 index 00000000..8983a2ef --- /dev/null +++ b/libraries/CurieBLE/examples/central/led_control/led_control.ino @@ -0,0 +1,127 @@ +/* + Arduino BLE Central LED Control example + Copyright (c) 2016 Arduino LLC. 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 + +// variables for button +const int buttonPin = 2; +int oldButtonState = LOW; + + +void setup() { + Serial.begin(9600); + + // configure the button pin as input + pinMode(buttonPin, INPUT); + + // initialize the BLE hardware + BLE.begin(); + + Serial.println("BLE Central - LED control"); + + // start scanning for peripherals + BLE.scan(); +} + +void loop() { + // check if a peripheral has been discovered + BLEDevice peripheral = BLE.available(); + + if (peripheral) { + // discovered a peripheral, print out address, local name, and advertised service + Serial.print("Found "); + Serial.print(peripheral.address()); + Serial.print(" '"); + Serial.print(peripheral.localName()); + Serial.print("' "); + Serial.print(peripheral.advertisedServiceUuid()); + Serial.println(); + + // see if peripheral is advertising the LED service + if (peripheral.advertisedServiceUuid() == "19b10000-e8f2-537e-4f6c-d104768a1214") { + // stop scanning + BLE.stopScan(); + + controlLed(peripheral); + + // peripheral disconnected, start scanning again + BLE.scan(); + } + } +} + +void controlLed(BLEDevice peripheral) { + // connect to the peripheral + Serial.println("Connecting ..."); + + if (peripheral.connect()) { + Serial.println("Connected"); + } else { + Serial.println("Failed to connect!"); + return; + } + + // discover peripheral attributes + Serial.println("Discovering attributes ..."); + if (peripheral.discoverAttributes()) { + Serial.println("Attributes discovered"); + } else { + Serial.println("Attribute discovery failed!"); + peripheral.disconnect(); + return; + } + + // retrieve the LED characteristic + BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10001-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) { + Serial.println("Peripheral does not have LED characteristic!"); + peripheral.disconnect(); + return; + } else if (!ledCharacteristic.canWrite()) { + Serial.println("Peripheral does not have a writable LED characteristic!"); + peripheral.disconnect(); + return; + } + + while (peripheral.connected()) { + // while the peripheral is connection + + // read the button pin + int buttonState = digitalRead(buttonPin); + + if (oldButtonState != buttonState) { + // button changed + oldButtonState = buttonState; + + if (buttonState) { + Serial.println("button pressed"); + + // button is pressed, write 0x01 to turn the LED on + ledCharacteristic.writeByte(0x01); + } else { + Serial.println("button released"); + + // button is released, write 0x00 to turn the LED of + ledCharacteristic.writeByte(0x00); + } + } + } +} diff --git a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino new file mode 100644 index 00000000..5014b7ee --- /dev/null +++ b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino @@ -0,0 +1,152 @@ +/* + Arduino BLE Central peripheral explorer example + Copyright (c) 2016 Arduino LLC. 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 + +void setup() { + Serial.begin(9600); + + // initialize the BLE hardware + BLE.begin(); + + Serial.println("BLE Central - Peripheral Explorer"); + + // start scanning for peripherals + BLE.scan(); +} + +void loop() { + // check if a peripheral has been discovered + BLEDevice peripheral = BLE.available(); + + if (peripheral) { + // discovered a peripheral, print out address, local name, and advertised service + Serial.print("Found "); + Serial.print(peripheral.address()); + Serial.print(" '"); + Serial.print(peripheral.localName()); + Serial.print("' "); + Serial.print(peripheral.advertisedServiceUuid()); + Serial.println(); + + // see if peripheral is a LED + if (peripheral.localName() == "LED") { + // stop scanning + BLE.stopScan(); + + explorerPeripheral(peripheral); + + // peripheral disconnected, we are done + while (1) { + // do nothing + } + } + } +} + +void explorerPeripheral(BLEDevice peripheral) { + // connect to the peripheral + Serial.println("Connecting ..."); + + if (peripheral.connect()) { + Serial.println("Connected"); + } else { + Serial.println("Failed to connect!"); + return; + } + + // discover peripheral attributes + Serial.println("Discovering attributes ..."); + if (peripheral.discoverAttributes()) { + Serial.println("Attributes discovered"); + } else { + Serial.println("Attribute discovery failed!"); + peripheral.disconnect(); + return; + } + + // read and print device name of peripheral + Serial.println(); + Serial.print("Device name: "); + Serial.println(peripheral.deviceName()); + + // loop the services of the peripheral and explore each + for (int i = 0; i < peripheral.serviceCount(); i++) { + BLEService service = peripheral.service(i); + + exploreService(service); + } + + Serial.println(); + + // we are done exploring, disconnect + Serial.println("Disconnecting ..."); + peripheral.disconnect(); + Serial.println("Disconnected"); +} + +void exploreService(BLEService service) { + // print the UUID of the service + Serial.print("Service "); + Serial.println(service.uuid()); + + // loop the characteristics of the service and explore each + for (int i = 0; i < service.characteristicCount(); i++) { + BLECharacteristic characteristic = service.characteristic(i); + + exploreCharacteristic(characteristic); + } +} + +void exploreCharacteristic(BLECharacteristic characteristic) { + // print the UUID and properies of the characteristic + Serial.print("\tCharacteristic "); + Serial.print(characteristic.uuid()); + Serial.print(", properties 0x"); + Serial.print(characteristic.properties()); + + // check if the characteristic is readable + if (characteristic.canRead()) { + // read the characteristic value + characteristic.read(); + delay(1000); + if (characteristic.valueLength() > 0) + { + // print out the value of the characteristic + Serial.print(", value 0x"); + printData(characteristic.value(), characteristic.valueLength()); + } + } + + Serial.println(); + +} + +void printData(const unsigned char data[], int length) { + for (int i = 0; i < length; i++) { + unsigned char b = data[i]; + + if (b < 16) { + Serial.print("0"); + } + + Serial.print(b, HEX); + } +} + diff --git a/libraries/CurieBLE/examples/central/scan/scan.ino b/libraries/CurieBLE/examples/central/scan/scan.ino new file mode 100644 index 00000000..32c77c97 --- /dev/null +++ b/libraries/CurieBLE/examples/central/scan/scan.ino @@ -0,0 +1,70 @@ +/* + Arduino BLE Central scan example + Copyright (c) 2016 Arduino LLC. 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 + +void setup() { + Serial.begin(9600); + + // initialize the BLE hardware + BLE.begin(); + + Serial.println("BLE Central scan"); + + // start scanning for peripheral + BLE.scan(); +} + +void loop() { + // check if a peripheral has been discovered + BLEDevice peripheral = BLE.available(); + + if (peripheral) { + // discovered a peripheral + Serial.println("Discovered a peripheral"); + Serial.println("-----------------------"); + + // print address + Serial.print("Address: "); + Serial.println(peripheral.address()); + + // print the local name, if present + if (peripheral.hasLocalName()) { + Serial.print("Local Name: "); + Serial.println(peripheral.localName()); + } + + // print the advertised service UUID's, if present + if (peripheral.hasAdvertisedServiceUuid()) { + Serial.print("Service UUID's: "); + for (int i = 0; i < peripheral.advertisedServiceUuidCount(); i++) { + Serial.print(peripheral.advertisedServiceUuid(i)); + Serial.print(" "); + } + Serial.println(); + } + + // print the RSSI + Serial.print("RSSI: "); + Serial.println(peripheral.rssi()); + + Serial.println(); + } +} + diff --git a/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino b/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino new file mode 100644 index 00000000..ddb8b14c --- /dev/null +++ b/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino @@ -0,0 +1,72 @@ +/* + Arduino BLE Central scan callback example + Copyright (c) 2016 Arduino LLC. 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 + +void setup() { + Serial.begin(9600); + + // initialize the BLE hardware + BLE.begin(); + + Serial.println("BLE Central scan callback"); + + // set the discovered event handle + BLE.setEventHandler(BLEDiscovered, bleCentralDiscoverHandler); + + // start scanning for peripherals with duplicates + BLE.scan(true); +} + +void loop() { + // poll the central for events + BLE.poll(); +} + +void bleCentralDiscoverHandler(BLEDevice peripheral) { + // discovered a peripheral + Serial.println("Discovered a peripheral"); + Serial.println("-----------------------"); + + // print address + Serial.print("Address: "); + Serial.println(peripheral.address()); + + // print the local name, if present + if (peripheral.hasLocalName()) { + Serial.print("Local Name: "); + Serial.println(peripheral.localName()); + } + + // print the advertised service UUID's, if present + if (peripheral.hasAdvertisedServiceUuid()) { + Serial.print("Service UUID's: "); + for (int i = 0; i < peripheral.advertisedServiceUuidCount(); i++) { + Serial.print(peripheral.advertisedServiceUuid(i)); + Serial.print(" "); + } + Serial.println(); + } + + // print the RSSI + Serial.print("RSSI: "); + Serial.println(peripheral.rssi()); + + Serial.println(); +} diff --git a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino new file mode 100644 index 00000000..aa47cbe8 --- /dev/null +++ b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino @@ -0,0 +1,121 @@ +/* + Arduino BLE Central SensorTag button example + Copyright (c) 2016 Arduino LLC. 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 + +void setup() { + Serial.begin(9600); + + // initialize the BLE hardware + BLE.begin(); + + Serial.println("BLE Central - SensorTag button"); + + // start scanning for peripheral + BLE.scan(); +} + +void loop() { + // check if a peripheral has been discovered + BLEDevice peripheral = BLE.available(); + + if (peripheral) { + // discovered a peripheral, print out address, local name, and advertised service + Serial.print("Found "); + Serial.print(peripheral.address()); + Serial.print(" '"); + Serial.print(peripheral.localName()); + Serial.print("' "); + Serial.print(peripheral.advertisedServiceUuid()); + Serial.println(); + + // see if peripheral is a SensorTag + if (peripheral.localName() == "SensorTag") { + // stop scanning + BLE.stopScan(); + + monitorSensorTagButtons(peripheral); + + // peripheral disconnected, start scanning again + BLE.scan(); + } + } +} + +void monitorSensorTagButtons(BLEDevice peripheral) { + // connect to the peripheral + Serial.println("Connecting ..."); + if (peripheral.connect()) { + Serial.println("Connected"); + } else { + Serial.println("Failed to connect!"); + return; + } + + // discover peripheral attributes + Serial.println("Discovering attributes ..."); + if (peripheral.discoverAttributes()) { + Serial.println("Attributes discovered"); + } else { + Serial.println("Attribute discovery failed!"); + peripheral.disconnect(); + return; + } + + // retrieve the simple key characteristic + BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("ffe1"); + + // subscribe to the simple key characteristic + Serial.println("Subscribing to simple key characteristic ..."); + if (!simpleKeyCharacteristic) { + Serial.println("no simple key characteristic found!"); + peripheral.disconnect(); + return; + } else if (!simpleKeyCharacteristic.canSubscribe()) { + Serial.println("simple key characteristic is not subscribable!"); + peripheral.disconnect(); + return; + } else if (!simpleKeyCharacteristic.subscribe()) { + Serial.println("subscription failed!"); + peripheral.disconnect(); + return; + } else { + Serial.println("Subscribed"); + } + + while (peripheral.connected()) { + // while the peripheral is connected + + // check if the value of the simple key characteristic has been updated + if (simpleKeyCharacteristic.valueUpdated()) { + // yes, get the value, characteristic is 1 byte so use char value + int value = simpleKeyCharacteristic.charValue(); + + if (value & 0x01) { + // first bit corresponds to the right button + Serial.println("Right button pressed"); + } + + if (value & 0x02) { + // second bit corresponds to the left button + Serial.println("Left button pressed"); + } + } + } +} diff --git a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino similarity index 69% rename from libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino rename to libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino index ee57d295..c3ac0ff0 100644 --- a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino +++ b/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino @@ -1,8 +1,20 @@ /* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ + Copyright (c) 2016 Arduino LLC. 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 #include @@ -25,7 +37,6 @@ imuFrameType imuBuf[MAX_IMU_RECORD]; unsigned seqNum = 0; -BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) BLEService bleImuService("F7580001-153E-D4F6-F26D-43D8D98EEB13"); // Tx IMU data Characteristic BLECharacteristic bleImuChar("F7580003-153E-D4F6-F26D-43D8D98EEB13", // standard 128-bit characteristic UUID BLERead | BLENotify, sizeof(imuBuf)); // remote clients will be able to @@ -33,31 +44,34 @@ BLECharacteristic bleImuChar("F7580003-153E-D4F6-F26D-43D8D98EEB13", // standard void setup() { Serial.begin(9600); // initialize serial communication + while (!Serial); pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected + BLE.begin(); + CurieIMU.begin(); /* Set a local name for the BLE device This name will appear in advertising packets and can be used by remote devices to identify this BLE device The name can be changed but maybe be truncated based on space left in advertisement packet */ - blePeripheral.setLocalName("Imu"); - blePeripheral.setAdvertisedServiceUuid(bleImuService.uuid()); // add the service UUID - blePeripheral.addAttribute(bleImuService); // Add the Imu service - blePeripheral.addAttribute(bleImuChar); // add the Imu characteristic + BLE.setLocalName("Imu"); + BLE.setAdvertisedServiceUuid(bleImuService.uuid()); // add the service UUID + + BLE.addService(bleImuService); // Add the Imu service + bleImuService.addCharacteristic(bleImuChar); // add the Imu characteristic /* Now activate the BLE device. It will start continuously transmitting BLE advertising packets and will be visible to remote BLE central devices until it receives a new connection */ - blePeripheral.begin(); // Start the IMU - CurieIMU.begin(); + BLE.advertise(); } void loop() { // listen for BLE peripherals to connect: // Since we are a peripheral we need a central object to connect to - BLECentralHelper central = blePeripheral.central(); + BLEDevice central = BLE.central(); // if a central is connected to peripheral: if (central) @@ -79,7 +93,7 @@ void loop() while (central.connected()) { // Take IMU data every 100 msec - if ((millis() - sentTime) >= 100) + if ((millis() - sentTime) >= 1000) { recordImuData(0); sentTime = millis(); @@ -109,24 +123,5 @@ void recordImuData(int index) imuBuf[index].slot[0] = (unsigned int)((ax << 16) | (ay & 0x0FFFF)); imuBuf[index].slot[1] = (unsigned int)((az << 16) | (gx & 0x0FFFF)); imuBuf[index].slot[2] = (unsigned int)((gy << 16) | (gz & 0x0FFFF)); - } - -/* - Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/peripheral/led/led.ino b/libraries/CurieBLE/examples/peripheral/led/led.ino new file mode 100644 index 00000000..55948fd1 --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/led/led.ino @@ -0,0 +1,84 @@ +/* + Arduino BLE Peripheral LED example + Copyright (c) 2016 Arduino LLC. 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 + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10000e8f2537e4f6cd104768a1214"); + +// create switch characteristic +BLECharCharacteristic switchCharacteristic("19b10001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial.begin(9600); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + // set advertised local name and service UUID + BLE.setLocalName("LED"); + BLE.setAdvertisedServiceUuid(ledService.uuid()); + + switchCharacteristic.addDescriptor(switchDescriptor); + ledService.addCharacteristic(switchCharacteristic); + + // add service and characteristic + BLE.addService(ledService); + + BLE.advertise(); + + Serial.println(F("BLE LED Peripheral")); +} + +void loop() { + BLEDevice central = BLE.central(); + + if (central) { + // central connected to peripheral + Serial.print(F("Connected to central: ")); + Serial.println(central.address()); + + while (central.connected()) { + // central still connected to peripheral + if (switchCharacteristic.written()) { + // central wrote new value to characteristic, update LED + if (switchCharacteristic.value()) { + Serial.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + } else { + Serial.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + } + } + } + + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } +} diff --git a/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino b/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino new file mode 100644 index 00000000..e00bf419 --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino @@ -0,0 +1,90 @@ +/* + Arduino BLE Peripheral LED callback example + Copyright (c) 2016 Arduino LLC. 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 +*/ + +// Import libraries +#include + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10000e8f2537e4f6cd104768a1214"); + +// create switch characteristic +BLECharCharacteristic switchCharacteristic("19b10001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); + +void setup() { + Serial.begin(9600); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + + // set advertised local name and service UUID + BLE.setLocalName("LED"); + BLE.setAdvertisedServiceUuid(ledService.uuid()); + + ledService.addCharacteristic(switchCharacteristic); + + // add service + BLE.addService(ledService); + + // assign event handlers for connected, disconnected to peripheral + BLE.setEventHandler(BLEConnected, bleDeviceConnectHandler); + BLE.setEventHandler(BLEDisconnected, bleDeviceDisconnectHandler); + + // assign event handlers for characteristic + switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten); + + BLE.advertise(); + + Serial.println(F("BLE LED Peripheral")); +} + +void loop() { + // poll peripheral + BLE.poll(); +} + +void bleDeviceConnectHandler(BLEDevice central) { + // central connected event handler + Serial.print(F("Connected event, central: ")); + Serial.println(central.address()); +} + +void bleDeviceDisconnectHandler(BLEDevice central) { + // central disconnected event handler + Serial.print(F("Disconnected event, central: ")); + Serial.println(central.address()); +} + +void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + // central wrote new value to characteristic, update LED + Serial.print(F("Characteristic event, writen: ")); + + if (switchCharacteristic.value()) { + Serial.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + } else { + Serial.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + } +} diff --git a/libraries/CurieBLE/keywords.txt b/libraries/CurieBLE/keywords.txt index bb0f37ea..2db97095 100644 --- a/libraries/CurieBLE/keywords.txt +++ b/libraries/CurieBLE/keywords.txt @@ -6,99 +6,150 @@ # Datatypes (KEYWORD1) ####################################### -BLEAttribute KEYWORD1 +BLEAttributeWithValue KEYWORD1 +BLEBoolCharacteristic KEYWORD1 +BLEByteCharacteristic KEYWORD1 BLECentral KEYWORD1 BLECharacteristic KEYWORD1 +BLECharCharacteristic KEYWORD1 BLEDescriptor KEYWORD1 +BLEDevice KEYWORD1 +BLEDoubleCharacteristic KEYWORD1 +BLEFloatCharacteristic KEYWORD1 +BLEIntCharacteristic KEYWORD1 +BLELongCharacteristic KEYWORD1 BLEPeripheral KEYWORD1 BLEService KEYWORD1 -BLETypedCharacteristic KEYWORD1 -BLEHelper KEYWORD1 -BLECentralHelper KEYWORD1 -BLEPeripheralHelper KEYWORD1 - -BLECharCharacteristic KEYWORD1 -BLEUnsignedCharCharacteristic KEYWORD1 BLEShortCharacteristic KEYWORD1 -BLEUnsignedShortCharacteristic KEYWORD1 -BLEIntCharacteristic KEYWORD1 +BLEUnsignedCharCharacteristic KEYWORD1 BLEUnsignedIntCharacteristic KEYWORD1 -BLELongCharacteristic KEYWORD1 BLEUnsignedLongCharacteristic KEYWORD1 -BLEFloatCharacteristic KEYWORD1 -BLEDoubleCharacteristic KEYWORD1 +BLEUnsignedShortCharacteristic KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### +valueSize KEYWORD2 +value KEYWORD2 +writeValue KEYWORD2 +stringValue KEYWORD2 +charValue KEYWORD2 +unsignedCharValue KEYWORD2 +byteValue KEYWORD2 +shortValue KEYWORD2 +unsignedShortValue KEYWORD2 +intValue KEYWORD2 +unsignedIntValue KEYWORD2 +longValue KEYWORD2 +unsignedLongValue KEYWORD2 +floatValue KEYWORD2 +doubleValue KEYWORD2 +writeString KEYWORD2 +writeString KEYWORD2 +writeChar KEYWORD2 +writeUnsignedChar KEYWORD2 +writeByte KEYWORD2 +writeShort KEYWORD2 +writeUnsignedShort KEYWORD2 +writeInt KEYWORD2 +writeUnsignedInt KEYWORD2 +writeLong KEYWORD2 +writeUnsignedLong KEYWORD2 +writeFloat KEYWORD2 +writeDouble KEYWORD2 +setEventHandler KEYWORD2 +descriptor KEYWORD2 +hasDescriptor KEYWORD2 +descriptorCount KEYWORD2 +addDescriptor KEYWORD2 +valueUpdated KEYWORD2 +unsubscribe KEYWORD2 +subscribe KEYWORD2 +write KEYWORD2 +read KEYWORD2 uuid KEYWORD2 -numAttributes KEYWORD2 - -connected KEYWORD2 -address KEYWORD2 -poll KEYWORD2 -disconnect KEYWORD2 -discover KEYWORD2 -startScan KEYWORD2 -stopScan KEYWORD2 - -getConnParams KEYWORD2 -setConnectionInterval KEYWORD2 - properties KEYWORD2 -valueSize KEYWORD2 -value KEYWORD2 valueLength KEYWORD2 setValue KEYWORD2 -setEventHandler KEYWORD2 +writeValue KEYWORD2 +broadcast KEYWORD2 written KEYWORD2 subscribed KEYWORD2 +canNotify KEYWORD2 +canIndicate KEYWORD2 +canRead KEYWORD2 +canWrite KEYWORD2 +canSubscribe KEYWORD2 +canUnsubscribe KEYWORD2 -begin KEYWORD2 +connected KEYWORD2 +address KEYWORD2 +disconnect KEYWORD2 +poll KEYWORD2 -stopAdvertising KEYWORD2 -setConnectable KEYWORD2 -startAdvertising KEYWORD2 +begin KEYWORD2 +poll KEYWORD2 +end KEYWORD2 +disconnect KEYWORD2 +address KEYWORD2 setAdvertisedServiceUuid KEYWORD2 -setAdvertisedServiceData KEYWORD2 +setAdvertisedService KEYWORD2 +setServiceSolicitationUuid KEYWORD2 +setManufacturerData KEYWORD2 setLocalName KEYWORD2 -setDeviceName KEYWORD2 -setAppearance KEYWORD2 +setAdvertisingInterval KEYWORD2 setConnectionInterval KEYWORD2 -addAttribute KEYWORD2 +setTxPower KEYWORD2 +setConnectable KEYWORD2 +setDeviceName KEYWORD2 +addService KEYWORD2 +advertise KEYWORD2 +stopAdvertise KEYWORD2 central KEYWORD2 - -setValueLE KEYWORD2 -valueLE KEYWORD2 -setValueBE KEYWORD2 -valueBE KEYWORD2 - -getPeerPeripheralBLE KEYWORD2 -getPeerCentralBLE KEYWORD2 -uuidCompare KEYWORD2 +peripheral KEYWORD2 +scan KEYWORD2 +scanForName KEYWORD2 +scanForUuid KEYWORD2 +stopScan KEYWORD2 +available KEYWORD2 +hasLocalName KEYWORD2 +hasAdvertisedServiceUuid KEYWORD2 +advertisedServiceUuidCount KEYWORD2 +localName KEYWORD2 +advertisedServiceUuid KEYWORD2 +rssi KEYWORD2 +connect KEYWORD2 +discoverAttributes KEYWORD2 +discoverAttributesByService KEYWORD2 +deviceName KEYWORD2 +serviceCount KEYWORD2 +hasService KEYWORD2 +service KEYWORD2 +characteristicCount KEYWORD2 +hasCharacteristic KEYWORD2 +characteristic KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### -BLETypeService LITERAL1 -BLETypeCharacteristic LITERAL1 -BLETypeDescriptor LITERAL1 +BLEWritten LITERAL1 +BLESubscribed LITERAL1 +BLEUnsubscribed LITERAL1 +BLEValueUpdated LITERAL1 +BLEBroadcast LITERAL1 BLERead LITERAL1 BLEWriteWithoutResponse LITERAL1 BLEWrite LITERAL1 BLENotify LITERAL1 BLEIndicate LITERAL1 +BLEDiscovered LITERAL1 BLEConnected LITERAL1 BLEDisconnected LITERAL1 -BLEUpdateParam LITERAL1 - -BLEWritten LITERAL1 -BLESubscribed LITERAL1 -BLEUnsubscribed LITERAL1 +BLEConParamUpdate LITERAL1 ble_conn_param_t LITERAL1 bt_uuid_t LITERAL1 diff --git a/libraries/CurieBLE/library.properties b/libraries/CurieBLE/library.properties index 4b17054d..c37a6a35 100644 --- a/libraries/CurieBLE/library.properties +++ b/libraries/CurieBLE/library.properties @@ -1,7 +1,7 @@ name=CurieBLE -version=1.0 -author=Emutex -maintainer=Emutex +version=2.0 +author=Lianggao +maintainer=Lianggao sentence=Library to manage the Bluetooth Low Energy module with Curie Core boards. paragraph=Using this library, it is possible to use BLE features to communicate and interact with other devices like smartphones and tablets. This library enables multiple types of functionalities through a number of different classes. category=Communication diff --git a/libraries/CurieBLE/src/BLEAttribute.cpp b/libraries/CurieBLE/src/BLEAttribute.cpp deleted file mode 100644 index dc49158f..00000000 --- a/libraries/CurieBLE/src/BLEAttribute.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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 "BLEAttribute.h" - -unsigned char BLEAttribute::_numAttributes = 0; - -BLEAttribute::BLEAttribute(const char* uuid, BLEAttributeType type) : - _uuid_cstr(uuid), - _type(type), - _handle(0) -{ - char temp[] = {0, 0, 0}; - int strLength = strlen(uuid); - int length = 0; - - _numAttributes++; - - memset (&_uuid, 0x00, sizeof(_uuid)); - - for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) - { - if (uuid[i] == '-') - { - i++; - continue; - } - - temp[0] = uuid[i - 1]; - temp[1] = uuid[i]; - - _uuid.val[length] = strtoul(temp, NULL, 16); - - length++; - } - - if (length == 2) - { - uint16_t temp = (_uuid.val[1] << 8)| _uuid.val[0]; - _uuid.uuid.type = BT_UUID_TYPE_16; - ((bt_uuid_16_t*)(&_uuid.uuid))->val = temp; - } - else - { - _uuid.uuid.type = BT_UUID_TYPE_128; - } -} - -const char* -BLEAttribute::uuid() const { - return _uuid_cstr; -} - -const char* -BLEAttribute::uuid_cstr() const { - return _uuid_cstr; -} - -bt_uuid_t *BLEAttribute::uuid(void) -{ - return (bt_uuid_t *)&_uuid; -} - - -BLEAttributeType -BLEAttribute::type() const { - return this->_type; -} - -uint16_t -BLEAttribute::handle() { - return _handle; -} - -void -BLEAttribute::setHandle(uint16_t handle) { - _handle = handle; -} - - -unsigned char -BLEAttribute::numAttributes() { - return _numAttributes; -} - -bool BLEAttribute::discovering() -{ - return _discoverying; -} - -bool BLEAttribute::uuidCompare(const uint8_t *data, uint8_t uuidsize) -{ - bt_uuid_t * serviceuuid = this->uuid(); - - bool status = true; - if(serviceuuid->type == BT_UUID_TYPE_16 && uuidsize == UUID_SIZE_16) - { - status = memcmp (&((bt_uuid_16_t*)serviceuuid)->val, data, UUID_SIZE_16); - } - else if(serviceuuid->type == BT_UUID_TYPE_128 && uuidsize == UUID_SIZE_128) - { - status = memcmp (((bt_uuid_128_t*)serviceuuid)->val, data, UUID_SIZE_128); - } - - return !status; -} diff --git a/libraries/CurieBLE/src/BLEAttribute.h b/libraries/CurieBLE/src/BLEAttribute.h deleted file mode 100644 index 1de33613..00000000 --- a/libraries/CurieBLE/src/BLEAttribute.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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_ATTRIBUTE_H_INCLUDED -#define _BLE_ATTRIBUTE_H_INCLUDED - -#include "BLECommon.h" - -/// BLE attribute tyep enum -typedef enum { - BLETypeService = 0x2800, ///< the service type - BLETypeCharacteristic = 0x2803, ///< the characteristic type - BLETypeDescriptor = 0x2900 ///< the descriptor type -}BLEAttributeType; - -// Class declare -class BLEProfile; -class BLEPeripheral; -class BLEPeripheralHelper; - -class BLEAttribute { -public: - - /** - * Get the string representation of the Attribute - * - * @return const char* string representation of the Attribute - */ - const char* uuid(void) const; - - /** - * Get the string representation of the Attribute - * - * @return const char* string representation of the Attribute - */ - const char* uuid_cstr(void) const; - - /** - * @brief Get the UUID raw data - * - * @param none - * - * @return bt_uuid_t* The pointer of UUID - * - * @note none - */ - bt_uuid_t *uuid(void); - - /** - * @brief Compare the UUID with the paramater data - * - * @param[in] data The pointer of data - * - * @param[in] uuidsize The max size of UUID - * - * @return bool true - UUID is the same with data - * false- UUID is not the same with data - * - * @note none - */ - bool uuidCompare(const uint8_t *data, uint8_t uuidsize); - -protected: - //friend BLEPeripheral; - friend BLEProfile; - - friend ssize_t profile_write_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - const void *buf, uint16_t len, - uint16_t offset); - friend ssize_t profile_read_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - void *buf, uint16_t len, - uint16_t offset); - - friend ssize_t profile_longwrite_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, - uint16_t offset); - friend int profile_longflush_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - uint8_t flags); - - BLEAttribute(const char* uuid, BLEAttributeType type); - - BLEAttributeType type(void) const; - uint16_t handle(void); - void setHandle(uint16_t handle); - - static unsigned char numAttributes(void); - // The below APIs are for central device to discover the - virtual void discover(bt_gatt_discover_params_t *params) = 0; - virtual void discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) = 0; - - /** - * @brief Get attribute's discover state - * - * @param none - * - * @return bool true - In discovering state - * false- Not discovering - * - * @note none - */ - bool discovering(); - - bool _discoverying; -private: - static unsigned char _numAttributes; - - const char* _uuid_cstr; - bt_uuid_128_t _uuid; - - BLEAttributeType _type; - uint16_t _handle; - -}; - -#endif // _BLE_ATTRIBUTE_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEAttributeWithValue.cpp b/libraries/CurieBLE/src/BLEAttributeWithValue.cpp new file mode 100644 index 00000000..88c629f1 --- /dev/null +++ b/libraries/CurieBLE/src/BLEAttributeWithValue.cpp @@ -0,0 +1,181 @@ +/* + BLE Attribute with value API + Copyright (c) 2016 Arduino LLC. 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 "BLEAttributeWithValue.h" + +BLEAttributeWithValue::BLEAttributeWithValue() +{ + +} + + // intepret the value of the attribute with the specified type +String BLEAttributeWithValue::stringValue() const +{ + const char *retTemp = (const char *)this->value(); + return retTemp; +} + +char BLEAttributeWithValue::charValue() const +{ + char ret = this->operator[](0); + return ret; +} + +unsigned char BLEAttributeWithValue::unsignedCharValue() const +{ + unsigned char ret = this->operator[](0); + return ret; +} + +byte BLEAttributeWithValue::byteValue() const +{ + return this->operator[](0); +} + +short BLEAttributeWithValue::shortValue() const +{ + short retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +unsigned short BLEAttributeWithValue::unsignedShortValue() const +{ + unsigned short retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +int BLEAttributeWithValue::intValue() const +{ + int retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +unsigned int BLEAttributeWithValue::unsignedIntValue() const +{ + unsigned int retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +long BLEAttributeWithValue::longValue() const +{ + long retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +unsigned long BLEAttributeWithValue::unsignedLongValue() const +{ + unsigned long retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +float BLEAttributeWithValue::floatValue() const +{ + float retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +double BLEAttributeWithValue::doubleValue() const +{ + double retTmp = 0; + memcpy(&retTmp, this->value(), sizeof(retTmp)); + return retTmp; +} + +// write the value of the attribute with the specified type +bool BLEAttributeWithValue::writeString(const String& s) +{ + if (s.length() > (unsigned int)this->valueSize()) + { + return false; + } + return this->writeValue((const byte*)s.c_str(), s.length()); +} + +bool BLEAttributeWithValue::writeString(const char* s) +{ + if (strlen(s) > (unsigned int)this->valueSize()) + { + return false; + } + return this->writeValue((const byte*)s, strlen(s)); +} + +bool BLEAttributeWithValue::writeChar(char c) +{ + return this->writeValue((const byte*)&c, sizeof(c)); +} + +bool BLEAttributeWithValue::writeUnsignedChar(unsigned char c) +{ + return this->writeValue((const byte*)&c, sizeof(c)); +} + +bool BLEAttributeWithValue::writeByte(byte b) +{ + return this->writeValue((const byte*)&b, sizeof(b)); +} + +bool BLEAttributeWithValue::writeShort(short s) +{ + return this->writeValue((const byte*)&s, sizeof(s)); +} + +bool BLEAttributeWithValue::writeUnsignedShort(unsigned short s) +{ + return this->writeValue((const byte*)&s, sizeof(s)); +} + +bool BLEAttributeWithValue::writeInt(int i) +{ + return this->writeValue((const byte*)&i, sizeof(i)); +} + +bool BLEAttributeWithValue::writeUnsignedInt(unsigned int i) +{ + return this->writeValue((const byte*)&i, sizeof(i)); +} + +bool BLEAttributeWithValue::writeLong(long l) +{ + return this->writeValue((const byte*)&l, sizeof(l)); +} + +bool BLEAttributeWithValue::writeUnsignedLong(unsigned int l) +{ + return this->writeValue((const byte*)&l, sizeof(l)); +} + +bool BLEAttributeWithValue::writeFloat(float f) +{ + return this->writeValue((const byte*)&f, sizeof(f)); +} + +bool BLEAttributeWithValue::writeDouble(double d) +{ + return this->writeValue((const byte*)&d, sizeof(d)); +} + + diff --git a/libraries/CurieBLE/src/BLEAttributeWithValue.h b/libraries/CurieBLE/src/BLEAttributeWithValue.h new file mode 100644 index 00000000..96971d56 --- /dev/null +++ b/libraries/CurieBLE/src/BLEAttributeWithValue.h @@ -0,0 +1,63 @@ +/* + BLE Attribute with value API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_ATTRIBUTE_WITH_VALUE__H +#define ARDUINO_BLE_ATTRIBUTE_WITH_VALUE__H + +class BLEAttributeWithValue +{ + public: + BLEAttributeWithValue(); + + virtual int valueSize() const = 0; // returns the length of the attribute value + virtual const byte* value() const = 0; // returns the value of the attribute array + virtual byte operator[] (int offset) const = 0; // access an attribute value at the specified offset + virtual bool writeValue(const byte value[], int length) = 0; + + // intepret the value of the attribute with the specified type + String stringValue() const; + char charValue() const; + unsigned char unsignedCharValue() const; + byte byteValue() const; + short shortValue() const; + unsigned short unsignedShortValue() const; + int intValue() const; + unsigned int unsignedIntValue() const; + long longValue() const; + unsigned long unsignedLongValue() const; + float floatValue() const; + double doubleValue() const; + + // write the value of the attribute with the specified type + bool writeString(const String& s); + bool writeString(const char* s); + bool writeChar(char c); + bool writeUnsignedChar(unsigned char c); + bool writeByte(byte b); + bool writeShort(short s); + bool writeUnsignedShort(unsigned short s); + bool writeInt(int i); + bool writeUnsignedInt(unsigned int i); + bool writeLong(long l); + bool writeUnsignedLong(unsigned int l); + bool writeFloat(float f); + bool writeDouble(double d); +}; + +#endif diff --git a/libraries/CurieBLE/src/BLECentral.cpp b/libraries/CurieBLE/src/BLECentral.cpp index 728b222c..862dc272 100644 --- a/libraries/CurieBLE/src/BLECentral.cpp +++ b/libraries/CurieBLE/src/BLECentral.cpp @@ -1,124 +1,61 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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 "BLECentralRole.h" - -#include "BLECentral.h" -#include "internal/ble_client.h" - -bool BLECentral::startScan() -{ - return BLECentralRole::instance()->startScan(); -} - -bool BLECentral::startScan(float interval, float window) -{ - setScanParam(interval, window); - return BLECentralRole::instance()->startScan(); -} - -bool BLECentral::stopScan() -{ - return BLECentralRole::instance()->stopScan(); -} - -bool BLECentral::connect(const bt_addr_le_t *addr, const ble_conn_param_t *param) -{ - bt_le_conn_param_t conn_param; - - conn_param.latency = param->latency; - conn_param.interval_max = (uint16_t)MSEC_TO_UNITS(param->interval_max, UNIT_1_25_MS); - conn_param.interval_min = (uint16_t)MSEC_TO_UNITS(param->interval_min, UNIT_1_25_MS); - conn_param.timeout = MSEC_TO_UNITS(param->timeout, UNIT_10_MS); - - pr_debug(LOG_MODULE_BLE,"Latency-%d\r\nInterval min-%d, max-%d\r\ntimeout:%d", - conn_param.latency, - conn_param.interval_min, - conn_param.interval_max, - conn_param.timeout); - - return BLECentralRole::instance()->connect(addr, &conn_param); -} - -void BLECentral::discover(BLEPeripheralHelper &peripheral) -{ - peripheral.discover(); -} - -void BLECentral::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) -{ - BLECentralRole::instance()->setEventHandler(event, callback); -} - -void BLECentral::setAdvertiseHandler(ble_advertise_handle_cb_t advcb) -{ - BLECentralRole::instance()->setAdvertiseHandler(advcb); -} - -void BLECentral::setScanParam(float interval, float window) -{ - bt_le_scan_param_t scan_param; - scan_param.type = BT_HCI_LE_SCAN_ACTIVE; - scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; - scan_param.interval = (uint16_t)MSEC_TO_UNITS(interval, UNIT_0_625_MS);; - scan_param.window = (uint16_t)MSEC_TO_UNITS(window, UNIT_0_625_MS);; - BLECentralRole::instance()->setScanParam(scan_param); -} - -BleStatus BLECentral::addAttribute(BLEAttribute& attribute) -{ - return BLECentralRole::instance()->addAttribute(attribute); -} - -bool BLECentral::begin(void) -{ - bool retval = BLECentralRole::instance()->begin(); - if (!retval) - { - pr_error(LOG_MODULE_BLE,"%s: Intit failed", __FUNCTION__); - return false; - } - - // Start scan - const bt_le_scan_param_t *scan_param = BLECentralRole::instance()->getScanParam(); - bt_le_scan_param_t zero_param; - memset(&zero_param, 0x00, sizeof (zero_param)); - if (0 == memcmp(&zero_param, scan_param, sizeof (zero_param))) - { - // Not set the scan parameter. - // Use the default scan parameter to scan - zero_param.type = BT_HCI_LE_SCAN_ACTIVE; - zero_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; - zero_param.interval = BT_GAP_SCAN_FAST_INTERVAL;//BT_GAP_SCAN_SLOW_INTERVAL_1;// - zero_param.window = BT_GAP_SCAN_FAST_WINDOW; //BT_GAP_SCAN_SLOW_WINDOW_1;// - retval = BLECentralRole::instance()->startScan(zero_param); - } - else - { - retval = BLECentralRole::instance()->startScan(); - } - return retval; -} - -BLEPeripheralHelper *BLECentral::getPeerPeripheralBLE(BLEHelper& peripheral) -{ - return (BLEPeripheralHelper *)(&peripheral); -} - - +/* + BLE Central API (deprecated) + Copyright (c) 2016 Arduino LLC. 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 "CurieBLE.h" + +BLECentral::BLECentral(BLEDevice& device) : + _device(&device) +{ + +} + +bool BLECentral::connected(void) +{ + return _device.connected(); +} + +const char* BLECentral::address(void) const +{ + return _device.address().c_str(); +} + +bool BLECentral::disconnect(void) +{ + return _device.disconnect(); +} + +void BLECentral::poll(void) +{ + _device.poll(); +} + +BLECentral::operator bool(void) const +{ + return _device; +} + +bool BLECentral::operator==(const BLECentral& rhs) const +{ + return (_device == rhs._device); +} + +bool BLECentral::operator!=(const BLECentral& rhs) const +{ + return (_device != rhs._device); +} diff --git a/libraries/CurieBLE/src/BLECentral.h b/libraries/CurieBLE/src/BLECentral.h index 09379b2b..47d98f77 100644 --- a/libraries/CurieBLE/src/BLECentral.h +++ b/libraries/CurieBLE/src/BLECentral.h @@ -1,165 +1,46 @@ /* - * Copyright (c) 2015 Intel Corporation. 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. + BLE Central API (deprecated) + Copyright (c) 2016 Arduino LLC. All right reserved. - * 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. + 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. - * 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 - * - */ + 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. -#ifndef _BLE_CENTRAL_H_INCLUDED -#define _BLE_CENTRAL_H_INCLUDED + 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 +*/ +// The API in this file is in DEPRECATED MODE, please DO NOT use it for Sketch construction +#ifndef ARDUINO_CENTRAL_H +#define ARDUINO_CENTRAL_H -#include "BLECommon.h" -#include "BLERoleBase.h" +class BLECentral { + public: + bool connected(void); // is the central connected + + const char* address(void) const; // address of the Central in string form -class BLEAttribute; + bool disconnect(void); // Disconnect the central if it is connected + void poll(void); // Poll the central for events -/** - * @brief A class defining the BLE central function - * - * This class abstract the BLE central. - */ -class BLECentral{ -public: - /** - * @brief Start scan - * - * @param none - * - * @return bool Indicate the success or error - * - * @note none - */ - bool startScan(); - - /** - * @brief Start scan with scan parameter - * - * @param[in] interval The scan interval in ms - * - * @param[in] window The scan window in ms - * - * @return bool Indicate the success or error - * - * @note none - */ - bool startScan(float interval, float window); - - /** - * @brief Stop scan - * - * @param none - * - * @return bool Indicate the success or error - * - * @note none - */ - bool stopScan(); - - /** - * @brief Schedule a connect request to peripheral to establish a connection - * - * @param[in] addr The MAC address of peripheral device that want to establish connection - * - * @param[in] param The connetion parameters - * - * @return bool Indicate the success or error - * - * @note none - */ - bool connect(const bt_addr_le_t *addr, const ble_conn_param_t *param); - - /** - * @brief Discover the peripheral device profile - * - * @param[in] peripheral The Peripheral that need to discover the profile - * - * @return none - * - * @note none - */ - void discover(BLEPeripheralHelper &peripheral); - - /** - * @brief Set the scan parameter - * - * @param[in] interval The scan interval in ms - * - * @param[in] window The scan window in ms - * - * @return none - * - * @note 1. The scale of the interval and window are 2.5 - 10240ms - * 2. The scan interval and window are like below. - * The device can see the ADV packet in the window. - * window - * ---- ---- - * | | | | - * --- ------- ---- - * |interval| - */ - void setScanParam(float interval, float window); - - /** - * @brief Add an attribute to the BLE Central Device - * - * @param[in] attribute Attribute to add to Central - * - * @return BleStatus indicating success or error - * - * @note The attribute will used for discover the peripheral handler - * Only need check return value at first call. Memory only alloc at first call - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * Provide a function to be called when events related to this Device are raised - * - * @param[in] event Event type for callback - * @param[in] callback Pointer to callback function to invoke when an event occurs. - */ - void setEventHandler(BLERoleEvent event, BLERoleEventHandler callback); - - /** - * @brief Provide a function to be called when scanned the advertisement - * - * @param[in] advcb Pointer to callback function to invoke when advertisement received - * - * @return none - * - * @note none - */ - void setAdvertiseHandler(ble_advertise_handle_cb_t advcb); - - /** - * @brief Setup attributes and start scan - * - * @return bool indicating success or error - */ - bool begin(void); - - /** - * @brief Get peer peripheral device - * - *@param peripheral peer peripheral device of the central board - * - * @return pointer of peer peripheral device - */ - BLEPeripheralHelper *getPeerPeripheralBLE(BLEHelper& peripheral); + operator bool(void) const; + bool operator==(const BLECentral& rhs) const; + bool operator!=(const BLECentral& rhs) const; protected: -private: - + friend class BLECharacteristicImp; + friend void bleBackCompatiblePeripheralConnectHandler(BLEDevice central); + friend void bleBackCompatiblePeripheralDisconnectHandler(BLEDevice central); + friend class BLEPeripheral; + BLECentral(BLEDevice& device); + private: + + BLEDevice _device; }; #endif diff --git a/libraries/CurieBLE/src/BLECentralHelper.h b/libraries/CurieBLE/src/BLECentralHelper.h deleted file mode 100644 index 4a563d97..00000000 --- a/libraries/CurieBLE/src/BLECentralHelper.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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_CENTRAL_HELPER_H_INCLUDED -#define _BLE_CENTRAL_HELPER_H_INCLUDED - -#include "BLECommon.h" -#include "BLEHelper.h" - -class BLEPeripheralRole; - -class BLECentralHelper: public BLEHelper{ - friend class BLEPeripheralRole; - friend class BLECentralRole; - - public: - /** - * Is the Central connected - * - * @return boolean_t true if the central is connected, otherwise false - */ - bool connected(void); - - /** - * Disconnect the central if it is connected - * - */ - bool disconnect(void); - - /** - * Poll the central for events - */ - void poll(void); - - protected: - BLECentralHelper(BLEPeripheralRole* peripheral); - - private: - BLEPeripheralRole* _peripheral; -}; - -#endif diff --git a/libraries/CurieBLE/src/BLECentralRole.cpp b/libraries/CurieBLE/src/BLECentralRole.cpp deleted file mode 100644 index 011a4031..00000000 --- a/libraries/CurieBLE/src/BLECentralRole.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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 "BLECentralRole.h" - - -void ble_central_device_found(const bt_addr_le_t *addr, - int8_t rssi, - uint8_t type, - const uint8_t *ad, - uint8_t len) -{ - char dev[BT_ADDR_LE_STR_LEN]; - - bt_addr_le_to_str(addr, dev, sizeof(dev)); - pr_debug(LOG_MODULE_BLE, "[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", - dev, type, len, rssi); - - BLECentralRole::instance()->handleDeviceFound(addr, rssi, type, - ad, len); -} - - -BLECentralRole* BLECentralRole::_ble_central_ins = NULL; - -BLECentralRole *BLECentralRole::instance() -{ - if (NULL == _ble_central_ins) - { - _ble_central_ins = new BLECentralRole(); - } - return _ble_central_ins; -} - -BLECentralRole::BLECentralRole(): - _central(NULL), _adv_event_handle(NULL) -{ - memset(_peripherial, 0, sizeof (_peripherial)); - for (int i = 0; i < BLE_MAX_CONN_CFG; i++) - { - _peripherial[i] = new BLEPeripheralHelper(this); - } - memset (&_scan_param, 0x00, sizeof (_scan_param)); - _central.setAddress(_local_bda); -} - - -BLECentralRole::~BLECentralRole() -{ - for (int i = 0; i < BLE_MAX_CONN_CFG; i++) - { - delete (_peripherial[i]); - //_peripherial[i] = NULL; - } -} - -const BLECentralHelper *BLECentralRole::central(void) const -{ - return &_central; -} - -bool BLECentralRole::connect(const bt_addr_le_t *addr, const bt_le_conn_param_t *param) -{ - BLEPeripheralHelper* temp = NULL; - BLEPeripheralHelper* unused = NULL; - bool link_existed = false; - bool retval = false; - - // Find free peripheral Items - for (int i = 0; i < BLE_MAX_CONN_CFG; i++) - { - temp = _peripherial[i]; - if (true == *temp) - { - if (*temp == *addr) - { - // Connect request has scheduled but connection don't established. - // The central can see the ADV and no need to send connect request. - link_existed = true; - break; - } - } - else - { - if (NULL == unused) - { - unused = temp; - } - } - } - - if (!link_existed) - { - // Send connect request - bt_conn_t* conn = bt_conn_create_le(addr, param); - if (NULL != conn) - { - unused->setAddress(*addr); - retval = true; - bt_conn_unref(conn); - } - } - return retval; -} - -bool BLECentralRole::startScan() -{ - int err = bt_le_scan_start(&_scan_param, ble_central_device_found); - if (err) - { - pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); - return false; - } - return true; -} - -bool BLECentralRole::startScan(const bt_le_scan_param_t &scan_param) -{ - setScanParam(scan_param); - return startScan(); -} - -void BLECentralRole::setScanParam(const bt_le_scan_param_t &scan_param) -{ - memcpy(&_scan_param, &scan_param, sizeof (_scan_param)); -} - -const bt_le_scan_param_t* BLECentralRole::getScanParam() -{ - return &_scan_param; -} - - -bool BLECentralRole::stopScan() -{ - int err = bt_le_scan_stop(); - if (err) - { - pr_info(LOG_MODULE_BLE, "Stop LE scan failed (err %d)\n", err); - return false; - } - return true; -} - -BLEPeripheralHelper* BLECentralRole::peripheral(bt_conn_t *conn) -{ - BLEPeripheralHelper* temp = NULL; - const bt_addr_le_t *addr = bt_conn_get_dst(conn); - // Find free peripheral Items - for (int i = 0; i < BLE_MAX_CONN_CFG; i++) - { - temp = _peripherial[i]; - if (*temp == *addr) - { - return temp; - } - } - return NULL; -} - - -void BLECentralRole::handleDeviceFound(const bt_addr_le_t *addr, - int8_t rssi, - uint8_t type, - const uint8_t *ad, - uint8_t data_len) -{ - const uint8_t *data = ad; - - if (_adv_event_handle == NULL) - { - return; - } - - /* We're only interested in connectable events */ - if (type == BT_LE_ADV_IND || type == BT_LE_ADV_DIRECT_IND) - { - pr_debug(LOG_MODULE_BLE, "%s", __FUNCTION__); - - while (data_len > 1) - { - uint8_t len = data[0]; - - /* Check for early termination */ - if (len == 0) { - return; - } - - if ((len + 1 > data_len) || (data_len < 2)) { - pr_info(LOG_MODULE_BLE, "AD malformed\n"); - return; - } - - if (!_adv_event_handle(data[1], &data[2], len - 1, addr)) - { - return; - } - - data_len -= len + 1; - data += len + 1; - } - pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); - } -} - -void BLECentralRole::handleConnectEvent(bt_conn_t *conn, uint8_t err) -{ - if (_event_handlers[BLEConnected]) - { - BLEPeripheralHelper *temp = peripheral(conn); - _event_handlers[BLEConnected](*temp); - } -} - -void BLECentralRole::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) -{ - if (_event_handlers[BLEDisconnected]) - { - BLEPeripheralHelper *temp = peripheral(conn); - _event_handlers[BLEDisconnected](*temp); - temp->linkLost(); - } -} - -void BLECentralRole::handleParamUpdated(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout) -{ - if (_event_handlers[BLEUpdateParam]) - { - BLEPeripheralHelper *temp = peripheral(conn); - temp->setConnectionParameters(interval, interval, latency, timeout); - _event_handlers[BLEUpdateParam](*temp); - } -} - -void BLECentralRole::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) -{ - - if (event < sizeof(_event_handlers)) - { - _event_handlers[event] = callback; - } -} - -void BLECentralRole::setAdvertiseHandler(ble_advertise_handle_cb_t advcb) -{ - _adv_event_handle = advcb; -} - -BleStatus BLECentralRole::addAttribute(BLEAttribute& attribute) -{ - BleStatus err = BLE_STATUS_SUCCESS; - for (int i = 0; i < BLE_MAX_CONN_CFG; i++) - { - err = _peripherial[i]->addAttribute(attribute); - if (err != BLE_STATUS_SUCCESS) - { - break; - } - } - return err; -} - -bool BLECentralRole::begin() -{ - BleStatus status; - status = _init(); - if (status != BLE_STATUS_SUCCESS) - { - return false; - } - return true; -} - -bool BLECentralRole::disconnect() -{ - return true; -} - - diff --git a/libraries/CurieBLE/src/BLECentralRole.h b/libraries/CurieBLE/src/BLECentralRole.h deleted file mode 100644 index 0ea6c008..00000000 --- a/libraries/CurieBLE/src/BLECentralRole.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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_CENTRALROLE_H_INCLUDED -#define _BLE_CENTRALROLE_H_INCLUDED -#include "BLECommon.h" -#include "BLEPeripheralHelper.h" -#include "BLECentralHelper.h" -#include "BLERoleBase.h" - -class BLECentralRole: public BLERoleBase { -public: - /** - * @brief Start scan - * - * @param none - * - * @return bool Indicate the success or error - * - * @note none - */ - bool startScan(); - - /** - * @brief Start scan with scan parameter - * - * @param none - * - * @return bool Indicate the success or error - * - * @note none - */ - bool startScan(const bt_le_scan_param_t &scan_param); - - /** - * @brief Stop scan - * - * @param none - * - * @return bool Indicate the success or error - * - * @note none - */ - bool stopScan(); - - /** - * @brief Schedule a connect request to peripheral to establish a connection - * - * @param[in] addr The MAC address of peripheral device that want to establish connection - * - * @param[in] param The connetion parameters - * - * @return bool Indicate the success or error - * - * @note none - */ - bool connect(const bt_addr_le_t *addr, const bt_le_conn_param_t *param); - - /** - * @brief Set the scan parameter - * - * @param[in] scan_param The scan parameter want to be set - * - * @return none - * - * @note none - */ - void setScanParam(const bt_le_scan_param_t &scan_param); - - /** - * @brief Get the scan parameter - * - * @param none - * - * @return const bt_le_scan_param_t* The scan parameter that current used - * - * @note none - */ - const bt_le_scan_param_t* getScanParam(); - - /** - * @brief Discover the peripheral device profile - * - * @param peripheral The Peripheral that need to discover the profile - * - * @return none - * - * @note none - */ - void discover(BLEPeripheralHelper &peripheral); - - /** - * @brief Add an attribute to the BLE Central Device - * - * @param[in] attribute Attribute to add to Central - * - * @return BleStatus indicating success or error - * - * @note The attribute will used for discover the peripheral handler - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * @brief Provide a function to be called when events related to this Device are raised - * - * @param[in] event Event type for callback - * @param[in] callback Pointer to callback function to invoke when an event occurs. - */ - void setEventHandler(BLERoleEvent event, BLERoleEventHandler callback); - - /** - * @brief Provide a function to be called when scanned the advertisement - * - * @param[in] advcb Pointer to callback function to invoke when advertisement received - * - * @return none - * - * @note none - */ - void setAdvertiseHandler(ble_advertise_handle_cb_t advcb); - - /** - * @brief Get BLE peripheral helper by conntion - * - * @param[in] conn The connection object - * - * @return BLEPeripheralHelper* The BLE peripheral helper - * - * @note none - */ - BLEPeripheralHelper* peripheral(bt_conn_t *conn); - - /** - * @brief Get BLE central helper that for APP use - * - * @param none - * - * @return const BLECentralHelper * The BLE central helper - * - * @note none - */ - const BLECentralHelper *central(void) const; - - /** - * Setup attributes and start advertising - * - * @return bool indicating success or error - */ - bool begin(); - - /** - * @brief Disconnect the central connected if there is one connected - * - * @param none - * - * @return bool Indicating success or error - * - * @note none - */ - bool disconnect(); - - /** - * @brief Get BLE Central instance. - * - * @param none - * - * @return BLECentralRole* The BLE Central instance - * - * @note Singleton. Only have one object to communicate with - * stack and manage the device - */ - static BLECentralRole *instance(); - -protected: - friend void ble_central_device_found(const bt_addr_le_t *addr, - int8_t rssi, - uint8_t type, - const uint8_t *ad, - uint8_t len); - - /** - * @brief Handle the connected event - * - * @param[in] conn The object that established the connection - * - * @param[in] err The code of the process - * - * @return none - * - * @note none - */ - void handleConnectEvent(bt_conn_t *conn, uint8_t err); - - /** - * @brief Handle the disconnected event - * - * @param[in] conn The object that lost the connection - * - * @param[in] reason The link lost reason - * - * @return none - * - * @note none - */ - void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); - - /** - * @brief Handle the conntion update request - * - * @param[in] conn The connection object that need to process the update request - * - * @param[in] interval The connection interval (N*1.25)ms - * - * @param[in] latency The connection latency - * - * @param[in] timeout The connection timeout (N*10)ms - * - * @return none - * - * @note none - */ - void handleParamUpdated(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout); - /** - * @brief Handle the advertisement - * - * @param[in] addr The device's MAC address that send out ADV - * - * @param[in] rssi The antenna's RSSI - * - * @param[in] type The advertise type - * - * @param[in] ad The advertisement RAW data - * - * @param[in] len The RAW data's length - * - * @return none - * - * @note none - */ - void handleDeviceFound(const bt_addr_le_t *addr, - int8_t rssi, - uint8_t type, - const uint8_t *ad, - uint8_t len); -private: - BLECentralRole(); - ~BLECentralRole(); - BLEPeripheralHelper* _peripherial[BLE_MAX_CONN_CFG]; - BLECentralHelper _central; - bt_le_scan_param_t _scan_param; - - static BLECentralRole* _ble_central_ins; - ble_advertise_handle_cb_t _adv_event_handle; -}; - -#endif - diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 3e46e0a1..f8432b11 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Intel Corporation. All rights reserved. + * Copyright (c) 2016 Intel Corporation. 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 @@ -17,423 +17,628 @@ * */ +#include + +#include "CurieBLE.h" + +#include "./internal/BLEUtils.h" + #include "BLECharacteristic.h" -#include "BLEPeripheralHelper.h" -#include "internal/ble_client.h" - -uint8_t profile_notify_process (bt_conn_t *conn, - bt_gatt_subscribe_params_t *params, - const void *data, uint16_t length); -uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, - bt_gatt_read_params_t *params, - const void *data, - uint16_t length); - -unsigned char BLECharacteristic::_numNotifyAttributes = 0; - -bt_uuid_16_t BLECharacteristic::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; -bt_uuid_16_t BLECharacteristic::_gatt_ccc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CCC_VAL}; - -BLECharacteristic::BLECharacteristic(const char* uuid, - const unsigned char properties, - const unsigned short maxLength) : - BLEAttribute(uuid, BLETypeCharacteristic), - _value_length(0), - _value_buffer(NULL), - _written(false), - _user_description(NULL), - _presentation_format(NULL), - _attr_chrc_declaration(NULL), - _attr_chrc_value(NULL), - _attr_cccd(NULL) -{ - _value_size = maxLength > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : maxLength; - _value = (unsigned char*)malloc(_value_size); - if (_value_size > BLE_MAX_ATTR_DATA_LEN) - { - _value_buffer = (unsigned char*)malloc(_value_size); - } - - memset(&_ccc_cfg, 0, sizeof(_ccc_cfg)); - memset(&_ccc_value, 0, sizeof(_ccc_value)); - memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); - memset(&_sub_params, 0, sizeof(_sub_params)); +#include "./internal/BLEProfileManager.h" +#include "./internal/BLEDeviceManager.h" + +#include "./internal/BLECharacteristicImp.h" + +BLECharacteristic::BLECharacteristic(): + _bledev(), _internal(NULL), _chrc_local_imp(NULL), _broadcast(false), + _properties(0), _value_size(0), _value(NULL)//, + //_event_handlers(NULL) +{ + memset(_uuid_cstr, 0, sizeof(_uuid_cstr)); + memset(_event_handlers, 0, sizeof(_event_handlers)); + memset(_oldevent_handlers, 0, sizeof(_oldevent_handlers)); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + unsigned short valueSize): + _bledev(), _internal(NULL), _chrc_local_imp(NULL), _broadcast(false), + _properties(properties), + _value(NULL)//, + //_event_handlers(NULL) +{ + bt_uuid_128 bt_uuid_tmp; + _value_size = valueSize > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueSize; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&bt_uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&bt_uuid_tmp, _uuid_cstr); + _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); + memset(_event_handlers, 0, sizeof(_event_handlers)); + memset(_oldevent_handlers, 0, sizeof(_oldevent_handlers)); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + const char* value): + BLECharacteristic(uuid, properties, strlen(value)) +{ + _setValue((const uint8_t*)value, strlen(value)); +} + +BLECharacteristic::BLECharacteristic(BLECharacteristicImp *characteristicImp, + const BLEDevice *bleDev): + _bledev(bleDev), _internal(characteristicImp), _chrc_local_imp(NULL), + _broadcast(false), _value(NULL)//,_event_handlers(NULL) +{ + BLEUtils::uuidBT2String(characteristicImp->bt_uuid(), _uuid_cstr); + _properties = characteristicImp->properties(); + _value_size = characteristicImp->valueSize(); + memset(_event_handlers, 0, sizeof(_event_handlers)); + memset(_oldevent_handlers, 0, sizeof(_oldevent_handlers)); +} + +BLECharacteristic::BLECharacteristic(const BLECharacteristic& rhs): + _value(NULL)//, + //_event_handlers(NULL) +{ + _chrc_local_imp = NULL; // Not copy + _value_size = rhs._value_size; + _internal = rhs._internal; + _bledev.setAddress(*rhs._bledev.bt_le_address()); + memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); + _properties = rhs._properties; - _ccc_value.cfg = &_ccc_cfg; - _ccc_value.cfg_len = 1; - if (BLERead & properties) - { - _gatt_chrc.properties |= BT_GATT_CHRC_READ; - } - if (BLEWrite & properties) + if (rhs._internal == NULL) { - _gatt_chrc.properties |= BT_GATT_CHRC_WRITE; - } - if (BLEWriteWithoutResponse & properties) - { - _gatt_chrc.properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; - } - if (BLENotify & properties) - { - _gatt_chrc.properties |= BT_GATT_CHRC_NOTIFY; - _sub_params.value |= BT_GATT_CCC_NOTIFY; + if (rhs._value != NULL) + { + _value = (unsigned char*)malloc(rhs._value_size); + if (NULL != _value) + { + memcpy(_value, rhs._value, rhs._value_size); + } + else + { + errno = ENOMEM; + } + + } + + //if (rhs._event_handlers != NULL) + { + //_event_handlers = (BLECharacteristicEventHandler*)malloc(sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast); + + //if (NULL != _event_handlers) + memcpy(_event_handlers, rhs._event_handlers, (sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast)); + } + memcpy(_oldevent_handlers, rhs._oldevent_handlers, (sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast)); } - if (BLEIndicate & properties) +} + +BLECharacteristic::~BLECharacteristic() +{ + if (_value) { - _gatt_chrc.properties |= BT_GATT_CHRC_INDICATE; - _sub_params.value |= BT_GATT_CCC_INDICATE; + free(_value); + _value = NULL; } - _gatt_chrc.uuid = this->uuid(); - memset(_event_handlers, 0, sizeof(_event_handlers)); - _numNotifyAttributes++; - if (properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)) + if (_chrc_local_imp != NULL) { - _numNotifyAttributes++; + delete _chrc_local_imp; + _chrc_local_imp = NULL; } - _sub_params.notify = profile_notify_process; } -BLECharacteristic::BLECharacteristic(const char* uuid, - const unsigned char properties, - const char* value) : - BLECharacteristic(uuid, properties, strlen(value)) +const char* BLECharacteristic::uuid() const { - setValue((const uint8_t*)value, strlen(value)); + return _uuid_cstr; } -BLECharacteristic::~BLECharacteristic() +unsigned char BLECharacteristic::properties() const { - if (_value) { - free(_value); - _value = NULL; + unsigned char property = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + property = characteristicImp->properties(); } + return property; } -unsigned char -BLECharacteristic::properties() const +int BLECharacteristic::valueSize() const { - return _gatt_chrc.properties; + int valuesize = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valuesize = characteristicImp->valueSize(); + } + return valuesize; } -bool -BLECharacteristic::setValue(const unsigned char value[], uint16_t length) +const byte* BLECharacteristic::value() const { - int status; - - _setValue(value, length); - - if (_attr_chrc_value) + const byte* value_temp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) { - // TODO: Notify for peripheral. - // Write request for central. - status = bt_gatt_notify(NULL, _attr_chrc_value, value, length, NULL); - if (0 != status) - { - return false; - } + value_temp = characteristicImp->value(); } - return true; + return value_temp; } -void -BLECharacteristic::setValue(BLEHelper& blehelper, const unsigned char* value, unsigned short length) +int BLECharacteristic::valueLength() const { - //BLEHelper *bledevice = ¢ral; - _setValue(value, length); - - _written = true; - _reading = false; - - if (_event_handlers[BLEWritten]) { - _event_handlers[BLEWritten](blehelper, *this); + int valueLength = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valueLength = characteristicImp->valueLength(); } + return valueLength; } -unsigned short -BLECharacteristic::valueSize() const +BLECharacteristic::operator bool() const { - return _value_size; + return (strlen(_uuid_cstr) > 3); } -const unsigned char* -BLECharacteristic::value() const +BLECharacteristic& BLECharacteristic::operator= (const BLECharacteristic& chrc) { - return _value; + if (this != &chrc) + { + memcpy(_uuid_cstr, chrc._uuid_cstr, sizeof(_uuid_cstr)); + _bledev.setAddress(*chrc._bledev.bt_le_address()); + _internal = chrc._internal; + _chrc_local_imp = NULL; // Not copy + _properties = chrc._properties; + + if (_value_size < chrc._value_size) + { + _value_size = chrc._value_size; + if (NULL != _value) + { + free(_value); + _value = NULL; + } + } + + if (_internal == NULL) + { + if (chrc._value != NULL) + { + if (NULL == _value) + _value = (unsigned char*) malloc(_value_size); + + if (NULL != _value) + memcpy(_value, chrc._value, chrc._value_size); + else { + _value_size = 0; + } + } + + //if (chrc._event_handlers != NULL) + { + //if (NULL == _event_handlers) + // _event_handlers = (BLECharacteristicEventHandler*)malloc(sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast); + + //if (NULL != _event_handlers) + memcpy(_event_handlers, chrc._event_handlers, (sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast)); + } + memcpy(_oldevent_handlers, chrc._oldevent_handlers, (sizeof(BLECharacteristicEventHandler) * BLECharacteristicEventLast)); + } + } + return *this; } -unsigned short -BLECharacteristic::valueLength() const +byte BLECharacteristic::operator[] (int offset) const { - return _value_length; + byte data = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + data = (*characteristicImp)[offset]; + } + return data; } -unsigned char -BLECharacteristic::operator[] (int offset) const +bool BLECharacteristic::setValue(const unsigned char value[], unsigned short length) { - return _value[offset]; + return writeValue(value, (int)length); } -bool -BLECharacteristic::written() +bool BLECharacteristic::writeValue(const byte value[], int length) { - boolean_t written = _written; - - _written = false; - - return written; + return writeValue(value, length, 0); } -bool -BLECharacteristic::subscribed() +bool BLECharacteristic::writeValue(const byte value[], int length, int offset) { - return (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)); + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + if (BLEUtils::isLocalBLE(_bledev) == true) + { + retVar = characteristicImp->writeValue(value, length, offset); + } + else + { + retVar = characteristicImp->write(value, (uint16_t)length); + } + + if (true == _broadcast && + true == BLEDeviceManager::instance()->advertising()) + { + BLEDeviceManager::instance()->stopAdvertising(); + BLEDeviceManager::instance()->setAdvertisedServiceData(characteristicImp->bt_uuid(), + characteristicImp->value(), + characteristicImp->valueLength()); + BLEDeviceManager::instance()->startAdvertising(); + } + } + return retVar; } -void -BLECharacteristic::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback) +bool BLECharacteristic::writeValue(const char* value) { - noInterrupts(); - if (event < sizeof(_event_handlers)) { - _event_handlers[event] = callback; - } - interrupts(); + return writeValue((const byte*)value, strlen(value)); } -uint16_t -BLECharacteristic::valueHandle() +bool BLECharacteristic::broadcast() { - uint16_t handle = 0; - if (NULL != _attr_chrc_value) + _broadcast = true; + BLEDeviceManager::instance()->setConnectable(false); + if (BLEDeviceManager::instance()->advertising()) { - handle = _attr_chrc_value->handle; + BLEDeviceManager::instance()->stopAdvertising(); + BLEDeviceManager::instance()->startAdvertising(); } - - return handle; + return _broadcast; } -uint16_t -BLECharacteristic::cccdHandle() +bool BLECharacteristic::written() { - uint16_t handle = 0; - if (NULL != _attr_cccd) + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) { - handle = _attr_cccd->handle; + retVar = characteristicImp->written(); } - return handle; + return retVar; } -void -BLECharacteristic::setUserDescription(BLEDescriptor *descriptor) +bool BLECharacteristic::subscribed() { - _user_description = descriptor; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->subscribed(); + } + return retVar; } -void -BLECharacteristic::setPresentationFormat(BLEDescriptor *descriptor) +bool BLECharacteristic::canNotify() { - _presentation_format = descriptor; + return (_properties & BLENotify); } -void -BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) +bool BLECharacteristic::canIndicate() { - if (length > _value_size) { - length = _value_size; - } - - memcpy(_value, value, length); - _value_length = length; + return (_properties & BLEIndicate); } -unsigned char -BLECharacteristic::numNotifyAttributes(void) { - return _numNotifyAttributes; -} - -_bt_gatt_ccc_t* BLECharacteristic::getCccCfg(void) +bool BLECharacteristic::canRead() { - return &_ccc_value; + return (_properties & BLERead); } -bt_gatt_chrc_t* BLECharacteristic::getCharacteristicAttValue(void) +bool BLECharacteristic::canWrite() { - return &_gatt_chrc; + return (_properties & BLEWrite); } -uint8_t BLECharacteristic::getPermission(void) +bool BLECharacteristic::canSubscribe() { - uint8_t perm = 0; - if (_gatt_chrc.properties & BT_GATT_CHRC_READ) - { - perm |= BT_GATT_PERM_READ; - } - if (_gatt_chrc.properties & (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP)) + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (_properties & (BLENotify | BLEIndicate) && + (NULL != characteristicImp)) { - perm |= BT_GATT_PERM_WRITE; + retVar = !characteristicImp->subscribed(); } - return perm; + return retVar; } -bt_uuid_t* BLECharacteristic::getCharacteristicAttributeUuid(void) +bool BLECharacteristic::canUnsubscribe() { - return (bt_uuid_t*) &_gatt_chrc_uuid; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->subscribed(); + } + return retVar; } -bt_uuid_t* BLECharacteristic::getClientCharacteristicConfigUuid(void) + +bool BLECharacteristic::read() { - return (bt_uuid_t*) &_gatt_ccc_uuid; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->read(); + } + return retVar; } - -void BLECharacteristic::addCharacteristicDeclaration(bt_gatt_attr_t *gatt_attr) +bool BLECharacteristic::write(const unsigned char* value, int length) { - _attr_chrc_declaration = gatt_attr; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->write(value, (uint16_t)length); + } + return retVar; } -void BLECharacteristic::addCharacteristicValue(bt_gatt_attr_t *gatt_attr) +bool BLECharacteristic::subscribe() { - _attr_chrc_value = gatt_attr; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->subscribe(); + } + return retVar; } -void BLECharacteristic::addCharacteristicConfigDescriptor(bt_gatt_attr_t *gatt_attr) +bool BLECharacteristic::unsubscribe() { - _attr_cccd = gatt_attr; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->unsubscribe(); + } + return retVar; } -void BLECharacteristic::discover(bt_gatt_discover_params_t *params) +bool BLECharacteristic::valueUpdated() { - params->type = BT_GATT_DISCOVER_CHARACTERISTIC; - params->uuid = this->uuid(); - // Start discovering - _discoverying = true; - // Re-Init the read/write parameter - _reading = false; + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->valueUpdated(); + } + return retVar; } - -void BLECharacteristic::discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) +int BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) { - if (!attr) + int retVar = BLE_STATUS_ERROR; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) { - // Discovery complete - _discoverying = false; - return; + retVar = characteristicImp->addDescriptor(descriptor); } - - // Chracteristic Char - if (params->uuid == this->uuid()) + else if (BLEUtils::isLocalBLE(_bledev) == true) { - // Set Discover CCCD parameter - params->start_handle = attr->handle + 2; - if (subscribed()) + // Only support the GATT server that create the service in local device. + _chrc_local_imp = new BLECharacteristicImp(*this, _bledev); + if (NULL == _chrc_local_imp) { - // Include CCCD - params->type = BT_GATT_DISCOVER_DESCRIPTOR; - params->uuid = this->getClientCharacteristicConfigUuid(); + retVar = BLE_STATUS_NO_MEMORY; } else { - // Complete the discover - _discoverying = false; + retVar = _chrc_local_imp->addDescriptor(descriptor); } } - else if (params->uuid == this->getClientCharacteristicConfigUuid()) + return retVar; +} + +BLECharacteristicImp* BLECharacteristic::fetchCharacteristicImp() +{ + BLECharacteristicImp* temp = _chrc_local_imp; + _chrc_local_imp = NULL; + return temp; +} + +int BLECharacteristic::descriptorCount() const +{ + int count = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) { - params->start_handle = attr->handle + 1; - _discoverying = false; + count = characteristicImp->descriptorCount(); } + return count; } -bt_gatt_subscribe_params_t *BLECharacteristic::getSubscribeParams() +bool BLECharacteristic::hasDescriptor(const char* uuid) const { - return &_sub_params; + BLEDescriptorImp* descriptorImp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + descriptorImp = characteristicImp->descrptor(uuid); + } + + return (descriptorImp != NULL); } -bool BLECharacteristic::read(BLEPeripheralHelper &peripheral) +bool BLECharacteristic::hasDescriptor(const char* uuid, int index) const { - int retval = 0; - bt_conn_t* conn = NULL; - if (_reading) + bool retVal = false; + BLEDescriptorImp* descriptorImp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) { - // Already in reading state - return false; + descriptorImp = characteristicImp->descrptor(index); + if (NULL != descriptorImp) + { + retVal = descriptorImp->compareUuid(uuid); + } } - _read_params.func = profile_read_rsp_process; - _read_params.handle_count = 1; - _read_params.single.handle = peripheral.valueHandle(this); - _read_params.single.offset = 0; + return retVal; +} + +BLEDescriptor BLECharacteristic::descriptor(int index) const +{ + BLEDescriptorImp* descriptorImp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + descriptorImp = characteristicImp->descrptor(index); + } - if (0 == _read_params.single.handle) + if (descriptorImp != NULL) + { + return BLEDescriptor(descriptorImp, &_bledev); + } + else { - // Discover not complete - return false; + return BLEDescriptor(); } +} +BLEDescriptor BLECharacteristic::descriptor(const char * uuid) const +{ + BLEDescriptorImp* descriptorImp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); - conn = bt_conn_lookup_addr_le(peripheral.bt_le_address()); - if (NULL == conn) + if (NULL != characteristicImp) { - return false; + descriptorImp = characteristicImp->descrptor(uuid); } - // Send read request - retval = bt_gatt_read(conn, &_read_params); - bt_conn_unref(conn); - if (0 == retval) + if (descriptorImp != NULL) { - _reading = true; + return BLEDescriptor(descriptorImp, &_bledev); + } + else + { + return BLEDescriptor(); } - return _reading; } -bool BLECharacteristic::write(BLEPeripheralHelper &peripheral, - const unsigned char value[], - uint16_t length) +BLEDescriptor BLECharacteristic::descriptor(const char * uuid, int index) const { - int retval = 0; - bt_conn_t* conn = NULL; + bool retVal = false; + BLEDescriptorImp* descriptorImp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); - conn = bt_conn_lookup_addr_le(peripheral.bt_le_address()); - if (NULL == conn) + if (NULL != characteristicImp) { - return false; + descriptorImp = characteristicImp->descrptor(index); + if (NULL != descriptorImp) + { + retVal = descriptorImp->compareUuid(uuid); + } } - // Send read request - retval = bt_gatt_write_without_response(conn, - peripheral.valueHandle(this), - value, length, false); - bt_conn_unref(conn); - return (0 == retval); + if (descriptorImp != NULL && true == retVal) + { + return BLEDescriptor(descriptorImp, &_bledev); + } + else + { + return BLEDescriptor(); + } } -void BLECharacteristic::setBuffer(BLEHelper& blehelper, - const uint8_t value[], - uint16_t length, - uint16_t offset) +void BLECharacteristic::setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandler eventHandler) { - if (length + offset > _value_size) { - // Ignore the data + BLECharacteristicImp *characteristicImp = getImplementation(); + if (event >= BLECharacteristicEventLast) + { return; } + + if (NULL != characteristicImp) + { + characteristicImp->setEventHandler(event, eventHandler); + } + else + { + _event_handlers[event] = eventHandler; + } +} - memcpy(_value_buffer + offset, value, length); +void BLECharacteristic::setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandlerOld eventHandler) +{ + BLECharacteristicImp *characteristicImp = getImplementation(); + if (event >= BLECharacteristicEventLast) + { + return; + } + + if (NULL != characteristicImp) + { + characteristicImp->setEventHandler(event, eventHandler); + } + else + { + _oldevent_handlers[event] = eventHandler; + } } -void BLECharacteristic::syncupBuffer2Value(BLEHelper& blehelper) + +void +BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) { - setValue(blehelper, _value_buffer, _value_size); + if (length > _value_size) { + length = _value_size; + } + + if (NULL == _value) + { + // Allocate the buffer for characteristic + _value = (unsigned char*)malloc(_value_size); + } + if (NULL == _value) + { + errno = ENOMEM; + return; + } + memcpy(_value, value, length); } -void BLECharacteristic::discardBuffer() +BLECharacteristicImp* BLECharacteristic::getImplementation() const { - memcpy(_value_buffer, _value, _value_size); + BLECharacteristicImp* tmp = NULL; + tmp = _internal; + if (NULL == tmp) + { + tmp = BLEProfileManager::instance()->characteristic(_bledev, (const char*)_uuid_cstr); + } + return tmp; } -bool BLECharacteristic::longCharacteristic() +void BLECharacteristic::setBLECharacteristicImp(BLECharacteristicImp *characteristicImp) { - return (_value_size > BLE_MAX_ATTR_DATA_LEN); + _internal = characteristicImp; } diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index 58274f67..c1887c73 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -1,339 +1,537 @@ /* - * Copyright (c) 2015 Intel Corporation. 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. + BLE Characteristic API + Copyright (c) 2016 Arduino LLC. All right reserved. - * 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. + 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. - * 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 - * - */ + 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. -#ifndef _BLE_CHARACTERISTIC_H_INCLUDED -#define _BLE_CHARACTERISTIC_H_INCLUDED + 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 "BLECommon.h" +#ifndef ARDUINO_BLE_CHARACTERISTIC_H +#define ARDUINO_BLE_CHARACTERISTIC_H -#include "BLEAttribute.h" -#include "BLECentralHelper.h" -#include "BLEDescriptor.h" +#include "CurieBLE.h" + +#include "BLEDevice.h" -/** - * BLE Characteristic Events - */ enum BLECharacteristicEvent { - BLEWritten = 0, - BLESubscribed = 1, - BLEUnsubscribed = 2, + BLEWritten = 0, + BLESubscribed = 1, + BLEUnsubscribed = 2, + BLEValueUpdated = 3, + BLECharacteristicEventLast +}; - BLECharacteristicEventLast = 3 +enum BLEProperty { + BLEBroadcast = 0x01, + BLERead = 0x02, + BLEWriteWithoutResponse = 0x04, + BLEWrite = 0x08, + BLENotify = 0x10, + BLEIndicate = 0x20 }; -/* Forward declaration needed for callback function prototype below */ -class BLECharacteristic; -class BLEPeripheral; -class BLEHelper; +typedef void (*BLECharacteristicEventHandler)(BLEDevice bledev, BLECharacteristic characteristic); -/** Function prototype for BLE Characteristic event callback */ -typedef void (*BLECharacteristicEventHandler)(BLEHelper &bleHelper, BLECharacteristic &characteristic); +typedef void (*BLECharacteristicEventHandlerOld)(BLECentral ¢ral, BLECharacteristic &characteristic); -/** - * BLE Characteristic Property types - */ -enum BLEProperty { - // broadcast (0x01) not supported - BLERead = 0x02, - BLEWriteWithoutResponse = 0x04, - BLEWrite = 0x08, - BLENotify = 0x10, - BLEIndicate = 0x20 -}; +//#include "BLECharacteristicImp.h" -/** - * BLE GATT Characteristic - */ -class BLECharacteristic : public BLEAttribute { +class BLECharacteristic: public BLEAttributeWithValue +{ public: + BLECharacteristic(); /** - * Constructor for BLE Characteristic + * @brief Create a characteristic with specified value size + * + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic * - * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard - * @param[in] properties Characteristic property mask - * @param[in] maxLength Maximum data length required for characteristic value (<= BLE_MAX_ATTR_DATA_LEN) + * @param valueSize The size of the characteristic data + * + * @return none + * + * @note none */ - BLECharacteristic(const char* uuid, - const unsigned char properties, - const unsigned short maxLength); - + BLECharacteristic(const char* uuid, + unsigned char properties, + unsigned short valueSize); + /** - * Constructor for BLE Characteristic + * @brief Create a characteristic with string value * - * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard - * @param[in] properties Characteristic property mask - * @param[in] value String value for characteristic (string length (<= BLE_MAX_ATTR_DATA_LEN)) + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic + * + * @param value The string of the characteristic data + * + * @return none + * + * @note The data length is string's size. Can't set a string that is longger + * than the this input */ - BLECharacteristic(const char* uuid, - const unsigned char properties, + BLECharacteristic(const char* uuid, + unsigned char properties, const char* value); + BLECharacteristic(const BLECharacteristic&); + virtual ~BLECharacteristic(); /** - * Set the current value of the Characteristic + * @brief Is the characteristic valid * - * @param[in] value New value to set, as a byte array. Data is stored in internal copy. - * @param[in] length Length, in bytes, of valid data in the array to write. - * Must not exceed maxLength set for this characteristic. + * @param none * - * @return bool true set value success, false on error + * @return bool true/false + * + * @note Invalid characteristic is NULL pointer or all zero with UUID */ - bool setValue(const unsigned char value[], unsigned short length); + virtual operator bool() const; // + + /** + * @brief Get the characteristic's UUID string + * + * @param none + * + * @return const char* The UUID string + * + * @note none + */ + const char* uuid() const; + + /** + * @brief Get the property mask of the characteristic + * + * @param none + * + * @return unsigned char The property mask of the characteristic + * + * @note none + */ + unsigned char properties() const; + + /** + * @brief Get the maximum size of the value + * + * @param none + * + * @return int The maximum size of the value + * + * @note none + */ + int valueSize() const; + + /** + * @brief Get the value buffer + * + * @param none + * + * @return const byte* The value buffer + * + * @note none + */ + virtual const byte* value() const; + + /** + * @brief Get the current length of the value + * + * @param none + * + * @return int The current length of the value string + * + * @note TODO: How to handle if the data is RAW data? This API is danger + */ + virtual int valueLength() const; + + /** + * @brief Get a byte of the value at the specified offset + * + * @param none + * + * @return byte A byte of the value at the specified offset + * + * @note none + */ + virtual byte operator[] (int offset) const; + BLECharacteristic& operator= (const BLECharacteristic& chrc); /** * Set the current value of the Characteristic * - * @param[in] central The central device that update the value. * @param[in] value New value to set, as a byte array. Data is stored in internal copy. * @param[in] length Length, in bytes, of valid data in the array to write. * Must not exceed maxLength set for this characteristic. * * @return bool true set value success, false on error + * @note GATT Server only */ - void setValue(BLEHelper& blehelper, const uint8_t value[], uint16_t length); + bool setValue(const unsigned char value[], unsigned short length); /** - * Get the property mask of the Characteristic + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @return bool true - Success, false - Failed * - * @return unsigned char property mask of the Characteristic + * @note none */ - unsigned char properties(void) const; - + virtual bool writeValue(const byte value[], int length); + /** - * Get the (maximum) size of the Characteristic + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @param offset The offset in the characteristic's data + * + * @return bool true - Success, false - Failed * - * @return unsigned size of characateristic in bytes + * @note none */ - unsigned short valueSize(void) const; - + bool writeValue(const byte value[], int length, int offset); + /** - * Get data pointer to the value of the Characteristic + * @brief Write the value of the characteristic * - * @return const unsigned char* pointer to the value of the Characteristic + * @param value The value string that want to write to characteristic + * + * @return bool true - Success, false - Failed + * + * @note none */ - const unsigned char* value(void) const; + bool writeValue(const char* value); + // peripheral mode + bool broadcast(); // broadcast the characteristic value in the advertisement data + + // GATT server /** - * Get the current length of the value of the Characteristic + * @brief Has the GATT client written a new value + * + * @param none + * + * @return bool true - Written, false - Not changed * - * @return unsigned short size of characateristic value in bytes + * @note GATT server only. GATT client always return false. */ - unsigned short valueLength() const; - - unsigned char operator[] (int offset) const; - + bool written(); + /** - * Has the value of the Characteristic been written by a central + * @brief Is the GATT client subscribed * - * @return bool true is central has updated characteristic value, otherwise false + * @param none + * + * @return bool true - Subscribed, false - Not subscribed + * + * @note GATT server and client */ - bool written(void); - + bool subscribed(); + + /** + * @brief Can a notification be sent to the GATT client + * + * @param none + * + * @return true - Yes, false - No + * + * @note GATT server only + */ + bool canNotify(); + /** - * Is a central listening for notifications or indications of the Characteristic + * @brief Can a indication be sent to the GATT client * - * @return bool true is central is subscribed, otherwise false + * @param none + * + * @return true - Yes, false - No + * + * @note GATT server only */ - bool subscribed(void); - + bool canIndicate(); + + // GATT /** - * Provide a function to be called when events related to this Characteristic are raised + * @brief Can the characteristic be read (based on properties) + * + * @param none * - * @param[in] event Event type to set event handler for - * @param[in] callback Pointer to callback function to invoke when the event occurs. + * @return true - readable, false - None + * + * @note none */ - void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback); - + bool canRead(); + /** - * @brief Get Notify Attribute counter that created + * @brief Can the characteristic be written (based on properties) * * @param none * - * @return unsigned char The totla number of the notify attributes + * @return true - writable, false - None * * @note none */ - static unsigned char numNotifyAttributes(void); + bool canWrite(); /** - * @brief Schedule the read request to read the characteristic in peripheral + * @brief Can the characteristic be subscribed to (based on properties) * - * @param[in] peripheral The peripheral device that want to read. + * @param none * - * @return bool Indicate the success or error + * @return true - Can be subscribed, false - No * - * @note Only for central device + * @note What different with canUnsubscribe? */ - bool read(BLEPeripheralHelper &peripheral); + bool canSubscribe(); /** - * @brief Schedule the write request to update the characteristic in peripheral + * @brief Can the characteristic be unsubscribed to (based on properties) * - * @param[in] peripheral The peripheral device that want to be updated - * @param[in] value New value to set, as a byte array. Data is stored in internal copy. - * @param[in] length Length, in bytes, of valid data in the array to write. - * Must not exceed maxLength set for this characteristic. + * @param none * - * @return bool true set value success, false on error + * @return true - Can be unsubscribed, false - No * * @note none */ - bool write(BLEPeripheralHelper &peripheral, - const unsigned char value[], - uint16_t length); + bool canUnsubscribe(); -protected: - friend class BLEProfile; - friend int profile_longflush_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - uint8_t flags); - friend ssize_t profile_longwrite_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, - uint16_t offset); - - void addCharacteristicDeclaration(bt_gatt_attr_t *gatt_attr); - void addCharacteristicValue(bt_gatt_attr_t *gatt_attr); - void addCharacteristicConfigDescriptor(bt_gatt_attr_t *gatt_attr); + /** + * @brief Read the characteristic value + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule read request to the GATT server + */ + virtual bool read(); - bool longCharacteristic(); + /** + * @brief Write the charcteristic value + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule write request to the GATT server + */ + virtual bool write(const unsigned char* value, int length); - void setBuffer(BLEHelper& blehelper, - const uint8_t value[], - uint16_t length, - uint16_t offset); - void discardBuffer(); - void syncupBuffer2Value(BLEHelper& blehelper); + /** + * @brief Subscribe to the characteristic + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule CCCD to the GATT server + */ + bool subscribe(); /** - * @brief Get the characteristic value handle + * @brief Unsubscribe to the characteristic * * @param none * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule CCCD to the GATT server + */ + bool unsubscribe(); + + + /** + * @brief Read response or notification updated the characteristic + * + * @param none + * + * @return bool true - Written, false - Not changed + * + * @note GATT client only. GATT server always return false. + */ + bool valueUpdated(); + + /** + * @brief Add the characteristic's descriptor + * + * @param descriptor The descriptor for characteristic + * * @return none * - * @note Only for peripheral + * @note none */ - uint16_t valueHandle(void); + int addDescriptor(BLEDescriptor& descriptor); /** - * @brief Get characteristic configuration descriptor value handle + * @brief Get the number of descriptors the characteristic has * * @param none * - * @return uint16_t The value handle - * 0 is invalid handle + * @return int the number of descriptors the characteristic has * - * @note Only for peripheral + * @note none */ - uint16_t cccdHandle(void); - + int descriptorCount() const; - void setUserDescription(BLEDescriptor *descriptor); - void setPresentationFormat(BLEDescriptor *descriptor); + /** + * @brief Does the characteristic have a descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasDescriptor(const char* uuid) const; + + /** + * @brief Does the characteristic have an nth descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @param index The index of descriptor + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasDescriptor(const char* uuid, int index) const; - _bt_gatt_ccc_t* getCccCfg(void); - bt_gatt_chrc_t* getCharacteristicAttValue(void); - static bt_uuid_t* getCharacteristicAttributeUuid(void); - static bt_uuid_t* getClientCharacteristicConfigUuid(void); + /** + * @brief Get the nth descriptor of the characteristic + * + * @param index The index of descriptor + * + * @return BLEDescriptor The descriptor + * + * @note none + */ + BLEDescriptor descriptor(int index) const; /** - * @brief Get the characteristic permission + * @brief Get the descriptor with the specified UUID * - * @param none + * @param uuid The descriptor's UUID * - * @return uint8_t The characteristic permission + * @return BLEDescriptor The descriptor * * @note none */ - uint8_t getPermission(void); + BLEDescriptor descriptor(const char * uuid) const; /** - * @brief For central to discover the peripherial profile + * @brief Get the nth descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @param index The index of descriptor + * + * @return BLEDescriptor The descriptor + * + * @note none + */ + BLEDescriptor descriptor(const char * uuid, int index) const; + + /** + * @brief Set an event handler (callback) * - * @param[in] attr The discover response + * @param event Characteristic event * - * @param[in] params The discover parameter that need to fill + * @param eventHandler The handler of characteristic * * @return none * - * @note Only for central + * @note none */ - void discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params); + void setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandler eventHandler); + void setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandlerOld eventHandler); +protected: + friend class BLEDevice; + friend class BLEService; + friend class BLEServiceImp; /** - * @brief For central to discover the peripherial profile + * @brief Create a characteristic with specified value size + * + * @param characteristicImp The implementation of the characteristic * - * @param[in] params The discover parameter that need to fill + * @param bleDev The peer BLE device * * @return none * - * @note Only for central + * @note none */ - void discover(bt_gatt_discover_params_t *params); + BLECharacteristic(BLECharacteristicImp *characteristicImp, + const BLEDevice *bleDev); /** - * @brief Get the subscribe parameter + * @brief Create a characteristic with string value * - * @param none + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic * - * @return bt_gatt_subscribe_params_t * the subscribe parameter + * @param value The string of the characteristic data * - * @note Only for central + * @param bleDev The peer BLE device + * + * @return none + * + * @note The data length is string's size. Can't set a string that is longger + * than the this input */ - bt_gatt_subscribe_params_t* getSubscribeParams(); - + //BLECharacteristic(const char* uuid, + // unsigned char properties, + // const char* value, + // BLEDevice *bleDev); + + // For GATT + void setBLECharacteristicImp(BLECharacteristicImp *characteristicImp); + BLECharacteristicImp* fetchCharacteristicImp(); private: void _setValue(const uint8_t value[], uint16_t length); - -private: + BLECharacteristicImp *getImplementation() const; - static unsigned char _numNotifyAttributes; - static bt_uuid_16_t _gatt_chrc_uuid; - static bt_uuid_16_t _gatt_ccc_uuid; - - unsigned short _value_size; - unsigned short _value_length; - unsigned char* _value; - unsigned char* _value_buffer; - bool _written; - - uint16_t _value_handle; - bt_gatt_ccc_cfg_t _ccc_cfg; - _bt_gatt_ccc_t _ccc_value; - bt_gatt_chrc_t _gatt_chrc; - - BLEDescriptor* _user_description; - BLEDescriptor* _presentation_format; +private: + char _uuid_cstr[37]; // The characteristic UUID + BLEDevice _bledev; // The GATT server BLE object. Only for GATT client to read/write + // NULL - GATT server + // None-NULL - GATT client + BLECharacteristicImp *_internal; // The real implementation of characteristic. + BLECharacteristicImp *_chrc_local_imp; + bool _broadcast; +protected: + friend class BLECharacteristicImp; + unsigned char _properties; // The characteristic property - bt_gatt_attr_t *_attr_chrc_declaration; - bt_gatt_attr_t *_attr_chrc_value; - bt_gatt_attr_t *_attr_cccd; + unsigned short _value_size; // The value size + unsigned char* _value; // The value. Will delete after create the _internal - // For central device to subscribe the Notification/Indication - bt_gatt_subscribe_params_t _sub_params; + BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; // Sid. Define the arr as in BLECharacteristicImp.h - bool _reading; - bt_gatt_read_params_t _read_params; - BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; + BLECharacteristicEventHandlerOld _oldevent_handlers[BLECharacteristicEventLast]; }; -#endif // _BLE_CHARACTERISTIC_H_INCLUDED +#endif + diff --git a/libraries/CurieBLE/src/BLECommon.h b/libraries/CurieBLE/src/BLECommon.h index 9c26bad3..76e6b804 100644 --- a/libraries/CurieBLE/src/BLECommon.h +++ b/libraries/CurieBLE/src/BLECommon.h @@ -21,6 +21,7 @@ #define _BLE_COMMON_H_INCLUDED #include "Arduino.h" +//#include "CurieBLE.h" #include "../src/services/ble_service/ble_protocol.h" @@ -32,12 +33,13 @@ #include #include #include +//#include #define BLE_ADDR_LEN 6 -#define UUID_SIZE_128 16 -#define UUID_SIZE_16 2 -#define MAX_UUID_SIZE UUID_SIZE_128 +#define UUID_SIZE_128 16 +#define UUID_SIZE_16 2 +#define MAX_UUID_SIZE UUID_SIZE_128 /* Theoretically we should be able to support attribute lengths up to 512 bytes * but this involves splitting it across multiple packets. For simplicity, @@ -54,6 +56,7 @@ /* Invalid BLE Address type */ #define BLE_DEVICE_ADDR_INVALID 0xFF +#if 0 /** BLE response/event status codes. */ enum BLE_STATUS { BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ @@ -71,12 +74,35 @@ enum BLE_STATUS { BLE_STATUS_GAP_BASE = 0x100, /**< GAP specific error base */ BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ }; +#endif + +typedef enum +{ + BLE_STATUS_SUCCESS = 0, + BLE_STATUS_FORBIDDEN, /**< The operation is forbidden. Central mode call peripheral API and vice versa */ + BLE_STATUS_PENDING, /**< Request received and execution started, response pending */ + BLE_STATUS_TIMEOUT, /**< Request timed out */ + BLE_STATUS_NOT_SUPPORTED, /**< Request/feature/parameter not supported */ + BLE_STATUS_NOT_FOUND, + BLE_STATUS_NOT_ALLOWED, /**< Request not allowed */ + BLE_STATUS_LINK_TIMEOUT, /**< Link timeout (link loss) */ + BLE_STATUS_NOT_ENABLED, /**< BLE not enabled, @ref ble_enable */ + BLE_STATUS_ERROR, /**< Generic Error */ + BLE_STATUS_ALREADY_REGISTERED, /**< BLE service already registered */ + BLE_STATUS_WRONG_STATE, /**< Wrong state for request */ + BLE_STATUS_ERROR_PARAMETER, /**< Parameter in request is wrong */ + BLE_STATUS_NO_MEMORY, /**< System doesn't have memory */ + BLE_STATUS_NO_SERVICE, /**< System doesn't have service */ +}BLE_STATUS_T; typedef uint16_t ble_status_t; /**< Response and event BLE service status type @ref BLE_STATUS */ typedef ble_status_t BleStatus; -#define BLE_MAX_CONN_CFG 2 +#define BLE_LIB_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) + +#define BLE_MAX_CONN_CFG 2 +#define BLE_MAX_ADV_BUFFER_CFG 3 typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *dataPtr, uint8_t data_len, const bt_addr_le_t *addrPtr); @@ -92,6 +118,10 @@ typedef struct ble_conn_param { extern "C" { #endif +#include "os/os.h" + +extern void __assert_fail(void); + /// Define the structure for app typedef struct bt_uuid bt_uuid_t; typedef struct bt_uuid_16 bt_uuid_16_t; diff --git a/libraries/CurieBLE/src/BLEDescriptor.cpp b/libraries/CurieBLE/src/BLEDescriptor.cpp index 889cd58d..31c4ba7e 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.cpp +++ b/libraries/CurieBLE/src/BLEDescriptor.cpp @@ -1,96 +1,171 @@ /* - * Copyright (c) 2015 Intel Corporation. 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 - * - */ - + BLE Descriptor API + Copyright (c) 2016 Arduino LLC. 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 + +#include "./internal/BLEAttribute.h" #include "BLEDescriptor.h" +#include "./internal/BLEUtils.h" +#include "./internal/BLEDescriptorImp.h" -#include "internal/ble_client.h" +BLEDescriptor::BLEDescriptor(): + _properties(0), + _value_size(0), + _value(NULL) +{ + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); +} + +BLEDescriptor::BLEDescriptor(BLEDescriptorImp* descriptorImp, + const BLEDevice *bleDev): + _bledev(bleDev), + _value_size(0), + _value(NULL) +{ + _properties = descriptorImp->properties(); + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidBT2String(descriptorImp->bt_uuid(), _uuid_cstr); + + _value_size = descriptorImp->valueSize(); + _value = (unsigned char*)malloc(_value_size); + if (NULL == _value) + { + memcpy(_value, descriptorImp->value(), _value_size); + } + else + { + errno = ENOMEM; + } +} -BLEDescriptor::BLEDescriptor(const char* uuid, const unsigned char value[], unsigned short valueLength) : - BLEAttribute(uuid, BLETypeDescriptor) +BLEDescriptor::BLEDescriptor(const char* uuid, + const unsigned char value[], + unsigned short valueLength): + _bledev() { - if (valueLength > BLE_MAX_ATTR_DATA_LEN) { - valueLength = BLE_MAX_ATTR_DATA_LEN; + bt_uuid_128_t uuid_tmp; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&uuid_tmp, _uuid_cstr); + + _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); + + _value_size = valueLength > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueLength; + _value = (unsigned char*)malloc(_value_size); + if (NULL == _value) + { + memcpy(_value, value, _value_size); } - _value_length = valueLength; - _value = (unsigned char*)malloc(_value_length); + else + { + errno = ENOMEM; + } +} - memcpy(_value, value, _value_length); +BLEDescriptor::BLEDescriptor(const char* uuid, + const char* value): + BLEDescriptor(uuid, (const unsigned char*)value, strlen(value)) +{} + +BLEDescriptor::BLEDescriptor(const BLEDescriptor& rhs) +{ + _value = (unsigned char*)malloc(rhs._value_size); // Sid. KW: allocate memory for _value, not local + if (_value) + { + memcpy(_value, rhs._value, rhs._value_size); + _value_size = rhs._value_size; + } + else + { + _value_size = 0; + errno = ENOMEM; + } + memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); + _properties = rhs._properties; + _bledev = BLEDevice(&rhs._bledev); +} + +BLEDescriptor& BLEDescriptor::operator= (const BLEDescriptor& rhs) +{ + if (this != &rhs) + { + memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); + _properties = rhs._properties; + _bledev = BLEDevice(&rhs._bledev); + if (_value_size < rhs._value_size) + { + _value_size = rhs._value_size; + + if (NULL != _value) + free(_value); + _value = (unsigned char*)malloc(_value_size); + } + + if (NULL != _value) + { + memcpy(_value, rhs._value, rhs._value_size); + } + else + { + _value_size = 0; + errno = ENOMEM; + } + } + return *this; } -BLEDescriptor::~BLEDescriptor() { - if (_value) { +BLEDescriptor::~BLEDescriptor() +{ + if (_value) + { free(_value); _value = NULL; } } -BLEDescriptor::BLEDescriptor(const char* uuid, const char* value) : - BLEDescriptor(uuid, (const uint8_t*)value, strlen(value)) +const char* BLEDescriptor::uuid() const { + return _uuid_cstr; } -const unsigned char* -BLEDescriptor::value() const +const byte* BLEDescriptor::value() const { return _value; } -unsigned short -BLEDescriptor::valueLength() const +int BLEDescriptor::valueLength() const { - return _value_length; + return _value_size; } -unsigned char -BLEDescriptor::operator[] (int offset) const +BLEDescriptor::operator bool() const { - return _value[offset]; + return (strlen(_uuid_cstr) > 3); } -void BLEDescriptor::discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) +unsigned char BLEDescriptor::properties() const { - if (!attr) - { - // Discovery complete - _discoverying = false; - return; - } - - // Chracteristic Char - if (params->uuid == this->uuid()) - { - // Set Discover CCCD parameter - params->start_handle = attr->handle + 1; - // Complete the discover - _discoverying = false; - } - + return _properties; } -void BLEDescriptor::discover(bt_gatt_discover_params_t *params) +int BLEDescriptor::valueSize() const { - params->type = BT_GATT_DISCOVER_DESCRIPTOR; - params->uuid = this->uuid(); - // Start discovering - _discoverying = true; + return _value_size; } - diff --git a/libraries/CurieBLE/src/BLEDescriptor.h b/libraries/CurieBLE/src/BLEDescriptor.h index 95f75ed5..e1a79f2a 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.h +++ b/libraries/CurieBLE/src/BLEDescriptor.h @@ -1,102 +1,109 @@ /* - * Copyright (c) 2015 Intel Corporation. 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_INCLUDED -#define _BLE_DESCRIPTOR_H_INCLUDED - -#include "BLEAttribute.h" - -/** - * BLE GATT Descriptor class - */ -class BLEDescriptor : public BLEAttribute { -public: - /** - * Constructor for BLE Descriptor - * - * @param[in] uuid 16-bit UUID (in string form) defined by BLE standard - * @param[in] value Value of descriptor, as a byte array. Data is stored in internal copy. - * @param[in] valueLength Data length required for descriptor value (<= BLE_MAX_ATTR_DATA_LEN) - */ - BLEDescriptor(const char* uuid, const unsigned char value[], unsigned short valueLength); + BLE Descriptor API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_DESCRIPTOR_H +#define ARDUINO_BLE_DESCRIPTOR_H + +#include "CurieBLE.h" + +#include "BLEDevice.h" + +class BLEDescriptor +{ + public: + BLEDescriptor(); + BLEDescriptor(const char* uuid, const unsigned char value[], unsigned short valueLength); // create a descriptor the specified uuid and value + BLEDescriptor(const char* uuid, const char* value); // create a descriptor the specified uuid and string value + + BLEDescriptor(BLEDescriptorImp* descriptorImp, const BLEDevice *bleDev); + BLEDescriptor(const BLEDescriptor&); + BLEDescriptor& operator=(const BLEDescriptor&); virtual ~BLEDescriptor(); - /** - * Constructor for BLE Descriptor - * - * @param[in] uuid 16-bit UUID (in string form) defined by BLE standard - * @param[in] value String value of descriptor. Data is stored in internal copy. - * (String length <= BLE_MAX_ATTR_DATA_LEN) - */ - BLEDescriptor(const char* uuid, const char* value); + const char* uuid() const; - /** - * Get data pointer to the value of the Descriptor - * - * @return const unsigned char* pointer to the value of the Descriptor - */ - const unsigned char* value(void) const; + virtual const byte* value() const; // returns the value buffer + virtual int valueLength() const; // returns the current length of the value + + virtual operator bool() const; // is the descriptor valid (discovered from peripheral) - /** - * Get the length of the value of the Descriptor - * - * @return unsigned short size of Descriptor value in bytes - */ - unsigned short valueLength(void) const; + unsigned char properties() const; + int valueSize() const; +private: + char _uuid_cstr[37]; // The characteristic UUID + BLEDevice _bledev; + + unsigned char _properties; // The characteristic property + + unsigned short _value_size; // The value size + unsigned char* _value; // The value. Will delete after create the _internal + // The API reserved for feature release + // move here for temp + /** - * @brief For central to discover the peripherial profile + * @brief Write the value of the descriptor * - * @param[in] attr The discover response + * @param value The value buffer that want to write to descriptor * - * @param[in] params The discover parameter that need to fill + * @param length The value buffer's length * - * @return none + * @return bool true - Success, false - Failed * - * @note Only for central + * @note none */ - void discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params); + //virtual bool writeValue(const byte value[], int length); /** - * @brief For central to discover the peripherial profile + * @brief Write the value of the descriptor + * + * @param value The value buffer that want to write to descriptor * - * @param[in] params The discover parameter that need to fill + * @param length The value buffer's length * - * @return none + * @param offset The offset in the descriptor's data * - * @note Only for central + * @return bool true - Success, false - Failed + * + * @note none */ - void discover(bt_gatt_discover_params_t *params); + //bool writeValue(const byte value[], int length, int offset); - - unsigned char operator[] (int offset) const; - -protected: - - friend BLEPeripheral; - -private: - unsigned short _value_length; - unsigned char* _value; + /** + * @brief Write the value of the descriptor + * + * @param value The value string that want to write to descriptor + * + * @return bool true - Success, false - Failed + * + * @note none + */ + //bool writeValue(const char* value); + //virtual byte operator[] (int offset) const; // returns a byte of the value at the specified offset + + // GATT client Write the value of the descriptor + //virtual bool write(const byte value[], int length); + //bool write(const byte value[], int length, int offset); + //bool write(const char* value); + //bool read(); }; -#endif // _BLE_DESCRIPTOR_H_INCLUDED +#endif diff --git a/libraries/CurieBLE/src/BLEDevice.cpp b/libraries/CurieBLE/src/BLEDevice.cpp new file mode 100644 index 00000000..e59b5462 --- /dev/null +++ b/libraries/CurieBLE/src/BLEDevice.cpp @@ -0,0 +1,492 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. 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 "CurieBLE.h" +#include "BLEDevice.h" + +#include "./internal/BLEUtils.h" + +#include "./internal/BLEProfileManager.h" +#include "./internal/BLEDeviceManager.h" +#include "./internal/BLECharacteristicImp.h" + +BLEDevice::BLEDevice() +{ + memset(&_bt_addr, 0, sizeof(_bt_addr)); + _conn_param.interval_max = BT_GAP_INIT_CONN_INT_MAX; + _conn_param.interval_min = BT_GAP_INIT_CONN_INT_MIN; + _conn_param.latency = 0; + _conn_param.timeout = 400; +} + +/* +BLEDevice::BLEDevice(String bleaddress) +{ + BLEUtils::macAddressString2BT(bleaddress.c_str(), _bt_addr); +} + +BLEDevice::BLEDevice(const char* bleaddress) +{ + BLEUtils::macAddressString2BT(bleaddress, _bt_addr); +} + +*/ + +BLEDevice::BLEDevice(const bt_addr_le_t* bleaddress): + BLEDevice() +{ + memcpy(&_bt_addr, bleaddress, sizeof(_bt_addr)); +} + +BLEDevice::BLEDevice(const BLEDevice* bledevice) +{ + memcpy(&_bt_addr, bledevice->bt_le_address(), sizeof(_bt_addr)); + memcpy(&_conn_param, &bledevice->_conn_param, sizeof (_conn_param)); +} + +BLEDevice::BLEDevice(const BLEDevice& bledevice) +{ + memcpy(&_bt_addr, bledevice.bt_le_address(), sizeof(_bt_addr)); + memcpy(&_conn_param, &bledevice._conn_param, sizeof (_conn_param)); +} + +BLEDevice::~BLEDevice() +{ + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); +} + +bool BLEDevice::begin() +{ + return BLEDeviceManager::instance()->begin(this); +} + +void BLEDevice::poll() +{ + BLEProfileManager::instance()->handleDisconnectedPutOffEvent(); + BLEDeviceManager::instance()->poll(); +} + +void BLEDevice::end() +{} + +bool BLEDevice::connected() const +{ + bool link_exist = BLEDeviceManager::instance()->connected(this); + { + // If release the discoverd attributes, + // the GATT client may has crash issue due to used release pointer + BLEProfileManager::instance()->handleDisconnectedPutOffEvent(); + } + return link_exist; +} + +bool BLEDevice::disconnect() +{ + bool retval = BLEDeviceManager::instance()->disconnect(this); + BLEProfileManager::instance()->handleDisconnectedPutOffEvent(); + return retval; +} + +String BLEDevice::address() const +{ + return BLEUtils::macAddressBT2String(_bt_addr); +} + +void BLEDevice::setAddress(const bt_addr_le_t& addr) +{ + memcpy(&_bt_addr, &addr, sizeof(_bt_addr)); +} + +void BLEDevice::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + BLEDeviceManager::instance()->setAdvertisedServiceUuid(advertisedServiceUuid); +} + +void BLEDevice::setAdvertisedService(const BLEService& service) +{ + setAdvertisedServiceUuid(service.uuid()); +} + +void BLEDevice::setServiceSolicitationUuid(const char* serviceSolicitationUuid) +{ + BLEDeviceManager::instance()->setServiceSolicitationUuid(serviceSolicitationUuid); +} + +void BLEDevice::setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength) +{ + BLEDeviceManager::instance()->setManufacturerData(manufacturerData, manufacturerDataLength); +} + +void BLEDevice::setLocalName(const char *localName) +{ + BLEDeviceManager::instance()->setLocalName(localName); +} + +void BLEDevice::setAdvertisingInterval(float advertisingInterval) +{ + BLEDeviceManager::instance()->setAdvertisingInterval(advertisingInterval); +} + +void BLEDevice::setConnectionInterval(int minimumConnectionInterval, + int maximumConnectionInterval) +{ + // TODO: Update the connection interval need more discussion + +} + +bool BLEDevice::setTxPower(int txPower) +{ + return BLEDeviceManager::instance()->setTxPower(txPower); +} + +void BLEDevice::setConnectable(bool connectable) +{ + BLEDeviceManager::instance()->setConnectable(connectable); +} + +void BLEDevice::setDeviceName(const char* deviceName) +{ + BLEDeviceManager::instance()->setDeviceName(deviceName); +} + +void BLEDevice::setAppearance(unsigned short appearance) +{ + BLEDeviceManager::instance()->setAppearance(appearance); +} + +int BLEDevice::addService(BLEService& attribute) +{ + BLEServiceImp *service_imp = BLEProfileManager::instance()->addService(*this, attribute); + if (NULL == service_imp) + { + return BLE_STATUS_NO_MEMORY; + } + return BLE_STATUS_SUCCESS; +} + +int BLEDevice::advertise() +{ + preCheckProfile(); + return BLEDeviceManager::instance()->startAdvertising(); +} + +void BLEDevice::stopAdvertise() +{ + BLEDeviceManager::instance()->stopAdvertising(); +} + +BLEDevice BLEDevice::central() +{ + return BLEDeviceManager::instance()->central(); +} + +BLEDevice BLEDevice::peripheral() +{ + return BLEDeviceManager::instance()->peripheral(); +} + +BLEDevice::operator bool() const +{ + return BLEUtils::macAddressValid(_bt_addr); +} + +BLEDevice& BLEDevice::operator=(const BLEDevice& device) +{ + if (this != &device) + { + memcpy(&(this->_bt_addr), &(device._bt_addr), sizeof (this->_bt_addr)); + memcpy(&this->_conn_param, &device._conn_param, sizeof (this->_conn_param)); + } + return *this; +} + +bool BLEDevice::operator==(const BLEDevice& device) const +{ + return (memcmp(this->_bt_addr.val, device._bt_addr.val, 6) == 0); +} + +bool BLEDevice::operator!=(const BLEDevice& device) const +{ + return (memcmp(this->_bt_addr.val, device._bt_addr.val, 6) != 0); +} + + +bool BLEDevice::startScan(bool withDuplicates) +{ + preCheckProfile(); + if (withDuplicates) + { + return BLEDeviceManager::instance()->startScanningWithDuplicates(); + } + else + { + return BLEDeviceManager::instance()->startScanning(); + } +} + + +void BLEDevice::scan() +{ + scan(false); +} + +void BLEDevice::scan(bool withDuplicates) +{ + BLEDeviceManager::instance()->clearAdvertiseCritical(); + startScan(withDuplicates); +} + +void BLEDevice::scanForName(String name) +{ + scanForName(name, false); +} + +void BLEDevice::scanForName(String name, bool withDuplicates) +{ + BLEDeviceManager::instance()->setAdvertiseCritical(name); + startScan(withDuplicates); +} + +void BLEDevice::scanForUuid(String uuid) +{ + scanForUuid(uuid, false); +} + +void BLEDevice::scanForUuid(String uuid, bool withDuplicates) +{ + BLEService service_temp(uuid.c_str()); + BLEDeviceManager::instance()->setAdvertiseCritical(service_temp); + startScan(withDuplicates); +} + +void BLEDevice::stopScan() +{ + BLEDeviceManager::instance()->stopScanning(); +} + +BLEDevice BLEDevice::available() +{ + BLEProfileManager::instance()->handleDisconnectedPutOffEvent(); + return BLEDeviceManager::instance()->available(); +} + +bool BLEDevice::hasLocalName() const +{ + return BLEDeviceManager::instance()->hasLocalName(this); +} + +bool BLEDevice::hasAdvertisedServiceUuid() const +{ + return BLEDeviceManager::instance()->hasAdvertisedServiceUuid(this); +} + +bool BLEDevice::hasAdvertisedServiceUuid(int index) const +{ + return BLEDeviceManager::instance()->hasAdvertisedServiceUuid(this, index); +} + +int BLEDevice::advertisedServiceUuidCount() const +{ + return BLEDeviceManager::instance()->advertisedServiceUuidCount(this); +} + +String BLEDevice::localName() const +{ + return BLEDeviceManager::instance()->localName(this); +} + +String BLEDevice::advertisedServiceUuid() const +{ + return BLEDeviceManager::instance()->advertisedServiceUuid(this); +} + +String BLEDevice::advertisedServiceUuid(int index) const +{ + return BLEDeviceManager::instance()->advertisedServiceUuid(this, index); +} + +int BLEDevice::rssi() const +{ + return BLEDeviceManager::instance()->rssi(this); +} + +bool BLEDevice::connect() +{ + return BLEDeviceManager::instance()->connect(*this); +} + +bool BLEDevice::discoverAttributes() +{ + return BLEProfileManager::instance()->discoverAttributes(this); +} + +bool BLEDevice::discoverAttributesByService(const char* svc_uuid) +{ + bt_uuid_128_t uuid; + BLEUtils::uuidString2BT(svc_uuid, (bt_uuid_t *)&uuid); + return BLEProfileManager::instance()->discoverAttributesByService(this, (const bt_uuid_t *)&uuid); +} + + +String BLEDevice::deviceName() +{ + return BLEDeviceManager::instance()->deviceName(this); +} + +// For GATT +int BLEDevice::serviceCount() const +{ + return BLEProfileManager::instance()->serviceCount(*this); +} + +bool BLEDevice::hasService(const char* uuid) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, uuid); + return (NULL != serviceImp); +} + +bool BLEDevice::hasService(const char* uuid, int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + return serviceImp->compareUuid(uuid); +} + +BLEService BLEDevice::service(int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + if (serviceImp != NULL) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +BLEService BLEDevice::service(const char * uuid) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, uuid); + if (serviceImp != NULL) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +BLEService BLEDevice::service(const char * uuid, int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + if (serviceImp != NULL && serviceImp->compareUuid(uuid)) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +int BLEDevice::characteristicCount() const +{ + return BLEProfileManager::instance()->characteristicCount(*this); +} + +bool BLEDevice::hasCharacteristic(const char* uuid) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid); + return (NULL != characteristicImp); +} + +bool BLEDevice::hasCharacteristic(const char* uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid, index); + return (NULL != characteristicImp); +} + +BLECharacteristic BLEDevice::characteristic(int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, index); + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid); + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not matching + characteristicImp = NULL; + } + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +// event handler +void BLEDevice::setEventHandler(BLEDeviceEvent event, + BLEDeviceEventHandler eventHandler) +{ + BLEDeviceManager::instance()->setEventHandler(event, eventHandler); +} + +const bt_addr_le_t* BLEDevice::bt_le_address() const +{ + return &_bt_addr; +} +const bt_le_conn_param* BLEDevice::bt_conn_param() const +{ + return &_conn_param; +} + +void BLEDevice::preCheckProfile() +{ + if (false == BLEProfileManager::instance()->hasRegisterProfile() && + BLEProfileManager::instance()->serviceCount(*this) > 0) + { + BLEProfileManager::instance()->registerProfile(*this); + delay(8); + } +} + diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h new file mode 100644 index 00000000..590d933a --- /dev/null +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -0,0 +1,688 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_DEVICE_H +#define ARDUINO_BLE_DEVICE_H + +#include + +enum BLEDeviceEvent { + BLEConnected = 0, // BLE device connected + BLEDisconnected = 1, // BLE device disconnected + BLEConParamUpdate = 2, // Update the connection parameter + // Connection update request in central + // Connection parameter updated in peripheral + BLEDiscovered, // The scanned BLE device + BLEDeviceLastEvent +}; + +typedef void (*BLEDeviceEventHandler)(BLEDevice device); + +class BLEDevice +{ + public: + /** + * @brief The BLE device constructure + * + * @param none + * + * @return none + * + * @note none + */ + BLEDevice(); + + /** + * @brief The BLE device constructure + * + * @param[in] bledevice BLE device + * + * @return none + * + * @note none + */ + BLEDevice(const BLEDevice* bledevice); + BLEDevice(const BLEDevice& bledevice); + virtual ~BLEDevice(); + + + /** + * @brief Initiliaze the BLE hardware + * + * @return bool indicating success or error + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + bool begin(); + + /** + * @brief Poll for events + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void poll(); // Do we need add the return value or + // input parameter to get the events? + // Events may inlcue: + // GAP : Connected, Disconnected, Update connetion parameter + // GATT: Discovered + + /** + * @brief Deinitiliaze the BLE hardware + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void end(); + + /** + * @brief Is the device connected with another BLE device. + * + * @param none + * + * @return bool indicating success or error + * + * @note none + */ + bool connected() const; + + /** + * @brief Disconnect the connected device/s. + * + * @param none + * + * @return bool indicating success or error + * + * @note The BLE may connected multiple devices. + * This call will disconnect all conected devices. + */ + bool disconnect(); + + + /** + * @brief Get the BLE address of the BLE in string format + * + * @param none + * + * @return String The address of the BLE in string format + * + * @note none + */ + String address() const; + + /** + * @brief Set the service UUID that the BLE Peripheral Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + + /** + * @brief Set the service that the BLE Peripheral Device will advertise this UUID + * + * @param[in] service The service the will in advertise data. + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedService(const BLEService& service); + + /** + * @brief Set the service UUID that is solicited in the BLE Peripheral + * Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setServiceSolicitationUuid(const char* serviceSolicitationUuid); + + /** + * @brief Set the manufacturer data in the BLE Peripheral Device advertises + * + * @param[in] manufacturerData The data about manufacturer will + * be set in advertisement + * @param[in] manufacturerDataLength The length of the manufacturer data + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength); + + /** + * Set the local name that the BLE Peripheral Device advertises + * + * @param[in] localName local name to advertise + * + * @note This method must be called before the begin method + */ + void setLocalName(const char *localName); + + /** + * @brief Set advertising interval + * + * @param[in] advertisingInterval Advertising Interval in ms + * + * @return none + * + * @note none + */ + void setAdvertisingInterval(float advertisingInterval); + + /** + * @brief Set the connection parameters and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @param[in] latency Connection Latency + * + * @param[in] timeout Supervision Timeout (ms) + * + * @return none + * + * @note none + */ + //void setConnectionInterval(int minimumConnectionInterval, + // int maximumConnectionInterval, + // uint16_t latency, + // uint16_t timeout); + + /** + * @brief Set the min and max connection interval and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(int minimumConnectionInterval, + int maximumConnectionInterval); + + /** + * @brief Set TX power of the radio in dBM + * + * @param[in] tx_power The antenna TX power + * + * @return boolean_t true if established connection, otherwise false + */ + bool setTxPower(int txPower); + + /** + * @brief Set advertising type as connectable/non-connectable + * + * @param[in] connectable true - The device connectable + * false - The device non-connectable + * + * @return none + * + * @note Only for peripheral mode. + * Default value is connectable + */ + void setConnectable(bool connectable); + + /** + * @brief Set the value of the device name characteristic + * + * @param[in] device User-defined name string for this device. Truncated if + * more than maximum allowed string length (20 bytes). + * + * @note This method must be called before the begin method + * If device name is not set, a default name will be used + */ + void setDeviceName(const char* deviceName); + + /** + * @brief Set the appearance type for the BLE Peripheral Device + * + * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml + * for available options. + * + * @param[in] appearance Appearance category identifier as defined by BLE Standard + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void setAppearance(unsigned short appearance); + + /** + * @brief Add a Service to the BLE Peripheral Device + * + * @param[in] attribute The service that will add to Peripheral + * + * @return int Indicating success or error type @enum BLE_STATUS_T + * + * @note This method must be called before the begin method + */ + int addService(BLEService& attribute); + + /** + * @brief Construct the ADV data and start send advertisement + * + * @param none + * + * @return int 0 - Success. Others - error code @enum BLE_STATUS_T + * + * @note none + */ + int advertise(); + + /** + * @brief Stop send advertisement + * + * @param none + * + * @return none + * + * @note none + */ + void stopAdvertise(); + + /** + * @brief Get currently connected central + * + * @return BLEDevice Connected central device + * + * @note Peripheral mode only + */ + BLEDevice central(); + + /** + * @brief Get currently connected peripheral + * + * @param none + * + * @return none + * + * @note Central mode only. How to distinguish the peripheral? + */ + BLEDevice peripheral(); + + operator bool() const; + bool operator==(const BLEDevice& device) const; + bool operator!=(const BLEDevice& device) const; + BLEDevice& operator=(const BLEDevice& device); + // central mode + + //void scanForAddress(String address); // Not include in baseline. Add here as feature for feature release. + + /** + * @brief Start scanning for peripherals without filter + * + * @param none + * + * @return none + * + * @note none + */ + void scan(); + + /** + * @brief Start scanning for peripherals with filter + * + * @param[in] withDuplicates true - with duplicate filter + * false- without duplicate filter + * + * @return none + * + * @note option to filter out duplicate addresses for Arduino. + * The current only support fileter duplicate mode. + */ + void scan(bool withDuplicates); + + /** + * @brief Start scanning for peripherals and filter by device name in ADV + * + * @param name The device's local name. + * + * @return none + * + * @note option to filter out duplicate addresses for Arduino. + * The current only support fileter duplicate mode. + */ + void scanForName(String name); + + /** + * @brief Start scanning for peripherals and filter by device name in ADV + * + * @param[in] name The device's local name. + * + * @param[in] withDuplicates true - with duplicate filter + * false- without duplicate filter + * + * @return none + * + * @note option to filter out duplicate addresses for Arduino. + * The current only support fileter duplicate mode. + */ + void scanForName(String name, bool withDuplicates); + + /** + * @brief Start scanning for peripherals and filter by service in ADV + * + * @param service The service + * + * @return none + * + * @note none + */ + void scanForUuid(String uuid); + + /** + * @brief Start scanning for peripherals and filter by service in ADV + * + * @param[in] service The service + * + * @param[in] withDuplicates true - with duplicate filter + * false- without duplicate filter + * + * @return none + * + * @note option to filter out duplicate addresses for Arduino. + * The current only support fileter duplicate mode. + */ + void scanForUuid(String uuid, bool withDuplicates); + + /** + * @brief Stop scanning for peripherals + * + * @param none + * + * @return none + * + * @note none + */ + void stopScan(); + + /** + * @brief Retrieve a discovered peripheral + * + * @param none + * + * @return BLEDevice The BLE device that central scanned + * + * @note none + */ + BLEDevice available(); + + /** + * @brief Does the peripheral advertise a local name + * + * @param none + * + * @return none + * + * @note none //TODO: The implementation doesn't save the ADV's local name. + */ + bool hasLocalName() const; + + bool hasAdvertisedServiceUuid() const; // does the peripheral advertise a service + bool hasAdvertisedServiceUuid(int index) const; // does the peripheral advertise a service n + int advertisedServiceUuidCount() const; // number of services the peripheral is advertising + + String localName() const; // returns the advertised local name as a String + String advertisedServiceUuid() const; // returns the advertised service as a UUID String + String advertisedServiceUuid(int index) const; // returns the nth advertised service as a UUID String + + int rssi() const; // returns the RSSI of the peripheral at discovery + + bool connect(); // connect to the peripheral + bool discoverAttributes(); // discover the peripheral's attributes + bool discoverAttributesByService(const char* svc_uuid); + + String deviceName(); // read the device name attribute of the peripheral, and return String value + //int appearance(); // read the appearance attribute of the peripheral and return value as int + + // For GATT + /** + * @brief returns the number of services the BLE device has + * + * @param none + * + * @return int The number of services + * + * @note none + */ + int serviceCount() const; + + /** + * @brief Does the peripheral have a service with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @return bool true - Found + * false- Not found + * + * @note none + */ + bool hasService(const char* uuid) const; + + /** + * @brief Does the peripheral have an nth service with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @param index The index + * + * @return bool true - Found + * false- Not found + * + * @note none + */ + bool hasService(const char* uuid, int index) const; + + /** + * @brief Return the nth service of the peripheral + * + * @param index The index + * + * @return BLEService The BLE service + * + * @note none + */ + BLEService service(int index) const; + + /** + * @brief Return the service with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @return BLEService The BLE service + * + * @note none + */ + BLEService service(const char * uuid) const; + + /** + * @brief Return the nth service with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @param index The index + * + * @return BLEService The BLE service + * + * @note none + */ + BLEService service(const char * uuid, int index) const; + + /** + * @brief Returns the number of characteristics the BLE device has + * + * @param none + * + * @return int The number of characteristics + * + * @note none + */ + int characteristicCount() const; + + /** + * @brief Does the device have a characteristic with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @return bool true - Found + * false- Not found + * + * @note none + */ + bool hasCharacteristic(const char* uuid) const; + + /** + * @brief Does the device have an nth characteristic with the + * specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @param index The index + * + * @return bool true - Found + * false- Not found + * + * @note none + */ + bool hasCharacteristic(const char* uuid, int index) const; + + /** + * @brief Return the nth characteristic of the BLE device + * + * @param index The index + * + * @return BLECharacteristic The BLE characteristic + * + * @note none + */ + BLECharacteristic characteristic(int index) const; + + /** + * @brief Return the characteristic with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @return BLECharacteristic The BLE characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid) const; + + /** + * @brief Return the nth characteristic with the specified UUID + * + * @param uuid The 128/16 bits UUID + * + * @param index The index + * + * @return BLECharacteristic The BLE characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid, int index) const; + + // event handler + /** + * @brief Set the event callbacks + * + * @param event The BLE device event + * + * @param eventHandler The BLE device event handler + * + * @return none + * + * @note none + */ + void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); // set an event handler (callback) + +protected: + friend class BLECharacteristicImp; + friend class BLEServiceImp; + friend class BLEDeviceManager; + friend class BLEProfileManager; + friend class BLECharacteristic; + friend class BLEDescriptor; + friend class BLEService; + friend uint8_t profile_notify_process (bt_conn_t *conn, + bt_gatt_subscribe_params_t *params, + const void *data, uint16_t length); + friend uint8_t profile_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); + const bt_addr_le_t* bt_le_address() const; + const bt_le_conn_param* bt_conn_param() const; + void setAddress(const bt_addr_le_t& addr); + + void setAdvertiseData(const uint8_t* adv_data, uint8_t len); + /** + * @brief The BLE device constructure + * + * @param[in] bleaddress BLE device address + * + * @return none + * + * @note none + */ + BLEDevice(const bt_addr_le_t* bleaddress); +private: + void preCheckProfile(); + + /** + * @brief Start scanning for peripherals with/without duplicate filter + * + * @param[in] withDuplicates true - with duplicate filter + * false- without duplicate filter + * + * @return none + * + * @note option to filter out duplicate addresses for Arduino. + * The current only support fileter duplicate mode. + */ + bool startScan(bool withDuplicates); + +private: + bt_addr_le_t _bt_addr; + + bt_le_conn_param _conn_param; +}; + +#endif diff --git a/libraries/CurieBLE/src/BLEHelper.cpp b/libraries/CurieBLE/src/BLEHelper.cpp deleted file mode 100644 index e3e1caa6..00000000 --- a/libraries/CurieBLE/src/BLEHelper.cpp +++ /dev/null @@ -1,160 +0,0 @@ - -#include "BLECentralHelper.h" - -#include "BLEPeripheral.h" - - -BLEHelper::BLEHelper() -{ - clearAddress(); - memset(&_conn_params, 0x00, sizeof(_conn_params)); - _conn_params.interval_max = BT_GAP_INIT_CONN_INT_MAX; - _conn_params.interval_min = BT_GAP_INIT_CONN_INT_MIN; - _conn_params.latency = 0; - _conn_params.timeout = 400; -} - -BLEHelper::~BLEHelper() -{ -} - -BLEHelper::operator bool() const -{ - bt_addr_le_t zero; - - memset(&zero, 0, sizeof(zero)); - - return (memcmp(&_address, &zero, sizeof(_address)) != 0); -} - -bool BLEHelper::operator==(const BLEHelper& rhs) const -{ - return (memcmp(&_address, &rhs._address, sizeof(_address)) == 0); -} - -bool -BLEHelper::operator==(const bt_addr_le_t& address) const { - return (memcmp(&_address, &address, sizeof(_address)) == 0); -} - -bool -BLEHelper::operator!=(const BLEHelper& rhs) const { - return !(*this == rhs); -} - -const char* -BLEHelper::address() const { - static char address[18]; - - String addressStr = ""; - - for (int i = 5; i >= 0; i--) { - unsigned char a = _address.val[i]; - - if (a < 0x10) { - addressStr += "0"; - } - - addressStr += String(a, 16); - - if (i > 0) { - addressStr += ":"; - } - } - - strcpy(address, addressStr.c_str()); - - return address; -} -/* -const bt_addr_t *BLEHelper::address(void) const -{ - return (bt_addr_t *)_address.val; -} -*/ - -const bt_addr_le_t *BLEHelper::bt_le_address(void) const -{ - return &_address; -} - -void -BLEHelper::poll() { - delay(1); -} - -void -BLEHelper::setAddress(const bt_addr_le_t &address) { - memcpy(&_address, &address, sizeof(bt_addr_le_t)); -} - -void -BLEHelper::clearAddress() { - memset(&_address, 0x00, sizeof(_address)); -} - -void BLEHelper::getConnParams(ble_conn_param_t &user_conn_params) -{ - user_conn_params.interval_min = UNITS_TO_MSEC(_conn_params.interval_min, UNIT_1_25_MS); - user_conn_params.interval_max = UNITS_TO_MSEC(_conn_params.interval_max, UNIT_1_25_MS); - user_conn_params.timeout = UNITS_TO_MSEC(_conn_params.timeout, UNIT_10_MS); - user_conn_params.latency = _conn_params.latency; -} - -void BLEHelper::setConnectionParameters(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout) -{ - _conn_params.interval_max = intervalmin; - _conn_params.interval_min = intervalmax; - _conn_params.latency = latency; - _conn_params.timeout = timeout; -} - -void BLEHelper::updateConnectionInterval(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout) -{ - setConnectionParameters(intervalmin, intervalmax, latency, timeout); - updateConnectionInterval(); -} - -void BLEHelper::updateConnectionInterval() -{ - bt_conn_t* conn = bt_conn_lookup_addr_le(&_address); - int ret = 0; - if (NULL != conn) - { - ret = bt_conn_le_param_update(conn, &_conn_params); - pr_debug(LOG_MODULE_BLE, "%s-ret:%d",__FUNCTION__, ret); - bt_conn_unref(conn); - } -} - -void BLEHelper::setConnectionInterval(float minInterval, - float maxInterval) -{ - uint16_t minVal = (uint16_t)MSEC_TO_UNITS(minInterval, UNIT_1_25_MS); - uint16_t maxVal = (uint16_t)MSEC_TO_UNITS(maxInterval, UNIT_1_25_MS); - _conn_params.interval_min = minVal; - _conn_params.interval_max = maxVal; - updateConnectionInterval(); -} - -void BLEHelper::setConnectionInterval(float minInterval, - float maxInterval, - uint16_t latency, - uint16_t timeout) -{ - uint16_t minVal = (uint16_t)MSEC_TO_UNITS(minInterval, UNIT_1_25_MS); - uint16_t maxVal = (uint16_t)MSEC_TO_UNITS(maxInterval, UNIT_1_25_MS); - uint16_t timeoutVal = MSEC_TO_UNITS(timeout, UNIT_10_MS); - _conn_params.interval_min = minVal; - _conn_params.interval_max = maxVal; - _conn_params.timeout = timeoutVal; - updateConnectionInterval(); -} - - diff --git a/libraries/CurieBLE/src/BLEHelper.h b/libraries/CurieBLE/src/BLEHelper.h deleted file mode 100644 index f0c6bc21..00000000 --- a/libraries/CurieBLE/src/BLEHelper.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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_HELPER_H_ -#define _BLE_HELPER_H_ - - -class BLEHelper { - public: - /** - * Is the Central connected - * - * @return boolean_t true if the central is connected, otherwise false - */ - virtual bool connected(void) = 0; - - /** - * Get the address of the BLE in string format - * - * @return const char* address of the BLE in string format - */ - const char* address(void) const; - - /** - * @brief Get the address of the BLE in raw format - * - * @param none - * - * @return const bt_addr_le_t * address of the BLE in raw format - * - * @note none - */ - const bt_addr_le_t *bt_le_address(void) const; - /** - * Disconnect the central if it is connected - * - */ - virtual bool disconnect(void) = 0; - - /** - * Poll the central for events - */ - void poll(void); - - operator bool(void) const; - bool operator==(const BLEHelper& rhs) const; - bool operator==(const bt_addr_le_t& rhs) const; - bool operator!=(const BLEHelper& rhs) const; - - /** - * @brief Get the connection paramter - * - * @param[out] user_conn_params connection paramter - * Minimum Connection Interval (ms) - * Maximum Connection Interval (ms) - * Connection Latency - * Supervision Timeout (ms) - * - * @return none - * - * @note none - */ - void getConnParams(ble_conn_param_t &user_conn_params); - - /** - * @brief Set the connection paramter and send connection - * update request - * - * @param[in] intervalmin Minimum Connection Interval (ms) - * - * @param[in] intervalmax Maximum Connection Interval (ms) - * - * @return none - * - * @note none - */ - void setConnectionInterval(float minInterval, - float maxInterval); - - /** - * @brief Set the connection paramter and send connection - * update request - * - * @param[in] intervalmin Minimum Connection Interval (ms) - * - * @param[in] intervalmax Maximum Connection Interval (ms) - * - * @param[in] latency Connection Latency - * - * @param[in] timeout Supervision Timeout (ms) - * - * @return none - * - * @note none - */ - void setConnectionInterval(float minInterval, - float maxInterval, - uint16_t latency, - uint16_t timeout); - - /** - * @brief Just set the connection parameter. - * Not send out connection update request. - * - * @param[in] intervalmin Minimum Connection Interval (N * 1.25 ms) - * - * @param[in] intervalmax Maximum Connection Interval (N * 1.25 ms) - * - * @param[in] latency Connection Latency - * - * @param[in] timeout Supervision Timeout (N * 10 ms) - * - * @return none - * - * @note The user should care the unit - */ - void setConnectionParameters(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout); - - /** - * @brief Schedule the link connection update request - * - * @param[in] intervalmin Minimum Connection Interval (N * 1.25 ms) - * - * @param[in] intervalmax Maximum Connection Interval (N * 1.25 ms) - * - * @param[in] latency Connection Latency - * - * @param[in] timeout Supervision Timeout (N * 10 ms) - * - * @return none - * - * @note The user should care the unit - */ - void updateConnectionInterval(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout); - - /** - * @brief Schedule the link connection update request - * - * @return none - * - * @note The connection update request will not send if - * parameter doesn't changed - */ - void updateConnectionInterval(); - - protected: - void setAddress(const bt_addr_le_t &address); - void clearAddress(); - BLEHelper(); - virtual ~BLEHelper(); - - private: - bt_addr_le_t _address; /// BT low energy address - bt_le_conn_param_t _conn_params; /// Connection parameter -}; - -#endif - - diff --git a/libraries/CurieBLE/src/BLEPeripheral.cpp b/libraries/CurieBLE/src/BLEPeripheral.cpp index efbb77f6..84b85c88 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -1,294 +1,196 @@ /* - * Copyright (c) 2015 Intel Corporation. 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 - * - */ + BLE Peripheral API (deprecated) + Copyright (c) 2016 Arduino LLC. All right reserved. -#include "BLEPeripheral.h" -#include "BLEPeripheralRole.h" + 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. -//#include "BLECharacteristic.h" + 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. -BLEPeripheral::BLEPeripheral(void) : - _local_name(NULL), - _appearance(0), - _adv_data_idx(0) -{ - memset(_adv_data, 0x00, sizeof(_adv_data)); - - // Default Advertising parameter - setConnectable(true); -} + 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 +*/ -BLEPeripheral::~BLEPeripheral(void) +#include "CurieBLE.h" + +#include "BLEPeripheral.h" + +static BLEPeripheralEventHandler m_eventHandlers[BLEDeviceLastEvent]; + +void bleBackCompatiblePeripheralConnectHandler(BLEDevice central) { + if (m_eventHandlers[BLEConnected]) + { + BLECentral temp(central); + m_eventHandlers[BLEConnected](temp); + } } -bool BLEPeripheral::begin() +void bleBackCompatiblePeripheralDisconnectHandler(BLEDevice central) { - bool ret = false; - - pr_info(LOG_MODULE_BLE, "%s: %d", __FUNCTION__, 1); - - ret = BLEPeripheralRole::instance()->begin(); - if (!ret) + if (m_eventHandlers[BLEDisconnected]) { - return false; + BLECentral temp(central); + m_eventHandlers[BLEDisconnected](temp); } - - pr_info(LOG_MODULE_BLE, "%s: %d", __FUNCTION__, 2); - - return (startAdvertising() == BLE_STATUS_SUCCESS); } -void -BLEPeripheral::poll() + +BLEPeripheral::BLEPeripheral(void) : + _initCalled(false), + _lastService(NULL), + _lastCharacteristic(NULL) { - // no-op for now - delay(1); } -void -BLEPeripheral::end() +BLEPeripheral::~BLEPeripheral(void) { - BLEPeripheralRole::instance()->stop(); } -void -BLEPeripheral::setAdvertisedServiceUuid(const bt_uuid_t* advertisedServiceUuid) +void BLEPeripheral::setAdvertisedServiceUuid(const char* advertisedServiceUuid) { - _advertise_service_uuid = advertisedServiceUuid; -} + if (!_initCalled) { + init(); + } -void -BLEPeripheral::setLocalName(const char* localName) + BLE.setAdvertisedServiceUuid(advertisedServiceUuid); +} +void BLEPeripheral::setLocalName(const char* localName) { - _local_name = localName; + if (!_initCalled) { + init(); + } + + BLE.setLocalName(localName); } -void -BLEPeripheral::setAdvertisedServiceData(const bt_uuid_t* serviceDataUuid, - uint8_t* serviceData, - uint8_t serviceDataLength) + +void BLEPeripheral::setDeviceName(const char *deviceName) { - _service_data_uuid = serviceDataUuid; - _service_data = serviceData; - _service_data_length = serviceDataLength; + if (!_initCalled) { + init(); + } + + BLE.setDeviceName(deviceName); } -void -BLEPeripheral::setAdvertisingInterval(float interval_min, - float interval_max) +void BLEPeripheral::setAppearance(const unsigned short appearance) { - uint16_t max = (uint16_t) MSEC_TO_UNITS(interval_max, UNIT_0_625_MS); - uint16_t min = (uint16_t) MSEC_TO_UNITS(interval_min, UNIT_0_625_MS); - BLEPeripheralRole::instance()->setAdvertisingInterval(min, max); + if (!_initCalled) { + init(); + } + + BLE.setAppearance(appearance); } -void -BLEPeripheral::setAdvertisingInterval(float advertisingInterval) +void BLEPeripheral::setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval) { - setAdvertisingInterval(advertisingInterval, advertisingInterval); + if (!_initCalled) { + init(); + } + + BLE.setConnectionInterval(minConnInterval, maxConnInterval); } -void -BLEPeripheral::setConnectable(bool connectable) +void BLEPeripheral::addAttribute(BLEService& service) { - uint8_t type = BT_LE_ADV_IND; - if (connectable == false) + if (!_initCalled) { - type = BT_LE_ADV_NONCONN_IND; + init(); } - BLEPeripheralRole::instance()->setAdvertisingType(type); + + BLE.addService(service); + _lastService = &service; } -void -BLEPeripheral::setDeviceName(const char deviceName[]) +void BLEPeripheral::addAttribute(BLECharacteristic& characteristic) { - BLEPeripheralRole::instance()->setDeviceName(deviceName); + if (!_initCalled) + { + init(); + } + + if (_lastService) + { + _lastService->addCharacteristic(characteristic); + _lastCharacteristic = &characteristic; + } } -void -BLEPeripheral::setAppearance(const uint16_t appearance) +void BLEPeripheral::addAttribute(BLEDescriptor& descriptor) { - _appearance = appearance; + if (!_initCalled) + { + init(); + } + + if (_lastCharacteristic) + { + _lastCharacteristic->addDescriptor(descriptor); + } } -void -BLEPeripheral::setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval) +void BLEPeripheral::setEventHandler(BLEPeripheralEvent event, BLEPeripheralEventHandler callback) { - BLEPeripheralRole::instance()->setConnectionInterval(minConnInterval, - maxConnInterval); + if (BLEConnected == event || BLEDisconnected == event) + { + m_eventHandlers[event] = callback; + } } -void -BLEPeripheral::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) +bool BLEPeripheral::begin(void) { - BLEPeripheralRole::instance()->setEventHandler(event, callback); + if (!_initCalled) + { + init(); + } + + BLE.setEventHandler(BLEDisconnected, bleBackCompatiblePeripheralDisconnectHandler); + BLE.setEventHandler(BLEConnected, bleBackCompatiblePeripheralConnectHandler); + + BLE.advertise(); + return true; } -bool -BLEPeripheral::disconnect() +void BLEPeripheral::poll(void) { - return BLEPeripheralRole::instance()->disconnect(); + BLE.poll(); } -BLECentralHelper -BLEPeripheral::central() +void BLEPeripheral::end(void) { - return BLEPeripheralRole::instance()->central(); + BLE.end(); } -bool -BLEPeripheral::connected() +bool BLEPeripheral::disconnect(void) { - return BLEPeripheralRole::instance()->connected(); + return BLE.disconnect(); } -BleStatus -BLEPeripheral::addAttribute(BLEAttribute& attribute) +BLECentral BLEPeripheral::central(void) { - return BLEPeripheralRole::instance()->addAttribute(attribute); + BLEDevice centralBle = BLE.central(); + return BLECentral(centralBle); } - -BleStatus -BLEPeripheral::_advDataInit(void) +bool BLEPeripheral::connected(void) { - uint8_t lengthTotal = 2; // Flags data length - _adv_data_idx = 0; - - /* Add flags */ - _adv_type = (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR); - _adv_data[_adv_data_idx].type = BT_DATA_FLAGS; - _adv_data[_adv_data_idx].data = &_adv_type; - _adv_data[_adv_data_idx].data_len = 1; - _adv_data_idx++; - - if (_advertise_service_uuid) - { - uint8_t type; - uint8_t length; - uint8_t *data = NULL; - - pr_info(LOG_MODULE_BLE, "ADV Type-%d", _advertise_service_uuid->type); - if (BT_UUID_TYPE_16 == _advertise_service_uuid->type) - { - //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); - data = (uint8_t *)&(((bt_uuid_16_t *)_advertise_service_uuid)->val); - length = UUID_SIZE_16; - type = BT_DATA_UUID16_ALL; - } - else if (BT_UUID_TYPE_128 == _advertise_service_uuid->type) - { - data = ((bt_uuid_128_t *)_advertise_service_uuid)->val; - length = UUID_SIZE_128; - type = BT_DATA_UUID128_ALL; - } - if (NULL != data) - { - _adv_data[_adv_data_idx].type = type; - _adv_data[_adv_data_idx].data = data; - _adv_data[_adv_data_idx].data_len = length; - _adv_data_idx++; - lengthTotal += length; - - pr_info(LOG_MODULE_BLE, "Service UUID Len -%d", length); - } - } - - if (_local_name) - { - /* Add device name (truncated if too long) */ - _adv_data[_adv_data_idx].type = BT_DATA_NAME_COMPLETE; - _adv_data[_adv_data_idx].data = (const uint8_t*)_local_name; - _adv_data[_adv_data_idx].data_len = strlen(_local_name); - _adv_data_idx++; - - lengthTotal += strlen(_local_name); - pr_info(LOG_MODULE_BLE, "Local Name -%s", _local_name); - pr_info(LOG_MODULE_BLE, "Local Name Len -%d", strlen(_local_name)); - } - - if (_service_data) - { - /* Add Service Data (if it will fit) */ - - /* A 128-bit Service Data UUID won't fit in an Advertising packet */ - if (BT_UUID_TYPE_16 != _service_data_uuid->type) - { - /* We support service data only for 16-bit service UUID */ - return BLE_STATUS_NOT_SUPPORTED; - } - - uint8_t block_len = sizeof(uint16_t) + _service_data_length; - if (1 + block_len > BLE_MAX_ADV_SIZE) - { - // Service data block is too large. - return BLE_STATUS_ERROR_PARAMETER; - } - - _adv_data[_adv_data_idx].type = BT_DATA_SVC_DATA16; - _adv_data[_adv_data_idx].data = _service_data_buf; - _adv_data[_adv_data_idx].data_len = block_len; - _adv_data_idx++; - - uint8_t *adv_tmp = _service_data_buf; - - UINT16_TO_LESTREAM(adv_tmp, (((bt_uuid_16_t *)_service_data_uuid)->val)); - memcpy(adv_tmp, _service_data, _service_data_length); - - lengthTotal += block_len; - pr_info(LOG_MODULE_BLE, "SVC Len -%d", block_len); - } - if (lengthTotal > BLE_MAX_ADV_SIZE) - { - pr_error(LOG_MODULE_BLE, "ADV Total length-%d", lengthTotal); - // Service data block is too large. - return BLE_STATUS_ERROR_PARAMETER; - } - return BLE_STATUS_SUCCESS; + return BLE.connected(); } -BleStatus -BLEPeripheral::startAdvertising() +void BLEPeripheral::init() { - BleStatus status = BLE_STATUS_SUCCESS; - status = _advDataInit(); - if (BLE_STATUS_SUCCESS != status) + if (!_initCalled) { - return status; + BLE.begin(); + memset(m_eventHandlers, 0, sizeof(m_eventHandlers)); + _initCalled = true; } - status = BLEPeripheralRole::instance()->startAdvertising(_adv_data, - _adv_data_idx, - NULL, - 0); - return status; -} - -BleStatus -BLEPeripheral::stopAdvertising() -{ - BleStatus status = BLE_STATUS_SUCCESS; - - status = BLEPeripheralRole::instance()->stopAdvertising(); - return status; } -BLECentralHelper *BLEPeripheral::getPeerCentralBLE(BLEHelper& central) -{ - return (BLECentralHelper *)(¢ral); -} diff --git a/libraries/CurieBLE/src/BLEPeripheral.h b/libraries/CurieBLE/src/BLEPeripheral.h index 33b8bef7..f099efa5 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.h +++ b/libraries/CurieBLE/src/BLEPeripheral.h @@ -1,279 +1,69 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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_PERIPHERAL_H_INCLUDED -#define _BLE_PERIPHERAL_H_INCLUDED - -#include "internal/ble_client.h" - -#include "BLECommon.h" -#include "BLERoleBase.h" -#include "BLEPeripheralHelper.h" - -/** Function prototype for BLE Peripheral Device event callback */ -typedef void (*BLEPeripheralEventHandler)(BLECentralHelper ¢ral); - -/** - * BLE Peripheral - */ -class BLEPeripheral{ -public: - /** - * Default Constructor for BLE Peripheral Device - */ - BLEPeripheral(void); - - /** - * Destructor for BLE Peripheral Device - */ - virtual ~BLEPeripheral(void); - - /** - * Set the service UUID that the BLE Peripheral Device advertises - * - * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis - * (in string form) - * - * @note This method must be called before the begin method - */ - void setAdvertisedServiceUuid(const bt_uuid_t* advertisedServiceUuid); - - /** - * Set the local name that the BLE Peripheral Device advertises - * - * @param[in] localName local name to advertise - * - * @note This method must be called before the begin method - */ - void setLocalName(const char* localName); - - /** - * Set the Service Data that the BLE Peripheral Device advertises - * - * @param[in] serviceDataUuid 16-bit Service UUID for this Service Data - * (in string form). Must match the UUID parameter - * of setAdvertisedServiceUuid(). To fit into BLE_MAX_ADV_SIZE, - * the UUID must be a 16-bit UUID. - * - * @param[in] serviceData binary array of Service Data. - * - * @param[in] serviceDataLength length (bytes) of serviceData[] - * - * @note the entire advertising packet must be no more than - * BLE_MAX_ADV_SIZE bytes, which is currently 31. - * This likely means that if you use Service Data - * there will not be room for a Local Name. - * - * @note if serviceDataUuid isn't 16-bits long, or if - * serviceDataLength won't fit in the advertising block, - * the service data will silently not be copied - * into the advertising block. - */ - void setAdvertisedServiceData(const bt_uuid_t* serviceDataUuid, - uint8_t* serviceData, - uint8_t serviceDataLength); - - /** - * @brief Set advertising interval - * - * @param[in] advertisingInterval Advertising Interval (N * 0.625) - * - * @return none - * - * @note none - */ - void setAdvertisingInterval(float advertisingInterval); - - /** - * @brief Set advertising interval - * - * @param[in] interval_min Minimum Advertising Interval (millisecond) - * - * @param[in] interval_max Maximum Advertising Interval (millisecond) - * - * @return none - * - * @note none - */ - void setAdvertisingInterval(float interval_min, - float interval_max); - - /** - * @brief Set advertising type as connectable/non-connectable - * - * @param[in] connectable true - The device connectable - * false - The device non-connectable - * - * @return none - * - * @note none - */ - void setConnectable(bool connectable); - - /** - * Set the device name for the BLE Peripheral Device - * - * If device name is not set, a default name will be used instead - * - * @param[in] device User-defined name string for this device. Truncated if - * more than maximum allowed string length (20 bytes). - * - * @note This method must be called before the begin method - */ - void setDeviceName(const char *deviceName); - - /** - * Set the appearance type for the BLE Peripheral Device - * - * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml - * for available options. - * - * @param[in] appearance Appearance category identifier as defined by BLE Standard - * - * @return BleStatus indicating success or error - * - * @note This method must be called before the begin method - */ - void setAppearance(const unsigned short appearance); - - /** - * Set the min and max connection interval BLE Peripheral Device - * - * @param[in] minConnInterval Minimum connection interval (1.25 ms units), minimum 0x0006 (7.5ms) - * @param[in] maxConnInterval Maximum connection interval (1.25 ms units), maximum 0x095f (2998.75ms) - * - * @note This method must be called before the begin method - */ - void setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval); - - /** - * Add an attribute to the BLE Peripheral Device - * - * @param[in] attribute Attribute to add to Peripheral - * - * @return BleStatus indicating success or error - * - * @note This method must be called before the begin method - * Only need check return value at first call. Memory only alloc at first call - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * Provide a function to be called when events related to this Device are raised - * - * @param[in] event Event type for callback - * @param[in] callback Pointer to callback function to invoke when an event occurs. - */ - void setEventHandler(BLERoleEvent event, BLERoleEventHandler callback); - - /** - * Setup attributes and start advertising - * - * @return bool indicating success or error - */ - bool begin(void); - - /** - * Poll the peripheral for events - */ - void poll(void); - - /** - * Stop advertising and disconnect a central if connected - */ - void end(void); - - /** - * Disconnect the central connected if there is one connected - * - * @return bool indicating success or error - */ - bool disconnect(void); - - /** - * Setup attributes and start advertising - * - * @return BleStatus indicating success or error - */ - BLECentralHelper central(void); - - /** - * Is a central connected? - * - * @return boolean_t true if central connected, otherwise false - */ - bool connected(void); - - /** - * @brief Init the ADV data and start send advertisement - * - * @param none - * - * @return BleStatus 0 - Success. Others - error code - * - * @note none - */ - BleStatus startAdvertising(void); - - /** - * @brief Stop send advertisement - * - * @param none - * - * @return BleStatus 0 - Success. Others - error code - * - * @note none - */ - BleStatus stopAdvertising(void); - - /** - * Get peer central device - * - *@param central peer central device of the peripheral board - * - * @return pointer of peer central device - */ - BLECentralHelper *getPeerCentralBLE(BLEHelper& central); - -protected: - -private: - - BleStatus _stop(void); - - BleStatus _advDataInit(void); - -private: - const char* _local_name; - - const bt_uuid_t* _service_data_uuid; - uint8_t* _service_data; - uint8_t _service_data_length; - uint8_t _service_data_buf[BLE_MAX_ADV_SIZE]; - - uint16_t _appearance; - - const bt_uuid_t* _advertise_service_uuid; - - uint8_t _adv_type; - bt_data_t _adv_data[4]; - size_t _adv_data_idx; -}; - -#endif // _BLE_DEVICE_H_INCLUDED +/* + BLE Peripheral API (deprecated) + Copyright (c) 2016 Arduino LLC. 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 +*/ +// The API in this file is in DEPRECATED MODE, please DO NOT use it for Sketch construction +#ifndef ARDUINO_BLE_PERIPHERAL_H +#define ARDUINO_BLE_PERIPHERAL_H + +typedef void (*BLEPeripheralEventHandler)(BLECentral ¢ral); + +typedef BLEDeviceEvent BLEPeripheralEvent; + +class BLEPeripheral { + public: + BLEPeripheral(void); + virtual ~BLEPeripheral(void); + + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); // set the advertised service uuid + void setLocalName(const char* localName); // set the local name + + + void setDeviceName(const char *deviceName); // set the device name + void setAppearance(const unsigned short appearance); // set the appearance type + + // Set the min and max connection interval + void setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval); + + // Add an attribute to the BLE Peripheral Device + void addAttribute(BLEService& service); + void addAttribute(BLECharacteristic& characteristic); + void addAttribute(BLEDescriptor& descriptor); + + void setEventHandler(BLEDeviceEvent event, BLEPeripheralEventHandler callback); // register an event handler + + bool begin(void); // Setup attributes and start advertising + + void poll(void); // poll the BLE radio for events + + void end(void); // Stop advertising and disconnect a central if connected + + bool disconnect(void); // disconnect the central if connected + + + BLECentral central(void); + bool connected(void); // Is a central connected? + +private: + void init(); + + bool _initCalled; + BLEService* _lastService; + BLECharacteristic* _lastCharacteristic; +}; + +#endif // ARDUINO_BLE_PERIPHERAL_H diff --git a/libraries/CurieBLE/src/BLEPeripheralHelper.cpp b/libraries/CurieBLE/src/BLEPeripheralHelper.cpp deleted file mode 100644 index 2a13c03c..00000000 --- a/libraries/CurieBLE/src/BLEPeripheralHelper.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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 "BLEPeripheralHelper.h" - -BLEAttribute *BLEPeripheralHelper::attribute(uint16_t handle) -{ - return _profile.attribute(handle); -} - -BLEAttribute *BLEPeripheralHelper::attribute(bt_gatt_subscribe_params_t *params) -{ - return _profile.attribute(params); -} - -uint8_t BLEPeripheralHelper::discover(const bt_gatt_attr_t *attr) -{ - // Not allow to call the discover - if (NULL == _central) - { - return BT_GATT_ITER_STOP; - } - return _profile.discover(attr); -} - -void BLEPeripheralHelper::discover() -{ - if (NULL == _central) - { - return; - } - _profile.discover(); -} - -BLEPeripheralHelper::BLEPeripheralHelper(BLECentralRole* central): - _profile(this), - _central(central) -{ - ; -} -BLEPeripheralHelper::~BLEPeripheralHelper() -{ - -} - -bool BLEPeripheralHelper::disconnect(void) -{ - int err = 0; - bt_conn_t* conn = bt_conn_lookup_addr_le(this->bt_le_address()); - if (NULL == conn) - { - return false; - } - - err = bt_conn_disconnect (conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); - bt_conn_unref(conn); - return (err == 0); -} - -bool BLEPeripheralHelper::connected(void) -{ - bt_conn_t* conn = bt_conn_lookup_addr_le(this->bt_le_address()); - if (NULL == conn) - { - return false; - } - bt_conn_unref(conn); - return true; -} - -void BLEPeripheralHelper::linkLost(void) -{ - clearAddress(); - if (NULL != _central) - { - // Only central role need to do - _profile.clearHandles(); - } -} - -BleStatus BLEPeripheralHelper::addAttribute(BLEAttribute& attribute) -{ - return _profile.addAttribute(attribute); -} - -int BLEPeripheralHelper::registerProfile() -{ - return _profile.registerProfile(); -} - -uint16_t BLEPeripheralHelper::valueHandle(BLEAttribute *attr) -{ - return _profile.valueHandle(attr); -} - -uint16_t BLEPeripheralHelper::cccdHandle(BLEAttribute *attr) -{ - return _profile.cccdHandle(attr); -} - - diff --git a/libraries/CurieBLE/src/BLEPeripheralHelper.h b/libraries/CurieBLE/src/BLEPeripheralHelper.h deleted file mode 100644 index bdb7c719..00000000 --- a/libraries/CurieBLE/src/BLEPeripheralHelper.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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_PERIPHERAL_HELPER_H_ -#define _BLE_PERIPHERAL_HELPER_H_ - -#include "BLECommon.h" -#include "BLEHelper.h" -#include "BLEProfile.h" - -class BLEAttribute; -class BLECentralRole; - -class BLEPeripheralHelper : public BLEHelper { - friend class BLECentralRole; - friend class BLEPeripheralRole; - public: - /** - * Is the Central connected - * - * @return boolean_t true if the central is connected, otherwise false - */ - bool connected(void); - - /** - * Disconnect the central if it is connected - * - */ - bool disconnect(void); - - /** - * Add an attribute to the BLE Peripheral helper - * - * @param[in] attribute Attribute to add to Peripheral - * - * @return BleStatus indicating success or error - * - * @note This method must be called before the begin method - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * @brief Get BLEAttribute by subscribe parameter - * - * @param[in] params Subscribe parameter - * - * @return BLEAttribute * NULL - Not found - * Not NULL - The BLEAttribute object - * - * @note none - */ - BLEAttribute *attribute(bt_gatt_subscribe_params_t *params); - - /** - * @brief Get BLEAttribute by characteristic handle - * - * @param[in] handle The characteristic handle - * - * @return BLEAttribute * NULL - Not found - * Not NULL - The BLEAttribute object - * - * @note none - */ - BLEAttribute *attribute(uint16_t handle); - - /** - * @brief Discover the BLE peripheral profile for central - * - * @param none - * - * @return none - * - * @note This function only for the central device. - * - * @note The central deivce didn't know the connected BLE's profile. - * Need send discover request to search the attribute in the BLE peripheral - */ - void discover(); - - /** - * @brief Process the discover response and - * discover the BLE peripheral profile - * - * @param[in] const bt_gatt_attr_t * The gatt attribute response - * - * @return uint8_t BT_GATT_ITER_STOP Stop discover the profile - * BT_GATT_ITER_CONTINUE Continue to send the discover request - * - * @note This function only for the central device. - */ - uint8_t discover(const bt_gatt_attr_t *attr); - - /** - * @brief For peripheral to register the profile tree - * - * @param none - * - * @return int 0 - success - * other - error code - * - * @note none - */ - int registerProfile(); - - /** - * @brief Process the link lost event - * - * @param none - * - * @return none - * - * @note none - */ - void linkLost(void); - - /** - * @brief Get the characteristic value handle - * - * @param[in] attr Attribute object - * - * @return uint16_t The value hander of attribute - * 0 is invalid - * - * @note Only for central mode - */ - uint16_t valueHandle(BLEAttribute *attr); - - /** - * @brief Get characteristic configuration descriptor value handle - * - * @param[in] attr Attribute object - * - * @return uint16_t The value hander of attribute - * 0 is invalid - * - * @note Only for central mode - */ - uint16_t cccdHandle(BLEAttribute *attr); - - protected: - BLEPeripheralHelper(BLECentralRole* central); - ~BLEPeripheralHelper(); - - private: - BLEProfile _profile; - BLECentralRole* _central; -}; - -#endif - diff --git a/libraries/CurieBLE/src/BLEPeripheralRole.cpp b/libraries/CurieBLE/src/BLEPeripheralRole.cpp deleted file mode 100644 index ff5baad6..00000000 --- a/libraries/CurieBLE/src/BLEPeripheralRole.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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 "BLEPeripheralRole.h" - -#include "BLECharacteristic.h" -#include "BLEDescriptor.h" -#include "BLEService.h" - -BLEPeripheralRole* BLEPeripheralRole::_ins = NULL; - -BLEPeripheralRole* BLEPeripheralRole::instance() -{ - if (NULL == _ins) - { - _ins = new BLEPeripheralRole(); - } - return _ins; -} - -BLEPeripheralRole::BLEPeripheralRole(void) : - _state(BLE_PERIPH_STATE_NOT_READY), - _min_conn_interval(DEFAULT_MIN_CONN_INTERVAL), - _max_conn_interval(DEFAULT_MAX_CONN_INTERVAL), - _peripheral(NULL), - _central(this) -{ - memset(_event_handlers, 0x00, sizeof(_event_handlers)); - _peripheral.setAddress(_local_bda); - - _adv_param.type = BT_LE_ADV_IND; - _adv_param.addr_type = _local_bda.type; - _adv_param.interval_min = 0xA0; - _adv_param.interval_max = 0xF0; -} - -BLEPeripheralRole::~BLEPeripheralRole(void) -{ -} - -bool BLEPeripheralRole::begin() -{ - BleStatus status; - - if (BLE_PERIPH_STATE_NOT_READY != _state) - return BLE_STATUS_WRONG_STATE; - - status = _init(); - if (status != BLE_STATUS_SUCCESS) { - return false; - } - _state = BLE_PERIPH_STATE_READY; - - // Set device name - setDeviceName(); - delay(4); - // Register profile - _peripheral.registerProfile(); - delay(8); // Temp solution for send data fast will makes ADV data set failed - return true; -} - -void -BLEPeripheralRole::poll() -{ - // no-op for now - delay(1); -} - -void -BLEPeripheralRole::setDeviceName(const char deviceName[]) -{ - memset(_device_name, 0, sizeof(_device_name)); - if (deviceName && deviceName[0]) { - int len = strlen(deviceName); - if (len > BLE_MAX_DEVICE_NAME) - len = BLE_MAX_DEVICE_NAME; - memcpy(_device_name, deviceName, len); - setDeviceName(); - } -} - -void -BLEPeripheralRole::setDeviceName() -{ - int len = strlen(_device_name); - bt_le_set_device_name(_device_name, len); -} - -void -BLEPeripheralRole::setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval) -{ - _min_conn_interval = minConnInterval; - _max_conn_interval = maxConnInterval; - - if (_min_conn_interval < MIN_CONN_INTERVAL) { - _min_conn_interval = MIN_CONN_INTERVAL; - } else if (_min_conn_interval > MAX_CONN_INTERVAL) { - _min_conn_interval = MAX_CONN_INTERVAL; - } - - if (_max_conn_interval < _min_conn_interval) { - _max_conn_interval = _min_conn_interval; - } else if (_max_conn_interval > MAX_CONN_INTERVAL) { - _max_conn_interval = MAX_CONN_INTERVAL; - } -} - -void -BLEPeripheralRole::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) -{ - if (event < sizeof(_event_handlers)) { - _event_handlers[event] = callback; - } -} - -bool -BLEPeripheralRole::disconnect() -{ - BleStatus status = BLE_STATUS_WRONG_STATE; - int err; - - if (BLE_PERIPH_STATE_CONNECTED == _state) - { - bt_conn_t *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); - if (NULL != central_conn) - { - err = bt_conn_disconnect (central_conn, - BT_HCI_ERR_REMOTE_USER_TERM_CONN); - status = errorno_to_ble_status(err); - bt_conn_unref(central_conn); - } - } - return (status == BLE_STATUS_SUCCESS); -} - -BLECentralHelper -BLEPeripheralRole::central() -{ - poll(); - - return _central; -} - -bool -BLEPeripheralRole::connected() -{ - poll(); - - return _central; -} - -BleStatus -BLEPeripheralRole::addAttribute(BLEAttribute& attribute) -{ - return _peripheral.addAttribute(attribute); -} - -BleStatus -BLEPeripheralRole::stopAdvertising() -{ - int err_code = 0; - BleStatus status = BLE_STATUS_WRONG_STATE; - - if (BLE_PERIPH_STATE_ADVERTISING == _state) - { - err_code = bt_le_adv_stop(); - status = errorno_to_ble_status(err_code); - } - - if (BLE_STATUS_SUCCESS != status) - return status; - - _state = BLE_PERIPH_STATE_READY; - return BLE_STATUS_SUCCESS; -} - -BleStatus -BLEPeripheralRole::startAdvertising(const bt_data_t *ad, - size_t ad_len, - const bt_data_t *sd, - size_t sd_len) -{ - int ret; - - pr_info(LOG_MODULE_BLE, "%s-ad_len%d", __FUNCTION__, ad_len); - if (_state != BLE_PERIPH_STATE_READY) - return BLE_STATUS_WRONG_STATE; - - ret = bt_le_adv_start(&_adv_param, ad, ad_len, sd, sd_len); - if (0 != ret) - { - pr_error(LOG_MODULE_APP, "[ADV] Start failed. Error: %d", ret); - return BLE_STATUS_WRONG_STATE; - } - _state = BLE_PERIPH_STATE_ADVERTISING; - return BLE_STATUS_SUCCESS; -} - -void BLEPeripheralRole::setAdvertisingInterval(uint16_t advertisingInterval) -{ - setAdvertisingInterval(advertisingInterval, advertisingInterval); -} - -void BLEPeripheralRole::setAdvertisingInterval(uint16_t interval_min, - uint16_t interval_max) -{ - _adv_param.interval_min = interval_min; - _adv_param.interval_max = interval_max; -} - -void -BLEPeripheralRole::setAdvertisingType(uint8_t type) -{ - _adv_param.type = type; -} - -BleStatus -BLEPeripheralRole::stop(void) -{ - int err_code; - BleStatus status; - - if (BLE_PERIPH_STATE_ADVERTISING == _state) - { - err_code = bt_le_adv_stop(); - status = errorno_to_ble_status(err_code); - } - else - status = disconnect(); - - if (BLE_STATUS_SUCCESS != status) - return status; - - _state = BLE_PERIPH_STATE_READY; - return BLE_STATUS_SUCCESS; -} - -void BLEPeripheralRole::handleConnectEvent(bt_conn_t *conn, uint8_t err) -{ - // Update the central address - const bt_addr_le_t *central_addr = bt_conn_get_dst(conn); - _central.setAddress(*central_addr); - - pr_info(LOG_MODULE_BLE, "Connected: %d", err); - // Call the CB - if (_event_handlers[BLEConnected]) - _event_handlers[BLEConnected](_central); - - if (BLE_PERIPH_STATE_ADVERTISING == _state) - _state = BLE_PERIPH_STATE_CONNECTED; -} - - -void BLEPeripheralRole::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) -{ - bt_conn_t *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); - if (conn == central_conn) - { - pr_info(LOG_MODULE_BLE, "Peripheral Disconnect reason: %d", reason); - if (_event_handlers[BLEDisconnected]) - _event_handlers[BLEDisconnected](_central); - } - _central.clearAddress(); - if (NULL != central_conn) - { - bt_conn_unref(central_conn); - } - - if (BLE_PERIPH_STATE_CONNECTED == _state) - _state = BLE_PERIPH_STATE_ADVERTISING; -} - -void BLEPeripheralRole::handleParamUpdated(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout) -{ - pr_info(LOG_MODULE_BLE, "Parameter updated\r\n\tConn: %p\r\n\tinterval: %d\r\n\tlatency: %d\r\n\ttimeout: %d", - conn, interval, latency, timeout); - if (_event_handlers[BLEUpdateParam]) - { - _central.setConnectionParameters(interval, interval, latency, timeout); - _event_handlers[BLEUpdateParam](_central); - } -} - - diff --git a/libraries/CurieBLE/src/BLEPeripheralRole.h b/libraries/CurieBLE/src/BLEPeripheralRole.h deleted file mode 100644 index 87c311b5..00000000 --- a/libraries/CurieBLE/src/BLEPeripheralRole.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. 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_PERIPHERALROLE_H_INCLUDED -#define _BLE_PERIPHERALROLE_H_INCLUDED - -#include "internal/ble_client.h" - -#include "BLECommon.h" -#include "BLERoleBase.h" -#include "BLEPeripheralHelper.h" - -/** - * BLE Peripheral Role - */ -class BLEPeripheralRole: public BLERoleBase{ -public: - /** - * Default Constructor for BLE Peripheral Device - */ - BLEPeripheralRole(void); - - /** - * Destructor for BLE Peripheral Device - */ - virtual ~BLEPeripheralRole(void); - - /** - * Set the device name for the BLE Peripheral Device - * - * If device name is not set, a default name will be used instead - * - * @param[in] deviceName User-defined name string for this device. Truncated if - * more than maximum allowed string length (20 bytes). - * - * @note This method must be called before the begin method - */ - void setDeviceName(const char *deviceName); - - /** - * Set the min and max connection interval BLE Peripheral Device - * - * @param[in] minConnInterval Minimum connection interval (1.25 ms units), minimum 0x0006 (7.5ms) - * @param[in] maxConnInterval Maximum connection interval (1.25 ms units), maximum 0x0C80 (4000ms) - * - * @note This method must be called before the begin method - */ - void setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval); - - /** - * Add an attribute to the BLE Peripheral Device - * - * @param[in] attribute Attribute to add to Peripheral - * - * @return BleStatus indicating success or error - * - * @note This method must be called before the begin method - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * Provide a function to be called when events related to this Device are raised - * - * @param[in] event Event type for callback - * @param[in] callback Pointer to callback function to invoke when an event occurs. - */ - void setEventHandler(BLERoleEvent event, BLERoleEventHandler callback); - - /** - * Setup attributes and start advertising - * - * @return bool indicating success or error - */ - bool begin(void); - - /** - * Poll the peripheral for events - */ - void poll(void); - - /** - * Stop advertising and disconnect a central if connected - */ - BleStatus stop(void); - - /** - * Disconnect the central connected if there is one connected - * - * @return bool indicating success or error - */ - bool disconnect(void); - - /** - * Setup attributes and start advertising - * - * @return BleStatus indicating success or error - */ - BLECentralHelper central(void); - - /** - * Is a central connected? - * - * @return boolean_t true if central connected, otherwise false - */ - bool connected(void); - - /** - * @brief Start peripheral advertising - * - * @param[in] ad The ADV data array - * - * @param[in] ad_len The ADV data array length - * - * @param[in] sd The Scan response data array - * - * @param[in] sd_len The Scan response data array length - * - * @return BleStatus - * - * @note none - */ - BleStatus startAdvertising(const bt_data_t *ad, - size_t ad_len, - const bt_data_t *sd, - size_t sd_len); - - /** - * @brief Stop send advertisement - * - * @param none - * - * @return none - * - * @note none - */ - BleStatus stopAdvertising(); - - /** - * @brief Set advertising parameter - * - * @param[in] advertisingInterval Advertising Interval (N * 0.625) - * - * @return none - * - * @note none - */ - void setAdvertisingInterval(uint16_t advertisingInterval); - - /** - * @brief Set advertising parameter - * - * @param[in] interval_min Minimum Advertising Interval (N * 0.625) - * - * @param[in] interval_max Maximum Advertising Interval (N * 0.625) - * - * @return none - * - * @note none - */ - void setAdvertisingInterval(uint16_t interval_min, - uint16_t interval_max); - - /** - * @brief Set advertising type - * - * @param[in] type Advertising type - * BT_LE_ADV_IND, BT_LE_ADV_NONCONN_IND - * - * @return none - * - * @note none - */ - void setAdvertisingType(uint8_t type); - - /** - * @brief Get BLE Peripheral instance. - * - * @param none - * - * @return BLEPeripheralRole* The BLE perpheral instance - * - * @note Singleton. Only have one object to communicate with - * stack and manage the device - */ - static BLEPeripheralRole* instance(); - -protected: - /** - * @brief Handle the connected event - * - * @param[in] conn The object that established the connection - * - * @param[in] err The code of the process - * - * @return none - * - * @note none - */ - void handleConnectEvent(bt_conn_t *conn, uint8_t err); - - /** - * @brief Handle the disconnected event - * - * @param[in] conn The object that lost the connection - * - * @param[in] reason The link lost reason - * - * @return none - * - * @note none - */ - void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); - - /** - * @brief Handle the conntion update request - * - * @param[in] conn The connection object that need to process the update request - * - * @param[in] interval The connection interval (N*1.25)ms - * - * @param[in] latency The connection latency - * - * @param[in] timeout The connection timeout (N*10)ms - * - * @return none - * - * @note none - */ - void handleParamUpdated(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout); - -private: - - /** - * Set the device name to Nordic BLE's profile - * - * @param none - * - * @note none - */ - void setDeviceName(); - - enum BLEPeripheralState { - BLE_PERIPH_STATE_NOT_READY = 0, - BLE_PERIPH_STATE_READY, - BLE_PERIPH_STATE_ADVERTISING, - BLE_PERIPH_STATE_CONNECTED, - }; - - BLEPeripheralState _state; - - uint16_t _min_conn_interval; - uint16_t _max_conn_interval; - - struct bt_le_adv_param _adv_param; - - BLEPeripheralHelper _peripheral; - BLECentralHelper _central; - - BLERoleEventHandler _event_handlers[BLERoleEventLast]; - static BLEPeripheralRole *_ins; -}; - -#endif // _BLE_DEVICE_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEProfile.cpp b/libraries/CurieBLE/src/BLEProfile.cpp deleted file mode 100644 index 10f0c91e..00000000 --- a/libraries/CurieBLE/src/BLEProfile.cpp +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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 "BLEProfile.h" -#include "BLEPeripheral.h" -#include "BLECentralRole.h" -#include "BLEPeripheralRole.h" - -// Only for peripheral -ssize_t profile_read_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - void *buf, uint16_t len, - uint16_t offset) -{ - const unsigned char *pvalue; - BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; - BLEAttributeType type = bleattr->type(); - if (BLETypeCharacteristic == type) - { - BLECharacteristic* blecharacteritic; - blecharacteritic = (BLECharacteristic*)bleattr; - pvalue = blecharacteritic->value(); - return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, - blecharacteritic->valueLength()); - } - else if (BLETypeDescriptor == type) - { - BLEDescriptor *bledescriptor = (BLEDescriptor *)bleattr; - pvalue = bledescriptor->value(); - return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, bledescriptor->valueLength()); - } - return 0; -} - -// Only for peripheral -ssize_t profile_write_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - const void *buf, uint16_t len, - uint16_t offset) -{ - pr_info(LOG_MODULE_BLE, "%s1", __FUNCTION__); - BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; - BLECharacteristic* blecharacteritic; - BLEAttributeType type = bleattr->type(); - BLECentralHelper central = BLEPeripheralRole::instance()->central(); - if ((BLETypeCharacteristic != type) || 0 != offset) - { - return 0; - } - - blecharacteritic = (BLECharacteristic*)bleattr; - blecharacteritic->setValue(*((BLEHelper *)¢ral), (const uint8_t *) buf, len); - - return len; -} - -ssize_t profile_longwrite_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, - uint16_t offset) -{ - pr_info(LOG_MODULE_BLE, "%s1", __FUNCTION__); - BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; - BLECharacteristic* blecharacteritic; - BLEAttributeType type = bleattr->type(); - BLECentralHelper central = BLEPeripheralRole::instance()->central(); - if (BLETypeCharacteristic != type) - { - return 0; - } - - blecharacteritic = (BLECharacteristic*)bleattr; - blecharacteritic->setBuffer(*((BLEHelper *)¢ral), (const uint8_t *) buf, len, offset); - - return len; -} - -int profile_longflush_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - uint8_t flags) -{ - BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; - BLECharacteristic* blecharacteritic; - BLEAttributeType type = bleattr->type(); - BLECentralHelper central = BLEPeripheralRole::instance()->central(); - if (BLETypeCharacteristic != type) - { - return 0; - } - - blecharacteritic = (BLECharacteristic*)bleattr; - - switch (flags) { - case BT_GATT_FLUSH_DISCARD: - /* Discard buffer reseting it back with data */ - blecharacteritic->discardBuffer(); - return 0; - case BT_GATT_FLUSH_SYNC: - /* Sync buffer to data */ - blecharacteritic->syncupBuffer2Value(*((BLEHelper *)¢ral)); - return 0; - } - - return -EINVAL; -} - - -// Only for central -uint8_t profile_notify_process (bt_conn_t *conn, - bt_gatt_subscribe_params_t *params, - const void *data, uint16_t length) -{ - BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn - BLEAttribute* notifyatt = peripheral->attribute(params); // Find attribute by params - BLECharacteristic *chrc = (BLECharacteristic *)notifyatt; - - //assert(notifyatt->type() == BLETypeCharacteristic); - pr_debug(LOG_MODULE_APP, "%s1", __FUNCTION__); - chrc->setValue(*((BLEHelper *)peripheral),(const unsigned char *)data, length); - return BT_GATT_ITER_CONTINUE; -} - -// Only for central -uint8_t profile_discover_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) -{ - BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn - return peripheral->discover(attr); -} - -// Only for central -uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, - bt_gatt_read_params_t *params, - const void *data, - uint16_t length) -{ - if (NULL == data) - { - return BT_GATT_ITER_STOP; - } - BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn - BLEAttribute* readatt = peripheral->attribute(params->single.handle); - BLECharacteristic *chrc = (BLECharacteristic *)readatt; - - //assert(readatt->type() == BLETypeCharacteristic); - chrc->setValue(*((BLEHelper *)peripheral), (const unsigned char *)data, length); - return BT_GATT_ITER_STOP; -} - -BLEProfile::BLEProfile (BLEPeripheralHelper *peripheral): - _attr_base(NULL), - _attr_index(0), - _attributes(NULL), - _num_attributes(0), - _sub_param(NULL), - _sub_param_idx(0) -{ - _peripheral = peripheral; - memset(&_discover_params, 0, sizeof(_discover_params)); - _discover_params.end_handle = 0xFFFF; - _discover_params.start_handle = 0x0001; - _discover_params.func = profile_discover_process; -} - -BLEProfile::~BLEProfile (void) -{ - if (this->_attributes) { - free(this->_attributes); - } - if (this->_attr_base) - { - free(this->_attr_base); - } - if (this->_sub_param) - { - free(this->_sub_param); - } -} - -BleStatus -BLEProfile::addAttribute (BLEAttribute& attribute) -{ - bt_gatt_attr_t *start; - BleStatus err_code = BLE_STATUS_SUCCESS; - - if (NULL == _attributes) - { - _attributes = (BLEAttribute**)malloc(BLEAttribute::numAttributes() * sizeof(BLEAttribute*)); - memset(_attributes, 0x00, BLEAttribute::numAttributes() * sizeof(BLEAttribute*)); - if (NULL == _attributes) - { - err_code = BLE_STATUS_NO_MEMORY; - } - } - if (NULL == _attr_base) - { - _attr_base = (bt_gatt_attr_t *)malloc((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(bt_gatt_attr_t)); - memset(_attr_base, 0x00, ((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(bt_gatt_attr_t))); - pr_info(LOG_MODULE_BLE, "_attr_base_-%p, size-%d", _attr_base, sizeof(_attr_base)); - if (NULL == _attr_base) - { - err_code = BLE_STATUS_NO_MEMORY; - } - } - if (NULL == _sub_param) - { - _sub_param = (bt_gatt_subscribe_params_t *)malloc((BLECharacteristic::numNotifyAttributes()) * sizeof(bt_gatt_subscribe_params_t)); - memset(_sub_param, 0x00, ((BLECharacteristic::numNotifyAttributes()) * sizeof(bt_gatt_subscribe_params_t))); - if (NULL == _sub_param) - { - err_code = BLE_STATUS_NO_MEMORY; - } - } - - if (BLE_STATUS_SUCCESS != err_code) - { - if (NULL != _attributes) - { - free(_attributes); - } - if (NULL != _attr_base) - { - free(_attr_base); - } - if (NULL != _sub_param) - { - free(_sub_param); - } - return err_code; - } - - _attributes[_num_attributes] = &attribute; - _num_attributes++; - start = _attr_base + _attr_index; - pr_info(LOG_MODULE_BLE, "_attr_base_-%p", _attr_base); - - BLEAttributeType type = attribute.type(); - pr_info(LOG_MODULE_BLE, "%s: idx-%d, %p, %d", __FUNCTION__,_num_attributes, &attribute ,attribute.uuid()->type); - - - if (BLETypeCharacteristic == type) - { - BLECharacteristic* characteritic = (BLECharacteristic*) &attribute; - - // Characteristic - memset(start, 0, sizeof(bt_gatt_attr_t)); - start->uuid = BLECharacteristic::getCharacteristicAttributeUuid(); - start->perm = BT_GATT_PERM_READ; - start->read = bt_gatt_attr_read_chrc; - start->user_data = characteritic->getCharacteristicAttValue(); - characteritic->addCharacteristicDeclaration(start); - - pr_info(LOG_MODULE_BLE, "chrc-%p, uuid type-%d", start, start->uuid->type); - - start++; - _attr_index++; - - // Descriptor - memset(start, 0, sizeof(bt_gatt_attr_t)); - start->uuid = characteritic->uuid(); - start->perm = characteritic->getPermission(); - start->user_data = (void*)&attribute; - characteritic->addCharacteristicValue(start); - start->read = profile_read_process; - - if (characteritic->longCharacteristic() == false) - { - // Normal characteristic MAX. 20 - start->write = profile_write_process; - } - else - { - // Long characteristic. MAX. 512 - start->write = profile_longwrite_process; - start->flush = profile_longflush_process; - } - pr_info(LOG_MODULE_BLE, "desc-%p, uuid: 0x%x", start, ((bt_uuid_16_t*) start->uuid)->val); - - start++; - _attr_index++; - // CCCD - if (characteritic->subscribed()) - { - // Descriptor - memset(start, 0, sizeof(bt_gatt_attr_t)); - start->uuid = characteritic->getClientCharacteristicConfigUuid(); - start->perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; - start->read = bt_gatt_attr_read_ccc; - start->write = bt_gatt_attr_write_ccc; - start->user_data = characteritic->getCccCfg(); - characteritic->addCharacteristicConfigDescriptor(start); - - pr_info(LOG_MODULE_BLE, "cccd-%p", start); - - start++; - _attr_index++; - } - } - else if (BLETypeService == type) - { - start->uuid = BLEService::getPrimayUuid(); - start->perm = BT_GATT_PERM_READ; - start->read = bt_gatt_attr_read_service; - start->user_data = attribute.uuid(); - - pr_debug(LOG_MODULE_BLE, "service-%p", start); - start++; - _attr_index++; - } - else if (BLETypeDescriptor == type) - { - start->uuid = attribute.uuid(); - start->perm = BT_GATT_PERM_READ; - start->read = profile_read_process; - start->user_data = (void*)&attribute; - - pr_debug(LOG_MODULE_BLE, "Descriptor-%p", start); - start++; - _attr_index++; - } - return err_code; -} - -int BLEProfile::registerProfile() -{ - int ret = 0; - -#if 0 - // Start debug - int i; - - for (i = 0; i < _attr_index; i++) { - { - pr_info(LOG_MODULE_APP, "gatt-: i %d, type %d, u16 0x%x", - i, - _attr_base[i].uuid->type, - BT_UUID_16(_attr_base[i].uuid)->val); - } - } - - delay(1000); - // End for debug -#endif - - ret = bt_gatt_register(_attr_base, - _attr_index); - pr_debug(LOG_MODULE_APP, "%s: ret, %d", __FUNCTION__, ret); - - return ret; -} - -void BLEProfile::characteristicDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr) -{ - bt_gatt_attr_t *attr_dec = declarationAttr(bleattr); - if ((NULL != attr) && (NULL != attr_dec)) - { - if (bt_uuid_cmp (attr_dec->uuid, attr->uuid) == 0) - { - attr_dec++; - attr_dec->handle = attr->handle + 1; - } - } - bleattr->discover(attr, &_discover_params); -} - -void BLEProfile::descriptorDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr) -{ - int err; - bt_gatt_attr_t *attr_dec = declarationAttr(bleattr); - if (BLETypeCharacteristic == bleattr->type()) - { - BLECharacteristic *chrc = (BLECharacteristic *)bleattr; - if (bt_uuid_cmp (chrc->getClientCharacteristicConfigUuid(), attr->uuid) == 0) - { - //CCCD - bt_gatt_attr_t *attr_chrc = attr_dec + 1; - bt_gatt_attr_t *attr_cccd = attr_dec + 2; - bt_gatt_subscribe_params_t *sub_param_tmp = chrc->getSubscribeParams(); - bt_gatt_subscribe_params_t *sub_param = _sub_param + _sub_param_idx; - bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); - if (NULL == conn) - { - // Link lost - return; - } - - _sub_param_idx++; - attr_cccd->handle = attr->handle; - memcpy(sub_param, sub_param_tmp, sizeof(bt_gatt_subscribe_params_t)); - sub_param->ccc_handle = attr_cccd->handle; - sub_param->value_handle = attr_chrc->handle; - - // Enable CCCD to allow peripheral send Notification/Indication - err = bt_gatt_subscribe(conn, sub_param); - bt_conn_unref(conn); - if (err && err != -EALREADY) - { - pr_debug(LOG_MODULE_APP, "Subscribe failed (err %d)\n", err); - } - bleattr->discover(attr, &_discover_params); - } - else - { - // Not CCCD - // If want to support more descriptor, - // change the offset 3 as a loop to search the ATTR - bt_gatt_attr_t *attr_descriptor = attr_dec + 3; - if (attr_descriptor->uuid != NULL && - bt_uuid_cmp (attr_descriptor->uuid, attr->uuid) == 0) - { - attr_descriptor->handle = attr->handle; - } - } - } - else if (BLETypeDescriptor == bleattr->type()) - { - bt_gatt_attr_t *attr_descriptor = attr_dec++; // The descriptor is separate - if (bt_uuid_cmp (attr_dec->uuid, attr->uuid) == 0) - { - attr_descriptor->handle = attr->handle; - } - bleattr->discover(attr, &_discover_params); - } -} - -uint8_t BLEProfile::discover(const bt_gatt_attr_t *attr) -{ - BLEAttribute* attribute_tmp = NULL; - int i; - int err; - uint8_t ret = BT_GATT_ITER_STOP; - bool send_discover = false; - - for (i = 0; i < _num_attributes; i++) - { - // Find the discovering attribute - attribute_tmp = _attributes[i]; - if (attribute_tmp->discovering()) - { - if (NULL == attr) - { - attribute_tmp->discover(attr, &_discover_params); - break; - } - // Discover success - switch (_discover_params.type) - { - case BT_GATT_DISCOVER_CHARACTERISTIC: - { - characteristicDiscoverRsp(attr, attribute_tmp); - send_discover = true; - break; - } - case BT_GATT_DISCOVER_DESCRIPTOR: - { - descriptorDiscoverRsp(attr, attribute_tmp); - break; - } - case BT_GATT_DISCOVER_PRIMARY: - send_discover = true; - default: - { - attribute_tmp->discover(attr, &_discover_params); - break; - } - } - break; - } - } - - // Find next attribute to discover - if (attribute_tmp->discovering() == false) - { - // Current attribute complete discovery - i++; - while (i < _num_attributes) - { - attribute_tmp = _attributes[i]; - if (attribute_tmp->type() == BLETypeDescriptor) - { - // The descriptor may have been discovered by previous descriptor - bt_gatt_attr_t *attr_gatt = NULL; - for (int j = 0; j < _attr_index; j++) - { - attr_gatt = _attr_base + i; - if (attribute_tmp->uuid() == attr_gatt->uuid) - { - break; - } - } - - if (attr_gatt->handle != 0) - { - // Skip discovered descriptor - i++; - continue; - } - } - - attribute_tmp->discover(&_discover_params); - ret = BT_GATT_ITER_CONTINUE; - break; - } - } - else - { - ret = BT_GATT_ITER_CONTINUE; - } - - // Send the discover request if necessary - if (send_discover && attribute_tmp->discovering()) - { - bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); - - ret = BT_GATT_ITER_STOP; - if (NULL == conn) - { - // Link lost - pr_debug(LOG_MODULE_APP, "Can't find connection\n"); - return ret; - } - err = bt_gatt_discover(conn, &_discover_params); - bt_conn_unref(conn); - if (err) - { - pr_debug(LOG_MODULE_APP, "Discover failed(err %d)\n", err); - return ret; - } - } - return ret; -} - - -void BLEProfile::discover() -{ - int err; - BLEService *serviceattr = (BLEService *)_attributes[0]; - bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); - - if (NULL == conn) - { - // Link lost - pr_debug(LOG_MODULE_APP, "Can't find connection\n"); - return; - } - - // Reset start handle - _discover_params.start_handle = 0x0001; - serviceattr->discover(&_discover_params); - - err = bt_gatt_discover(conn, &_discover_params); - bt_conn_unref(conn); - if (err) - { - pr_debug(LOG_MODULE_APP, "Discover failed(err %d)\n", err); - return; - } -} - -BLEAttribute *BLEProfile::attribute(bt_gatt_subscribe_params_t *params) -{ - return attribute(params->value_handle); -} - -BLEAttribute *BLEProfile::attribute(const bt_uuid_t* uuid) -{ - int i; - BLEAttribute *attr_tmp = NULL; - BLECharacteristic *chrc_tmp = NULL; - bool att_found = false; - - for (i = 0; i < _num_attributes; i++) - { - attr_tmp = _attributes[i]; - if ((NULL == attr_tmp) || (attr_tmp->type() != BLETypeCharacteristic)) - { - continue; - } - chrc_tmp = (BLECharacteristic *)attr_tmp; - if (chrc_tmp->uuid() == uuid); - { - att_found = true; - break; - } - } - - if (false == att_found) - { - pr_debug(LOG_MODULE_APP, "Attributes not found"); - // Didn't found the characteristic - chrc_tmp = NULL; - } - return chrc_tmp; -} - - -BLEAttribute *BLEProfile::attribute(uint16_t handle) -{ - int i; - bt_gatt_attr_t *attr_gatt = NULL; - for (i = 0; i < _attr_index; i++) - { - attr_gatt = _attr_base + i; - if (handle == attr_gatt->handle) - { - break; - } - } - - if (i < _attr_index && i > 1) - { - // Found the GATT ATTR - // Serach the attribute - // Characteristic Declaration - // Characteristic Descriptor - // CCCD - attr_gatt--; - if (attr_gatt->uuid == BLECharacteristic::getCharacteristicAttributeUuid()) - { - attr_gatt++; - } - else - { - attr_gatt--; - if (attr_gatt->uuid == BLECharacteristic::getCharacteristicAttributeUuid()) - { - attr_gatt++; - } - else - { - attr_gatt = NULL; - } - } - } - else - { - attr_gatt = NULL; - } - - if (NULL != attr_gatt) - { - return attribute(attr_gatt->uuid); - } - return NULL; -} - - -void BLEProfile::clearHandles(void) -{ - int i; - bt_gatt_attr_t *attr = NULL; - // Didn't need to unsubscribe - // The stack will unsubscribe the notify when disconnected. - // The sub_param has some pointer. So can't call memset. Just reset the index. - _sub_param_idx = 0; - - for (i = 0; i < _attr_index; i++) - { - // Clear the handle - attr = _attr_base + i; - attr->handle = 0; - } -} - -bt_gatt_attr_t* BLEProfile::declarationAttr(BLEAttribute *attr) -{ - int i; - bt_gatt_attr_t *attr_gatt = NULL; - - for (i = 0; i < _attr_index; i++) - { - // Clear the handle - attr_gatt = _attr_base + i; - if (attr->uuid() == attr_gatt->uuid) - { - attr_gatt--; - return attr_gatt; - } - } - return NULL; -} - -uint16_t BLEProfile::valueHandle(BLEAttribute *attr) -{ - uint16_t handle = 0; - bt_gatt_attr_t *attr_gatt = declarationAttr(attr); - attr_gatt++; - if (attr_gatt->uuid == attr->uuid()) - { - handle = attr_gatt->handle; - } - return handle; -} - -uint16_t BLEProfile::cccdHandle(BLEAttribute *attr) -{ - uint16_t handle = 0; - bt_gatt_attr_t *attr_gatt = declarationAttr(attr); - attr_gatt+= 2; - if (attr_gatt->uuid == BLECharacteristic::getClientCharacteristicConfigUuid()) - { - handle = attr_gatt->handle; - } - return handle; -} - - - diff --git a/libraries/CurieBLE/src/BLEProfile.h b/libraries/CurieBLE/src/BLEProfile.h deleted file mode 100644 index 300d54e8..00000000 --- a/libraries/CurieBLE/src/BLEProfile.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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_PROFILE_H__ -#define __BLE_PROFILE_H__ - -#include "BLECommon.h" -#include "BLEAttribute.h" -#include "BLECentralHelper.h" -#include "BLECharacteristic.h" -#include "BLEService.h" - -class BLEProfile{ -public: - BLEProfile(BLEPeripheralHelper *peripheral); - ~BLEProfile (void); - - /** - * @brief Add an attribute to the BLE Peripheral Device - * - * @param[in] attribute Attribute to add to Peripheral - * - * @return BleStatus indicating success or error - * - * @note This method must be called before the begin method - */ - BleStatus addAttribute(BLEAttribute& attribute); - - /** - * @brief Register the profile to Nordic BLE stack - * - * @param none - * - * @return int std C errno - * - * @note none - */ - int registerProfile(); - - /** - * @brief Get BLEAttribute by subscribe parameter - * - * @param[in] params Subscribe parameter - * - * @return BLEAttribute * NULL - Not found - * Not NULL - The BLEAttribute object - * - * @note none - */ - BLEAttribute *attribute(bt_gatt_subscribe_params_t *params); - - /** - * @brief Get BLEAttribute by characteristic handle - * - * @param[in] handle The characteristic handle - * - * @return BLEAttribute * NULL - Not found - * Not NULL - The BLEAttribute object - * - * @note none - */ - BLEAttribute *attribute(uint16_t handle); - - /** - * @brief Process the discover response and - * discover the BLE peripheral profile - * - * @param[in] const bt_gatt_attr_t * The gatt attribute response - * - * @return uint8_t BT_GATT_ITER_STOP Stop discover the profile - * BT_GATT_ITER_CONTINUE Continue to send the discover request - * - * @note This function only for the central device. - */ - uint8_t discover(const bt_gatt_attr_t *attr); - - /** - * @brief Discover the BLE peripheral profile - * - * @param none - * - * @return none - * - * @note This function only for the central device. - * - * @note The central deivce didn't know the connected BLE's profile. - * Need send discover request to search the attribute in the BLE peripheral - */ - void discover(); - - /** - * @brief Clear the handle in central mode - * - * @param none - * - * @return none - * - * @note The peripheral can't call this. - * Because the central need discover the handles. - * Peripheral device only get the handle when register the profile. - */ - void clearHandles(void); - - /** - * @brief Get the characteristic value handle - * - * @param[in] attr Attribute object - * - * @return uint16_t The value handle - * 0 is invalid handle - * - * @note none - */ - uint16_t valueHandle(BLEAttribute *attr); - - /** - * @brief Get characteristic configuration descriptor value handle - * - * @param[in] attr Attribute object - * - * @return uint16_t The value handle - * 0 is invalid handle - * - * @note none - */ - uint16_t cccdHandle(BLEAttribute *attr); -protected: - friend ssize_t profile_write_process(bt_conn_t *conn, - const bt_gatt_attr_t *attr, - const void *buf, uint16_t len, - uint16_t offset); -private: - /** - * @brief Get BLEAttribute by UUID - * - * @param[in] const bt_uuid_t* The UUID - * - * @return BLEAttribute * NULL - Not found - * Not NULL - The BLEAttribute object - * - * @note Use the pointer value instead the UUID value. - * Because the uuid pointer in bt_gatt_attr is got from BLEAttribute - * So set this as private. - */ - BLEAttribute *attribute(const bt_uuid_t* uuid); - - /** - * @brief Get bt_gatt_attr by BLEAttribute class - * - * @param[in] BLEAttribute * The BLEAttribute object - * - * @return bt_gatt_attr_t* NULL - Not found - * Not NULL - The bt_gatt_attr in the stack - * - * @note none - */ - bt_gatt_attr_t* declarationAttr(BLEAttribute *attr); - - /** - * @brief Process the descriptor discover response - * - * @param[in] const bt_gatt_attr_t * The discover response - * - * @param[in] BLEAttribute * The BLEAttribute object in discovering - * - * @return none - * - * @note none - */ - void descriptorDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr); - - /** - * @brief Process the characteristic discover response - * - * @param[in] const bt_gatt_attr_t * The discover response - * - * @param[in] BLEAttribute * The BLEAttribute object in discovering - * - * @return none - * - * @note none - */ - void characteristicDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr); - -private: - BLEPeripheralHelper *_peripheral; - bt_gatt_attr_t *_attr_base; - int _attr_index; - - BLEAttribute** _attributes; - uint16_t _num_attributes; - - bt_gatt_subscribe_params_t *_sub_param; - int _sub_param_idx; - - bt_gatt_discover_params_t _discover_params; -}; - -#endif - diff --git a/libraries/CurieBLE/src/BLERoleBase.cpp b/libraries/CurieBLE/src/BLERoleBase.cpp deleted file mode 100644 index 0285b857..00000000 --- a/libraries/CurieBLE/src/BLERoleBase.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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 "internal/ble_client.h" - -#include "BLERoleBase.h" - -void bleConnectEventHandler(bt_conn_t *conn, - uint8_t err, - void *param) -{ - BLERoleBase* p = (BLERoleBase*)param; - - p->handleConnectEvent(conn, err); -} - - -void bleDisconnectEventHandler(bt_conn_t *conn, - uint8_t reason, - void *param) -{ - BLERoleBase* p = (BLERoleBase*)param; - - pr_info(LOG_MODULE_BLE, "Connect lost. Reason: %d", reason); - - p->handleDisconnectEvent(conn, reason); -} - -void bleParamUpdatedEventHandler(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout, - void *param) -{ - BLERoleBase* p = (BLERoleBase*)param; - - p->handleParamUpdated(conn, interval, latency, timeout); -} - -uint8_t BLERoleBase::m_init_cnt = 0; - -void BLERoleBase::setTxPower (int8_t tx_power) -{ - ble_gap_set_tx_power(tx_power); -} - - -BleStatus -BLERoleBase::_init() -{ - // Curie may support multi-role at same time in future. - // Make sure the BLE only init once. - if (this->m_init_cnt == 0) - { - ble_client_init (bleConnectEventHandler, this, - bleDisconnectEventHandler, this, - bleParamUpdatedEventHandler, this); - } - this->m_init_cnt++; - - return BLE_STATUS_SUCCESS; -} - -BLERoleBase::BLERoleBase(): m_connected(false) -{ - memset (_event_handlers, 0x00, sizeof (_event_handlers)); - ble_client_get_factory_config(&_local_bda, _device_name); -} - - diff --git a/libraries/CurieBLE/src/BLERoleBase.h b/libraries/CurieBLE/src/BLERoleBase.h deleted file mode 100644 index b8fc5169..00000000 --- a/libraries/CurieBLE/src/BLERoleBase.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. 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 __BLEROLEBASE_H__ -#define __BLEROLEBASE_H__ - -#include "BLECommon.h" - -/** - * BLE Events - */ -enum BLERoleEvent { - BLEConnected = 0, - BLEDisconnected = 1, - BLEUpdateParam, - - BLERoleEventLast -}; - -class BLEHelper; - -typedef void (*BLERoleEventHandler)(BLEHelper &role); - - -class BLERoleBase{ -public: - virtual bool begin()=0; - virtual bool disconnect()=0; - BLERoleBase(); - - /** - * Is connected? - * - * @return boolean_t true if established connection, otherwise false - */ - bool connected (void) {return m_connected;} - - /** - * Set TX output power - * - * @param[in] tx_power The antenna TX power - * - * @return boolean_t true if established connection, otherwise false - */ - void setTxPower (int8_t tx_power); -protected: - virtual BleStatus _init(void); - - friend void bleConnectEventHandler(bt_conn_t *conn, - uint8_t err, - void *param); - friend void bleDisconnectEventHandler(bt_conn_t *conn, - uint8_t reason, - void *param); - friend void bleParamUpdatedEventHandler(bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout, - void *param); - - /** - * @brief Handle the connected event - * - * @param[in] conn The object that established the connection - * - * @param[in] err The code of the process - * - * @return none - * - * @note virtual function. Just define the interface and the children need to implement - */ - virtual void handleConnectEvent(bt_conn_t *conn, uint8_t err) = 0; - - /** - * @brief Handle the disconnected event - * - * @param[in] conn The object that lost the connection - * - * @param[in] reason The link lost reason - * - * @return none - * - * @note virtual function. Just define the interface and the children need to implement - */ - virtual void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) = 0; - - /** - * @brief Handle the conntion update request - * - * @param[in] conn The connection object that need to process the update request - * - * @param[in] interval The connection interval (N*1.25)ms - * - * @param[in] latency The connection latency - * - * @param[in] timeout The connection timeout (N*10)ms - * - * @return none - * - * @note virtual function. Just define the interface and the children need to implement - */ - virtual void handleParamUpdated (bt_conn_t *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout) = 0; - - char _device_name[BLE_MAX_DEVICE_NAME+1]; - bt_addr_le_t _local_bda; - BLERoleEventHandler _event_handlers[BLERoleEventLast]; - -private: - bool m_connected; - static uint8_t m_init_cnt; // Reserved for support multi-role at same time -}; - -#endif - diff --git a/libraries/CurieBLE/src/BLEService.cpp b/libraries/CurieBLE/src/BLEService.cpp index 906dc17d..676cded1 100644 --- a/libraries/CurieBLE/src/BLEService.cpp +++ b/libraries/CurieBLE/src/BLEService.cpp @@ -1,52 +1,247 @@ /* - * Copyright (c) 2015 Intel Corporation. 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 "internal/ble_client.h" + BLE Service API + Copyright (c) 2016 Arduino LLC. 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 "BLEService.h" -bt_uuid_16_t BLEService::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; -bt_uuid_t *BLEService::getPrimayUuid(void) + +#include "./internal/BLEProfileManager.h" +#include "./internal/BLECharacteristicImp.h" + +#include "./internal/BLEUtils.h" + +BLEService::BLEService():_bledevice(), + _service_imp(NULL), + _service_local_imp(NULL) { - return (bt_uuid_t *)&_gatt_primary_uuid; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); } -BLEService::BLEService(const char* uuid) : - BLEAttribute(uuid, BLETypeService) +BLEService::BLEService(const char* uuid):_bledevice(), + _service_imp(NULL), + _service_local_imp(NULL) { + bt_uuid_128_t uuid_tmp; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&uuid_tmp, _uuid_cstr); + + _bledevice.setAddress(*BLEUtils::bleGetLoalAddress()); } +BLEService::BLEService(const bt_uuid_t* uuid):_bledevice(), + _service_imp(NULL), + _service_local_imp(NULL) +{ + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidBT2String(uuid, _uuid_cstr); +} -void BLEService::discover(bt_gatt_discover_params_t *params) +BLEService::BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev): + _bledevice(bledev),_service_imp(serviceImp), + _service_local_imp(NULL) { - params->type = BT_GATT_DISCOVER_PRIMARY; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); - params->uuid = this->uuid(); - // Start discovering - _discoverying = true; + BLEUtils::uuidBT2String(serviceImp->bt_uuid(), _uuid_cstr); +} + +BLEService::~BLEService() +{ + if (NULL != _service_local_imp) + { + delete _service_local_imp; + _service_local_imp = NULL; + } +} + +BLEService::operator bool() const +{ + return (strlen(_uuid_cstr) > 3); +} + +BLEService& BLEService::operator= (const BLEService& service) +{ + memcpy(_uuid_cstr, service._uuid_cstr, sizeof(_uuid_cstr)); + _bledevice.setAddress(*service._bledevice.bt_le_address()); + _service_imp = service._service_imp; + _service_local_imp = NULL; // Not copy + return *this; +} + +const char* BLEService::uuid() const +{ + return _uuid_cstr; } -void BLEService::discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) +int BLEService::addCharacteristic(BLECharacteristic& characteristic) { - params->start_handle = attr->handle + 1; + BLEServiceImp* serviceImp = getServiceImp(); + int retVar = BLE_STATUS_ERROR; - // Complete the discover - _discoverying = false; + if (NULL != serviceImp) + { + retVar = serviceImp->addCharacteristic(_bledevice, characteristic); + } + else if (BLEUtils::isLocalBLE(_bledevice) == true) + { + // Only support the GATT server that create the service in local device. + _service_local_imp = new BLEServiceImp(*this); + if (NULL == _service_local_imp) + { + return BLE_STATUS_NO_MEMORY; + } + retVar = _service_local_imp->addCharacteristic(_bledevice, characteristic); + } + return retVar; } +BLEServiceImp* BLEService::getLocalServiceImp() +{ + return _service_local_imp; +} + +BLEServiceImp* BLEService::fetchOutLocalServiceImp() +{ + BLEServiceImp* temp = _service_local_imp; + _service_local_imp = NULL; + return temp; +} + +int BLEService::characteristicCount() const +{ + int count = 0; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + count = serviceImp->getCharacteristicCount(); + } + return count; +} + +bool BLEService::hasCharacteristic(const char* uuid) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(uuid); + } + return (NULL != characteristicImp); +} + +bool BLEService::hasCharacteristic(const char* uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + return (NULL != characteristicImp); +} + +BLECharacteristic BLEService::characteristic(int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + } + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLECharacteristic BLEService::characteristic(const char * uuid) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(uuid); + } + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLECharacteristic BLEService::characteristic(const char * uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLEServiceImp* BLEService::getServiceImp() +{ + if (NULL == _service_imp) + { + _service_imp = BLEProfileManager::instance()->service(_bledevice, uuid()); + } + return _service_imp; +} + +BLEServiceImp* BLEService::getServiceImp() const +{ + return _service_imp; +} + +void BLEService::setServiceImp(BLEServiceImp* serviceImp) +{ + _service_imp = serviceImp; +} + diff --git a/libraries/CurieBLE/src/BLEService.h b/libraries/CurieBLE/src/BLEService.h index 24f468ef..3d5785f3 100644 --- a/libraries/CurieBLE/src/BLEService.h +++ b/libraries/CurieBLE/src/BLEService.h @@ -1,54 +1,151 @@ /* - * Copyright (c) 2015 Intel Corporation. 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_INCLUDED -#define _BLE_SERVICE_H_INCLUDED - -#include "BLEAttribute.h" -#include "BLECommon.h" -#include "BLEProfile.h" - -class BLEPeripheral; -class BLEProfile; - -/** - * BLE GATT Service - */ -class BLEService : public BLEAttribute { + BLE Service API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_SERVICE_H +#define ARDUINO_BLE_SERVICE_H + +#include "CurieBLE.h" +#include "BLEDevice.h" + +//class BLECharacteristic; +class BLEServiceImp; + +class BLEService +{ public: + BLEService(); + BLEService(const char* uuid); + virtual ~BLEService(); + + virtual operator bool() const; // is the service valid + BLEService& operator= (const BLEService& service); + + const char* uuid() const; + /** - * Constructor for BLE Service + * @brief Add a characteristic in service * - * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard + * @param characteristic The characteristic want to be added to service + * + * @return int 0 - Success. Others - Error codes + * + * @note none */ - BLEService(const char* uuid); + int addCharacteristic(BLECharacteristic& characteristic); + /** + * @brief Get the number of characteristics the service has + * + * @param none + * + * @return none + * + * @note none + */ + int characteristicCount() const; + + /** + * @brief Does the service have a characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasCharacteristic(const char* uuid) const; + + /** + * @brief Does the service have an nth characteristic with the + * specified UUID + * + * @param uuid The UUID of the characteristic + * + * @param index The index of characteristic + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasCharacteristic(const char* uuid, int index) const; + + /** + * @brief Return the nth characteristic of the service + * + * @param index The index of characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(int index) const; + + /** + * @brief Return the characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid) const; + + /** + * @brief return the nth characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @param index The index of characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid, int index) const; + protected: - friend BLEPeripheral; - friend BLEProfile; - void discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params); - void discover(bt_gatt_discover_params_t *params); + friend class BLEDevice; + friend class BLEServiceImp; + friend class BLEProfileManager; + friend uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); + + BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev); + BLEService(const bt_uuid_t* uuid); + void setServiceImp(BLEServiceImp* serviceImp); - static bt_uuid_t *getPrimayUuid(void); + BLEServiceImp* getLocalServiceImp(); + BLEServiceImp* fetchOutLocalServiceImp(); private: - static bt_uuid_16 _gatt_primary_uuid; + BLEServiceImp* getServiceImp(); + BLEServiceImp* getServiceImp() const; + +private: + BLEDevice _bledevice; + BLEServiceImp* _service_imp; + char _uuid_cstr[37]; + + BLEServiceImp* _service_local_imp; // This not allow copy }; -#endif // _BLE_SERVICE_H_INCLUDED +#endif diff --git a/libraries/CurieBLE/src/BLETypedCharacteristic.h b/libraries/CurieBLE/src/BLETypedCharacteristic.h index 65842d0f..cd519ca6 100644 --- a/libraries/CurieBLE/src/BLETypedCharacteristic.h +++ b/libraries/CurieBLE/src/BLETypedCharacteristic.h @@ -20,7 +20,7 @@ #ifndef _BLE_TYPED_CHARACTERISTIC_H_INCLUDED #define _BLE_TYPED_CHARACTERISTIC_H_INCLUDED -#include "Arduino.h" +#include "CurieBLE.h" #include "BLECharacteristic.h" @@ -52,6 +52,19 @@ template class BLETypedCharacteristic : public BLECharacteristic * @note none */ bool setValue(T value); + + /** + * @brief Update the characteristic value + * + * @param[in] value New value to set + * + * @return bool true - set value success, + * false - on error + * + * @note none + */ + bool writeValue(T value); + /** * @brief Get the value of the Characteristic * @@ -106,20 +119,6 @@ template class BLETypedCharacteristic : public BLECharacteristic * @note none */ T valueBE(void); - - /** - * @brief Set the peer peripheral device's characteristic value - * - * @param[in] Peripheral The peer peripheral device that want to set the characteristic value - * - * @param[in] value New value to set - * - * @return bool true - set value success, - * false - on error - * - * @note none - */ - bool write(BLEPeripheralHelper &Peripheral, T value); private: /** @@ -147,6 +146,10 @@ template bool BLETypedCharacteristic::setValue(T value) { return BLECharacteristic::setValue((unsigned char*)&value, sizeof(T)); } +template bool BLETypedCharacteristic::writeValue(T value) { + return BLECharacteristic::writeValue((unsigned char*)&value, sizeof(T)); +} + template T BLETypedCharacteristic::value() { T value; @@ -171,10 +174,6 @@ template T BLETypedCharacteristic::valueBE() { return byteSwap(value()); } -template bool BLETypedCharacteristic::write(BLEPeripheralHelper &Peripheral, T value){ - return BLECharacteristic::write(Peripheral, (unsigned char *)(&value), sizeof(T)); -} - template T BLETypedCharacteristic::byteSwap(T value) { T result; unsigned char* src = (unsigned char*)&value; diff --git a/libraries/CurieBLE/src/BLETypedCharacteristics.cpp b/libraries/CurieBLE/src/BLETypedCharacteristics.cpp index c9c89e24..94bbb6ad 100644 --- a/libraries/CurieBLE/src/BLETypedCharacteristics.cpp +++ b/libraries/CurieBLE/src/BLETypedCharacteristics.cpp @@ -19,6 +19,10 @@ #include "BLETypedCharacteristics.h" +BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned char properties) : + BLETypedCharacteristic(uuid, properties) { +} + BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned char properties) : BLETypedCharacteristic(uuid, properties) { } diff --git a/libraries/CurieBLE/src/BLETypedCharacteristics.h b/libraries/CurieBLE/src/BLETypedCharacteristics.h index 1ecd2a6c..fc62d70c 100644 --- a/libraries/CurieBLE/src/BLETypedCharacteristics.h +++ b/libraries/CurieBLE/src/BLETypedCharacteristics.h @@ -40,6 +40,24 @@ class BLEBoolCharacteristic : public BLETypedCharacteristic { BLEBoolCharacteristic(const char* uuid, unsigned char properties); }; +class BLEByteCharacteristic : public BLETypedCharacteristic { +public: + /** + * @brief Instantiate a Byte Typed Characteristic. + * Default constructor for BLE Byte Characteristic + * + * @param[in] uuid The characteristic UUID 16/128 bits + * + * @param[in] properties The property of the characteristic (BLERead, + * BLEWrite or BLE Notify. Combine with | ) + * + * @return none + * + * @note none + */ + BLEByteCharacteristic(const char* uuid, unsigned char properties); +}; + class BLECharCharacteristic : public BLETypedCharacteristic { public: /** diff --git a/libraries/CurieBLE/src/CurieBLE.h b/libraries/CurieBLE/src/CurieBLE.h index bc1be251..cb1828da 100644 --- a/libraries/CurieBLE/src/CurieBLE.h +++ b/libraries/CurieBLE/src/CurieBLE.h @@ -1,27 +1,48 @@ /* - * Copyright (c) 2015 Intel Corporation. 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 - * - */ + BLE API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_H +#define ARDUINO_BLE_H + +#define ARDUINO_BLE_API_VERSION 10000 // version 1.0.0 + +class BLEDevice; +class BLECentral; +class BLECharacteristic; +class BLEDescriptor; +class BLEService; +class BLECharacteristicImp; +class BLEDescriptorImp; -#include "BLECharacteristic.h" #include "BLECommon.h" + +#include "BLEDevice.h" +#include "BLEAttributeWithValue.h" +#include "BLECharacteristic.h" #include "BLEDescriptor.h" #include "BLEService.h" -#include "BLEPeripheral.h" + #include "BLETypedCharacteristics.h" #include "BLECentral.h" +#include "BLEPeripheral.h" + +extern BLEDevice BLE; + +#endif diff --git a/libraries/CurieBLE/src/internal/BLEAttribute.cpp b/libraries/CurieBLE/src/internal/BLEAttribute.cpp new file mode 100644 index 00000000..fbb33ed9 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEAttribute.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 Intel Corporation. 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 "CurieBLE.h" +#include "BLEAttribute.h" + +#include "./internal/BLEUtils.h" + + +BLEAttribute::BLEAttribute(const char* uuid, BLEAttributeType type) : + _type(type) +{ + memset(&_uuid, 0, sizeof (_uuid)); + BLEUtils::uuidString2BT(uuid, (bt_uuid_t*)&_uuid); +} + +BLEAttribute::BLEAttribute(const bt_uuid_t* uuid, BLEAttributeType type) : + _type(type) +{ + memcpy(&_uuid, uuid, sizeof (_uuid)); +} + +const bt_uuid_t *BLEAttribute::bt_uuid(void) +{ + return (bt_uuid_t *)&_uuid; +} + + +BLEAttributeType +BLEAttribute::type() const { + return this->_type; +} + +bool BLEAttribute::compareUuid(const bt_uuid_t* uuid) +{ + int cmpresult = 0; + cmpresult = bt_uuid_cmp(uuid, (const bt_uuid_t*)&_uuid); + return (cmpresult == 0); + +} + +bool BLEAttribute::compareUuid(const char* uuid) +{ + bt_uuid_128_t temp; + BLEUtils::uuidString2BT(uuid,(bt_uuid_t *)&temp); + return compareUuid((bt_uuid_t *)&temp); +} + diff --git a/libraries/CurieBLE/src/internal/BLEAttribute.h b/libraries/CurieBLE/src/internal/BLEAttribute.h new file mode 100644 index 00000000..db69cf32 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEAttribute.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015 Intel Corporation. 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_ATTRIBUTE_H_INCLUDED +#define _BLE_ATTRIBUTE_H_INCLUDED + +#include "BLECommon.h" + +/// BLE attribute tyep enum +typedef enum { + BLETypeService = 0x2800, ///< the service type + BLETypeCharacteristic = 0x2803, ///< the characteristic type + BLETypeDescriptor = 0x2900 ///< the descriptor type +}BLEAttributeType; + + +class BLEAttribute { +public: + /** + * @brief Get the UUID raw data + * + * @param none + * + * @return bt_uuid_t* The pointer of UUID + * + * @note none + */ + const bt_uuid_t *bt_uuid(void); + + /** + * @brief Compare the UUID with the paramater data + * + * @param[in] data The pointer of data + * + * @param[in] uuidsize The max size of UUID + * + * @return bool true - UUID is the same with data + * false- UUID is not the same with data + * + * @note none + */ + bool compareUuid(const char* uuid); + bool compareUuid(const bt_uuid_t* uuid); + + BLEAttributeType type(void) const; + +protected: + BLEAttribute(const char* uuid, BLEAttributeType type); + BLEAttribute(const bt_uuid_t* uuid, BLEAttributeType type); +private: + bt_uuid_128_t _uuid; + + BLEAttributeType _type; + +}; + +#endif // _BLE_ATTRIBUTE_H_INCLUDED diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp new file mode 100644 index 00000000..b6234750 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "CurieBLE.h" + +#include "BLEAttribute.h" +#include "BLECharacteristicImp.h" +#include "BLEDeviceManager.h" +#include "BLEProfileManager.h" + +// GATT Server Only +ssize_t profile_read_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + void *buf, uint16_t len, + uint16_t offset) +{ + const unsigned char *pvalue; + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLEAttributeType type = bleattr->type(); + if (BLETypeCharacteristic == type) + { + BLECharacteristicImp* blecharacteritic = (BLECharacteristicImp*)bleattr; + pvalue = blecharacteritic->value(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, + blecharacteritic->valueLength()); + } + else if (BLETypeDescriptor == type) + { + BLEDescriptorImp* bledescriptor = (BLEDescriptorImp*)bleattr; + pvalue = bledescriptor->value(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, bledescriptor->valueLength()); + } + return 0; +} + +// GATT server only +ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset) +{ + pr_info(LOG_MODULE_BLE, "%s1", __FUNCTION__); + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLECharacteristicImp* blecharacteritic; + BLEAttributeType type = bleattr->type(); + if ((BLETypeCharacteristic != type) || 0 != offset) + { + return 0; + } + + blecharacteritic = (BLECharacteristicImp*)bleattr; + blecharacteritic->setValue((const uint8_t *) buf, len); + return len; +} + +ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset) +{ + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + + blecharacteritic->setBuffer((const uint8_t *) buf, len, offset); + + return len; +} + +int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags) +{ + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + + switch (flags) + { + case BT_GATT_FLUSH_DISCARD: + /* Discard buffer reseting it back with data */ + blecharacteritic->discardBuffer(); + return 0; + case BT_GATT_FLUSH_SYNC: + /* Sync buffer to data */ + blecharacteritic->syncupBuffer2Value(); + return 0; + } + + return -EINVAL; +} + + +// GATT client only +uint8_t profile_notify_process (bt_conn_t *conn, + bt_gatt_subscribe_params_t *params, + const void *data, uint16_t length) +{ + //BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn + //BLEAttribute* notifyatt = peripheral->attribute(params); // Find attribute by params + BLECharacteristicImp* chrc = NULL; + BLEDevice bleDevice(bt_conn_get_dst(conn)); + chrc = BLEProfileManager::instance()->characteristic(bleDevice, params->value_handle); + + //assert(notifyatt->type() == BLETypeCharacteristic); + pr_debug(LOG_MODULE_APP, "%s1", __FUNCTION__); + if (NULL != chrc) + { + chrc->setValue((const unsigned char *)data, length); + } + return BT_GATT_ITER_CONTINUE; +} + +// GATT client only +uint8_t profile_discover_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + uint8_t ret = BT_GATT_ITER_STOP; + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + ret = BLEProfileManager::instance()->discoverResponseProc(conn, attr, params); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return ret; +} + +// GATT Client only +uint8_t profile_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + if (NULL == data) + { + return BT_GATT_ITER_STOP; + } + BLECharacteristicImp *chrc = NULL; + BLEDevice bleDevice(bt_conn_get_dst(conn)); + + // Get characteristic by handle params->single.handle + chrc = BLEProfileManager::instance()->characteristic(bleDevice, params->single.handle); + + if (chrc) // KW issue: may be NULL and will be dereferenced + chrc->setValue((const unsigned char *)data, length); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return BT_GATT_ITER_STOP; +} + +uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + uint8_t ret = BLEProfileManager::instance()->serviceReadRspProc(conn, err, params, data, length); + pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d", __FUNCTION__, __LINE__, ret); + return ret; +} + + + +void bleConnectEventHandler(bt_conn_t *conn, + uint8_t err, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + p->handleConnectEvent(conn, err); +} + + +void bleDisconnectEventHandler(bt_conn_t *conn, + uint8_t reason, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + pr_info(LOG_MODULE_BLE, "Connect lost. Reason: %d", reason); + + p->handleDisconnectEvent(conn, reason); +} + +void bleParamUpdatedEventHandler(bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + p->handleParamUpdated(conn, interval, latency, timeout); +} + + +void ble_central_device_found(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t len) +{ + //char dev[BT_ADDR_LE_STR_LEN]; + + //bt_addr_le_to_str(addr, dev, sizeof(dev)); + //pr_debug(LOG_MODULE_BLE, "[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", + // dev, type, len, rssi); + + BLEDeviceManager::instance()->handleDeviceFound(addr, rssi, type, + ad, len); +} + + diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.h b/libraries/CurieBLE/src/internal/BLECallbacks.h new file mode 100644 index 00000000..882c134a --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLECallbacks.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 __BLECALLBACKS_H__ +#define __BLECALLBACKS_H__ + +uint8_t profile_notify_process (bt_conn_t *conn, + bt_gatt_subscribe_params_t *params, + const void *data, uint16_t length); +uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); +int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags); +ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); +ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset); +ssize_t profile_read_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + void *buf, uint16_t len, + uint16_t offset); + +uint8_t profile_discover_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + +void bleConnectEventHandler(bt_conn_t *conn, + uint8_t err, + void *param); + +void bleDisconnectEventHandler(bt_conn_t *conn, + uint8_t reason, + void *param); + +void bleParamUpdatedEventHandler(bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param); + +void ble_central_device_found(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t len); + +uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); + +#endif + diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp new file mode 100644 index 00000000..a5a60c57 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "BLEAttribute.h" +#include "BLEServiceImp.h" +#include "BLECharacteristicImp.h" + +#include "BLECallbacks.h" +#include "BLEUtils.h" + +bt_uuid_16_t BLECharacteristicImp::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; +bt_uuid_16_t BLECharacteristicImp::_gatt_ccc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CCC_VAL}; + +BLECharacteristicImp::BLECharacteristicImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + const BLEDevice& bledevice): + BLEAttribute(uuid, BLETypeCharacteristic), + _value_length(0), + _value_buffer(NULL), + _value_updated(false), + _value_handle(handle), + _cccd_handle(0), + _attr_chrc_value(NULL), + _attr_cccd(NULL), + _subscribed(false), + _reading(false), + _ble_device() +{ + _value_size = BLE_MAX_ATTR_DATA_LEN;// Set as MAX value. TODO: long read/write need to twist + _value = (unsigned char*)malloc(_value_size); + + // TODO: Enable when max value is not set. + // if (_value_size > BLE_MAX_ATTR_DATA_LEN) + // { + // _value_buffer = (unsigned char*)malloc(_value_size); + // } + + if (_value) + { + memset(_value, 0, _value_size); + } + else + { + errno = ENOMEM; + } + + memset(&_ccc_cfg, 0, sizeof(_ccc_cfg)); + memset(&_ccc_value, 0, sizeof(_ccc_value)); + memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); + memset(&_sub_params, 0, sizeof(_sub_params)); + memset(&_discover_params, 0, sizeof(_discover_params)); + + _ccc_value.cfg = &_ccc_cfg; + _ccc_value.cfg_len = 1; + if (BLERead & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_READ; + } + if (BLEWrite & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE; + } + if (BLEWriteWithoutResponse & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; + } + if (BLENotify & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_NOTIFY; + _sub_params.value |= BT_GATT_CCC_NOTIFY; + } + if (BLEIndicate & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_INDICATE; + _sub_params.value |= BT_GATT_CCC_INDICATE; + } + _gatt_chrc.uuid = (bt_uuid_t*)this->bt_uuid();//&_characteristic_uuid;//this->uuid(); + memset(_event_handlers, 0, sizeof(_event_handlers)); + memset(_oldevent_handlers, 0, sizeof(_oldevent_handlers)); + + _sub_params.notify = profile_notify_process; + + // Update BLE device object + _ble_device.setAddress(*bledevice.bt_le_address()); + + memset(&_descriptors_header, 0, sizeof(_descriptors_header)); +} + +BLECharacteristicImp::BLECharacteristicImp(BLECharacteristic& characteristic, + const BLEDevice& bledevice): + BLEAttribute(characteristic.uuid(), BLETypeCharacteristic), + _value_length(0), + _value_buffer(NULL), + _value_updated(false), + _value_handle(0), + _cccd_handle(0), + _attr_chrc_value(NULL), + _attr_cccd(NULL), + _subscribed(false), + _reading(false), + _ble_device() +{ + unsigned char properties = characteristic._properties; + _value_size = characteristic._value_size; + _value = (unsigned char*)malloc(_value_size); + if (_value == NULL) + { + errno = ENOMEM; + } + if (_value_size > BLE_MAX_ATTR_DATA_LEN) + { + _value_buffer = (unsigned char*)malloc(_value_size); + } + + memset(&_ccc_cfg, 0, sizeof(_ccc_cfg)); + memset(&_ccc_value, 0, sizeof(_ccc_value)); + memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); + memset(&_sub_params, 0, sizeof(_sub_params)); + memset(&_discover_params, 0, sizeof(_discover_params)); + + _ccc_value.cfg = &_ccc_cfg; + _ccc_value.cfg_len = 1; + if (BLERead & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_READ; + } + if (BLEWrite & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE; + } + if (BLEWriteWithoutResponse & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; + } + if (BLENotify & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_NOTIFY; + _sub_params.value |= BT_GATT_CCC_NOTIFY; + } + if (BLEIndicate & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_INDICATE; + _sub_params.value |= BT_GATT_CCC_INDICATE; + } + _gatt_chrc.uuid = (bt_uuid_t*)this->bt_uuid();//&_characteristic_uuid;//this->uuid(); + + memcpy(_event_handlers, characteristic._event_handlers, sizeof(_event_handlers)); + memcpy(_oldevent_handlers, characteristic._oldevent_handlers, sizeof(_oldevent_handlers)); + + _sub_params.notify = profile_notify_process; + + if (NULL != characteristic._value) + { + memcpy(_value, characteristic._value, _value_size); + } + + // Update BLE device object + _ble_device.setAddress(*bledevice.bt_le_address()); + + characteristic.setBLECharacteristicImp(this); + memset(&_descriptors_header, 0, sizeof(_descriptors_header)); +} + +BLECharacteristicImp::~BLECharacteristicImp() +{ + releaseDescriptors(); + if (_value) + { + free(_value); + _value = (unsigned char *)NULL; + } + + if (_value_buffer) + { + free(_value_buffer); + _value_buffer = (unsigned char *)NULL; + } +} + +unsigned char +BLECharacteristicImp::properties() const +{ + return _gatt_chrc.properties; +} + +bool BLECharacteristicImp::writeValue(const byte value[], int length) +{ + int status; + bool retVal = false; + + _setValue(value, length, 0); + + // Address same is GATT server. Send notification if CCCD enabled + // Different is GATT client. Send write request + if (true == BLEUtils::isLocalBLE(_ble_device) && + NULL != _attr_chrc_value) + { + // Notify for peripheral. + status = bt_gatt_notify(NULL, _attr_chrc_value, value, length, NULL); + // Sid. KW found status is always 0 + // if (!status) + // { + retVal = true; + // } + } + + //Not schedule write request for central + // The write request may failed. + // If user want to get latest set value. Call read and get the real value + return retVal; +} + +bool BLECharacteristicImp::writeValue(const byte value[], int length, int offset) +{ + int status; + bool retVal = false; + + _setValue(value, length, offset); + + // Address same is GATT server. Send notification if CCCD enabled + // Different is GATT client. Send write request + if (true == BLEUtils::isLocalBLE(_ble_device) && + NULL != _attr_chrc_value) + { + // Notify for peripheral. + status = bt_gatt_notify(NULL, _attr_chrc_value, value, length, NULL); + // Sid. KW found status is always 0. + // if (!status) + // { + retVal = true; + // } + } + + //Not schedule write request for central + // The write request may failed. + // If user want to get latest set value. Call read and get the real value + return retVal; +} + +bool +BLECharacteristicImp::setValue(const unsigned char value[], uint16_t length) +{ + _setValue(value, length, 0); + if (BLEUtils::isLocalBLE(_ble_device) == true) + { + // GATT server + // Write request for GATT server + if (_event_handlers[BLEWritten]) + { + BLECharacteristic chrcTmp(this, &_ble_device); + _event_handlers[BLEWritten](_ble_device, chrcTmp); + } + + if (_oldevent_handlers[BLEWritten]) + { + BLECharacteristic chrcTmp(this, &_ble_device); + BLECentral central(_ble_device); + _oldevent_handlers[BLEWritten](central, chrcTmp); + } + } + else + { + // GATT client + // Discovered attribute + // Read response/Notification/Indication for GATT client + if (_reading) + { + // Read response received. Not block the other reading. + _reading = false; + } + + if (_event_handlers[BLEValueUpdated]) + { + BLECharacteristic chrcTmp(this, &_ble_device); + _event_handlers[BLEValueUpdated](_ble_device, chrcTmp); + } + + if (_oldevent_handlers[BLEValueUpdated]) + { + BLECharacteristic chrcTmp(this, &_ble_device); + BLECentral central(_ble_device); + _oldevent_handlers[BLEValueUpdated](central, chrcTmp); + } + } + + return true; +} + +unsigned short +BLECharacteristicImp::valueSize() const +{ + return _value_size; +} + +const unsigned char* +BLECharacteristicImp::value() const +{ + return _value; +} + +unsigned short +BLECharacteristicImp::valueLength() const +{ + return _value_length; +} + +unsigned char +BLECharacteristicImp::operator[] (int offset) const +{ + return _value[offset]; +} + +bool +BLECharacteristicImp::written() +{ + bool written = false; + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server. The characteristic on local device + written = _value_updated; + _value_updated = false; + } + + return written; +} + +bool BLECharacteristicImp::valueUpdated() +{ + bool updated = false; + if (false == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT client. The characteristic on remote device. + updated = _value_updated; + _value_updated = false; + } + return updated; +} + +bool +BLECharacteristicImp::subscribed() +{ + if (false == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT client + return _subscribed; + } + else + { + // GATT server + return (_ccc_value.value & (BT_GATT_CCC_NOTIFY | BT_GATT_CCC_INDICATE)); + } +} + +bool BLECharacteristicImp::canNotify() +{ + if (false == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT client can't subscribe + return false; + } + + // GATT server + return (_ccc_value.value & BT_GATT_CCC_NOTIFY); +} + +bool BLECharacteristicImp::canIndicate() +{ + if (false == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT client can't subscribe + return false; + } + + // GATT server + return (_ccc_value.value & BT_GATT_CCC_INDICATE); +} + +bool BLECharacteristicImp::unsubscribe(void) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't subscribe + return false; + } + + if (false == _subscribed) + { + return true; + } + + _sub_params.value = 0; + + if (0 == (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) + { + // The characteristic not support the Notify and Indicate + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + bt_addr_le_copy(&_sub_params._peer, bt_conn_get_dst(conn)); + _sub_params.ccc_handle = _cccd_handle; + _sub_params.value_handle = _value_handle; + + // Enable CCCD to allow peripheral send Notification/Indication + retval = bt_gatt_unsubscribe(conn, &_sub_params); + bt_conn_unref(conn); + if (0 == retval) + { + _subscribed = false; + } + return _subscribed; +} + +bool BLECharacteristicImp::subscribe(void) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't subscribe + return false; + } + + if (_gatt_chrc.properties & BT_GATT_CHRC_NOTIFY) + { + _sub_params.value |= BT_GATT_CCC_NOTIFY; + } + + if (_gatt_chrc.properties & BT_GATT_CHRC_INDICATE) + { + _sub_params.value |= BT_GATT_CCC_INDICATE; + } + + if (_sub_params.value == 0) + { + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + bt_addr_le_copy(&_sub_params._peer, bt_conn_get_dst(conn)); + _sub_params.ccc_handle = _cccd_handle; + _sub_params.value_handle = _value_handle; + + // Enable CCCD to allow peripheral send Notification/Indication + retval = bt_gatt_subscribe(conn, &_sub_params); + bt_conn_unref(conn); + if (0 == retval) + { + _subscribed = true; + } + return _subscribed; +} + +void +BLECharacteristicImp::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback) +{ + noInterrupts(); + if (event < BLECharacteristicEventLast) { + _event_handlers[event] = callback; + } + interrupts(); +} + +void +BLECharacteristicImp::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandlerOld callback) +{ + noInterrupts(); + if (event < BLECharacteristicEventLast) { + _oldevent_handlers[event] = callback; + } + interrupts(); +} + +void +BLECharacteristicImp::setHandle(uint16_t handle) +{ + // GATT client + _value_handle = handle; +} + +void +BLECharacteristicImp::setCCCDHandle(uint16_t handle) +{ + // GATT client + _cccd_handle = handle; +} + +uint16_t +BLECharacteristicImp::valueHandle() +{ + uint16_t handle = 0; + if (NULL != _attr_chrc_value) + { + //GATT server + handle = _attr_chrc_value->handle; + } + else + { + // GATT client + handle = _value_handle; + } + + return handle; +} + +void +BLECharacteristicImp::_setValue(const uint8_t value[], uint16_t length, uint16_t offset) +{ + if (length + offset > _value_size) + { + if (_value_size > offset) + { + uint16_t temp_len = _value_size - offset; + if (length > temp_len) + { + length = temp_len; + } + } + else + { + return; + } + } + + _value_updated = true; + memcpy(_value + offset, value, length); + _value_length = length; +} + +_bt_gatt_ccc_t* BLECharacteristicImp::getCccCfg(void) +{ + return &_ccc_value; +} + +bt_gatt_chrc_t* BLECharacteristicImp::getCharacteristicAttValue(void) +{ + return &_gatt_chrc; +} + +uint8_t BLECharacteristicImp::getPermission(void) +{ + uint8_t perm = 0; + if (_gatt_chrc.properties & BT_GATT_CHRC_READ) + { + perm |= BT_GATT_PERM_READ; + } + if (_gatt_chrc.properties & (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP)) + { + perm |= BT_GATT_PERM_WRITE; + } + return perm; +} + +bt_uuid_t* BLECharacteristicImp::getCharacteristicAttributeUuid(void) +{ + return (bt_uuid_t*) &_gatt_chrc_uuid; +} + +bt_uuid_t* BLECharacteristicImp::getClientCharacteristicConfigUuid(void) +{ + return (bt_uuid_t*) &_gatt_ccc_uuid; +} + +bool BLECharacteristicImp::read() +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't write + return false; + } + + if (_reading) + { + // Already in reading state + return false; + } + + _read_params.func = profile_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = _value_handle; + _read_params.single.offset = 0; + + if (0 == _read_params.single.handle) + { + // Discover not complete + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + // Send read request + retval = bt_gatt_read(conn, &_read_params); + bt_conn_unref(conn); + if (0 == retval) + { + _reading = true; + } + return _reading; +} + +bool BLECharacteristicImp::write(const unsigned char value[], + uint16_t length) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't write + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + // Send read request + retval = bt_gatt_write_without_response(conn, + _value_handle, + value, + length, + false); + bt_conn_unref(conn); + return (0 == retval); +} + +void BLECharacteristicImp::setBuffer(const uint8_t value[], + uint16_t length, + uint16_t offset) +{ + if ((length + offset > _value_size) || + ((unsigned char *)NULL == _value_buffer)) { + // Ignore the data + return; + } + + memcpy(_value_buffer + offset, value, length); +} + +void BLECharacteristicImp::syncupBuffer2Value() +{ + setValue(_value_buffer, _value_size); +} + +void BLECharacteristicImp::discardBuffer() +{ + if(_value_buffer) + memcpy(_value_buffer, _value, _value_size); +} + +bool BLECharacteristicImp::longCharacteristic() +{ + return (_value_size > BLE_MAX_ATTR_DATA_LEN); +} + +int BLECharacteristicImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + int base_index = index; + int offset = 0; + int counter = 0; + + // Characteristic declare + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = getCharacteristicAttributeUuid(); + start->perm = BT_GATT_PERM_READ; + start->read = bt_gatt_attr_read_chrc; + start->user_data = this->getCharacteristicAttValue(); + pr_info(LOG_MODULE_BLE, "chrc-%p, uuid type-%d", start, start->uuid->type); + + start++; + index++; + counter++; + + // Descriptor + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = (bt_uuid_t *)bt_uuid(); + start->perm = this->getPermission(); + start->user_data = (void*)((BLEAttribute*)this); + start->read = profile_read_process; + + if (this->longCharacteristic() == false) + { + // Normal characteristic MAX. 20 + start->write = profile_write_process; + } + else + { + // Long characteristic. MAX. 512 + start->write = profile_longwrite_process; + start->flush = profile_longflush_process; + } + _attr_chrc_value = start; + pr_debug(LOG_MODULE_BLE, "chrcdescripor-%p, chimp-%p type-%d", start, this, this->type()); + + start++; + index++; + counter++; + + if (0 != (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) + { + // Descriptor + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = this->getClientCharacteristicConfigUuid(); + start->perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; + start->read = bt_gatt_attr_read_ccc; + start->write = bt_gatt_attr_write_ccc; + start->user_data = this->getCccCfg(); + + pr_info(LOG_MODULE_BLE, "cccd-%p", start); + + start++; + index++; + counter++; + } + + BLEDescriptorNodePtr node = _descriptors_header.next; + while (NULL != node) + { + BLEDescriptorImp *descriptorImp = node->value; + start = attr_start + index - base_index; + offset = descriptorImp->updateProfile(start, index); + counter += offset; + node = node->next; + } + pr_debug(LOG_MODULE_BLE, "%s:type-%d", __FUNCTION__, this->type()); + return counter; +} + +int BLECharacteristicImp::addDescriptor(BLEDescriptor& descriptor) +{ + BLEDescriptorImp* descriptorImp = descrptor(descriptor.uuid()); + if (NULL != descriptorImp) + { + return BLE_STATUS_SUCCESS; + } + + descriptorImp = new BLEDescriptorImp(_ble_device, descriptor); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == descriptorImp) + { + return BLE_STATUS_NO_MEMORY; + } + + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + BLEDescriptorNodePtr node = link_node_create(descriptorImp); + if (NULL == node) + { + delete descriptorImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_descriptors_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +int BLECharacteristicImp::addDescriptor(const bt_uuid_t* uuid, + unsigned char property, + uint16_t handle) +{ + BLEDescriptorImp* descriptorImp = descrptor(uuid); + if (NULL != descriptorImp) + { + return BLE_STATUS_SUCCESS; + } + + descriptorImp = new BLEDescriptorImp(uuid, property, handle, _ble_device); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == descriptorImp) + { + return BLE_STATUS_NO_MEMORY; + } + + BLEDescriptorNodePtr node = link_node_create(descriptorImp); + if (NULL == node) + { + delete descriptorImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_descriptors_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +BLEDescriptorImp* BLECharacteristicImp::descrptor(const bt_uuid_t* uuid) +{ + BLEDescriptorImp* descriptorImp = NULL; + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + + while (NULL != node) + { + descriptorImp = node->value; + if (true == descriptorImp->compareUuid(uuid)) + { + break; + } + node = node->next; + } + + if (NULL == node) + { + descriptorImp = NULL; + } + return descriptorImp; +} + +BLEDescriptorImp* BLECharacteristicImp::descrptor(const char* uuid) +{ + bt_uuid_128_t uuid_tmp; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + return descrptor((const bt_uuid_t *)&uuid_tmp); +} + + +BLEDescriptorImp* BLECharacteristicImp::descrptor(int index) +{ + BLEDescriptorImp* descriptorImp = NULL; + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + while (NULL != node) + { + if (0 >= index) + { + descriptorImp = node->value; + break; + } + index--; + node = node->next; + } + return descriptorImp; +} + +void BLECharacteristicImp::releaseDescriptors() +{ + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + + while (NULL != node) + { + BLEDescriptorImp* descriptorImp = node->value; + delete descriptorImp; + link_node_remove_first(&_descriptors_header); + node = link_node_get_first(&_descriptors_header); + } +} + +int BLECharacteristicImp::getAttributeCount() +{ + int counter = link_list_size(&_descriptors_header) + 2; // Declaration and descriptor + // Notification/Indecation + if (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)) + { + counter++; + } + return counter; +} + +int BLECharacteristicImp::descriptorCount() const +{ + int counter = link_list_size(&_descriptors_header); + return counter; +} + +bool BLECharacteristicImp::discoverAttributes(BLEDevice* device) +{ + + int err; + bt_conn_t* conn; + bt_gatt_discover_params_t* temp = NULL; + const bt_uuid_t* service_uuid = bt_uuid(); + + if (service_uuid->type == BT_UUID_TYPE_16) + { + uint16_t uuid_tmp ;//= ((bt_uuid_16_t*)service_uuid)->val; + memcpy(&uuid_tmp, &((bt_uuid_16_t*)service_uuid)->val, sizeof(uuid_tmp)); + if (BT_UUID_GAP_VAL == uuid_tmp || + BT_UUID_GATT_VAL == uuid_tmp) + { + return false; + } + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return false; + } + temp = &_discover_params; + temp->start_handle = _value_handle + 1; + temp->end_handle = _value_handle + 20; // TODO: the max descriptor is not more than 20 + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_DESCRIPTOR; + temp->func = profile_discover_process; + pr_debug(LOG_MODULE_BLE, "%s-%d-charc",__FUNCTION__, __LINE__); + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return false; + } + return true; +} + +bool BLECharacteristicImp::isClientCharacteristicConfigurationDescriptor(const bt_uuid_t* uuid) +{ + bool ret = false; + uint16_t cccd_uuid = BT_UUID_GATT_CCC_VAL; + if (uuid->type == BT_UUID_TYPE_16) + { + if (0 == memcmp(&BT_UUID_16(uuid)->val, &cccd_uuid, sizeof(uint16_t))) + { + ret = true; + } + } + return ret; +} + +uint8_t BLECharacteristicImp::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + + pr_debug(LOG_MODULE_BLE, "%s-%d: type-%d", __FUNCTION__, __LINE__, params->type); + + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_DESCRIPTOR: + { + if (NULL != attr) + { + retVal = BT_GATT_ITER_CONTINUE; + const bt_uuid_t* desc_uuid = attr->uuid; + uint16_t desc_handle = attr->handle; + pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, desc_handle); + if (isClientCharacteristicConfigurationDescriptor(desc_uuid)) + { + setCCCDHandle(desc_handle); + } + else if (bt_uuid_cmp(BLEServiceImp::getPrimayUuid(), desc_uuid) == 0 || + bt_uuid_cmp(getCharacteristicAttributeUuid(), desc_uuid) == 0 ) + { + retVal = BT_GATT_ITER_STOP; + } + else + { + int retval = (int)addDescriptor(desc_uuid, + attr->perm, + desc_handle); + + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + errno = ENOMEM; + retVal = BT_GATT_ITER_STOP; + } + + } + } + break; + } + default: + { + break; + } + } + return retVal; +} + + diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h new file mode 100644 index 00000000..c6cee5ab --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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_CHARACTERISTICIMP_H_INCLUDED +#define _BLE_CHARACTERISTICIMP_H_INCLUDED + +//#include "BLECommon.h" + +//#include "BLEDevice.h" +//#include "BLEDescriptor.h" + +#include "CurieBLE.h" +#include "BLEDescriptorImp.h" + +#include "BLEDevice.h" + +#include "LinkList.h" +class BLEDescriptorImp; +/** + * BLE GATT Characteristic : public BLEAttribute + */ +class BLECharacteristicImp: public BLEAttribute{ +public: + + virtual ~BLECharacteristicImp(); + + + /** + * @brief Add the characteristic's descriptor + * + * @param descriptor The descriptor for characteristic + * + * @return none + * + * @note none + */ + int addDescriptor(BLEDescriptor& descriptor); + int addDescriptor(const bt_uuid_t* uuid, + unsigned char property, + uint16_t handle); + + void releaseDescriptors(); + + /** + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @param offset The offset in the characteristic's data + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const byte value[], int length); + bool writeValue(const byte value[], int length, int offset); + + /** + * Set the current value of the Characteristic + * + * @param[in] value New value to set, as a byte array. Data is stored in internal copy. + * @param[in] length Length, in bytes, of valid data in the array to write. + * Must not exceed maxLength set for this characteristic. + * + * @return bool true set value success, false on error + */ + bool setValue(const unsigned char value[], unsigned short length); + + /** + * Get the property mask of the Characteristic + * + * @return unsigned char property mask of the Characteristic + */ + unsigned char properties(void) const; + + /** + * Get the (maximum) size of the Characteristic + * + * @return unsigned size of characateristic in bytes + */ + unsigned short valueSize(void) const; + + /** + * Get data pointer to the value of the Characteristic + * + * @return const unsigned char* pointer to the value of the Characteristic + */ + const unsigned char* value(void) const; + + /** + * Get the current length of the value of the Characteristic + * + * @return unsigned short size of characateristic value in bytes + */ + unsigned short valueLength() const; + + unsigned char operator[] (int offset) const; + + /** + * Has the value of the Characteristic been written by a central + * + * @return bool true is central has updated characteristic value, otherwise false + */ + bool written(void); + bool valueUpdated(); + + /** + * Is a central listening for notifications or indications of the Characteristic + * + * @return bool true is central is subscribed, otherwise false + */ + bool subscribed(void); + bool canNotify(); + bool canIndicate(); + + bool subscribe(void); + bool unsubscribe(void); + + /** + * Provide a function to be called when events related to this Characteristic are raised + * + * @param[in] event Event type to set event handler for + * @param[in] callback Pointer to callback function to invoke when the event occurs. + */ + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback); + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandlerOld callback); + + /** + * @brief Schedule the read request to read the characteristic in peripheral + * + * @param[in] none + * + * @return bool Indicate the success or error + * + * @note Only for central device + */ + bool read(); + + /** + * @brief Schedule the write request to update the characteristic in peripheral + * + * @param[in] peripheral The peripheral device that want to be updated + * @param[in] value New value to set, as a byte array. Data is stored in internal copy. + * @param[in] length Length, in bytes, of valid data in the array to write. + * Must not exceed maxLength set for this characteristic. + * + * @return bool true set value success, false on error + * + * @note none + */ + bool write(const unsigned char value[], + uint16_t length); + + int descriptorCount() const; + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + + bool discoverAttributes(BLEDevice* device); + + BLEDescriptorImp* descrptor(const bt_uuid_t* uuid); + BLEDescriptorImp* descrptor(const char* uuid); + BLEDescriptorImp* descrptor(int index); + +protected: + friend class BLEProfileManager; + friend class BLEServiceImp; + friend class BLECharacteristic; + /** + * Constructor for BLE Characteristic + * + * @param[in] characteristic The characteristic + * @param[in] bledevice The device that has this characteristic + */ + BLECharacteristicImp(BLECharacteristic& characteristic, const BLEDevice& bledevice); + BLECharacteristicImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + const BLEDevice& bledevice); + + friend int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags); + friend ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); + + int getAttributeCount(); + + bool longCharacteristic(); + + void setBuffer(const uint8_t value[], + uint16_t length, + uint16_t offset); + void discardBuffer(); + void syncupBuffer2Value(); + + /** + * @brief Get the characteristic value handle + * + * @param none + * + * @return none + * + * @note Only for peripheral + */ + uint16_t valueHandle(void); + + /** + * @brief Get characteristic configuration descriptor value handle + * + * @param none + * + * @return uint16_t The value handle + * 0 is invalid handle + * + * @note Only for peripheral + */ + uint16_t cccdHandle(void); + + inline _bt_gatt_ccc_t* getCccCfg(void); + inline bt_gatt_chrc_t* getCharacteristicAttValue(void); + static bt_uuid_t* getCharacteristicAttributeUuid(void); + static bt_uuid_t* getClientCharacteristicConfigUuid(void); + + /** + * @brief Get the characteristic permission + * + * @param none + * + * @return uint8_t The characteristic permission + * + * @note none + */ + uint8_t getPermission(void); + + /** + * @brief For central to discover the peripherial profile + * + * @param[in] attr The discover response + * + * @param[in] params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + + /** + * @brief For central to discover the peripherial profile + * + * @param[in] params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(bt_gatt_discover_params_t *params); + + /** + * @brief Get the subscribe parameter + * + * @param none + * + * @return bt_gatt_subscribe_params_t * the subscribe parameter + * + * @note Only for central + */ + bt_gatt_subscribe_params_t* getSubscribeParams(); + +private: + + void setCCCDHandle(uint16_t handle); + void setHandle(uint16_t handle); + void _setValue(const uint8_t value[], uint16_t length, uint16_t offset); + bool isClientCharacteristicConfigurationDescriptor(const bt_uuid_t* uuid); + +private: + // Those 2 UUIDs are used for define the characteristic. + static bt_uuid_16_t _gatt_chrc_uuid; // Characteristic UUID + static bt_uuid_16_t _gatt_ccc_uuid; // CCCD UUID + + unsigned short _value_size; + unsigned short _value_length; + unsigned char* _value; + unsigned char* _value_buffer; + bool _value_updated; + + uint16_t _value_handle; // GATT client only + uint16_t _cccd_handle; // GATT client only + bt_gatt_discover_params_t _discover_params;// GATT client only + + bt_gatt_ccc_cfg_t _ccc_cfg; + _bt_gatt_ccc_t _ccc_value; + bt_gatt_chrc_t _gatt_chrc; + + bt_gatt_attr_t *_attr_chrc_value; // GATT server only + bt_gatt_attr_t *_attr_cccd; // GATT server only + + // For GATT Client to subscribe the Notification/Indication + bt_gatt_subscribe_params_t _sub_params; + bool _subscribed; + + bool _reading; + bt_gatt_read_params_t _read_params; // GATT read parameter + + typedef LinkNode BLEDescriptorLinkNodeHeader; + typedef LinkNode* BLEDescriptorNodePtr; + typedef LinkNode BLEDescriptorNode; + + BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; + BLECharacteristicEventHandlerOld _oldevent_handlers[BLECharacteristicEventLast]; + BLEDescriptorLinkNodeHeader _descriptors_header; + BLEDevice _ble_device; +}; + +#endif // _BLE_CHARACTERISTIC_H_INCLUDED diff --git a/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp new file mode 100644 index 00000000..0586bc09 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "BLEAttribute.h" +#include "BLEDescriptorImp.h" + +#include "internal/ble_client.h" + +#include "BLECallbacks.h" + +BLEDescriptorImp::BLEDescriptorImp(BLEDevice& bledevice, + BLEDescriptor &descriptor): + BLEAttribute(descriptor.uuid(), BLETypeDescriptor), + _value_handle(0) +{ + + _properties = descriptor.properties(); + _value_length = descriptor.valueLength(); + _value = (unsigned char*)malloc(_value_length); + + if (_value) + memcpy(_value, descriptor.value(), _value_length); + else + _value_length = 0; +} + +BLEDescriptorImp::BLEDescriptorImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + BLEDevice& bledevice): + BLEAttribute(uuid, BLETypeDescriptor), + _value_handle(handle), + _properties(properties) +{ + _value_length = BLE_MAX_ATTR_DATA_LEN; + _value = (unsigned char*)malloc(_value_length); + + if (_value) + memset(_value, 0, _value_length); + else + _value_length = 0; +} + + +BLEDescriptorImp::BLEDescriptorImp(const BLEDescriptorImp& rhs) : + BLEAttribute(rhs) +{ + _value_length = rhs._value_length; + _value = (unsigned char *)malloc(_value_length); + if (_value) + memcpy(_value, rhs._value, sizeof(_value_length)); + else + _value_length = 0; + + _value_handle = rhs._value_handle; + _properties = rhs._properties; + _descriptor_uuid = rhs._descriptor_uuid; + _bledev = BLEDevice(&rhs._bledev); +} + + +BLEDescriptorImp& BLEDescriptorImp::operator=(const BLEDescriptorImp& that) +{ + if (this != &that) { + + BLEAttribute::operator=(that); + if (_value) + free(_value); + + _value_length = that._value_length; + _value = (unsigned char *)malloc(_value_length); + if (_value) + memcpy(_value, that._value, sizeof(_value_length)); + else + _value_length = 0; + + _value_handle = that._value_handle; + _properties = that._properties; + _descriptor_uuid = that._descriptor_uuid; + _bledev = BLEDevice(&that._bledev); + } + return *this; +} + +BLEDescriptorImp::~BLEDescriptorImp() { + if (_value != (unsigned char *)NULL) { + free(_value); + _value = (unsigned char *)NULL; + } +} + +const unsigned char* +BLEDescriptorImp::value() const +{ + return _value; +} + +unsigned short +BLEDescriptorImp::valueLength() const +{ + return _value_length; +} + +unsigned char +BLEDescriptorImp::operator[] (int offset) const +{ + return _value[offset]; +} + +int BLEDescriptorImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + start->uuid = (struct bt_uuid *)bt_uuid(); + start->perm = BT_GATT_PERM_READ; + start->read = profile_read_process; + start->user_data = (void*)((BLEAttribute*)this); + + pr_debug(LOG_MODULE_BLE, "Descriptor-%p", start); + index++; + return 1; +} + +unsigned char BLEDescriptorImp::properties() const +{ + return _properties; +} + +int BLEDescriptorImp::valueSize() const +{ + return _value_length; +} + + diff --git a/libraries/CurieBLE/src/internal/BLEDescriptorImp.h b/libraries/CurieBLE/src/internal/BLEDescriptorImp.h new file mode 100644 index 00000000..32fceb1a --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEDescriptorImp.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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_DESCRIPTORIMP_H_INCLUDED +#define _BLE_DESCRIPTORIMP_H_INCLUDED + +#include "CurieBLE.h" + +/** + * BLE GATT Descriptor class + */ +class BLEDescriptorImp: public BLEAttribute{ +public: + /** + * Constructor for BLE Descriptor + * + * @param[in] uuid 16-bit UUID (in string form) defined by BLE standard + * @param[in] value Value of descriptor, as a byte array. Data is stored in internal copy. + * @param[in] valueLength Data length required for descriptor value (<= BLE_MAX_ATTR_DATA_LEN) + */ + BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor); + BLEDescriptorImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + BLEDevice& bledevice); + + BLEDescriptorImp(const BLEDescriptorImp& rhs); + + BLEDescriptorImp& operator=(const BLEDescriptorImp& that); + + virtual ~BLEDescriptorImp(); + + /** + * Get data pointer to the value of the Descriptor + * + * @return const unsigned char* pointer to the value of the Descriptor + */ + const unsigned char* value(void) const; + + /** + * Get the length of the value of the Descriptor + * + * @return unsigned short size of Descriptor value in bytes + */ + unsigned short valueLength(void) const; + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); + + unsigned char operator[] (int offset) const; + unsigned char properties() const; + int valueSize() const; + +protected: + + +private: + unsigned short _value_length; + unsigned short _value_handle; + unsigned char* _value; + unsigned char _properties; // The characteristic property + + bt_uuid_128 _descriptor_uuid; + + BLEDevice _bledev; +}; + +#endif // _BLE_DESCRIPTOR_H_INCLUDED diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp new file mode 100644 index 00000000..930e9faf --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -0,0 +1,1300 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. 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 "CurieBLE.h" +#include "BLEDeviceManager.h" +#include "BLEProfileManager.h" + +#include "internal/ble_client.h" + +#include +#include "../src/services/ble/conn_internal.h" + +#include "BLEUtils.h" +#include "BLECallbacks.h" + +BLEDeviceManager* BLEDeviceManager::_instance; + +BLEDeviceManager::BLEDeviceManager(): + _min_conn_interval(0), + _max_conn_interval(0), + _adv_critical_local_name(""), + _wait_for_connect_peripheral_adv_rssi(0), + _available_for_connect_peripheral_adv_rssi(0), + _connecting(false), + _has_service_uuid(false), + _has_service_solicit_uuid(false), + _appearance(0), + _manufacturer_data_length(0), + _service_data_length(0), + _adv_type(0), + _adv_data_idx(0), + _local_name(""), + _state(BLE_PERIPH_STATE_NOT_READY), + _local_ble(NULL), + _peer_peripheral_index(0) +{ + memset(&_local_bda, 0, sizeof(_local_bda)); + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); + + memset(&_service_uuid, 0, sizeof(_service_uuid)); + memset(&_service_solicit_uuid, 0, sizeof(_service_solicit_uuid)); + memset(&_service_data_uuid, 0, sizeof(_service_data_uuid)); + memset(_service_data, 0, sizeof(_service_data)); + memset(_service_data_buf, 0, sizeof(_service_data_buf)); + memset(_adv_data, 0, sizeof(_adv_data)); + + memset(&_peer_central, 0, sizeof (bt_addr_le_t)); + + ble_client_get_factory_config(&_local_bda, _device_name); + + _adv_param.type = BT_LE_ADV_IND; + _adv_param.addr_type = _local_bda.type; + _adv_param.interval_min = 0xA0; + _adv_param.interval_max = 0xF0; + + _scan_param.type = BT_HCI_LE_SCAN_ACTIVE; + _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; + _scan_param.interval = BT_GAP_SCAN_FAST_INTERVAL; + _scan_param.window = BT_GAP_SCAN_FAST_WINDOW; + + memset(_peer_adv_buffer, 0, sizeof(_peer_adv_buffer)); + memset(_peer_adv_mill, 0, sizeof(_peer_adv_mill)); + memset(&_adv_accept_critical, 0, sizeof(_adv_accept_critical)); + memset(&_adv_critical_service_uuid, 0, sizeof(_adv_critical_service_uuid)); + + memset(_peer_peripheral, 0, sizeof(_peer_peripheral)); + memset(_peer_peripheral_adv_data, 0, sizeof(_peer_peripheral_adv_data)); + memset(_peer_peripheral_adv_data_len, 0, sizeof(_peer_peripheral_adv_data_len)); + memset(_peer_peripheral_adv_rssi, 0, sizeof(_peer_peripheral_adv_rssi)); + + memset(_device_events, 0, sizeof(_device_events)); + memset(_manufacturer_data, 0, sizeof(_manufacturer_data)); + memset(_peer_adv_data, 0, sizeof(_peer_adv_data)); + memset(_peer_adv_data_len, 0, sizeof(_peer_adv_data_len)); + memset(_peer_adv_rssi, 0, sizeof(_peer_adv_rssi)); +} + +BLEDeviceManager::~BLEDeviceManager() +{ + +} + +bool BLEDeviceManager::begin(BLEDevice *device) +{ + if (NULL == _local_ble && false == *device) + { + _local_ble = device; + _local_ble->setAddress(_local_bda); + bt_le_set_mac_address(_local_bda); + // Set device name + setDeviceName(); + _state = BLE_PERIPH_STATE_READY; + delay(4); + // TODO: Olny allow call one time + ble_client_init (bleConnectEventHandler, this, + bleDisconnectEventHandler, this, + bleParamUpdatedEventHandler, this); + return true; + } + else + { + return false; + } +} + +void BLEDeviceManager::poll() +{ + if (NULL != _device_events[BLEDiscovered]) + { + BLEDevice tempdev = available(); + + while (tempdev) + { + _device_events[BLEDiscovered](tempdev); + tempdev = available(); + } + } +} + +void BLEDeviceManager::end() +{} + +bool BLEDeviceManager::connected(const BLEDevice *device) const +{ + bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + bool retval = false; + //pr_debug(LOG_MODULE_BLE, "%s-%d: add-%s", __FUNCTION__, __LINE__, device->address().c_str()); + if (NULL != conn) + { + //pr_debug(LOG_MODULE_BLE, "%s-%d: state-%d", __FUNCTION__, __LINE__,conn->state); + if (conn->state == BT_CONN_CONNECTED) + { + retval = true; + } + bt_conn_unref(conn); + } + return retval; +} + +bool BLEDeviceManager::disconnectSingle(const bt_addr_le_t *peer) +{ + int err = 0; + + bt_conn_t* conn = bt_conn_lookup_addr_le(peer); + if (NULL == conn) + { + return false; + } + + err = bt_conn_disconnect (conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + while (err == 0 && conn->state != BT_CONN_DISCONNECTED) + { + delay(10); + } + bt_conn_unref(conn); + return true; +} + + +bool BLEDeviceManager::disconnect(BLEDevice *device) +{ + bool ret = true; + if (false == BLEUtils::isLocalBLE(*device)) + { + // Remote device disconnect one + ret = disconnectSingle(device->bt_le_address()); + } + else + { + // Local device disconnect all connections + if (true == BLEUtils::macAddressValid(_peer_central)) + { + ret = disconnectSingle(&_peer_central); + } + + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if (true == BLEUtils::macAddressValid(_peer_peripheral[i])) + { + ret = disconnectSingle(&_peer_central); + } + } + } + return ret; +} + +void BLEDeviceManager::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + _has_service_uuid = true; + BLEUtils::uuidString2BT(advertisedServiceUuid, (bt_uuid_t *)&_service_uuid); +} + +void BLEDeviceManager::setAdvertisedServiceData(const bt_uuid_t* serviceDataUuid, + const uint8_t* serviceData, + uint8_t serviceDataLength) +{ + memcpy(&_service_data_uuid, serviceDataUuid, sizeof(_service_data_uuid)); + if (serviceDataLength > BLE_MAX_ADV_SIZE) + { + serviceDataLength = BLE_MAX_ADV_SIZE; + } + + memcpy(_service_data, serviceData, serviceDataLength); + _service_data_length = serviceDataLength; +} + +void BLEDeviceManager::setServiceSolicitationUuid(const char* serviceSolicitationUuid) +{ + _has_service_solicit_uuid = true; + BLEUtils::uuidString2BT(serviceSolicitationUuid, (bt_uuid_t *)&_service_solicit_uuid); +} + +void BLEDeviceManager::setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength) +{ + if (manufacturerDataLength > BLE_MAX_ADV_SIZE) + { + manufacturerDataLength = BLE_MAX_ADV_SIZE; + } + _manufacturer_data_length = manufacturerDataLength; + memcpy(_manufacturer_data, manufacturerData, manufacturerDataLength); +} + +void BLEDeviceManager::setLocalName(const char *localName) +{ + _local_name = localName; +} + +void BLEDeviceManager::setAdvertisingInterval(float advertisingInterval) +{ + uint16_t interval = (uint16_t) MSEC_TO_UNITS(advertisingInterval, UNIT_0_625_MS); + + _adv_param.interval_min = interval; + _adv_param.interval_max = interval; +} + +void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout) +{ +} + +void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval) +{ + +} + +bool BLEDeviceManager::setTxPower(int txPower) +{ + ble_gap_set_tx_power(txPower); + return true; +} + +void BLEDeviceManager::setConnectable(bool connectable) +{ + uint8_t type = BT_LE_ADV_IND; + if (connectable == false) + { + type = BT_LE_ADV_NONCONN_IND; + } + _adv_param.type = type; +} + +void BLEDeviceManager::setDeviceName(const char* deviceName) +{ + memset(_device_name, 0, sizeof(_device_name)); + if (deviceName && deviceName[0]) + { + int len = strlen(deviceName); + if (len > BLE_MAX_DEVICE_NAME) + len = BLE_MAX_DEVICE_NAME; + memcpy(_device_name, deviceName, len); + setDeviceName(); + } +} + +void +BLEDeviceManager::setDeviceName() +{ + int len = strlen(_device_name); + bt_le_set_device_name(_device_name, len); +} + +void BLEDeviceManager::setAppearance(unsigned short appearance) +{ + _appearance = appearance; +} + +BLE_STATUS_T +BLEDeviceManager::_advDataInit(void) +{ + uint8_t lengthTotal = 2; // Flags data length + _adv_data_idx = 0; + + /* Add flags */ + _adv_type = (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR); + _adv_data[_adv_data_idx].type = BT_DATA_FLAGS; + _adv_data[_adv_data_idx].data = &_adv_type; + _adv_data[_adv_data_idx].data_len = 1; + _adv_data_idx++; + + if (_has_service_uuid) + { + uint8_t type; + uint8_t length; + uint8_t *data = NULL; + + pr_info(LOG_MODULE_BLE, "ADV Type-%d", _service_uuid.uuid.type); + if (BT_UUID_TYPE_16 == _service_uuid.uuid.type) + { + //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_uuid)->val); + length = UUID_SIZE_16; + type = BT_DATA_UUID16_ALL; + } + else // Sid. KW, default is BT_UUID_TYPE_128 + { + data = _service_uuid.val; + length = UUID_SIZE_128; + type = BT_DATA_UUID128_ALL; + } + + // if (data) // Sid. KW, data is always initialized + { + _adv_data[_adv_data_idx].type = type; + _adv_data[_adv_data_idx].data = data; + _adv_data[_adv_data_idx].data_len = length; + _adv_data_idx++; + lengthTotal += length; + + pr_info(LOG_MODULE_BLE, "Service UUID Len -%d", length); + } + } + + if (_has_service_solicit_uuid) + { + uint8_t type; + uint8_t length; + uint8_t *data = NULL; + + pr_info(LOG_MODULE_BLE, "ADV Type-%d", _service_solicit_uuid.uuid.type); + if (BT_UUID_TYPE_16 == _service_solicit_uuid.uuid.type) + { + //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_solicit_uuid)->val); + length = UUID_SIZE_16; + type = BT_DATA_SOLICIT16; + } + else // Sid. KW, default is BT_UUID_TYPE_128 + { + data = _service_solicit_uuid.val; + length = UUID_SIZE_128; + type = BT_DATA_SOLICIT128; + } + // Sid. KW, data is always initialized. if (data) + { + _adv_data[_adv_data_idx].type = type; + _adv_data[_adv_data_idx].data = data; + _adv_data[_adv_data_idx].data_len = length; + _adv_data_idx++; + lengthTotal += length; + + pr_info(LOG_MODULE_BLE, "Service UUID Len -%d", length); + } + } + + if (_local_name.length() > 0) + { + /* Add device name (truncated if too long) */ + _adv_data[_adv_data_idx].type = BT_DATA_NAME_COMPLETE; + _adv_data[_adv_data_idx].data = (const uint8_t*)_local_name.c_str(); + _adv_data[_adv_data_idx].data_len = _local_name.length(); + _adv_data_idx++; + + lengthTotal += _local_name.length(); + pr_info(LOG_MODULE_BLE, "Local Name -%s", _local_name.c_str()); + pr_info(LOG_MODULE_BLE, "Local Name Len -%d", _local_name.length()); + } + + if (_manufacturer_data_length > 0) + { + // Add manufacturer data + _adv_data[_adv_data_idx].type = BT_DATA_MANUFACTURER_DATA; + _adv_data[_adv_data_idx].data = _manufacturer_data; + _adv_data[_adv_data_idx].data_len = _manufacturer_data_length; + _adv_data_idx++; + + lengthTotal += _manufacturer_data_length; + } + + if (_service_data_length > 0) + { + /* Add Service Data (if it will fit) */ + + /* A 128-bit Service Data UUID won't fit in an Advertising packet */ + if (BT_UUID_TYPE_16 != _service_data_uuid.uuid.type) + { + /* We support service data only for 16-bit service UUID */ + return BLE_STATUS_NOT_SUPPORTED; + } + + uint8_t block_len = sizeof(uint16_t) + _service_data_length; + if (1 + block_len > BLE_MAX_ADV_SIZE) + { + // Service data block is too large. + return BLE_STATUS_ERROR_PARAMETER; + } + + _adv_data[_adv_data_idx].type = BT_DATA_SVC_DATA16; + _adv_data[_adv_data_idx].data = _service_data_buf; + _adv_data[_adv_data_idx].data_len = block_len; + _adv_data_idx++; + + uint8_t *adv_tmp = _service_data_buf; + + //UINT16_TO_LESTREAM(adv_tmp, (((bt_uuid_16_t *)&_service_data_uuid)->val)); + memcpy(adv_tmp, &((bt_uuid_16_t*)&_service_data_uuid)->val, sizeof(uint16_t)); + adv_tmp += 2; + memcpy(adv_tmp, _service_data, _service_data_length); + + lengthTotal += block_len; + pr_info(LOG_MODULE_BLE, "SVC Len -%d", block_len); + } + + if (lengthTotal > BLE_MAX_ADV_SIZE) + { + pr_error(LOG_MODULE_BLE, "ADV Total length-%d", lengthTotal); + // Service data block is too large. + return BLE_STATUS_ERROR_PARAMETER; + } + return BLE_STATUS_SUCCESS; +} + +BLE_STATUS_T BLEDeviceManager::startAdvertising() +{ + int ret; + BLE_STATUS_T status; + status = _advDataInit(); + if (BLE_STATUS_SUCCESS != status) + { + return status; + } + + pr_info(LOG_MODULE_BLE, "%s-ad_len%d", __FUNCTION__, _adv_data_idx); + if (_state != BLE_PERIPH_STATE_READY) + return BLE_STATUS_WRONG_STATE; + + ret = bt_le_adv_start(&_adv_param, _adv_data, _adv_data_idx, NULL, 0); + if (0 != ret) + { + pr_error(LOG_MODULE_APP, "[ADV] Start failed. Error: %d", ret); + return BLE_STATUS_WRONG_STATE; + } + delay(10); + _state = BLE_PERIPH_STATE_ADVERTISING; + return BLE_STATUS_SUCCESS; +} + +bool BLEDeviceManager::advertising() +{ + return (BLE_PERIPH_STATE_ADVERTISING == _state); +} + +BLE_STATUS_T BLEDeviceManager::stopAdvertising() +{ + int err_code = 0; + BLE_STATUS_T status = BLE_STATUS_WRONG_STATE; + + if (BLE_PERIPH_STATE_ADVERTISING == _state) + { + err_code = bt_le_adv_stop(); + status = errorno_to_ble_status(err_code); + } + + if (BLE_STATUS_SUCCESS != status) + return status; + + _state = BLE_PERIPH_STATE_READY; + return BLE_STATUS_SUCCESS; +} + +BLEDevice BLEDeviceManager::central() +{ + BLEDevice temp(&_peer_central); + return temp; +} + +BLEDevice BLEDeviceManager::peripheral() +{ + BLEDevice temp; + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if (_peer_peripheral_index >= BLE_MAX_CONN_CFG) + { + _peer_peripheral_index = 0; + } + const bt_addr_le_t & addr = _peer_peripheral[_peer_peripheral_index]; + _peer_peripheral_index++; + + if (true == BLEUtils::macAddressValid(addr)) + { + temp.setAddress(addr); + break; + } + } + return temp; +} + +bool BLEDeviceManager::startScanning() +{ + _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE;//BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + int err = bt_le_scan_start(&_scan_param, ble_central_device_found); + if (err) + { + pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); + return false; + } + return true; +} + +bool BLEDeviceManager::startScanningWithDuplicates() +{ + _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; + int err = bt_le_scan_start(&_scan_param, ble_central_device_found); + if (err) + { + pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); + return false; + } + return false; +} + +bool BLEDeviceManager::stopScanning() +{ + int err = bt_le_scan_stop(); + + if (err) // Sid. TODO: KW detected bt_le_scan_stop return only 0. + { + pr_info(LOG_MODULE_BLE, "Stop LE scan failed (err %d)\n", err); + return false; + } + return true; +} + +void BLEDeviceManager::clearAdvertiseCritical() +{ + memset(&_adv_accept_critical, 0, sizeof(_adv_accept_critical)); + //memset(&_adv_critical_service_uuid, 0, sizeof(_adv_critical_service_uuid)); +} + +void BLEDeviceManager::setAdvertiseCritical(String name) +{ + _adv_critical_local_name = name; + _adv_accept_critical.type = BT_DATA_NAME_COMPLETE; + _adv_accept_critical.data_len = name.length(); + _adv_accept_critical.data = (const uint8_t*)_adv_critical_local_name.c_str(); +} + +void BLEDeviceManager::setAdvertiseCritical(BLEService& service) +{ + BLEUtils::uuidString2BT(service.uuid(),(bt_uuid_t *)&_adv_critical_service_uuid); + uint8_t type = 0; + uint8_t length = 0; + uint8_t *data = NULL; + + pr_info(LOG_MODULE_BLE, "ADV Type-%d", _adv_critical_service_uuid.uuid.type); + if (BT_UUID_TYPE_16 == _adv_critical_service_uuid.uuid.type) + { + data = (uint8_t *)&(((bt_uuid_16_t *)&_adv_critical_service_uuid)->val); + length = UUID_SIZE_16; + type = BT_DATA_UUID16_ALL; + } + else // Sid. KW, default is BT_UUID_TYPE_128 + { + data = _adv_critical_service_uuid.val; + length = UUID_SIZE_128; + type = BT_DATA_UUID128_ALL; + } + _adv_accept_critical.type = type; + _adv_accept_critical.data_len = length; + _adv_accept_critical.data = data; +} + +bool BLEDeviceManager::hasLocalName(const BLEDevice* device) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return (_local_name.length() != 0); + } + + const uint8_t* adv_data = NULL; + uint8_t adv_data_len = 0; + getDeviceAdvertiseBuffer(device->bt_le_address(), + adv_data, + adv_data_len); + if (NULL == adv_data) + { + return false; + } + + while (adv_data_len > 1) + { + uint8_t len = adv_data[0]; + uint8_t type = adv_data[1]; + + /* Check for early termination */ + if (len == 0) + { + return false; + } + + if ((len + 1) > adv_data_len) { // Sid. KW, can't be (adv_data_len < 2) + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return false; + } + + if (type == BT_DATA_NAME_COMPLETE) + { + return true; + } + + adv_data_len -= len + 1; + adv_data += len + 1; + } + return false; +} + +bool BLEDeviceManager::hasAdvertisedServiceUuid(const BLEDevice* device) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return _has_service_uuid; + } + + uint8_t service_cnt = advertisedServiceUuidCount(device); + return (service_cnt > 0); +} + +bool BLEDeviceManager::hasAdvertisedServiceUuid(const BLEDevice* device, int index) const +{ + uint8_t service_cnt = advertisedServiceUuidCount(device); + return (service_cnt > index); +} + +void BLEDeviceManager::getDeviceAdvertiseBuffer(const bt_addr_le_t* addr, + const uint8_t* &adv_data, + uint8_t &adv_len) const +{ + const bt_addr_le_t* temp = NULL; + // Connected device + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + temp = &_peer_peripheral[i]; + if (bt_addr_le_cmp(temp, addr) == 0) + { + adv_data = _peer_peripheral_adv_data[i]; + adv_len = _peer_peripheral_adv_data_len[i]; + return; + } + } + + // Connecting device + if (bt_addr_le_cmp(&_wait_for_connect_peripheral, addr) == 0) + { + adv_data = _wait_for_connect_peripheral_adv_data; + adv_len = _wait_for_connect_peripheral_adv_data_len; + return; + } + + // Available device + if (bt_addr_le_cmp(&_available_for_connect_peripheral, addr) == 0) + { + adv_data = _available_for_connect_peripheral_adv_data; + adv_len = _available_for_connect_peripheral_adv_data_len; + return; + } + return; +} + +int BLEDeviceManager::advertisedServiceUuidCount(const BLEDevice* device) const +{ + const uint8_t* adv_data = NULL; + uint8_t adv_data_len = 0; + uint8_t service_cnt = 0; + + if (BLEUtils::isLocalBLE(*device) == true) + { + if (_has_service_uuid) + service_cnt++; + return service_cnt; + } + + getDeviceAdvertiseBuffer(device->bt_le_address(), + adv_data, + adv_data_len); + if (NULL == adv_data) + { + return service_cnt; + } + + while (adv_data_len > 1) + { + uint8_t len = adv_data[0]; + uint8_t type = adv_data[1]; + + /* Check for early termination */ + if (len == 0) + { + return service_cnt; + } + + if ((len + 1) > adv_data_len) { // Sid. KW, can't be (adv_data_len < 2) + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return service_cnt; + } + + if (type == BT_DATA_UUID16_ALL || + type == BT_DATA_UUID128_ALL) + { + service_cnt++; + } + + adv_data_len -= len + 1; + adv_data += len + 1; + } + return service_cnt; +} + +String BLEDeviceManager::localName(const BLEDevice* device) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return _local_name; + } + const uint8_t* adv_data = NULL; + uint8_t adv_data_len = 0; + char localname_string[BLE_MAX_ADV_SIZE]; + memset(localname_string, 0, sizeof(localname_string)); + + getDeviceAdvertiseBuffer(device->bt_le_address(), + adv_data, + adv_data_len); + + if (NULL == adv_data) { + String temp(localname_string); + return temp; + } + + while (adv_data_len > 1) + { + uint8_t len = adv_data[0]; + uint8_t type = adv_data[1]; + + /* Check for early termination */ + if (len == 0) { + String temp(localname_string); + return temp; + } + + if ((len + 1) > adv_data_len) { // Sid. KW, cannot be (adv_data_len < 2) + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + String temp(localname_string); + return temp; + } + + if (type == BT_DATA_NAME_COMPLETE) + { + if (len >= BLE_MAX_ADV_SIZE) + { + len = BLE_MAX_ADV_SIZE-1; + } + uint8_t copy_len = len - 1; + memcpy(localname_string, &adv_data[2], copy_len); + localname_string[copy_len] = '\0'; + break; + } + + adv_data_len -= len + 1; + adv_data += len + 1; + } + + String temp(localname_string); + return temp; +} + +String BLEDeviceManager::advertisedServiceUuid(const BLEDevice* device) const +{ + return advertisedServiceUuid(device, 0); +} + +String BLEDeviceManager::advertisedServiceUuid(const BLEDevice* device, int index) const +{ + const uint8_t* adv_data = NULL; + uint8_t adv_data_len = 0; + uint8_t service_cnt = 0; + bt_uuid_128_t service_uuid; + char uuid_string[37]; + + memset(uuid_string, 0, sizeof(uuid_string)); + + if (BLEUtils::isLocalBLE(*device) == true) + { + // Local device only support advertise 1 service now. + if (_has_service_uuid && index == 0) + { + BLEUtils::uuidBT2String(&_service_uuid.uuid, uuid_string); + } + return String(uuid_string); + } + + getDeviceAdvertiseBuffer(device->bt_le_address(), + adv_data, + adv_data_len); + + if ((uint8_t *)NULL == adv_data) + { + return String(uuid_string); + } + + while (adv_data_len > 1) + { + uint8_t len = adv_data[0]; + uint8_t type = adv_data[1]; + + /* Check for early termination */ + if (len == 0) + { + return String(uuid_string); + } + + if ((len + 1) > adv_data_len) { // Sid. KW, cannot be adv_data_len < 2 + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return String(uuid_string); + } + + if (type == BT_DATA_UUID16_ALL || + type == BT_DATA_UUID128_ALL) + { + service_cnt++; + } + + if (index < service_cnt) + { + if (type == BT_DATA_UUID16_ALL) + { + service_uuid.uuid.type = BT_UUID_TYPE_16; + memcpy(&BT_UUID_16(&service_uuid.uuid)->val, &adv_data[2], 2); + } + else + { + service_uuid.uuid.type = BT_UUID_TYPE_128; + memcpy(service_uuid.val, &adv_data[2], 16); + } + + BLEUtils::uuidBT2String(&service_uuid.uuid, uuid_string); + + break; + } + + adv_data_len -= len + 1; + adv_data += len + 1; + } + return String(uuid_string); +} + +int BLEDeviceManager::rssi(const BLEDevice* device) const +{ + const bt_addr_le_t* temp = NULL; + const bt_addr_le_t* addr = device->bt_le_address(); + // Connected device + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + temp = &_peer_peripheral[i]; + if (bt_addr_le_cmp(temp, addr) == 0) + { + return _peer_peripheral_adv_rssi[i]; + } + } + + // Connecting device + if (bt_addr_le_cmp(&_wait_for_connect_peripheral, addr) == 0) + { + return _wait_for_connect_peripheral_adv_rssi; + } + + // Available device + if (bt_addr_le_cmp(&_available_for_connect_peripheral, addr) == 0) + { + return _available_for_connect_peripheral_adv_rssi; + } + return 0; +} + +bool BLEDeviceManager::connect(BLEDevice &device) +{ + // + uint64_t timestamp = millis(); + uint64_t timestampcur = timestamp; + bool ret = true; + bt_addr_le_copy(&_wait_for_connect_peripheral, device.bt_le_address()); + // Buffer the ADV data + memcpy(_wait_for_connect_peripheral_adv_data, _available_for_connect_peripheral_adv_data, BLE_MAX_ADV_SIZE); + _wait_for_connect_peripheral_adv_data_len = _available_for_connect_peripheral_adv_data_len; + _wait_for_connect_peripheral_adv_rssi = _available_for_connect_peripheral_adv_rssi; + + startScanning(); + + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + // Wait for the connection + while (ret && (true == BLEUtils::macAddressValid(_wait_for_connect_peripheral))) + { + timestampcur = millis(); + // TODO: dismiss the magic number + ret = (timestampcur - timestamp < 3000); // Time out + } + + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + if (ret == false) + { + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); + if (_connecting == true) + { + ret = true; + while (_connecting == true) + { + pr_info(LOG_MODULE_BLE, "%s-%d: Connect request sent, wait for response", __FUNCTION__, __LINE__); + delay(2000); + } + } + else + { + stopScanning(); + } + } + return ret; +} + +bool BLEDeviceManager::connectToDevice(BLEDevice &device) +{ + bt_addr_le_t* temp = NULL; + bt_addr_le_t* unused = NULL; + bool link_existed = false; + bool retval = false; + + pr_debug(LOG_MODULE_BLE, "%s-%d-1", __FUNCTION__, __LINE__); + + // Find free peripheral Items + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + temp = &_peer_peripheral[i]; + if (true == BLEUtils::macAddressValid(*temp)) + { + if (bt_addr_le_cmp(temp, device.bt_le_address()) == 0) + { + // Connect request has scheduled but connection don't established. + // The central can see the ADV and no need to send connect request. + link_existed = true; + break; + } + } + else + { + if (NULL == unused) + { + unused = temp; + // Buffer the ADV data + memcpy(_peer_peripheral_adv_data[i], + _wait_for_connect_peripheral_adv_data, + BLE_MAX_ADV_SIZE); + _peer_peripheral_adv_data_len[i] = _wait_for_connect_peripheral_adv_data_len; + _peer_peripheral_adv_rssi[i] = _wait_for_connect_peripheral_adv_rssi; + } + } + } + pr_debug(LOG_MODULE_BLE, "%s-%d:link_existed-%d unused-%p", __FUNCTION__, __LINE__, link_existed, unused); + + if (!link_existed && NULL != unused) + { + pr_debug(LOG_MODULE_BLE, "%s-%d-Device:%s", __FUNCTION__, __LINE__, device.address().c_str()); + // Send connect request + bt_conn_t* conn = bt_conn_create_le(device.bt_le_address(), device.bt_conn_param()); + if (NULL != conn) + { + memcpy(unused, device.bt_le_address(), sizeof(bt_addr_le_t)); + retval = true; + _connecting = true; + bt_conn_unref(conn); + } + } + return retval; +} + +String BLEDeviceManager::deviceName(const BLEDevice* device) +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return _device_name; + } + return String(""); +} + +int BLEDeviceManager::appearance() +{ + return _appearance; +} + +BLEDeviceManager* BLEDeviceManager::instance() +{ + if (_instance == NULL) + { + _instance = new BLEDeviceManager(); + BLE_LIB_ASSERT(_instance != NULL); + } + return _instance; +} + +void BLEDeviceManager::setEventHandler(BLEDeviceEvent event, + BLEDeviceEventHandler eventHandler) +{ + if (event < BLEDeviceLastEvent) + _device_events[event] = eventHandler; +} + +void BLEDeviceManager::handleConnectEvent(bt_conn_t *conn, uint8_t err) +{ + struct bt_conn_info role_info; + bt_conn_get_info(conn, &role_info); + pr_info(LOG_MODULE_BLE, "%s-%d: role-%d", __FUNCTION__, __LINE__, role_info.role); + if (BT_CONN_ROLE_SLAVE == role_info.role) + { + // Central has established the connection with this peripheral device + memcpy(&_peer_central, bt_conn_get_dst(conn), sizeof (bt_addr_le_t)); + } + else + { + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); + _connecting = false; + // Peripheral has established the connection with this Central device + BLEProfileManager::instance()->handleConnectedEvent(bt_conn_get_dst(conn)); + } + + if (NULL != _device_events[BLEConnected]) + { + BLEDevice tempdev(bt_conn_get_dst(conn)); + _device_events[BLEConnected](tempdev); + } +} + +void BLEDeviceManager::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) +{ + struct bt_conn_info role_info; + bt_conn_get_info(conn, &role_info); + pr_info(LOG_MODULE_BLE, "%s-%d: role-%d", __FUNCTION__, __LINE__, role_info.role); + if (BT_CONN_ROLE_SLAVE == role_info.role) + { + // Central has established the connection with this peripheral device + memset(&_peer_central, 0, sizeof (bt_addr_le_t)); + } + else + { + bt_addr_le_t* temp = NULL; + const bt_addr_le_t* disConnAddr = bt_conn_get_dst(conn); + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + temp = &_peer_peripheral[i]; + if (bt_addr_le_cmp(temp, disConnAddr) == 0) + { + memset(temp, 0, sizeof(bt_addr_le_t)); + memset(_peer_peripheral_adv_data[i], 0, BLE_MAX_ADV_SIZE); + _peer_peripheral_adv_data_len[i] = 0; + _peer_peripheral_adv_rssi[i] = 0; + break; + } + } + // Peripheral has established the connection with this Central device + BLEProfileManager::instance()->handleDisconnectedEvent(bt_conn_get_dst(conn)); + } + + if (NULL != _device_events[BLEDisconnected]) + { + BLEDevice tempdev(bt_conn_get_dst(conn)); + _device_events[BLEDisconnected](tempdev); + } +} + +void BLEDeviceManager::handleParamUpdated (bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout) +{ + if (NULL != _device_events[BLEConParamUpdate]) + { + BLEDevice tempdev(bt_conn_get_dst(conn)); + _device_events[BLEConParamUpdate](tempdev); + } +} + +bool BLEDeviceManager::advertiseDataProc(uint8_t type, + const uint8_t *dataPtr, + uint8_t data_len) +{ + //Serial1.print("[AD]:"); + //Serial1.print(type); + //Serial1.print(" data_len "); + //Serial1.println(data_len); + + //const bt_data_t zero = {0, 0,0}; + if (_adv_accept_critical.type == 0 && + _adv_accept_critical.data_len == 0 && + _adv_accept_critical.data == NULL) + { + // Not set the critical. Accept all. + return true; + } + if (type == _adv_accept_critical.type && + data_len == _adv_accept_critical.data_len && + 0 == memcmp(dataPtr, _adv_accept_critical.data, data_len)) + { + // Now Only support 1 critical. Change those code if want support multi-criticals + return true; + } + + return false; +} + +BLEDevice BLEDeviceManager::available() +{ + BLEDevice tempdevice; + bt_addr_le_t* temp = NULL; + uint64_t timestamp = millis(); + uint8_t index = BLE_MAX_ADV_BUFFER_CFG; + uint8_t i = 0; + uint64_t max_delta = 0; + + for (i = 0; i < BLE_MAX_ADV_BUFFER_CFG; i++) + { + uint64_t timestamp_delta = timestamp - _peer_adv_mill[i]; + temp = &_peer_adv_buffer[i]; + if ((timestamp_delta <= 2000) && (max_delta < timestamp_delta)) + { + max_delta = timestamp_delta; + index = i; + } + } + //pr_debug(LOG_MODULE_BLE, "%s-%d:index %d, i-%d", __FUNCTION__, __LINE__, index, i); + + if (index < BLE_MAX_ADV_BUFFER_CFG) + { + temp = &_peer_adv_buffer[index]; + if (true == BLEUtils::macAddressValid(*temp)) + { + tempdevice.setAddress(*temp); + bt_addr_le_copy(&_available_for_connect_peripheral, temp); + memcpy(_available_for_connect_peripheral_adv_data, _peer_adv_data[index], BLE_MAX_ADV_SIZE); + _available_for_connect_peripheral_adv_data_len = _peer_adv_data_len[index]; + _available_for_connect_peripheral_adv_rssi = _peer_adv_rssi[index]; + + pr_debug(LOG_MODULE_BLE, "%s-%d:Con addr-%s", __FUNCTION__, __LINE__, BLEUtils::macAddressBT2String(*temp).c_str()); + _peer_adv_mill[index] -= 2000; // Set it as expired + } + } + return tempdevice; +} + +bool BLEDeviceManager::setAdvertiseBuffer(const bt_addr_le_t* bt_addr, + const uint8_t *ad, + uint8_t data_len, + int8_t rssi) +{ + bt_addr_le_t* temp = NULL; + uint64_t timestamp = millis(); + uint8_t index = BLE_MAX_ADV_BUFFER_CFG; + uint8_t i = 0; + uint64_t max_delta = 0; + bool retval = false; + //pr_debug(LOG_MODULE_BLE, "%s-%d-1", __FUNCTION__, __LINE__); + for (i = 0; i < BLE_MAX_ADV_BUFFER_CFG; i++) + { + uint64_t timestamp_delta = timestamp - _peer_adv_mill[i]; + temp = &_peer_adv_buffer[i]; + if (max_delta < timestamp_delta) + { + max_delta = timestamp_delta; + if (max_delta > 2000) // expired + index = i; + } + + if (bt_addr_le_cmp(temp, bt_addr) == 0) + { + // The device alread in the buffer + index = i; + break; + } + } + //pr_debug(LOG_MODULE_BLE, "%s-%d:index %d, i-%d", __FUNCTION__, __LINE__, index, i); + + //pr_debug(LOG_MODULE_BLE, "%s-%d-2", __FUNCTION__, __LINE__); + if (index < BLE_MAX_ADV_BUFFER_CFG) + { + temp = &_peer_adv_buffer[index]; + if (i >= BLE_MAX_ADV_BUFFER_CFG) + { + memcpy(temp, bt_addr, sizeof (bt_addr_le_t)); + } + if (data_len > BLE_MAX_ADV_SIZE) + { + data_len = BLE_MAX_ADV_SIZE; + } + memcpy(_peer_adv_data[index], ad, data_len); + _peer_adv_data_len[index] = data_len; + _peer_adv_rssi[index] = rssi; + // Update the timestamp + _peer_adv_mill[index] = timestamp; + retval = true; + } + + return retval; +} + +void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len) +{ + const uint8_t *data = ad; + uint8_t real_adv_len = data_len; + + /* We're only interested in connectable events */ + if (type == BT_LE_ADV_IND || type == BT_LE_ADV_DIRECT_IND) + { + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + while (data_len > 1) + { + uint8_t len = data[0]; + + /* Check for early termination */ + if (len == 0) + { + return; + } + + if ((len + 1) > data_len) { // Sid. KW, cannot be (data_len < 2) + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return; + } + + if (true == advertiseDataProc(data[1], &data[2], len - 1)) + { + if (true == BLEUtils::macAddressValid(_wait_for_connect_peripheral)) + { + // Not add to the buffer when try to establish the connection + if (true == BLEUtils::macAddressSame(*addr, _wait_for_connect_peripheral)) + { + BLEDevice testdev(addr); + stopScanning(); + connectToDevice(testdev); + } + } + else + { + // The critical is accepted + // Find the oldest and expired buffer + if(false == setAdvertiseBuffer(addr, ad, real_adv_len, rssi)) + { + pr_info(LOG_MODULE_BLE, "No buffer to store the ADV\n"); + } + } + pr_debug(LOG_MODULE_BLE, "%s-%d: Done", __FUNCTION__, __LINE__); + return; + } + + data_len -= len + 1; + data += len + 1; + } + //pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); + } + +} + + + diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h new file mode 100644 index 00000000..3ad80a41 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -0,0 +1,437 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. 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 ARDUINO_BLE_DEVICE_MANAGER_H +#define ARDUINO_BLE_DEVICE_MANAGER_H + +#include + +class BLEDeviceManager +{ + public: + /** + * @brief The BLE device constructure + * + * @param bleaddress BLE device address + * + * @return none + * + * @note none + */ + BLEDeviceManager(); + + virtual ~BLEDeviceManager(); + + + /** + * @brief Initiliaze the BLE hardware + * + * @return bool indicating success or error + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + bool begin(BLEDevice *device); + + /** + * @brief Poll for events + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void poll(); // Do we need add the return value or + // input parameter to get the events? + // Events may inlcue: + // GAP : Connected, Disconnected, Update connetion parameter + // GATT: Discovered + + /** + * @brief Deinitiliaze the BLE hardware + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void end(); + + /** + * @brief Is the device connected with another BLE device. + * + * @param none + * + * @return none + * + * @note none + */ + bool connected(const BLEDevice *device) const; + + /** + * @brief Disconnect the connected device/s. + * + * @param none + * + * @return none + * + * @note The BLE may connected multiple devices. + * This call will disconnect all conected devices. + */ + bool disconnect(BLEDevice *device); + + void setEventHandler(BLEDeviceEvent event, + BLEDeviceEventHandler eventHandler); + /** + * @brief Set the service UUID that the BLE Peripheral Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + + /** + * @brief Set the service UUID that is solicited in the BLE Peripheral + * Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setServiceSolicitationUuid(const char* serviceSolicitationUuid); + void setAdvertisedServiceData(const bt_uuid_t* serviceDataUuid, + const uint8_t* serviceData, + uint8_t serviceDataLength); + + /** + * @brief Set the manufacturer data in the BLE Peripheral Device advertises + * + * @param[in] manufacturerData The data about manufacturer will + * be set in advertisement + * @param[in] manufacturerDataLength The length of the manufacturer data + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength); + + /** + * Set the local name that the BLE Peripheral Device advertises + * + * @param[in] localName local name to advertise + * + * @note This method must be called before the begin method + */ + void setLocalName(const char *localName); + + /** + * @brief Set advertising interval + * + * @param[in] advertisingInterval Advertising Interval in ms + * + * @return none + * + * @note none + */ + void setAdvertisingInterval(float advertisingInterval); + + /** + * @brief Set the connection parameters and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @param[in] latency Connection Latency + * + * @param[in] timeout Supervision Timeout (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout); + + /** + * @brief Set the min and max connection interval and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval); + + /** + * @brief Set TX power of the radio in dBM + * + * @param[in] tx_power The antenna TX power + * + * @return boolean_t true if established connection, otherwise false + */ + bool setTxPower(int txPower); + + /** + * @brief Set advertising type as connectable/non-connectable + * + * @param[in] connectable true - The device connectable + * false - The device non-connectable + * + * @return none + * + * @note Only for peripheral mode. + * Default value is connectable + */ + void setConnectable(bool connectable); + + /** + * @brief Set the value of the device name characteristic + * + * @param[in] device User-defined name string for this device. Truncated if + * more than maximum allowed string length (20 bytes). + * + * @note This method must be called before the begin method + * If device name is not set, a default name will be used + */ + void setDeviceName(const char* deviceName); + void setDeviceName(); + /** + * @brief Set the appearance type for the BLE Peripheral Device + * + * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml + * for available options. + * + * @param[in] appearance Appearance category identifier as defined by BLE Standard + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void setAppearance(unsigned short appearance); + + /** + * @brief Add a Service to the BLE Peripheral Device + * + * @param[in] attribute The service that will add to Peripheral + * + * @return BLE_STATUS_T Indicating success or error type + * + * @note This method must be called before the begin method + */ + BLE_STATUS_T addService(BLEService& attribute); + + /** + * @brief Construct the ADV data and start send advertisement + * + * @param none + * + * @return BLE_STATUS_T 0 - Success. Others - error code + * + * @note none + */ + BLE_STATUS_T startAdvertising(); + + bool advertising(); + + /** + * @brief Stop send advertisement + * + * @param none + * + * @return none + * + * @note none + */ + BLE_STATUS_T stopAdvertising(); + + /** + * @brief Get currently connected central + * + * @return BLEDeviceManager Connected central device + * + * @note Peripheral mode only + */ + BLEDevice central(); + + /** + * @brief Get currently connected peripheral + * + * @param none + * + * @return none + * + * @note Central mode only. How to distinguish the peripheral? + */ + BLEDevice peripheral(); + + operator bool() const; + + // central mode + void clearAdvertiseCritical(); + void setAdvertiseCritical(String name); + void setAdvertiseCritical(BLEService& service); + bool startScanning(); // start scanning for peripherals + bool startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates + bool stopScanning(); // stop scanning for peripherals + + void setAcceptAdvertiseLocalName(String name); + void setAcceptAdvertiseLocalName(BLEService& service); + void setAcceptAdvertiseCallback(String name); + + BLEDevice available(); // retrieve a discovered peripheral + + bool hasLocalName(const BLEDevice* device) const; // does the peripheral advertise a local name + bool hasAdvertisedServiceUuid(const BLEDevice* device) const; // does the peripheral advertise a service + bool hasAdvertisedServiceUuid(const BLEDevice* device, int index) const; // does the peripheral advertise a service n + int advertisedServiceUuidCount(const BLEDevice* device) const; // number of services the peripheral is advertising + + String localName(const BLEDevice* device) const; // returns the advertised local name as a String + String advertisedServiceUuid(const BLEDevice* device) const; // returns the advertised service as a UUID String + String advertisedServiceUuid(const BLEDevice* device, int index) const; // returns the nth advertised service as a UUID String + + int rssi(const BLEDevice* device) const; // returns the RSSI of the peripheral at discovery + + bool connect(BLEDevice &device); // connect to the peripheral + bool connectToDevice(BLEDevice &device); + + String deviceName(const BLEDevice* device); // read the device name attribute of the peripheral, and return String value + int appearance(); // read the appearance attribute of the peripheral and return value as int + + static BLEDeviceManager* instance(); + + void handleConnectEvent(bt_conn_t *conn, uint8_t err); + void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); + void handleParamUpdated (bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout); + void handleDeviceFound(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len); + +protected: + +private: + BLE_STATUS_T _advDataInit(void); + bool advertiseDataProc(uint8_t type, + const uint8_t *dataPtr, + uint8_t data_len); + bool setAdvertiseBuffer(const bt_addr_le_t* bt_addr, + const uint8_t *ad, + uint8_t data_len, + int8_t rssi); + void getDeviceAdvertiseBuffer(const bt_addr_le_t* addr, + const uint8_t* &adv_data, + uint8_t &adv_len) const; + bool disconnectSingle(const bt_addr_le_t *peer); + +private: + uint16_t _min_conn_interval; + uint16_t _max_conn_interval; + bt_addr_le_t _local_bda; + char _device_name[BLE_MAX_DEVICE_NAME + 1]; + + // For Central + bt_le_scan_param_t _scan_param; // Scan parameter + bt_addr_le_t _peer_adv_buffer[BLE_MAX_ADV_BUFFER_CFG]; // Accepted peer device adress + uint64_t _peer_adv_mill[BLE_MAX_ADV_BUFFER_CFG]; // The ADV found time stamp + uint8_t _peer_adv_data[BLE_MAX_ADV_BUFFER_CFG][BLE_MAX_ADV_SIZE]; + uint8_t _peer_adv_data_len[BLE_MAX_ADV_BUFFER_CFG]; + int8_t _peer_adv_rssi[BLE_MAX_ADV_BUFFER_CFG]; + bt_data_t _adv_accept_critical; // The filters for central device + String _adv_critical_local_name; + bt_uuid_128_t _adv_critical_service_uuid; + + bt_addr_le_t _wait_for_connect_peripheral; + uint8_t _wait_for_connect_peripheral_adv_data[BLE_MAX_ADV_SIZE]; + uint8_t _wait_for_connect_peripheral_adv_data_len; + int8_t _wait_for_connect_peripheral_adv_rssi; + + bt_addr_le_t _available_for_connect_peripheral; + uint8_t _available_for_connect_peripheral_adv_data[BLE_MAX_ADV_SIZE]; + uint8_t _available_for_connect_peripheral_adv_data_len; + int8_t _available_for_connect_peripheral_adv_rssi; + volatile bool _connecting; + + // For peripheral + struct bt_le_adv_param _adv_param; + bool _has_service_uuid; + bt_uuid_128_t _service_uuid; + bool _has_service_solicit_uuid; + bt_uuid_128_t _service_solicit_uuid; + uint16_t _appearance; + uint8_t _manufacturer_data[BLE_MAX_ADV_SIZE]; + uint8_t _manufacturer_data_length; + bt_uuid_128_t _service_data_uuid; + uint8_t _service_data[BLE_MAX_ADV_SIZE]; + uint8_t _service_data_buf[BLE_MAX_ADV_SIZE]; + uint8_t _service_data_length; + + // ADV data for peripheral + uint8_t _adv_type; + bt_data_t _adv_data[6]; // KW: fount _advDataInit() can use 6 slots. + size_t _adv_data_idx; + + String _local_name; + // Peripheral states + enum BLEPeripheralState { + BLE_PERIPH_STATE_NOT_READY = 0, + BLE_PERIPH_STATE_READY, + BLE_PERIPH_STATE_ADVERTISING, + BLE_PERIPH_STATE_CONNECTED, + }; + + BLEPeripheralState _state; + + // Local + static BLEDeviceManager* _instance; + BLEDevice *_local_ble; + // Connected device object + bt_addr_le_t _peer_central; + bt_addr_le_t _peer_peripheral[BLE_MAX_CONN_CFG]; + uint8_t _peer_peripheral_index; + uint8_t _peer_peripheral_adv_data[BLE_MAX_CONN_CFG][BLE_MAX_ADV_SIZE]; + uint8_t _peer_peripheral_adv_data_len[BLE_MAX_CONN_CFG]; + uint8_t _peer_peripheral_adv_rssi[BLE_MAX_CONN_CFG]; + + BLEDeviceEventHandler _device_events[BLEDeviceLastEvent]; +}; + +#endif diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp new file mode 100644 index 00000000..c3b9cd58 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp @@ -0,0 +1,1076 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "BLECommon.h" +#include "BLEProfileManager.h" +#include "BLECharacteristicImp.h" + +#include "BLECallbacks.h" +#include "BLEUtils.h" + +BLEDevice BLE; + +BLEProfileManager* BLEProfileManager::_instance = NULL; + +BLEProfileManager* BLEProfileManager::instance() +{ + if (NULL == _instance) + { + _instance = new BLEProfileManager(); + BLE_LIB_ASSERT(_instance != NULL); + } + //pr_debug(LOG_MODULE_BLE, "%s-%d: %p", __FUNCTION__, __LINE__, _instance); + return _instance; +} + +BLEProfileManager::BLEProfileManager (): + _start_discover(false), + _discovering(false), + _discover_rsp_timestamp(0), + _cur_discover_service(NULL), + _discover_one_service(false), + _reading(false), + _attr_base(NULL), + _attr_index(0), + _profile_registered(false), + _disconnect_bitmap(0) +{ + //memset(_service_header_array, 0, sizeof(_service_header_array)); + memset(_discover_params, 0, sizeof(_discover_params)); + memset(_discover_uuid, 0, sizeof(_discover_uuid)); + + memset(_addresses, 0, sizeof(_addresses)); + memset(&_discovering_ble_addresses, 0, sizeof(_discovering_ble_addresses)); + memset(&_read_params, 0, sizeof(_read_params)); + memset(&_read_service_header, 0, sizeof(_read_service_header)); + bt_addr_le_copy(&_addresses[BLE_MAX_CONN_CFG], BLEUtils::bleGetLoalAddress()); + for (int i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + _service_header_array[i].next = NULL; + _service_header_array[i].value = NULL; + } + + pr_debug(LOG_MODULE_BLE, "%s-%d: Construct", __FUNCTION__, __LINE__); +} + +BLEProfileManager::~BLEProfileManager (void) +{ + if (_attr_base) + { + free(_attr_base); + _attr_base = (bt_gatt_attr_t *)NULL; + } + ServiceReadLinkNodePtr node = link_node_get_first(&_read_service_header); + while (NULL != node) + { + link_node_remove_first(&_read_service_header); + node = link_node_get_first(&_read_service_header); + } +} + +BLEServiceImp * +BLEProfileManager::addService (BLEDevice &bledevice, BLEService& service) +{ + + BLEServiceLinkNodeHeader* serviceheader = getServiceHeader(bledevice); + if (NULL == serviceheader) + { + int index = getUnusedIndex(); + if (index >= BLE_MAX_CONN_CFG) + { + return NULL; + } + serviceheader = &_service_header_array[index]; + bt_addr_le_copy(&_addresses[index], bledevice.bt_le_address()); + } + BLEServiceImp *serviceImp = NULL;//this->service(bledevice, service.uuid()); + //if (NULL != serviceImp) + //{ + // The service alreay exist + // return serviceImp; + //} + + //if (NULL == serviceImp) // May trigger KW warning + { + serviceImp = service.fetchOutLocalServiceImp(); + } + + if (NULL == serviceImp) + { + serviceImp = new BLEServiceImp(service); + if (NULL == serviceImp) + { + return serviceImp; + } + } + + BLEServiceNodePtr node = link_node_create(serviceImp); + if (NULL == node) + { + delete serviceImp; + return NULL; + } + link_node_insert_last(serviceheader, node); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return serviceImp; +} + +BLEServiceImp * +BLEProfileManager::addService (BLEDevice &bledevice, const bt_uuid_t* uuid) +{ + BLEService svc_obj(uuid); + return addService(bledevice, svc_obj); +} + +BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader(const BLEDevice &bledevice) +{ + int i; + for (i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + //if (true == BLEUtils::macAddressSame(*bledevice.bt_le_address(), _addresses[i])) + { + break; + } + } + if (i > BLE_MAX_CONN_CFG) + { + return NULL; + } + return &_service_header_array[i]; +} + +const BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader(const BLEDevice &bledevice) const +{ + int i; + for (i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + //if (true == BLEUtils::macAddressSame(*bledevice.bt_le_address(), _addresses[i])) + { + break; + } + } + if (i > BLE_MAX_CONN_CFG) + { + return NULL; + } + return &_service_header_array[i]; +} + +int BLEProfileManager::getUnusedIndex() +{ + int i; + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if (BLEUtils::macAddressValid(_addresses[i]) == false) + { + break; + } + } + + return i; +} + +int BLEProfileManager::getAttributeCount(BLEDevice &bledevice) +{ + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + + int attrCounter = 0; + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + attrCounter += service->getAttributeCount(); + node = node->next; + } + return attrCounter; +} + +int BLEProfileManager::characteristicCount(const BLEDevice &bledevice) const +{ + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + + int counter = 0; + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + counter += service->getCharacteristicCount(); + node = node->next; + } + return counter; +} + +int BLEProfileManager::serviceCount(const BLEDevice &bledevice) const +{ + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + return link_list_size(serviceHeader); +} + +int BLEProfileManager::registerProfile(BLEDevice &bledevice) +{ + int ret = 0; + + bt_gatt_attr_t *start; + BleStatus err_code = BLE_STATUS_SUCCESS; + + // The device is local BLE device. Register the service only allow local BLE device + BLEServiceLinkNodeHeader* serviceHeader = &_service_header_array[BLE_MAX_CONN_CFG]; + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[BLE_MAX_CONN_CFG]) != 0)) + { + return BLE_STATUS_FORBIDDEN; + } + + int attr_counter = getAttributeCount(bledevice); + if (0 == attr_counter) + { + return BLE_STATUS_NO_SERVICE; + } + + if (NULL == _attr_base) + { + _attr_base = (bt_gatt_attr_t *)malloc(attr_counter * sizeof(bt_gatt_attr_t)); + if (NULL == _attr_base) { + err_code = BLE_STATUS_NO_MEMORY; + } + else + { + memset((void *)_attr_base, 0x00, (attr_counter * sizeof(bt_gatt_attr_t))); + pr_info(LOG_MODULE_BLE, "_attr_base_-%p, size-%d, attr_counter-%d", _attr_base, sizeof(_attr_base), attr_counter); + } + } + + if (BLE_STATUS_SUCCESS != err_code) + { + if (NULL != _attr_base) + { + free(_attr_base); + } + return err_code; + } + + pr_info(LOG_MODULE_BLE, "_attr_base_-%p", _attr_base); + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + start = _attr_base + _attr_index; + service->updateProfile(start, _attr_index); + node = node->next; + } + +#if 0 + // Start debug + int i; + + for (i = 0; i < _attr_index; i++) { + { + pr_info(LOG_MODULE_APP, "gatt-: i %d, type %d, u16 0x%x", + i, + _attr_base[i].uuid->type, + BT_UUID_16(_attr_base[i].uuid)->val); + } + } + + delay(1000); + // End for debug +#endif + + ret = bt_gatt_register(_attr_base, + _attr_index); + pr_debug(LOG_MODULE_APP, "%s: ret, %d,_attr_index-%d", __FUNCTION__, ret, _attr_index); + if (0 == ret) + { + _profile_registered = true; + } + return ret; +} + +void BLEProfileManager::clearProfile(BLEServiceLinkNodeHeader* serviceHeader) +{ + if (NULL == serviceHeader) + { + return; + } + + BLEServiceNodePtr node = link_node_get_first(serviceHeader); + + while (NULL != node) + { + BLEServiceImp *service = node->value; + delete service; + link_node_remove_first(serviceHeader); + node = link_node_get_first(serviceHeader); + } +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, int index) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + int counter = 0; + BLEServiceNodePtr node = serviceHeader->next; + while (node != NULL) + { + BLEServiceImp *service = node->value; + int counterTmp = service->getCharacteristicCount(); + if (counter + counterTmp > index) + { + break; + } + counter += counterTmp; + node = node->next; + } + + if (NULL != node) + { + BLEServiceImp *service = node->value; + characteristicImp = service->characteristic(index - counter); + } + return characteristicImp; +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, uint16_t handle) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + + BLEServiceNodePtr node = serviceHeader->next; + while (node != NULL) + { + BLEServiceImp *service = node->value; + characteristicImp = service->characteristic(handle); + if (NULL != characteristicImp) + { + break; + } + node = node->next; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, + const char* uuid, + int index) +{ + BLECharacteristicImp* characteristicImp = characteristic(bledevice, index); + if (NULL != characteristicImp) + { + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + return characteristicImp; +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, + const char* uuid) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + while (node != NULL) + { + BLEServiceImp *service = node->value; + characteristicImp = service->characteristic(uuid); + if (NULL != characteristicImp) + { + break; + } + node = node->next; + } + + return characteristicImp; +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const char * uuid) const +{ + bt_uuid_128_t uuid_tmp; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return service(bledevice, (const bt_uuid_t *)&uuid_tmp); +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const bt_uuid_t* uuid) const +{ + BLEServiceImp* serviceImp = NULL; + #if 1 + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + + // Just for debug + char uuid_tmp[37]; + BLEUtils::uuidBT2String(uuid, uuid_tmp); + pr_debug(LOG_MODULE_BLE, "%s-%d: %s", __FUNCTION__, __LINE__, uuid_tmp); + + while (node != NULL) + { + serviceImp = node->value; + if (true == serviceImp->compareUuid(uuid)) + { + break; + } + node = node->next; + } + + if (NULL == node) + { + serviceImp = NULL; + } + #endif + return serviceImp; +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, int index) const +{ + BLEServiceImp* serviceImp = NULL; + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + + while (node != NULL) + { + if (0 == index) + { + break; + } + index--; + node = node->next; + } + if (NULL == node) + { + serviceImp = NULL; + } + else + { + serviceImp = node->value; + } + return serviceImp; +} + +void BLEProfileManager::handleConnectedEvent(const bt_addr_le_t* deviceAddr) +{ + int index = getUnusedIndex(); + if (index >= BLE_MAX_CONN_CFG) + { + //BLE_STATUS_NO_MEMORY + return; + } + bt_addr_le_copy(&_addresses[index], deviceAddr); +} + +void BLEProfileManager::handleDisconnectedEvent(const bt_addr_le_t* deviceAddr) +{ + int i; + if ((bt_addr_le_cmp(deviceAddr, &_discovering_ble_addresses) == 0)) + { + _start_discover = false; + memset(&_discovering_ble_addresses, 0, sizeof(_discovering_ble_addresses)); + } + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(deviceAddr, &_addresses[i]) == 0)) + { + bitSet(_disconnect_bitmap, i); + break; + } + } + +} + +void BLEProfileManager::handleDisconnectedPutOffEvent() +{ + BLEServiceLinkNodeHeader* serviceheader = NULL; + int i; + if (_disconnect_bitmap == 0) + { + return; + } + + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if (bitRead(_disconnect_bitmap, i) != 0) + { + serviceheader = &_service_header_array[i]; + clearProfile(serviceheader); + memset(&_addresses[i], 0, sizeof(bt_addr_le_t)); + bitClear(_disconnect_bitmap, i); + } + } +} + +bool BLEProfileManager::discoverAttributes(BLEDevice* device) +{ + int err; + bt_conn_t* conn; + int i = getDeviceIndex(device); + bool ret = false; + bt_gatt_discover_params_t* temp = NULL; + + errno = 0; + pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d,fun-%p", __FUNCTION__, __LINE__, i,profile_discover_process); + + if (_start_discover) + { + // Already in discover state + return false; + } + + + if (i >= BLE_MAX_CONN_CFG) + { + // The device already in the buffer. + // This function only be called after connection established. + return ret; + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return ret; + } + temp = &_discover_params[i]; + temp->start_handle = 1; + temp->end_handle = 0xFFFF; + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_PRIMARY; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return ret; + } + // Block it + memcpy(&_discovering_ble_addresses, device->bt_le_address(), sizeof(_discovering_ble_addresses)); + _discover_rsp_timestamp = millis(); + _start_discover = true; + ret = true; + while (_start_discover) // Sid. KW warning acknowldged + { + delay(10); + if ((millis() - _discover_rsp_timestamp) > 5000) + { + // Doesn't receive the Service read response + _start_discover = false; + _cur_discover_service = NULL; + ret = false; + _reading = false; + } + + if (ENOMEM == errno) + { + pr_debug(LOG_MODULE_BLE, "%s-%d:Sys errno(err %d)\n", __FUNCTION__, __LINE__, errno); + ret = false; + break; + } + } + return ret; +} + +bool BLEProfileManager::discoverAttributesByService(BLEDevice* device, const bt_uuid_t* svc_uuid) +{ + errno = 0; + if (_start_discover) + { + // Already in discover state + return false; + } + + bool ret = discoverService(device, svc_uuid); + if (false == ret) + { + return false; + } + // Block it + memcpy(&_discovering_ble_addresses, device->bt_le_address(), sizeof(_discovering_ble_addresses)); + _discover_rsp_timestamp = millis(); + _start_discover = true; + _discover_one_service = true; + + while (_start_discover) // Sid. KW warning acknowldged + { + delay(10); + if ((millis() - _discover_rsp_timestamp) > 5000) + { + // Doesn't receive the Service read response + _start_discover = false; + _cur_discover_service = NULL; + ret = false; + _reading = false; + } + + if (ENOMEM == errno) + { + pr_debug(LOG_MODULE_BLE, "%s-%d:Sys errno(err %d)", __FUNCTION__, __LINE__, errno); + ret = false; + break; + } + } + pr_debug(LOG_MODULE_BLE, "%s-%d:Discover Done", __FUNCTION__, __LINE__); + _discover_one_service = false; + + return ret; +} + + +int BLEProfileManager::getDeviceIndex(const bt_addr_le_t* macAddr) +{ + int i; + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(macAddr, &_addresses[i]) == 0)) + { + break; + } + } + return i; +} + +int BLEProfileManager::getDeviceIndex(const BLEDevice* device) +{ + return getDeviceIndex(device->bt_le_address()); +} + +bool BLEProfileManager::discovering() +{ + bool ret = _discovering; + if (_cur_discover_service != NULL) + { + ret = ret || _cur_discover_service->discovering(); + } + return ret; +} + +void BLEProfileManager::setDiscovering(bool discover) +{ + _discovering = discover; +} + +uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + int i = getDeviceIndex(dst_addr); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + BLEServiceImp* service_tmp = NULL; + _discover_rsp_timestamp = millis(); + //pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d", __FUNCTION__, __LINE__, i); + + if (i >= BLE_MAX_CONN_CFG) + { + return BT_GATT_ITER_STOP; + } + + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + case BT_GATT_DISCOVER_DESCRIPTOR: + { + if (NULL != _cur_discover_service) + { + retVal = _cur_discover_service->discoverResponseProc(conn, + attr, + params); + } + break; + } + case BT_GATT_DISCOVER_PRIMARY: + { + if (NULL != attr) + { + struct bt_gatt_service *svc_value = (struct bt_gatt_service *)attr->user_data; + const bt_uuid_t* svc_uuid = svc_value->uuid; + uint16_t le16; + memcpy(&le16, &BT_UUID_16(svc_uuid)->val, sizeof(le16)); + setDiscovering(false); + + if (svc_uuid->type == BT_UUID_TYPE_16 && + le16 == 0) + { + // Discover failed. The service may unknow type. + // Need read the value and discovery again. + readService(device, attr->handle); + retVal = BT_GATT_ITER_CONTINUE; + } + else + { + service_tmp = addService(device, svc_value->uuid); + params->uuid = NULL; + + if (NULL != service_tmp) + { + service_tmp->setHandle(attr->handle); + service_tmp->setEndHandle(svc_value->end_handle); + if (_discover_one_service == false) + retVal = BT_GATT_ITER_CONTINUE; + } + else + { + retVal = BT_GATT_ITER_STOP; + errno = ENOMEM; + pr_debug(LOG_MODULE_BLE, "%s-%d: Add service failed", + __FUNCTION__, __LINE__); + } + } + } + else + { + // Service discover complete + retVal = BT_GATT_ITER_STOP; + } + } + default: + { + break; + } + } + + if (retVal == BT_GATT_ITER_STOP) + { + if (errno == ENOMEM) + { + // No memory. Stop discovery + _cur_discover_service = NULL; + _discover_one_service = false; + return retVal; + } + + pr_debug(LOG_MODULE_BLE, "%s-%d: Discover one service-%d", + __FUNCTION__, __LINE__, _discover_one_service); + if (true == _discover_one_service) + { + if (NULL != service_tmp) + { + pr_debug(LOG_MODULE_BLE, "%s-%d: Discover service", + __FUNCTION__, __LINE__); + bool result = service_tmp->discoverAttributes(&device); + if (result == true) + { + // Record the current discovering service + _cur_discover_service = service_tmp; + } + else + { + // Failed + _discover_one_service = false; + } + } + else + { + if (discovering() == false) + { + // Complete + _cur_discover_service = NULL; + _discover_one_service = false; + } + } + + if (_discover_one_service == false) + { + // Discover complete + _start_discover = false; + memset(&_discovering_ble_addresses, 0, sizeof(_discovering_ble_addresses)); + } + return retVal; + } + + checkReadService(); + if (discovering() == false) + { + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(device); + BLEServiceImp* serviceCurImp = NULL; + if (NULL == serviceHeader) + { + // Doesn't find the service + return BT_GATT_ITER_STOP; + } + BLEServiceNodePtr node = serviceHeader->next; + + // Discover next service + while (node != NULL) + { + serviceCurImp = node->value; + + if (NULL == _cur_discover_service) + { + bool result = serviceCurImp->discoverAttributes(&device); + if (result == true) + { + // Record the current discovering service + _cur_discover_service = serviceCurImp; + break; + } + } + else if (_cur_discover_service == serviceCurImp) + { + // Find next discoverable service + _cur_discover_service = NULL; + } + + node = node->next; + } + if (NULL == node) + { + pr_debug(LOG_MODULE_BLE, "%s-%d: Discover completed", + __FUNCTION__, __LINE__); + _start_discover = false; + memset(&_discovering_ble_addresses, 0, sizeof(_discovering_ble_addresses)); + } + } + } + return retVal; +} + +void BLEProfileManager::serviceDiscoverComplete(const BLEDevice &bledevice) +{ + BLEServiceImp* serviceCurImp = NULL; + BLEServiceImp* servicePrevImp = NULL; + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return ; + } + + BLEServiceNodePtr node = serviceHeader->next; + if (NULL != node) + { + servicePrevImp = node->value; + node = node->next; + } + + // Update the service handles + while (node != NULL) + { + serviceCurImp = node->value; + if (NULL != serviceCurImp) + { + if (servicePrevImp) // KW issue: Chk for NULL. + servicePrevImp->setEndHandle(serviceCurImp->startHandle() - 1); + } + + if (servicePrevImp) + { + pr_debug(LOG_MODULE_BLE, "Curr: start-%d, end-%d", servicePrevImp->startHandle(), servicePrevImp->endHandle()); + } + servicePrevImp = serviceCurImp; + if (servicePrevImp) // KW issue: Chk for NULL. + pr_debug(LOG_MODULE_BLE, "Curr: start-%d, end-%d", servicePrevImp->startHandle(), servicePrevImp->endHandle()); + node = node->next; + } + return; +} + +bool BLEProfileManager::readService(const BLEDevice &bledevice, uint16_t handle) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(bledevice)) + { + // GATT server can't write + return false; + } + + if (_reading) + { + // Read response not back + // Add to buffer + ServiceRead_t temp; + bt_addr_le_copy(&temp.address, bledevice.bt_le_address()); + temp.handle = handle; + ServiceReadLinkNodePtr node = link_node_create(temp); + link_node_insert_last(&_read_service_header, node); + return true; + } + + _read_params.func = profile_service_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = handle; + _read_params.single.offset = 0; + + if (0 == _read_params.single.handle) + { + // Discover not complete + return false; + } + + conn = bt_conn_lookup_addr_le(bledevice.bt_le_address()); + if (NULL == conn) + { + return false; + } + // Send read request + retval = bt_gatt_read(conn, &_read_params); + bt_conn_unref(conn); + if (0 == retval) + { + setDiscovering(true); + _reading = true; + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return _reading; +} + +void BLEProfileManager::checkReadService() +{ + ServiceReadLinkNodePtr node = link_node_get_first(&_read_service_header); + while (NULL != node) + { + BLEDevice temp(&node->value.address); + bool readResult = readService(temp, node->value.handle); + link_node_remove_first(&_read_service_header); + if (true == readResult) + { + break; + } + node = link_node_get_first(&_read_service_header); + } +} + +bool BLEProfileManager::discoverService(BLEDevice* device, const bt_uuid_t* svc_uuid) +{ + int err = 0; + bt_conn_t* conn; + int i = getDeviceIndex(device); + bool ret = false; + bt_gatt_discover_params_t* temp = NULL; + + pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d,fun-%p", __FUNCTION__, __LINE__, i,profile_discover_process); + + if (i >= BLE_MAX_CONN_CFG) + { + // The device already in the buffer. + // This function only be called after connection established. + return ret; + } + + //BLEServiceImp* serviceImp = service(device, svc_uuid); + //if (NULL == serviceImp) + { + //return ret; + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return ret; + } + + memcpy(&_discover_uuid[i], svc_uuid, sizeof(bt_uuid_128_t)); + + temp = &_discover_params[i]; + temp->start_handle = 1; + temp->end_handle = 0xFFFF; + temp->uuid = (bt_uuid_t*) &_discover_uuid[i]; + temp->type = BT_GATT_DISCOVER_PRIMARY; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return ret; + } + return true; +} + +uint8_t BLEProfileManager::serviceReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + _reading = false; + _discover_rsp_timestamp = millis(); + if (NULL == data) + { + return BT_GATT_ITER_STOP; + } + BLEDevice bleDevice(bt_conn_get_dst(conn)); + + pr_debug(LOG_MODULE_BLE, "%s-%d:length-%d", __FUNCTION__, __LINE__, length); + if (length == UUID_SIZE_128) + { + bt_uuid_128_t uuid_tmp; + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + memcpy(uuid_tmp.val, data, UUID_SIZE_128); + BLEProfileManager::instance()->discoverService(&bleDevice, (const bt_uuid_t *)&uuid_tmp); + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + return BT_GATT_ITER_STOP; +} + + + diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.h b/libraries/CurieBLE/src/internal/BLEProfileManager.h new file mode 100644 index 00000000..02c42c9c --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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_PROFILE_MANAGER_H__ +#define __BLE_PROFILE_MANAGER_H__ + +#include "CurieBLE.h" + +#include "BLEServiceImp.h" + +//#include "BLECommon.h" +//#include "BLEDevice.h" +//#include "BLEService.h" +typedef struct { + bt_addr_le_t address; + uint16_t handle; +}ServiceRead_t; + +class BLEProfileManager{ +public: + /** + * @brief Get the BLEProfile Manager instance + * + * @param none + * + * @return BLEProfileManager* BLE Profile manager + * + * @note none + */ + static BLEProfileManager* instance(); + + /** + * @brief Add an service to the BLE Device + * + * @param[in] bledevice The BLE device that owned the service + * + * @param[in] service The service to add to BLE device profile + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method in GATT server role + * Or be called in discover process. + */ + BLEServiceImp *addService (BLEDevice &bledevice, BLEService& service); + BLEServiceImp *addService (BLEDevice &bledevice, const bt_uuid_t* uuid); + + /** + * @brief Register the profile to Nordic BLE stack + * + * @param[in] bledevice The BLE Device + * + * @return int std C errno + * + * @note none + */ + int registerProfile(BLEDevice &bledevice); + + inline bool hasRegisterProfile(){return _profile_registered;} + + /** + * @brief Get the BLE's Characteristic implementation object by uuid and index + * + * @param[in] bledevice The BLE device + * + * @param[in] uuid The characteristic UUID + * + * @param[in] index The characteristic index in the profile + * + * @return BLECharacteristicImp* The BLE characteristic implementation object + * + * @note none + */ + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + const char* uuid, + int index); + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + const char* uuid); + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + int index); + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + uint16_t handle); + BLEServiceImp* service(const BLEDevice &bledevice, const char * uuid) const; + BLEServiceImp* service(const BLEDevice &bledevice, int index) const; + BLEServiceImp* service(const BLEDevice &bledevice, const bt_uuid_t* uuid) const; + int serviceCount(const BLEDevice &bledevice) const; + int characteristicCount(const BLEDevice &bledevice) const; + + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + + bool discoverAttributes(BLEDevice* device); + bool discoverAttributesByService(BLEDevice* device, const bt_uuid_t* svc_uuid); + void handleConnectedEvent(const bt_addr_le_t* deviceAddr); + void handleDisconnectedEvent(const bt_addr_le_t* deviceAddr); + void handleDisconnectedPutOffEvent(); + uint8_t serviceReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); +protected: + friend ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset); + bool discoverService(BLEDevice* device, const bt_uuid_t* svc_uuid); +private: + typedef LinkNode BLEServiceLinkNodeHeader; + typedef LinkNode* BLEServiceNodePtr; + typedef LinkNode BLEServiceNode; + + typedef LinkNode ServiceReadLinkNodeHeader; + typedef LinkNode* ServiceReadLinkNodePtr; + typedef LinkNode ServiceReadLinkNode; + + BLEProfileManager(); + ~BLEProfileManager (void); + + void serviceDiscoverComplete(const BLEDevice &bledevice); + + int getDeviceIndex(const bt_addr_le_t* macAddr); + int getDeviceIndex(const BLEDevice* device); + /** + * @brief Get the unused service header index + * + * @param none + * + * @return int The unused BLE profile index + * + * @note This object has a buffer to manage all devices profile. + * The buffer is an array. The different profiles + * distinguished by BLE address. + */ + int getUnusedIndex(); + + /** + * @brief Get the Service header by BLE device + * + * @param[in] bledevice The BLE device + * + * @return none + * + * @note none + */ + BLEServiceLinkNodeHeader* getServiceHeader(const BLEDevice &bledevice); + const BLEServiceLinkNodeHeader* getServiceHeader(const BLEDevice &bledevice) const; + + /** + * @brief Get the BLE attribute counter based on services, characteristics + * and descriptors. + * + * @param none + * + * @return none + * + * @note none + */ + int getAttributeCount(BLEDevice &bledevice); + + /** + * @brief Discard the profile by BLE device + * + * @param[in] bledevice The BLE device + * + * @return none + * + * @note none + */ + void clearProfile(BLEServiceLinkNodeHeader* serviceHeader); + + bool readService(const BLEDevice &bledevice, uint16_t handle); + bool discovering(); + void setDiscovering(bool discover); + void checkReadService(); + +private: + // The last header is for local BLE + BLEServiceLinkNodeHeader _service_header_array[BLE_MAX_CONN_CFG + 1]; // The connected devices' service and self service + bt_addr_le_t _addresses[BLE_MAX_CONN_CFG + 1]; // The BLE devices' address + bt_addr_le_t _discovering_ble_addresses; + + bool _start_discover; + + bool _discovering; + uint64_t _discover_rsp_timestamp; + bt_gatt_discover_params_t _discover_params[BLE_MAX_CONN_CFG]; + bt_uuid_128_t _discover_uuid[BLE_MAX_CONN_CFG]; + BLEServiceImp* _cur_discover_service; + bool _discover_one_service; + bt_gatt_read_params_t _read_params; + bool _reading; + ServiceReadLinkNodeHeader _read_service_header; + + bt_gatt_attr_t *_attr_base; // Allocate the memory for BLE stack + int _attr_index; + + static BLEProfileManager* _instance; // The profile manager instance + bool _profile_registered; + uint8_t _disconnect_bitmap; +}; + +#endif + diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.cpp b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp new file mode 100644 index 00000000..4f4d3dc1 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "internal/ble_client.h" + +#include "BLEServiceImp.h" +#include "BLECallbacks.h" +#include "BLEUtils.h" +#include "BLECharacteristicImp.h" + +bt_uuid_16_t BLEServiceImp::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; + +bt_uuid_t *BLEServiceImp::getPrimayUuid(void) +{ + return (bt_uuid_t *)&_gatt_primary_uuid; +} + +BLEServiceImp::BLEServiceImp(BLEService& service): + BLEAttribute(service.uuid(), BLETypeService), + _start_handle(0), + _end_handle(0xFFFF), + _cur_discover_chrc(NULL) +{ + memset(&_characteristics_header, 0, sizeof(_characteristics_header)); + service.setServiceImp(this); +} + +BLEServiceImp::BLEServiceImp(const bt_uuid_t* uuid): + BLEAttribute(uuid, BLETypeService), + _start_handle(0), + _end_handle(0xFFFF), + _cur_discover_chrc(NULL) +{ + memset(&_characteristics_header, 0, sizeof(_characteristics_header)); +} + +BLEServiceImp::~BLEServiceImp() +{ + releaseCharacteristic(); +} + + +int BLEServiceImp::addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic) +{ + BLECharacteristicImp* characteristicImp = NULL; + + characteristicImp = characteristic.fetchCharacteristicImp(); + if (NULL == characteristicImp) + { + characteristicImp = new BLECharacteristicImp(characteristic, bledevice); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == characteristicImp) + { + return BLE_STATUS_NO_MEMORY; + } + } + + BLECharacteristicNodePtr node = link_node_create(characteristicImp); + if (NULL == node) + { + delete characteristicImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_characteristics_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +int BLEServiceImp::addCharacteristic(BLEDevice& bledevice, + const bt_uuid_t* uuid, + uint16_t handle, + unsigned char properties) +{ + BLECharacteristicImp* characteristicImp = NULL; + + pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d",__FUNCTION__, __LINE__,handle); + characteristicImp = new BLECharacteristicImp(uuid, + properties, + handle, + bledevice); + if (NULL == characteristicImp) + { + return BLE_STATUS_NO_MEMORY; + } + + BLECharacteristicNodePtr node = link_node_create(characteristicImp); + if (NULL == node) + { + delete characteristicImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_characteristics_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +int BLEServiceImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + int base_index = index; + int offset = 0; + int counter = 0; + start->uuid = BLEServiceImp::getPrimayUuid(); + start->perm = BT_GATT_PERM_READ; + start->read = bt_gatt_attr_read_service; + start->user_data = (void *)bt_uuid(); + + pr_debug(LOG_MODULE_BLE, "service-%p", start); + start++; + index++; + counter++; + + BLECharacteristicNodePtr node = _characteristics_header.next; + while (NULL != node) + { + BLECharacteristicImp *characteristicImp = node->value; + start = attr_start + index - base_index; + offset = characteristicImp->updateProfile(start, index); + counter += offset; + node = node->next; + } + return counter; +} + +int BLEServiceImp::getAttributeCount() +{ + int counter = 1; // Service itself + + BLECharacteristicNodePtr node = _characteristics_header.next; + while (NULL != node) + { + BLECharacteristicImp *characteristicImp = node->value; + + counter += characteristicImp->getAttributeCount(); + node = node->next; + } + return counter; +} + +int BLEServiceImp::getCharacteristicCount() +{ + return link_list_size(&_characteristics_header); +} + +void BLEServiceImp::releaseCharacteristic() +{ + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + while (NULL != node) + { + BLECharacteristicImp* characteristicImp = node->value; + delete characteristicImp; + link_node_remove_first(&_characteristics_header); + node = link_node_get_first(&_characteristics_header); + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); +} + + +BLECharacteristicImp* BLEServiceImp::characteristic(int index) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + if (0 >= index) + { + characteristicImp = node->value; + break; + } + index--; + node = node->next; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEServiceImp::characteristic(uint16_t handle) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + characteristicImp = node->value; + if (handle == characteristicImp->valueHandle()) + { + break; + } + node = node->next; + } + if (NULL == node) + { + characteristicImp = NULL; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEServiceImp::characteristic(const bt_uuid_t* uuid) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + + while (NULL != node) + { + characteristicImp = node->value; + if (true == characteristicImp->compareUuid(uuid)) + { + break; + } + node = node->next; + } + + if (NULL == node) + { + characteristicImp = NULL; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) +{ + bt_uuid_128_t uuid_tmp; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + return characteristic((const bt_uuid_t *)&uuid_tmp); +} + +bool BLEServiceImp::discovering() +{ + return (_cur_discover_chrc != NULL); +} + +bool BLEServiceImp::discoverAttributes(BLEDevice* device) +{ + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + int err; + bt_conn_t* conn; + bt_gatt_discover_params_t* temp = NULL; + const bt_uuid_t* service_uuid = bt_uuid(); + + if (service_uuid->type == BT_UUID_TYPE_16) + { + uint16_t uuid_tmp;// = ((bt_uuid_16_t*)service_uuid)->val; + + memcpy(&uuid_tmp, &((bt_uuid_16_t*)service_uuid)->val, sizeof(uuid_tmp)); + if (BT_UUID_GAP_VAL == uuid_tmp || + BT_UUID_GATT_VAL == uuid_tmp) + { + return false; + } + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return false; + } + temp = &_discover_params; + temp->start_handle = _start_handle; + temp->end_handle = _end_handle; + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_CHARACTERISTIC; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return false; + } + return true; +} + +uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + + //pr_debug(LOG_MODULE_BLE, "%s-%d: type-%d", __FUNCTION__, __LINE__, params->type); + + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + { + if (NULL != attr) + { + //const bt_uuid_t* chrc_uuid = attr->uuid; + uint16_t chrc_handle = attr->handle + 1; + struct bt_gatt_chrc* psttemp = (struct bt_gatt_chrc*)attr->user_data; + int retval = (int)addCharacteristic(device, + psttemp->uuid, + chrc_handle, + psttemp->properties); + + //pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, chrc_handle); + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + errno = ENOMEM; + } + else + { + retVal = BT_GATT_ITER_CONTINUE; + } + } + break; + } + case BT_GATT_DISCOVER_DESCRIPTOR: + { + // + + if (NULL != _cur_discover_chrc) + { + retVal = _cur_discover_chrc->discoverResponseProc(conn, + attr, + params); + } + break; + } + default: + { + //attribute_tmp->discover(attr, &_discover_params); + break; + } + } + + pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d",__FUNCTION__, __LINE__, retVal); + if (retVal == BT_GATT_ITER_STOP) + { + if (errno == ENOMEM) + { + _cur_discover_chrc = NULL; + return retVal; + } + const BLECharacteristicLinkNodeHeader* chrcHeader = &_characteristics_header; + BLECharacteristicImp* chrcCurImp = NULL; + BLECharacteristicNodePtr node = chrcHeader->next; + + pr_debug(LOG_MODULE_BLE, "%s-%d: node-%p",__FUNCTION__, __LINE__, node); + // Discover next service + while (node != NULL) + { + chrcCurImp = node->value; + + if (NULL == _cur_discover_chrc) + { + bool result = chrcCurImp->discoverAttributes(&device); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (result == true) + { + // Record the current discovering service + _cur_discover_chrc = chrcCurImp; + break; + } + } + else if (_cur_discover_chrc == chrcCurImp) + { + // Find next discoverable service + _cur_discover_chrc = NULL; + } + node = node->next; + } + } + return retVal; +} + + diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.h b/libraries/CurieBLE/src/internal/BLEServiceImp.h new file mode 100644 index 00000000..d6c63389 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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_IMP_H_INCLUDED +#define _BLE_SERVICE_IMP_H_INCLUDED + +#include "CurieBLE.h" + +#include "BLEAttribute.h" + +#include "LinkList.h" + +/** + * BLE GATT Service + */ +class BLEServiceImp: public BLEAttribute{ +public: + /** + * Constructor for BLE Service + * + * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard + */ + BLEServiceImp(BLEService& service); + BLEServiceImp(const bt_uuid_t* uuid); + ~BLEServiceImp(); + + /** + * @brief Add a characteristic in service + * + * @param[in] bledevice The BLE device want to add the characteristic + * + * @param[in] characteristic The characteristic want to be added to service + * + * @return none + * + * @note none + */ + int addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic); + int addCharacteristic(BLEDevice& bledevice, + const bt_uuid_t* uuid, + uint16_t handle, + unsigned char properties); + int getCharacteristicCount(); + + BLECharacteristicImp* characteristic(const bt_uuid_t* uuid); + BLECharacteristicImp* characteristic(const char* uuid); + BLECharacteristicImp* characteristic(int index); + BLECharacteristicImp* characteristic(uint16_t handle); + inline void setHandle(uint16_t handle){_start_handle = handle;} + inline void setEndHandle(uint16_t handle){_end_handle = handle;} + inline uint16_t endHandle(){return _end_handle;} + inline uint16_t startHandle(){return _start_handle;} + + bool discoverAttributes(BLEDevice* device); + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + bool discovering(); + + static bt_uuid_t *getPrimayUuid(void); +protected: + friend class BLEProfileManager; + + int getAttributeCount(); + + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); +private: + typedef LinkNode BLECharacteristicLinkNodeHeader; + typedef LinkNode* BLECharacteristicNodePtr; + typedef LinkNode BLECharacteristicNode; + + uint16_t _start_handle; + uint16_t _end_handle; + + void releaseCharacteristic(); + BLECharacteristicImp *_cur_discover_chrc; + + static bt_uuid_16_t _gatt_primary_uuid; + bt_gatt_discover_params_t _discover_params; + + BLECharacteristicLinkNodeHeader _characteristics_header; // The characteristic link list +}; + +#endif // _BLE_SERVICE_H_INCLUDED diff --git a/libraries/CurieBLE/src/internal/BLEUtils.cpp b/libraries/CurieBLE/src/internal/BLEUtils.cpp new file mode 100644 index 00000000..0142db07 --- /dev/null +++ b/libraries/CurieBLE/src/internal/BLEUtils.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 "CurieBLE.h" +#include "BLEUtils.h" +#include "internal/ble_client.h" + +String BLEUtils::macAddressBT2String(const bt_addr_le_t &bd_addr) +{ + char mac_string[BT_ADDR_STR_LEN]; + snprintf(mac_string, BT_ADDR_STR_LEN, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + bd_addr.val[5], bd_addr.val[4], bd_addr.val[3], + bd_addr.val[2], bd_addr.val[1], bd_addr.val[0]); + String temp(mac_string); + return temp; +} + +void BLEUtils::macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(mac_str); + int length = 0; + + bd_addr.type = BT_ADDR_LE_PUBLIC; + + for (int i = strLength - 1; i >= 0 && length < BLE_ADDR_LEN; i -= 2) + { + if (mac_str[i] == ':') + { + i++; + continue; + } + + temp[0] = mac_str[i - 1]; + temp[1] = mac_str[i]; + + bd_addr.val[length] = strtoul(temp, NULL, 16); + + length++; + } + +} + +bool BLEUtils::macAddressSame(const bt_addr_le_t &bd_addr1, + const bt_addr_le_t &bd_addr2) +{ + bool temp = true;//(memcmp(bd_addr1.val, bd_addr2.val, 6) != 0);// + #if 1 + for (int i = 0; i < 6; i++) + { + if (bd_addr1.val[i] != bd_addr2.val[i]) + { + temp = false; + break; + } + } + #endif + return temp; + +} + +bool BLEUtils::macAddressValid(const bt_addr_le_t &bd_addr) +{ + bool temp = false; +#if 0 + static const bt_addr_le_t zero = {0,{0,0,0,0,0,0}}; + temp = (memcmp(bd_addr.val, zero.val, 6) != 0); +#else + for (int i = 0; i < 6; i++) + { + if (bd_addr.val[i] != 0) + { +//pr_info(LOG_MODULE_BLE, "%s-idx %d-%.2x:%.2x", __FUNCTION__, i ,bd_addr.val[i], zero.val[i]); +//pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(zero).c_str()); + temp = true; + break; + } + } +#endif + return temp; +} + + +bt_addr_le_t* BLEUtils::bleGetLoalAddress() +{ + static bt_addr_le_t board_addr; + if (false == macAddressValid(board_addr)) + ble_client_get_mac_address(&board_addr); + return &board_addr; +} + + + +void BLEUtils::uuidString2BT(const char* uuid, bt_uuid_t* pstuuid) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(uuid); + int length = 0; + bt_uuid_128_t uuid_tmp; + + memset (&uuid_tmp, 0x00, sizeof(uuid_tmp)); + + for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) + { + if (uuid[i] == '-') + { + i++; + continue; + } + + temp[0] = uuid[i - 1]; + temp[1] = uuid[i]; + + uuid_tmp.val[length] = strtoul(temp, NULL, 16); + + length++; + } + + if (length == 2) + { + uint16_t temp = (uuid_tmp.val[1] << 8)| uuid_tmp.val[0]; + uint8_t* uuid16_val = (uint8_t*)&((bt_uuid_16_t*)(&uuid_tmp.uuid))->val; + uuid_tmp.uuid.type = BT_UUID_TYPE_16; + memcpy(uuid16_val, &temp, sizeof (uint16_t)); + } + else + { + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + } + memcpy(pstuuid, &uuid_tmp, sizeof (uuid_tmp)); +} + +void BLEUtils::uuidBT2String(const bt_uuid_t* pstuuid, char* uuid) +{ + unsigned int tmp1, tmp5; + uint16_t tmp0, tmp2, tmp3, tmp4; + // TODO: Change the magic number 37 + switch (pstuuid->type) { + case BT_UUID_TYPE_16: + memcpy(&tmp0, &BT_UUID_16(pstuuid)->val, sizeof(tmp0)); + snprintf(uuid, 37, "%.4x", tmp0); + break; + case BT_UUID_TYPE_128: + memcpy(&tmp0, &BT_UUID_128(pstuuid)->val[0], sizeof(tmp0)); + memcpy(&tmp1, &BT_UUID_128(pstuuid)->val[2], sizeof(tmp1)); + memcpy(&tmp2, &BT_UUID_128(pstuuid)->val[6], sizeof(tmp2)); + memcpy(&tmp3, &BT_UUID_128(pstuuid)->val[8], sizeof(tmp3)); + memcpy(&tmp4, &BT_UUID_128(pstuuid)->val[10], sizeof(tmp4)); + memcpy(&tmp5, &BT_UUID_128(pstuuid)->val[12], sizeof(tmp5)); + snprintf(uuid, 37, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); + break; + default: + memset(uuid, 0, 37); + return; + } +} + +bool BLEUtils::uuidBTSame(const bt_uuid_t* pstuuid1, + const bt_uuid_t* pstuuid2) +{ + bool temp = (pstuuid1->type == pstuuid2->type); + if (true == temp) + { + if (pstuuid1->type == BT_UUID_TYPE_16) + { + temp = (0 == memcmp(&BT_UUID_16(pstuuid1)->val, &BT_UUID_16(pstuuid2)->val, 2)); + } + else + { + temp = (0 == memcmp(BT_UUID_128(pstuuid1)->val, BT_UUID_128(pstuuid2)->val, 16)); + } + } + return temp; + +} + +BLEDevice& BLEUtils::getLoacalBleDevice() +{ + return BLE; +} + +bool BLEUtils::isLocalBLE(const BLEDevice& device) +{ + return (device == BLE); +} + + + diff --git a/libraries/CurieBLE/src/BLECentralHelper.cpp b/libraries/CurieBLE/src/internal/BLEUtils.h similarity index 56% rename from libraries/CurieBLE/src/BLECentralHelper.cpp rename to libraries/CurieBLE/src/internal/BLEUtils.h index e48426aa..e756843a 100644 --- a/libraries/CurieBLE/src/BLECentralHelper.cpp +++ b/libraries/CurieBLE/src/internal/BLEUtils.h @@ -17,35 +17,20 @@ * */ -#include "BLECentralHelper.h" -#include "BLEPeripheralRole.h" - - -BLECentralHelper::BLECentralHelper(BLEPeripheralRole* peripheral) : - _peripheral(peripheral) +namespace BLEUtils { - clearAddress(); -} - -bool -BLECentralHelper::connected() { - poll(); - - return (*this && *this == _peripheral->central()); -} - -void -BLECentralHelper::poll() { - _peripheral->poll(); -} - -bool -BLECentralHelper::disconnect() { - if (connected()) { - return _peripheral->disconnect(); - } - - return false; + String macAddressBT2String(const bt_addr_le_t &bd_addr); + void macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr); + bool macAddressValid(const bt_addr_le_t &bd_addr); + bool macAddressSame(const bt_addr_le_t &bd_addr1, const bt_addr_le_t &bd_addr2); + bt_addr_le_t* bleGetLoalAddress(); + void uuidString2BT(const char* uuid, bt_uuid_t* pstuuid); + void uuidBT2String(const bt_uuid_t* pstuuid, char* uuid); + bool uuidBTSame(const bt_uuid_t* pstuuid1, + const bt_uuid_t* pstuuid2); + + BLEDevice& getLoacalBleDevice(); + bool isLocalBLE(const BLEDevice& device); } diff --git a/libraries/CurieBLE/src/internal/LinkList.h b/libraries/CurieBLE/src/internal/LinkList.h new file mode 100644 index 00000000..180741c1 --- /dev/null +++ b/libraries/CurieBLE/src/internal/LinkList.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Intel Corporation. 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 _LINKLIST_H_ +#define _LINKLIST_H_ + +template struct LinkNode { + LinkNode *next; + T value; +}; + +template LinkNode* link_node_create(T value) +{ + LinkNode* node = (LinkNode*)malloc(sizeof(LinkNode)); + + if (node) { + node->value = value; + node->next = NULL; + } + return node; +} + +template void link_node_insert_last(LinkNode *root, LinkNode *node) +{ + while(root->next != 0) + { + root = root->next; + } + root->next = node; +} + +template void link_node_remove_last(LinkNode *root) +{ + LinkNode *temp1, *temp2; + if (root->next != NULL) + { + temp1 = root->next; + while(temp1->next != NULL) + { + temp2 = temp1; + temp1 = temp1->next; + } + + free(temp1); + temp2->next = NULL; + } +} + +template void link_node_remove_first(LinkNode *root) +{ + LinkNode *temp1; + if (root->next != NULL) + { + temp1 = root->next; + root->next = temp1->next; + free(temp1); + } +} + +template LinkNode * link_node_get_first(LinkNode *root) +{ + return root->next; +} + +template void link_node_insert_first(LinkNode *root, LinkNode *node) +{ + LinkNode* temp = root->next; + root->next = node; + node->next = temp; +} + +template int link_list_size(const LinkNode *root) +{ + int counter = 0; + while(root->next != 0) + { + root = root->next; + counter++; + } + return counter; +} + +#endif + diff --git a/libraries/CurieBLE/src/internal/ble_client.c b/libraries/CurieBLE/src/internal/ble_client.c index 78afa966..5a9e6164 100644 --- a/libraries/CurieBLE/src/internal/ble_client.c +++ b/libraries/CurieBLE/src/internal/ble_client.c @@ -112,9 +112,7 @@ static struct bt_conn_cb conn_callbacks = { .le_param_updated = on_le_param_updated }; - - -void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) +void ble_client_get_mac_address(bt_addr_le_t *bda) { struct curie_oem_data *p_oem = NULL; unsigned i; @@ -132,6 +130,13 @@ void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) } } } +} + +void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) +{ + struct curie_oem_data *p_oem = NULL; + + ble_client_get_mac_address(bda); /* Set a default name if one has not been specified */ if (name) { @@ -165,6 +170,7 @@ void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) if (bda && bda->type != BLE_DEVICE_ADDR_INVALID) { *suffix++ = '-'; + p_oem = (struct curie_oem_data *) &global_factory_data->oem_data.project_data; BYTE_TO_STR(suffix, p_oem->bt_address[4]); BYTE_TO_STR(suffix, p_oem->bt_address[5]); *suffix = 0; /* NULL-terminate the string. Note the macro BYTE_TO_STR @@ -198,9 +204,9 @@ void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_par return; } -BleStatus errorno_to_ble_status(int err) +BLE_STATUS_T errorno_to_ble_status(int err) { - BleStatus err_code; + BLE_STATUS_T err_code; err = 0 - err; switch(err) { diff --git a/libraries/CurieBLE/src/internal/ble_client.h b/libraries/CurieBLE/src/internal/ble_client.h index 30a33a98..75c3d59f 100644 --- a/libraries/CurieBLE/src/internal/ble_client.h +++ b/libraries/CurieBLE/src/internal/ble_client.h @@ -107,8 +107,9 @@ void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_par ble_client_update_param_event_cb_t update_param_cb, void* update_param_param); void ble_client_get_factory_config(bt_addr_le_t *bda, char *name); void ble_gap_set_tx_power(int8_t tx_power); -BleStatus errorno_to_ble_status(int err); +BLE_STATUS_T errorno_to_ble_status(int err); +void ble_client_get_mac_address(bt_addr_le_t *bda); #ifdef __cplusplus } diff --git a/libraries/CurieIMU/src/BMI160.h b/libraries/CurieIMU/src/BMI160.h index 6834ace6..1384f1c4 100644 --- a/libraries/CurieIMU/src/BMI160.h +++ b/libraries/CurieIMU/src/BMI160.h @@ -657,7 +657,7 @@ class BMI160Class { bool accelDataReady(); protected: - virtual int serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt); + virtual int serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt) = 0; private: uint8_t reg_read (uint8_t reg); diff --git a/platform.txt b/platform.txt index 89747de3..308ce967 100644 --- a/platform.txt +++ b/platform.txt @@ -19,7 +19,7 @@ compiler.c.elf.cmd=arc-elf32-gcc compiler.c.elf.flags=-nostartfiles -nodefaultlibs -nostdlib -static -Wl,-X -Wl,-N -Wl,-mcpu=quarkse_em -Wl,-marcelf -Wl,--gc-sections compiler.S.flags=-c -g -x assembler-with-cpp compiler.cpp.cmd=arc-elf32-g++ -compiler.cpp.flags=-c -mcpu=quarkse_em -mlittle-endian -g -Os -Wall -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections -fsigned-char -MMD -fno-rtti -fno-exceptions -D__ARDUINO_ARC__ -std=c++11 -DCONFIG_BLUETOOTH_PERIPHERAL -DCONFIG_BLUETOOTH_CENTRAL -DCONFIG_BLUETOOTH_GATT_CLIENT +compiler.cpp.flags=-c -mcpu=quarkse_em -mlittle-endian -g -Os -Wall -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections -fsigned-char -MMD -fno-rtti -fno-exceptions -fcheck-new -D__ARDUINO_ARC__ -std=c++11 -DCONFIG_BLUETOOTH_PERIPHERAL -DCONFIG_BLUETOOTH_CENTRAL -DCONFIG_BLUETOOTH_GATT_CLIENT compiler.ar.cmd=arc-elf32-ar compiler.ar.flags=rcs compiler.objcopy.cmd=arc-elf32-objcopy diff --git a/system/libarc32_arduino101/bootcode/init.S b/system/libarc32_arduino101/bootcode/init.S index 28aab7d6..a8bee403 100644 --- a/system/libarc32_arduino101/bootcode/init.S +++ b/system/libarc32_arduino101/bootcode/init.S @@ -61,9 +61,10 @@ _do_reset: .balign 4 _do_fault: _exit_halt: - /* Set halt flag */ - flag 0x01 + /* Set halt flag + flag 0x0 */ nop + j @_Fault nop nop /* loop forever */ @@ -76,8 +77,15 @@ _exit_halt: */ .balign 4 _do_isr: - /* Init the SP for the FIRQ bank */ - mov sp, @__firq_stack_start + /* Init the SP for the FIRQ bank */ + mov sp, @__firq_stack_start + /* Save the loop count related register */ + mov r0, lp_count + lr r1, [ARC_V2_LP_START] + lr r2, [ARC_V2_LP_END] + push_s r0 + push_s r1 + push_s r2 /* Read IRQ Cause */ lr r0, [ARC_V2_ICAUSE] sub r0, r0, 16 @@ -89,5 +97,13 @@ _do_isr: jl_s.d [r1] nop + /* Restore the loop count related register */ + pop_s r2 + pop_s r1 + pop_s r0 + + sr r2, [ARC_V2_LP_END] + sr r1, [ARC_V2_LP_START] + mov lp_count, r0 rtie diff --git a/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h b/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h index e87cf2ab..faf03b2f 100644 --- a/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h +++ b/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h @@ -345,6 +345,7 @@ int bt_br_set_connectable(bool enable); #endif void bt_le_set_device_name(char *device_name, int len); +void bt_le_set_mac_address(bt_addr_le_t bda); #ifdef __cplusplus } diff --git a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c index b2f6c08f..accd8b2c 100644 --- a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c +++ b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c @@ -155,7 +155,7 @@ static void ipc_uart_push_frame(uint16_t len, uint8_t *p_data) // "len %d, src %d, channel %d", ipc.rx_hdr.len, len, // ipc.rx_hdr.src_cpu_id, // ipc.rx_hdr.channel); - pr_debug(LOG_MODULE_IPC,"data[0 - 1]: %x-%x", p_data[0], p_data[1]); + //pr_debug(LOG_MODULE_IPC,"data[0 - 1]: %x-%x", p_data[0], p_data[1]); if ((ipc.rx_hdr.channel < IPC_UART_MAX_CHANNEL) && (ipc.channels[ipc.rx_hdr.channel].cb != NULL)) { @@ -172,165 +172,165 @@ static void ipc_uart_push_frame(uint16_t len, uint8_t *p_data) void ipc_uart_isr() { - /* TODO: remove once IRQ supports parameter */ - uint8_t *p_tx; + /* TODO: remove once IRQ supports parameter */ + uint8_t *p_tx; - while (UART_IRQ_HW_UPDATE(IPC_UART) && - UART_IRQ_IS_PENDING(IPC_UART)) { - if (UART_IRQ_ERR_DETECTED(IPC_UART)) + while (UART_IRQ_HW_UPDATE(IPC_UART) && + UART_IRQ_IS_PENDING(IPC_UART)) { + if (UART_IRQ_ERR_DETECTED(IPC_UART)) { - uint8_t c; - if (UART_BREAK_CHECK(IPC_UART)) { - panic(-1); - } - UART_POLL_IN(IPC_UART, &c); - } + uint8_t c; + if (UART_BREAK_CHECK(IPC_UART)) { + panic(-1); + } + UART_POLL_IN(IPC_UART, &c); + } if (UART_IRQ_RX_READY(IPC_UART)) { - int rx_cnt; - - while ((rx_cnt = - UART_FIFO_READ(IPC_UART, - ipc.rx_ptr, - ipc.rx_size)) != 0) - { - if ((ipc.uart_enabled) && - (ipc.rx_state == STATUS_RX_IDLE)) { - /* acquire wakelock until frame is fully received */ - //pm_wakelock_acquire(&info->rx_wl); - ipc.rx_state = STATUS_RX_HDR; - } - - /* Until UART has enabled at least one channel, data should be discarded */ - if (ipc.uart_enabled) { - ipc.rx_size -= rx_cnt; - ipc.rx_ptr += rx_cnt; - } - - if (ipc.rx_size == 0) { - if (ipc.rx_state == STATUS_RX_HDR) { - pr_error(0, "%s-%d", __FUNCTION__, ipc.rx_hdr.len); - ipc.rx_ptr = balloc( - ipc.rx_hdr.len, NULL); + int rx_cnt; + + while ((rx_cnt = + UART_FIFO_READ(IPC_UART, + ipc.rx_ptr, + ipc.rx_size)) != 0) + { + if ((ipc.uart_enabled) && + (ipc.rx_state == STATUS_RX_IDLE)) { + /* acquire wakelock until frame is fully received */ + //pm_wakelock_acquire(&info->rx_wl); + ipc.rx_state = STATUS_RX_HDR; + } + + /* Until UART has enabled at least one channel, data should be discarded */ + if (ipc.uart_enabled) { + ipc.rx_size -= rx_cnt; + ipc.rx_ptr += rx_cnt; + } + + if (ipc.rx_size == 0) { + if (ipc.rx_state == STATUS_RX_HDR) { + //pr_error(0, "%s-%d", __FUNCTION__, ipc.rx_hdr.len); + ipc.rx_ptr = balloc( + ipc.rx_hdr.len, NULL); - //pr_debug( - // LOG_MODULE_IPC, - // "ipc_uart_isr: rx_ptr is %p", - // ipc.rx_ptr); - ipc.rx_size = ipc.rx_hdr.len; - ipc.rx_state = STATUS_RX_DATA; - } else { + //pr_debug( + // LOG_MODULE_IPC, + // "ipc_uart_isr: rx_ptr is %p", + // ipc.rx_ptr); + ipc.rx_size = ipc.rx_hdr.len; + ipc.rx_state = STATUS_RX_DATA; + } else { #ifdef IPC_UART_DBG_RX - uint8_t *p_rx = ipc.rx_ptr - - ipc.rx_hdr.len; - for (int i = 0; - i < ipc.rx_hdr.len; - i++) { - pr_debug( - LOG_MODULE_IPC, - "ipc_uart_isr: %d byte is %d", - i, p_rx[i]); - } + uint8_t *p_rx = ipc.rx_ptr - + ipc.rx_hdr.len; + for (int i = 0; + i < ipc.rx_hdr.len; + i++) { + pr_debug( + LOG_MODULE_IPC, + "ipc_uart_isr: %d byte is %d", + i, p_rx[i]); + } #endif - ipc_uart_push_frame( - ipc.rx_hdr.len, - ipc.rx_ptr - - ipc.rx_hdr.len); - ipc.rx_size = sizeof(ipc.rx_hdr); - ipc.rx_ptr = - (uint8_t *)&ipc.rx_hdr; - ipc.rx_state = STATUS_RX_IDLE; - } - } - } - } + ipc_uart_push_frame( + ipc.rx_hdr.len, + ipc.rx_ptr - + ipc.rx_hdr.len); + ipc.rx_size = sizeof(ipc.rx_hdr); + ipc.rx_ptr = + (uint8_t *)&ipc.rx_hdr; + ipc.rx_state = STATUS_RX_IDLE; + } + } + } + } if (UART_IRQ_TX_READY(IPC_UART)) { - int tx_len; - - if (ipc.tx_state == STATUS_TX_DONE) { - uint8_t lsr = UART_LINE_STATUS(IPC_UART); - ipc.tx_state = STATUS_TX_IDLE; - UART_IRQ_TX_DISABLE(IPC_UART); - - /* wait for FIFO AND THR being empty! */ - while ((lsr & BOTH_EMPTY) != BOTH_EMPTY) { - lsr = UART_LINE_STATUS(IPC_UART); - } - - /* No more TX activity, send event and release wakelock */ - if (ipc.tx_cb) { - ipc.tx_cb(0, ipc.tx_cb_param); - } - //pm_wakelock_release(&info->tx_wl); - ipc.tx_wakelock_acquired = 0; - return; - } - if (NULL == ipc.tx_data) { - pr_warning(LOG_MODULE_IPC, - "ipc_uart_isr: Bad Tx data"); - return; - } - - if (!ipc.tx_wakelock_acquired) { - ipc.tx_wakelock_acquired = 1; - /* Starting TX activity, send wake assert event and acquire wakelock */ - if (ipc.tx_cb) { - ipc.tx_cb(1, ipc.tx_cb_param); - } - //pm_wakelock_acquire(&info->tx_wl); - } - if (ipc.send_counter < sizeof(ipc.tx_hdr)) { - p_tx = (uint8_t *)&ipc.tx_hdr + - ipc.send_counter; - tx_len = sizeof(ipc.tx_hdr) - ipc.send_counter; - } else { - p_tx = ipc.tx_data + - (ipc.send_counter - sizeof(ipc.tx_hdr)); - tx_len = ipc.tx_hdr.len - - (ipc.send_counter - sizeof(ipc.tx_hdr)); - } - ipc.send_counter += UART_FIFO_FILL(IPC_UART, - p_tx, - tx_len); - - if (ipc.send_counter == - (ipc.tx_hdr.len + sizeof(ipc.tx_hdr))) { - ipc.send_counter = 0; + int tx_len; + + if (ipc.tx_state == STATUS_TX_DONE) { + uint8_t lsr = UART_LINE_STATUS(IPC_UART); + ipc.tx_state = STATUS_TX_IDLE; + UART_IRQ_TX_DISABLE(IPC_UART); + + /* wait for FIFO AND THR being empty! */ + while ((lsr & BOTH_EMPTY) != BOTH_EMPTY) { + lsr = UART_LINE_STATUS(IPC_UART); + } + + /* No more TX activity, send event and release wakelock */ + if (ipc.tx_cb) { + ipc.tx_cb(0, ipc.tx_cb_param); + } + //pm_wakelock_release(&info->tx_wl); + ipc.tx_wakelock_acquired = 0; + return; + } + if (NULL == ipc.tx_data) { + pr_warning(LOG_MODULE_IPC, + "ipc_uart_isr: Bad Tx data"); + return; + } + + if (!ipc.tx_wakelock_acquired) { + ipc.tx_wakelock_acquired = 1; + /* Starting TX activity, send wake assert event and acquire wakelock */ + if (ipc.tx_cb) { + ipc.tx_cb(1, ipc.tx_cb_param); + } + //pm_wakelock_acquire(&info->tx_wl); + } + if (ipc.send_counter < sizeof(ipc.tx_hdr)) { + p_tx = (uint8_t *)&ipc.tx_hdr + + ipc.send_counter; + tx_len = sizeof(ipc.tx_hdr) - ipc.send_counter; + } else { + p_tx = ipc.tx_data + + (ipc.send_counter - sizeof(ipc.tx_hdr)); + tx_len = ipc.tx_hdr.len - + (ipc.send_counter - sizeof(ipc.tx_hdr)); + } + ipc.send_counter += UART_FIFO_FILL(IPC_UART, + p_tx, + tx_len); + + if (ipc.send_counter == + (ipc.tx_hdr.len + sizeof(ipc.tx_hdr))) { + ipc.send_counter = 0; #ifdef IPC_UART_DBG_TX - pr_debug( - LOG_MODULE_IPC, - "ipc_uart_isr: sent IPC FRAME " - "len %d", ipc.tx_hdr.len); + pr_debug( + LOG_MODULE_IPC, + "ipc_uart_isr: sent IPC FRAME " + "len %d", ipc.tx_hdr.len); #endif - p_tx = ipc.tx_data; - ipc.tx_data = NULL; - ipc.tx_state = STATUS_TX_DONE; - - /* free sent message and pull send next frame one in the queue */ - if (ipc.channels[ipc.tx_hdr.channel].cb) - { - ipc.channels[ipc.tx_hdr.channel].cb( - ipc.tx_hdr.channel, - IPC_MSG_TYPE_FREE, - ipc.tx_hdr.len, - p_tx); - } - else - { - bfree(p_tx); - } - + p_tx = ipc.tx_data; + ipc.tx_data = NULL; + ipc.tx_state = STATUS_TX_DONE; + + /* free sent message and pull send next frame one in the queue */ + if (ipc.channels[ipc.tx_hdr.channel].cb) + { + ipc.channels[ipc.tx_hdr.channel].cb( + ipc.tx_hdr.channel, + IPC_MSG_TYPE_FREE, + ipc.tx_hdr.len, + p_tx); + } + else + { + bfree(p_tx); + } + #ifdef IPC_UART_DBG_TX - uint8_t lsr = UART_LINE_STATUS(IPC_UART);//(info->uart_num); - pr_debug(LOG_MODULE_IPC, - "ipc_isr_tx: tx_idle LSR: 0x%2x\n", - lsr); + uint8_t lsr = UART_LINE_STATUS(IPC_UART);//(info->uart_num); + pr_debug(LOG_MODULE_IPC, + "ipc_isr_tx: tx_idle LSR: 0x%2x\n", + lsr); #endif - } - } - - } + } + } + + } } void *ipc_uart_channel_open(int channel_id, @@ -368,7 +368,7 @@ int ipc_uart_ns16550_send_pdu(void *handle, int len, void *p_data) { struct ipc_uart_channels *chan = (struct ipc_uart_channels *)handle; - pr_debug(LOG_MODULE_IPC, "%s: %d", __FUNCTION__, ipc.tx_state); + //pr_debug(LOG_MODULE_IPC, "%s: %d", __FUNCTION__, ipc.tx_state); if (ipc.tx_state == STATUS_TX_BUSY) { return IPC_UART_TX_BUSY; diff --git a/system/libarc32_arduino101/drivers/ns16550.c b/system/libarc32_arduino101/drivers/ns16550.c index f32d5d32..f4e529e5 100644 --- a/system/libarc32_arduino101/drivers/ns16550.c +++ b/system/libarc32_arduino101/drivers/ns16550.c @@ -340,7 +340,7 @@ unsigned char uart_poll_out( ) { /* wait for transmitter to ready to accept a character */ - while ((INBYTE(LSR(which)) & LSR_THRE) == 0) + while ((INBYTE(LSR(which)) & LSR_TEMT) == 0) ; OUTBYTE(THR(which), outChar); @@ -352,8 +352,6 @@ unsigned char uart_poll_out( * * uart_fifo_fill - fill FIFO with data * -* It is up to the caller to make sure that FIFO capcity is not exceeded -* * RETURNS: number of bytes sent */ @@ -364,8 +362,8 @@ int uart_fifo_fill(int which, /* UART on which to send */ { int i; - for (i = 0; i < size ; i++) - { + for (i = 0; i < size && (INBYTE(LSR(which)) & + LSR_BOTH_EMPTY) != 0; i++) { OUTBYTE(THR(which), txData[i]); } return i; @@ -624,6 +622,7 @@ void uart_int_connect(int which, /* UART to which to connect */ ) { interrupt_connect((unsigned int)uart[which].irq, isr); + interrupt_priority_set ((int)uart[which].irq, uart[which].intPri); interrupt_enable((unsigned int)uart[which].irq); /* set the Host Processor Interrupt Routing Mask */ SOC_UNMASK_INTERRUPTS(INT_UART_0_MASK + (which * UART_REG_ADDR_INTERVAL)); @@ -642,19 +641,6 @@ uint8_t uart_tx_complete(int which) return INBYTE(LSR(which)) & LSR_TEMT; } -/******************************************************************************* -* -* uart_tx_complete - check if tx holding register is empty -* -* RETURNS: zero if register is non-empty, -* non-zero if register is empty (ready to receive new data) -*/ - -uint8_t uart_tx_ready(int which) -{ - return INBYTE(LSR(which)) & LSR_THRE; -} - /******************************************************************************* * * uart_loop_enable - enable loopback diff --git a/system/libarc32_arduino101/framework/src/cfw/service_api.c b/system/libarc32_arduino101/framework/src/cfw/service_api.c index 28b5bfe5..183167b6 100644 --- a/system/libarc32_arduino101/framework/src/cfw/service_api.c +++ b/system/libarc32_arduino101/framework/src/cfw/service_api.c @@ -111,6 +111,11 @@ struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size) { struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv) { struct cfw_message * evt = (struct cfw_message *) cfw_alloc_message(size, NULL); + if (NULL == evt) + { + return NULL; + } + CFW_MESSAGE_TYPE(evt) = TYPE_INT; CFW_MESSAGE_ID(evt) = msg_id; CFW_MESSAGE_LEN(evt) = size; diff --git a/system/libarc32_arduino101/framework/src/infra/port.c b/system/libarc32_arduino101/framework/src/infra/port.c index e9f7eef6..d150ccd3 100644 --- a/system/libarc32_arduino101/framework/src/infra/port.c +++ b/system/libarc32_arduino101/framework/src/infra/port.c @@ -121,6 +121,7 @@ static struct port * get_port(uint16_t port_id) if (port_id == 0 || port_id > MAX_PORTS) { pr_error(LOG_MODULE_MAIN, "Invalid port: %d", port_id); panic(-1); /*TODO: replace with an assert */ + return NULL; } return &ports[port_id - 1]; } @@ -128,7 +129,10 @@ static struct port * get_port(uint16_t port_id) void port_set_queue(uint16_t port_id, void * queue) { struct port * p = get_port(port_id); - p->queue = queue; + if (p) + { + p->queue = queue; + } } #ifdef CONFIG_INFRA_IS_MASTER @@ -175,8 +179,11 @@ uint16_t port_alloc(void *queue) void port_set_handler(uint16_t port_id, void (*handler)(struct message*, void*), void *param) { struct port * port = get_port(port_id); - port->handle_message = handler; - port->handle_param = param; + if (port) + { + port->handle_message = handler; + port->handle_param = param; + } } struct message * message_alloc(int size, OS_ERR_TYPE * err) @@ -192,7 +199,7 @@ struct message * message_alloc(int size, OS_ERR_TYPE * err) void port_process_message(struct message * msg) { struct port * p = get_port(msg->dst_port_id); - if (p->handle_message != NULL) { + if (p && p->handle_message != NULL) { p->handle_message(msg, p->handle_param); } } @@ -200,19 +207,32 @@ void port_process_message(struct message * msg) void port_set_cpu_id(uint16_t port_id, uint8_t cpu_id) { struct port * p = get_port(port_id); - p->cpu_id = cpu_id; + if (p) + { + p->cpu_id = cpu_id; + } } void port_set_port_id(uint16_t port_id) { struct port * p = get_port(port_id); - p->id = port_id; + if (p) + { + p->id = port_id; + } } uint8_t port_get_cpu_id(uint16_t port_id) { struct port * p = get_port(port_id); - return p->cpu_id; + if (p) + { + return p->cpu_id; + } + else + { + return 0; + } } #ifdef INFRA_MULTI_CPU_SUPPORT @@ -257,7 +277,7 @@ int port_send_message(struct message * message) pr_info(LOG_MODULE_MAIN, "Sending message %p to port %p(q:%p) ret: %d", message, port, port->queue, err); #endif struct port *src_port = get_port(MESSAGE_SRC(message)); - if (src_port->cpu_id == get_cpu_id()) { + if (src_port && src_port->cpu_id == get_cpu_id()) { /* We bypass the software queue here and process directly * due to lack of background thread on this implementation */ @@ -277,6 +297,10 @@ int port_send_message(struct message * message) void message_free(struct message * msg) { struct port * port = get_port(MESSAGE_SRC(msg)); + if (!port) + { + return; + } pr_debug(LOG_MODULE_MAIN, "free message %p: port %p[%d] this %d id %d", msg, port, port->cpu_id, get_cpu_id(), MESSAGE_SRC(msg)); if (port->cpu_id == get_cpu_id()) { @@ -290,8 +314,12 @@ void message_free(struct message * msg) int port_send_message(struct message * msg) { - struct port * port = get_port(MESSAGE_DST(msg)); OS_ERR_TYPE err; + struct port * port = get_port(MESSAGE_DST(msg)); + if (!port) + { + return E_OS_ERR_NO_MEMORY; + } if (src_port->cpu_id == get_cpu_id()) { /* We bypass the software queue here and process directly * due to lack of background thread on this implementation @@ -317,7 +345,7 @@ uint16_t queue_process_message(T_QUEUE queue) uint16_t id = 0; queue_get_message(queue, &m, OS_NO_WAIT, &err); message = (struct message *) m; - if ( message != NULL && err == E_OS_OK) { + if ( message != NULL) { // && err == E_OS_OK dismiss Klock scan issue id = MESSAGE_ID(message); port_process_message(message); } diff --git a/system/libarc32_arduino101/framework/src/os/balloc.c b/system/libarc32_arduino101/framework/src/os/balloc.c new file mode 100644 index 00000000..1122ec4a --- /dev/null +++ b/system/libarc32_arduino101/framework/src/os/balloc.c @@ -0,0 +1,511 @@ + +/** + * @ingroup os_mem_alloc Memory Allocation + * Defines balloc and bfree functions for dynamic memory allocation. + * @{ + */ + +#include + +#include "os/os.h" +#include "infra/log.h" + +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER +#include "misc/printk.h" +#include +#endif +extern void panic(int x); +#define BITS_PER_U32 (sizeof(uint32_t) * 8) + +/** If defined, allow to use a block larger than required when all smaller blocks are already reserved */ +#define MALLOC_ALLOW_OUTCLASS + +/** Descriptor for a memory pool */ +typedef struct { + uint32_t *track; /** block allocation tracker */ + uint32_t start; /** start address of the pool */ + uint32_t end; /** end address of the pool */ + uint16_t count; /** total number of blocks within the pool */ + uint16_t size; /** size of each memory block within the pool */ +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER + uint32_t **owners; +#endif + uint32_t max; /** maximum number of allocated blocks at the same time */ + uint32_t cur; /** current number of allocated blocks */ + uint32_t sum; /** Cumulative size in bytes */ + uint32_t nbrs; /** Cumulative block allocated */ +#endif +}T_POOL_DESC; + +/********************************************************** +************** Private variables ************************ +**********************************************************/ + +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS + +/** Allocate the memory blocks and tracking variables for each pool */ +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER +#define DECLARE_MEMORY_POOL(index, size, count) \ + uint8_t mblock_ ## index[count][size] __aligned(4); \ + uint32_t mblock_alloc_track_ ## index[count / BITS_PER_U32 + 1] = { 0 }; \ + uint32_t *mblock_owners_ ## index[count] = { 0 }; +#else +#define DECLARE_MEMORY_POOL(index, size, count) \ + uint8_t mblock_ ## index[count][size] __aligned(4); \ + uint32_t mblock_alloc_track_ ## index[count / BITS_PER_U32 + \ + 1] = { 0 }; +#endif + +#include "memory_pool_list.def" + + +/** Pool descriptor definition */ +T_POOL_DESC mpool[] = +{ +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER +#define DECLARE_MEMORY_POOL(index, size, count) \ + { \ +/* T_POOL_DESC.track */ mblock_alloc_track_ ## index, \ +/* T_POOL_DESC.start */ (uint32_t)mblock_ ## index, \ +/* T_POOL_DESC.end */ (uint32_t)mblock_ ## index + count * size, \ +/* T_POOL_DESC.count */ count, \ +/* T_POOL_DESC.size */ size, \ +/* T_POOL_DESC.owners */ mblock_owners_ ## index, \ +/* T_POOL_DESC.max */ 0, \ +/* T_POOL_DESC.cur */ 0, \ +/* T_POOL_DESC.sum */ 0, \ +/* T_POOL_DESC.nbrs */ 0 \ + }, +#else +#define DECLARE_MEMORY_POOL(index, size, count) \ + { \ +/* T_POOL_DESC.track */ mblock_alloc_track_ ## index, \ +/* T_POOL_DESC.start */ (uint32_t)mblock_ ## index, \ +/* T_POOL_DESC.end */ (uint32_t)mblock_ ## index + count * size, \ +/* T_POOL_DESC.count */ count, \ +/* T_POOL_DESC.size */ size, \ +/* T_POOL_DESC.max */ 0, \ +/* T_POOL_DESC.cur */ 0, \ +/* T_POOL_DESC.sum */ 0, \ +/* T_POOL_DESC.nbrs */ 0 \ + }, +#endif + +#include "memory_pool_list.def" +}; + + +#else + +/** Allocate the memory blocks and tracking variables for each pool */ +#define DECLARE_MEMORY_POOL(index, size, count) \ + uint8_t mblock_ ## index[count][size]; \ + uint32_t mblock_alloc_track_ ## index[count / BITS_PER_U32 + 1] = { 0 }; + +#include "memory_pool_list.def" + + + +/** Pool descriptor definition */ +T_POOL_DESC mpool [] = +{ +#define DECLARE_MEMORY_POOL(index, size, count) \ + { \ +/* T_POOL_DESC.track */ mblock_alloc_track_ ## index, \ +/* T_POOL_DESC.start */ (uint32_t)mblock_ ## index, \ +/* T_POOL_DESC.end */ (uint32_t)mblock_ ## index + count * size, \ +/* T_POOL_DESC.count */ count, \ +/* T_POOL_DESC.size */ size \ + }, + +#include "memory_pool_list.def" +}; + + + +#endif + + +/** Number of memory pools */ +#define NB_MEMORY_POOLS (sizeof(mpool) / sizeof(T_POOL_DESC)) + +/********************************************************** +************** Private functions ************************ +**********************************************************/ + +/** + * Return the next free block of a pool and + * mark it as reserved/allocated. + * + * @param pool index of the pool in mpool + * + * @return allocated buffer or NULL if none is + * available + */ +static void *memblock_alloc(uint32_t pool) +{ + uint16_t block; + uint32_t flags = interrupt_lock();//irq_lock(); + + for (block = 0; block < mpool[pool].count; block++) { + if (((mpool[pool].track)[block / BITS_PER_U32] & 1 << + (BITS_PER_U32 - 1 - (block % BITS_PER_U32))) == 0) { + (mpool[pool].track)[block / BITS_PER_U32] = + (mpool[pool].track)[block / BITS_PER_U32] | + (1 << (BITS_PER_U32 - 1 - (block % BITS_PER_U32))); +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS + mpool[pool].cur = mpool[pool].cur + 1; +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER + /* get return address */ + uint32_t ret_a = (uint32_t)__builtin_return_address(0); + mpool[pool].owners[block] = + (uint32_t *)(((ret_a & 0xFFFF0U) >> 4) | + ((get_uptime_ms() & 0xFFFF0) << 12)); +#endif + if (mpool[pool].cur > mpool[pool].max) + mpool[pool].max = mpool[pool].cur; +#endif + interrupt_unlock(flags);//irq_unlock(flags); + return (void *)(mpool[pool].start + + mpool[pool].size * block); + } + } + //irq_unlock(flags); + interrupt_unlock(flags); + return NULL; +} + + + +/** + * Free an allocated block from a pool. + * + * @param pool index of the pool in mpool + * + * @param ptr points to the start of the block + * to free + * + */ +static void memblock_free(uint32_t pool, void *ptr) +{ + uint16_t block; + uint32_t flags; + + block = ((uint32_t)ptr - mpool[pool].start) / mpool[pool].size; + if (block < mpool[pool].count) { + flags = interrupt_lock();//irq_lock(); + (mpool[pool].track)[block / BITS_PER_U32] &= + ~(1 << (BITS_PER_U32 - 1 - (block % BITS_PER_U32))); + interrupt_unlock(flags);//irq_unlock(flags); +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS + mpool[pool].cur = mpool[pool].cur - 1; +#endif + } else { + pr_debug( + LOG_MODULE_UTIL, + "ERR: memblock_free: ptr 0x%X is not within pool %d [0x%X , 0x%X]", + ptr, pool, mpool[pool].start, mpool[pool].end); + } +} + + + + +/** + * Test if a block is allocated. + * + * @param pool index of the pool in mpool + * + * @param ptr points to the start of the block + * + * @return true if the block is allocated/reserved, + * false if the block is free + * + */ +static bool memblock_used(uint32_t pool, void *ptr) +{ + uint16_t block; + + block = ((uint32_t)ptr - mpool[pool].start) / mpool[pool].size; + if (block < mpool[pool].count) { + if (((mpool[pool].track)[block / BITS_PER_U32] & + (1 << (BITS_PER_U32 - 1 - (block % BITS_PER_U32)))) != 0) + return true; + } + return false; +} + + +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER + +#define PRINT_METHOD_PRINTK 0 +#define PRINT_METHOD_TCMD_RSP 1 +#define PRINT_METHOD_PR_INFO 2 +#define PRINT_POOL(method, str, ctx) \ + do { \ + if (method == PRINT_METHOD_PRINTK) { \ + printk("%s\n", str); } \ + else if (method == PRINT_METHOD_TCMD_RSP) { \ + TCMD_RSP_PROVISIONAL(((struct tcmd_handler_ctx *)ctx), \ + str); } \ + else if (method == PRINT_METHOD_PR_INFO) { \ + pr_info(LOG_MODULE_UTIL, str); \ + local_task_sleep_ms(100); \ + } \ + } while (0); +static void print_pool(int method, void *ctx) +{ + char tmp[128]; + uint32_t pool; + uint32_t average; + uint16_t block; + uint8_t str_count; + char *cur = tmp; + + for (pool = 0; pool < NB_MEMORY_POOLS; pool++) { + str_count = 0; + average = 0; + if (mpool[pool].nbrs) + average = mpool[pool].sum / mpool[pool].nbrs; + snprintf( + tmp, sizeof(tmp), + "\npool %-4d bytes count:%-2d cur:%-2d max:%-2d avg: %-3d \n", + mpool[pool].size, + mpool[pool].count, + mpool[pool].cur, + mpool[pool].max, + average); + PRINT_POOL(method, tmp, ctx); + + memset(tmp, 0, sizeof(tmp)); + str_count = 0; + + for (block = 0; block < mpool[pool].count; block++) { + if (((mpool[pool].track)[block / BITS_PER_U32] & 1 << + (BITS_PER_U32 - 1 - + (block % BITS_PER_U32)))) { + if (str_count == 0) { + cur = tmp; + PRINT_POOL(method, " owners:", ctx); + } + str_count++; + snprintf(cur, 7, " T%04u", + (((uint32_t)mpool[pool].owners[ + block]) & + (uint32_t)0xFFFF0000) >> 16); + cur += 6; + snprintf(cur, 6, "C%04x", + (((uint32_t)mpool[pool].owners[ + block]) & + (uint32_t)0xFFFF)); + cur += 5; /* hack to print the owner */ + if (str_count % 4 == 0) { + PRINT_POOL(method, tmp, ctx); + memset(tmp, 0, sizeof(tmp)); + cur = tmp; + } + } + } + if (str_count % 4) + PRINT_POOL(method, tmp, ctx); + } + PRINT_POOL(method, "*** END", ctx); +} + +#endif +#endif + + +/********************************************************** +************** Exported functions ************************ +**********************************************************/ + +/*----- Initialization */ + +/** + * Initialize the resources used by the framework's memory allocation services + * + * IMPORTANT : This function must be called during the initialization + * of the OS abstraction layer. + * This function shall only be called once after reset. + */ +void os_abstraction_init_malloc(void) +{ +} + +/** + * Reserves a block of memory. + * + * Authorized execution levels: task, fiber, ISR + * + * This function returns a pointer on the start of + * a reserved memory block whose size is equal or + * larger than the requested size. + * + * The returned pointer shall be null if the function + * fails. + * + * This function may panic if err is null and + * - size is null or bigger than allowed, or + * - there is not enough available memory + * + * @param size number of bytes to reserve + * + * + * @param err execution status: + * E_OS_OK : block was successfully reserved + * E_OS_ERR : size is null + * E_OS_ERR_NO_MEMORY: there is not enough available + * memory + * E_OS_ERR_NOT_ALLOWED : size is bigger than the + * biggest block size defined in os_config.h + * + * @return pointer to the reserved memory block + * or null if no block is available + */ +void *balloc(uint32_t size, OS_ERR_TYPE *err) +{ + OS_ERR_TYPE localErr = E_OS_OK; + void *buffer = NULL; + uint8_t poolIdx; + + if (size > 0) { + /* find the first block size greater or equal to requested size */ + poolIdx = 0; + while (poolIdx < NB_MEMORY_POOLS && + (size > mpool[poolIdx].size)) + poolIdx++; + + /* reserve the block */ + if (poolIdx < NB_MEMORY_POOLS) { +#ifdef MALLOC_ALLOW_OUTCLASS + /* loop until an available (maybe larger) block is found */ + do { + if (size <= mpool[poolIdx].size) { /* this condition may be false if pools are not sorted according to block size */ +#endif + buffer = memblock_alloc(poolIdx); +#ifdef CONFIG_MEMORY_POOLS_BALLOC_STATISTICS + if ((buffer != NULL) && + ((poolIdx == 0) || + (size > mpool[poolIdx - 1].size))) { + mpool[poolIdx].nbrs += 1; + mpool[poolIdx].sum += size; + } +#endif +#ifdef MALLOC_ALLOW_OUTCLASS + } + + if (NULL == buffer) + poolIdx++; + } + while ((poolIdx < NB_MEMORY_POOLS) && (NULL == buffer)) ; +#endif + if (NULL == buffer) { /* All blocks of relevant size are already reserved */ + pr_debug(LOG_MODULE_UTIL, + "Attempt to allocate %d bytes failed", + size); + localErr = E_OS_ERR_NO_MEMORY; + } + } else { /* Configuration does not define blocks large enough for the requested size */ + localErr = E_OS_ERR_NOT_ALLOWED; + } + } else { /* invalid function parameter */ + localErr = E_OS_ERR; + } + + /* set err or panic if err == NULL and localErr != E_OS_OK */ + if (err != NULL) { + *err = localErr; + } else { + if (localErr != E_OS_OK) { +#ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER +#ifdef CONFIG_NANOKERNEL + /* disable stack checking */ + uint32_t status32 = _arc_v2_aux_reg_read( + _ARC_V2_STATUS32); + status32 &= ~(_ARC_V2_STATUS32_SC); + __asm__ volatile ("kflag %0" : : "ir" (status32)); +#endif + if (localErr == E_OS_ERR_NO_MEMORY) + print_pool(PRINT_METHOD_PRINTK, NULL); +#endif + panic(localErr); + } + } + + return buffer; +} + +/** + * Frees a block of memory. + * + * Authorized execution levels: task, fiber, ISR + * + * This function frees a memory block that was + * reserved by malloc. + * + * The "buffer" parameter must point to the + * start of the reserved block (i.e. it shall + * be a pointer returned by malloc). + * + * @param buffer pointer returned by malloc + * + * @return execution status: + * E_OS_OK : block was successfully freed + * E_OS_ERR : "buffer" param did not match + * any reserved block + */ +OS_ERR_TYPE bfree(void *buffer) +{ + OS_ERR_TYPE err = E_OS_ERR; + uint8_t poolIdx; + + /* find which pool the buffer was allocated from */ + poolIdx = 0; + while ((NULL != buffer) && (poolIdx < NB_MEMORY_POOLS)) { + /* check if buffer is within mpool[poolIdx] */ + if (((uint32_t)buffer >= mpool[poolIdx].start) && + ((uint32_t)buffer < mpool[poolIdx].end)) { + if (false != memblock_used(poolIdx, buffer)) { + memblock_free(poolIdx, buffer); + err = E_OS_OK; + } + /* else: buffer is not marked as used, keep err = E_OS_ERR */ + else { + pr_debug( + LOG_MODULE_UTIL, + "ERR: memory_free: buffer %p is already free\n", + buffer); + } + buffer = NULL; /* buffer was found in the pools, end the loop */ + } else { /* buffer does not belong to mpool[poolIdx], go to the next one */ + poolIdx++; + } + } + return err; +} + + +#ifdef CONFIG_DBG_POOL_TCMD + +void tcmd_pool(int argc, char *argv[], struct tcmd_handler_ctx *ctx) +{ +#ifdef CONFIG_QUARK + /* Display with TCMD response on Quark */ + print_pool(PRINT_METHOD_TCMD_RSP, ctx); +#endif +#ifdef CONFIG_ARC + /* Display with pr_info on ARC to avoid message overflow and panic */ + print_pool(PRINT_METHOD_PR_INFO, ctx); +#endif + TCMD_RSP_FINAL(ctx, ""); +} + + +DECLARE_TEST_COMMAND_ENG(dbg, pool, tcmd_pool); + +#endif + +/** @} */ diff --git a/system/libarc32_arduino101/framework/src/os/memory_pool_list.def b/system/libarc32_arduino101/framework/src/os/memory_pool_list.def new file mode 100644 index 00000000..b331f9b7 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/os/memory_pool_list.def @@ -0,0 +1,21 @@ +/* + * Definition of the memory pools used by balloc/bfree: + * DECLARE_MEMORY_POOL( , , , ) + * : must start at 0 and be of consecutive values * + * : size in bytes of each block from the pool + * : number of blocks in the pool + * + * * Pool definitions must be sorted according the block size + * value: pool with 0 must have the smallest . + */ + +DECLARE_MEMORY_POOL(0,8,32) +DECLARE_MEMORY_POOL(1,16,32) +DECLARE_MEMORY_POOL(2,32,32) +DECLARE_MEMORY_POOL(3,64,8) +DECLARE_MEMORY_POOL(4,128,4) +DECLARE_MEMORY_POOL(5,256,2) +DECLARE_MEMORY_POOL(6,512,2) +DECLARE_MEMORY_POOL(7,2096,1) + +#undef DECLARE_MEMORY_POOL diff --git a/system/libarc32_arduino101/framework/src/os/os.c b/system/libarc32_arduino101/framework/src/os/os.c index ed84eb8b..f9e6faa3 100644 --- a/system/libarc32_arduino101/framework/src/os/os.c +++ b/system/libarc32_arduino101/framework/src/os/os.c @@ -33,6 +33,9 @@ /************************* MEMORY *************************/ + +#if 0 + #ifdef TRACK_ALLOCS #include "infra/log.h" int alloc_count = 0; @@ -72,6 +75,7 @@ OS_ERR_TYPE bfree(void *ptr) { cfw_free(ptr, NULL); return E_OS_OK; } +#endif /************************* QUEUES *************************/ @@ -131,7 +135,8 @@ void queue_delete(T_QUEUE queue, OS_ERR_TYPE* err) { q_t * q = (q_t*) queue; while((element = list_get(&q->lh)) != NULL) list_remove(&q->lh, element); - cfw_free(q, NULL); + //cfw_free(q, NULL); + q->used = 0; } /************************* MUTEXES *************************/ diff --git a/system/libarc32_arduino101/framework/src/os/panic.c b/system/libarc32_arduino101/framework/src/os/panic.c index 9fe88ef9..c77fc501 100644 --- a/system/libarc32_arduino101/framework/src/os/panic.c +++ b/system/libarc32_arduino101/framework/src/os/panic.c @@ -1,6 +1,9 @@ #include "os/os.h" +#include "infra/log.h" +#include "aux_regs.h" + extern void _do_fault(); void panic(int x) { @@ -14,3 +17,16 @@ void __assert_fail() } +void __attribute__((weak)) _Fault(void) +{ + uint32_t exc_addr = aux_reg_read(ARC_V2_EFA); + uint32_t ecr = aux_reg_read(ARC_V2_ECR); + + pr_error(0, "Exception vector: 0x%x, cause code: 0x%x, parameter 0x%x\n", + ARC_V2_ECR_VECTOR(ecr), + ARC_V2_ECR_CODE(ecr), + ARC_V2_ECR_PARAMETER(ecr)); + pr_error(0, "Address 0x%x\n", exc_addr); + while (1); // Sid. Acknowledge KW warning. +} + diff --git a/system/libarc32_arduino101/framework/src/services/ble/gap.c b/system/libarc32_arduino101/framework/src/services/ble/gap.c index 4cf71d47..26858281 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/gap.c +++ b/system/libarc32_arduino101/framework/src/services/ble/gap.c @@ -910,3 +910,14 @@ void bt_le_set_device_name(char *device_name, int len) nble_gap_service_write_req(&gap_service_params); } +void bt_le_set_mac_address(bt_addr_le_t bda) +{ + // Update the MAC addr + struct nble_set_bda_params params; + params.cb = NULL; + params.user_data = NULL; + params.bda = bda; + + nble_set_bda_req(¶ms); +} + diff --git a/system/libarc32_arduino101/framework/src/services/ble/gatt.c b/system/libarc32_arduino101/framework/src/services/ble/gatt.c index d3ef4c09..fe5ce8e0 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/gatt.c +++ b/system/libarc32_arduino101/framework/src/services/ble/gatt.c @@ -83,8 +83,9 @@ static uint8_t bt_gatt_uuid_memcpy(uint8_t *buf, /* Store the UUID data */ if (uuid->type == BT_UUID_TYPE_16) { uint16_t le16; - - le16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val); + + memcpy(&le16, &BT_UUID_16(uuid)->val, sizeof(le16)); + le16 = sys_cpu_to_le16(le16); memcpy(ptr, &le16, sizeof(le16)); ptr += sizeof(le16); } else { @@ -208,10 +209,12 @@ void on_nble_gatt_register_rsp(const struct nble_gatt_register_rsp *rsp, for (i = 0; i < rsp->attr_count; i++) { if (handles[i].handle != 0) { + uint16_t le16; + memcpy(&le16, &BT_UUID_16(rsp->attr_base[i].uuid)->val, sizeof(le16)); BT_DBG("gatt: i %d, h %d, type %d, u16 0x%x", i, handles[i].handle, rsp->attr_base[i].uuid->type, - BT_UUID_16(rsp->attr_base[i].uuid)->val); + le16); } } } @@ -245,7 +248,10 @@ ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, struct bt_uuid *uuid = attr->user_data; if (uuid->type == BT_UUID_TYPE_16) { - uint16_t uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val); + uint16_t uuid16; + + memcpy(&uuid16, &BT_UUID_16(uuid)->val, sizeof(uuid16)); + uuid16 = sys_cpu_to_le16(uuid16); return bt_gatt_attr_read(conn, attr, buf, len, offset, &uuid16, 2); @@ -279,7 +285,8 @@ ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, * Bluetooth UUID. */ if (incl->uuid->type == BT_UUID_TYPE_16) { - pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(incl->uuid)->val); + memcpy(&pdu.uuid16, &BT_UUID_16(incl->uuid)->val, sizeof(pdu.uuid16)); + pdu.uuid16 = sys_cpu_to_le16(pdu.uuid16); value_len += sizeof(pdu.uuid16); } @@ -325,7 +332,8 @@ ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle); if (chrc->uuid->type == BT_UUID_TYPE_16) { - pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(chrc->uuid)->val); + memcpy(&pdu.uuid16, &BT_UUID_16(chrc->uuid)->val, sizeof(pdu.uuid16)); + pdu.uuid16 = sys_cpu_to_le16(pdu.uuid16); value_len += 2; } else { memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16); @@ -1040,6 +1048,7 @@ void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, struct bt_gatt_attr *attr = NULL; if (rsp->type == BT_GATT_DISCOVER_PRIMARY) { + //BT_DBG("%s-%d", __FUNCTION__, __LINE__); const struct nble_gattc_primary *gattr = (void *)&data[i * sizeof(*gattr)]; if ((gattr->range.start_handle < params->start_handle) && @@ -1052,7 +1061,7 @@ void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, goto complete; } svc_value.end_handle = gattr->range.end_handle; - svc_value.uuid = params->uuid; + svc_value.uuid = (struct bt_uuid*)(&(gattr->uuid));//params->uuid; attr = (&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&svc_value)); attr->handle = gattr->handle; last_handle = svc_value.end_handle; diff --git a/system/libarc32_arduino101/framework/src/services/ble/uuid.c b/system/libarc32_arduino101/framework/src/services/ble/uuid.c index dbacfd94..71748f50 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/uuid.c +++ b/system/libarc32_arduino101/framework/src/services/ble/uuid.c @@ -61,9 +61,11 @@ static void uuid_to_uuid128(const struct bt_uuid *src, struct bt_uuid_128 *dst) { switch (src->type) { case BT_UUID_TYPE_16: - *dst = uuid128_base; - u16_to_uuid128(&dst->val[UUID_16_BASE_OFFSET], - BT_UUID_16(src)->val); + //*dst = uuid128_base; + memcpy(dst, &uuid128_base, sizeof(*dst)); + //u16_to_uuid128(&dst->val[UUID_16_BASE_OFFSET], + // BT_UUID_16(src)->val); + memcpy(&dst->val[UUID_16_BASE_OFFSET], &BT_UUID_16(src)->val, 2); return; case BT_UUID_TYPE_128: memcpy(dst, src, sizeof(*dst)); @@ -89,7 +91,7 @@ int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) switch (u1->type) { case BT_UUID_TYPE_16: - return (int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val; + return memcmp(&BT_UUID_16(u1)->val, &BT_UUID_16(u2)->val, 2);//(int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val; case BT_UUID_TYPE_128: return memcmp(BT_UUID_128(u1)->val, BT_UUID_128(u2)->val, 16); } diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c index 405c984a..07231cb1 100644 --- a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c @@ -113,9 +113,11 @@ static void handle_msg_id_ble_rpc_callin(struct message *msg, void *priv) { struct ble_rpc_callin *rpc = container_of(msg, struct ble_rpc_callin, msg); /* handle incoming message */ + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); rpc_deserialize(rpc->p_data, rpc->len); bfree(rpc->p_data); message_free(msg); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); } static void ble_set_bda_cb(int status, void *user_data) @@ -192,7 +194,7 @@ static void handle_ble_disable(struct ble_enable_req *req, struct _ble_service_c resp = (void *)cfw_alloc_rsp_msg(&req->header, MSG_ID_BLE_ENABLE_RSP, sizeof(*resp)); - cfw_send_message(resp); + cfw_send_message(resp); // Sid. KW warning ack. } @@ -225,7 +227,7 @@ static void ble_service_message_handler(struct cfw_message *msg, void *param) MSG_ID_BLE_ENABLE_RSP, sizeof(*resp)); resp->status = -EINPROGRESS; resp->enable = 0; - cfw_send_message(resp); + cfw_send_message(resp); // Sid. KW warning ack. } } break; diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index 2f8ce4f076a5fadf399bc63c203cf2f33a93eca6..fc6fa9920598992207aad417d72d3fc9aaddd3e5 100644 GIT binary patch delta 173392 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

q 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%0DhkslAIOeCiBh$w%>$K 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 delta 160538 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μK)*(a1#Jm;=BZSV3jwMD7MWSt(i7DXLb#VRX ziUM6sp>;y%?Q|cpB({qfiE&Qz6=Ip`_(14P>`P)Ka24!)BXrL4{ybs6=r4huON@jAXdfMj={q5uEp*x$D)I_#qf~HP;0nQeggF=50^y4Dm z9Q0p_Q|VVBM59||$!I8v7>2%qLK<EccZO$smp?CFd1nhSynPImr)S+1Ew3{xgX61 z^L?jv2+j?Wr+eYapB*B@bwZ?5mkVBrdjXH-0t4@4oG|);!G+kQ6ITglhUlkw@@7$r z$gB&IzcECfZi?r|+$WeV);N;55GibRHNqGG27MM!;dQ~di0;Z2C=l-xdMn`1L-5}P z(+>R|&ko;Ph$-*GMe}eS;vg7Qpd&;E`?D0mYy}x1I7@IZ(CHshz7ued5E;JDkWSqJ zA@cN(JpXB|U{szJoEUOaE||@c&XT7P43SwuEcXLnLnuQ-S}<9+hsYRD8+SN^nGFV} zyz#oDQ2#u6mI`1{nNF1Fe0?C8GGB%m{WgStOfYqS4UsWRa(KNE;&Bhr;o@m>2yX1> zT@Nai=|p)>Ry)B=xJwA`E|~Iks66?RAu=Vz6hN2~LgxYu%9BN(*~qZhqeJC6T&n~# z;k6<72EkNjH{;1a5F+z9u_SEP`JmsSofkrM-wu(XpB3~xv9HL0SH>zj9D>dIAJA!+ zj+W<$1q3qzE;u5cI6*Mw>2!JWT&CyA6cW1`4O(Y~FnS6m@0?RY?!q8(%Epln%*4t= zFqhhqPF*@>bxCvX@&{6+1v>`;ZB@$P;L5sCL|nZXK~q+r!bya5}qDHuMkY-1tBu@_B?O!I>Ai%h7im(gp^^YL~qZNe~6jcv-Jw?xzyjL(2ekcS#Dwy*0AU*luYav4K6H87%3!#50n5?5AGIT9H*E38o z+hBAEjuo5pI{mq z5`sqvrd|5Uo_u+TOck*tY~BK4lXk8M(Oq>)CTJXG&yhB-intb3xSK+P?-xwFbf7)? zXG3J(AeMyR522g4N!TIls}LD<4VFJm7d{r5q*td!C+Ncusb9k1B(Y9+|wUX41vrLkyuOv9#ea#L^6}B1Ua7Fin4!E_w{3!C8i4!E$Y-O>o&2A?&3j zN!@LB*_%Vy&ygfm__51=6YPK(|3&wBA~K^%9xs59_eFvNn_?spTn#jS?Yn z5sKIE-4H_GNG!Gga0vY|V!0A;hS1HzeYq-!Lg?W~dD2*KFOKM4Q}cwHYU|lMu#}z`81BMJ-M_sPDxM@)26{l!x%J7NK7e5m!+3A?S2J`DO^4*VHq4065g-_pZ)|Fq!;uhz>8b zrxT|2oTx0_>S@?L5F%4QL?#19L+uvYCaafG=lc*HHUZBr=ORLNM!Gt$Kqta#@73;* zbYD`WbS-$7HfB?vK@CKEA~2bvhve9)_F9)c73@|GL)g6XUaGg8k?O;)&J)yW9-_nU z*3;Q_hR$9rjO`tks3V_+QeKEsT8Pp|XDA&Nr38l(V1|D&Q@LWye~LJJq%vZT7*0GCwcNx*khVGMw}Osi11oZ_}0iE6Yhe; zOPJ#}o(|>a;okU+K!V3hhg@hu)((LOtWbU$?1sZL$iW{^i8e+xMgJN?@L-2C48ArL za-h*(_Yix7&#<>S&9d%fVhl5Icn15QVX*#4G)FLLun32jL5?<2hYgIOPYb-Gc?NM# z9uE#ah05i(;@8n<&p9JMRk%%f^}OI&nh6?lkuZwRMP1;EGCRe(q`+cG83x(z{Ntl6qOntKof3h>o+r_>pN@ij{S?x0}48I(mg+oXlWmsTz%;7*sQ<> zNZhuj|M_>UXPy38^nbEH20vo0Q~l9f7yNE%ktfwlSx86-~w(V>B$Lo}; z-`KZSRsZ9|NWcEAJ60LZ{8dF)*Zp)~WC~|V>G5%3`dL(51HprR*ME~9+ zKA4-rQM}g36szN2NOGwJagyIzc`!ZYLnLsCkm?{`iv|0`oR`3Ipi1vVdQf?MhIIFyus1yYW&U3o6rNDy3rawH4RhE0BRMVoIqS0CzI zwhx&w|^$5Hc~qzX+XQ19FmzmTz)af7>^B7}ofCIVls6pDBV< z5ib&aH!Q3aJOUPO5Zn;DTLf1kh3$e_+vW`fHf_*(PW!!KXRqLIQLfK?)&+*auh|X> z<6ba+68sBe6p_jofF3J&G7Om|9iTS^y-?^!5%&;0&e?W2DW(8ti-fYxdFOD3{f|1% zPlvB<@fJ*&ZyjM`2e28s1ZHhrjO;MU=bd|x5@O-(7l>3kHzd-qvEY?5>)9KSZ@cCbhB zwOigSClkBu=1ix0q4!_c>TGin|o=X3T95F}bwa_`zQ!MmvKsO&9!am2Y&y7NV=Dli75CvA>EKz8H zDy|axXeb1QJ_PioLjS>O_rr+pRVey?q2LK-9TEI6iy`=9Xw|_>i}%8uNbFY6b3f#i zu_k(wgp$pNxjw>X$%YAi8|dc=oh2(1`hL*O7mzT=20UBnU4SnU`O(gZAD5)OgH$r$ zxOlIS1;lP9j{caKd;sioNJ1tuaruS|d#cm)L{c(M&k*hua4Us7+8J`9YZ+_q3*lmt z-}*|pw0T_UpM!ov=)8PbLTu8C!KJQ4ECq-cdS}p+gg)9icp^g`aO(Y(Q8yfE-6Qnp z!mEpZT3A1LDH^*s%Cd$SmBR2OG16touz_(s7`Q)%H-bVZ-kadLni#ejO!p`^1D~2+ z%ddwrdCfeVINogGgNGtdWCZ0hVmOox8;PYYKSeBW_(zCk1jQ^8lUMt}HP^(bU12Is z4@^))@X6!`MpgPCqm{aAeKi-~A6D1LS+@2yKvbbeAhOqX*IOE>!S-X__4kYqbl1g+ zYOu1jlL)DXdOxE)-7X2yQoV%H8vRa^nrmNY7VGpuwxIvG7} zxt(FhPSk1T*v;v0WwG|gj(Q8&;oZ7&08Dp##rIdcjpP@$@6gHYHqx)=-dB9Z34!iN z-o~yCzuUm9_UnlMwcSP{YWDxlS84y=4fB?Q2j7Zy3Fh2!;!0b>3g=dh7r-d;RiM=Q|4e{%O zJLye_d`bPeEb)Mlz5qTf_#p82g1-m;m*8K474pe^aS)=u;O_b)%mulnNT!fDuT9^F zqKlwjAh-@>%n&ZVt4B}k0>B`_1mK<6qdV)l?P3hoa2JA!9{{;A+Y&^;)a1LJ1B z1$5WydPjUoYO~Hdg67T1@j=j{)o2v6MDUf!(Nw|onU@G|2l{fsoQhr}_&8G7Ah;Zu zO_?@v4P9Ro_%LY3*96W$T0a_JQ-o|}gbEkJXcNI)g<)0$gMKFrbt4^ohJhkehWK1! z893$(C+%FJGmiQ))JEMGQGOz*bO^i|1hYN>Qq+E!bZFylv&{Mc&~x<$Y7f!75Cyq9 z`CpWhCu0Qiz%-~$hltwzZn~k+x%CO%deYgZvV_idkuUT|K<^-QE{84>`sbjZE%eR6 zL&7XwcFfn)P7K#uj`_N%sru7nxPE2%q+@}&?>Lvr4OI+vJP)9sFfy#ws?NkUGZ zLv4-`c1JzyxGxvw+jQJFO}$+c^_}n2K#Pe;&#PbN+p8@OXnJKos(aL^q?*iMe1~lN z`=+}7Ngp4Q<4^jA-~m|8sNXuDe~22nZiIi6DqZ&-@I2jH`RA%ay+h%wla8_xEz%d- z{?>LcTmKRpoo@e>r|+}XZEW=!#oL_!9m3(HBUm|#u(l?$Cn;_ zAG8j*3H+eFkJt=g0DpnzSuAum_kn`TVQ;u#K3YZ#{tb3Y1;31Vvf$eg&lJqAc!A*8 zVe<-(A~NAYXrU{G@g)pt!5mSc0m|@xd_?eBkl7*l9^j`1^TA~vlaS{-sCi5RHwK-3 zHSKWX>;u6aI1TuzoZN}r{ax_+hz|;G07LADsk{d=Cj?Id{a3;3LH8+FhAW{X1=mHv z8VKf_cD7)4967|nX71pDnM4C-=t>6g>xgli8T>Tjk%H-I@GYD&3vfBg1T(``f-gqG zmkH*Y7ISAq{$6Bmt(#^1z3D?>%iR zF%n#+oe_9Myp7X0Ny6Q*QeknSupiZXMi^`BNBYufwE0NiC5$(Ybkp94IC&k6n^BXx zZfP#ZBhoeA`dl|JW6t&ULDp_R7v*?SzbMq7&PB#yEc-lPPBNGJ{SEKP-dp0g#3qNc z&hsTDzldBV;zg7>{0g<$h#2e#^(L~Hp~AW67ukMT8Y%1-^&YbOfV7k(JOUXUVK37s z!A_y^oua)AqWgsXU6{^0-li%!5bIoChXo& z8!kAA&)R3(IW@1G?;8^c_C|uW{TQM#+>uROe z%`}U=ZVzKSi*;qpYE^dm2un$iw`%Sv^(FhU!^5r#cvN@N^(G?1OaP;ydK;rL`Xr-8 zddMW-5c{QUy?K%kdjn%$jC#8zkCR-sz>aUomdqHrAL0J2sf zm~S7Q1;66#xIM!@uAI+qPdtnLv3Yhvft|LQ6aZ#l%dU@x9s|BcFngjK1Yha2+p@?0 z*>ZLk4sm*{3#%S=$1Tb}8t(jb=OrrEnROQiL7ZLqUFOu=+FHfwrlSH$I%k4ET5Wbp zwqkEyG@TUZnyneC)Y-N*%ia>Ek9G-5R6Ct*ccnU!cQ*=u4tMMumYGDd-wbnx-kllF z53d?q`oK%-uf`(^I&>~H-k80&v}V-H>V-_TMYnm-PWyw({NLI4v~x}BG4-S`+-!Mj z)$IFTb^A9X5_;!9u>nQ16RQ6oJ6+mNipa0I^@O@h)x7Xe^`dGzWJJl?!-toQ96j`G zZ%g`%XU-{~JagiNH282eQNO4WSe`M)j<3N?)6%FE4&@uyEFE)2xOB!f1OCL;(Ghlf zkkc$?kODNi`^HTJ=BpNN5i|ZrhoYC@<>*v~2tJ0c$eU;S3^HXxFF=};1m6yurb)r( zJ+_Qs0B-|-nQ_ftN-UqH?H24q%1#*A+On}r?VF|k&l zhYLp6V|)YB3xVT>UX6@46g&;|Cd5Gqbw*B`3xh9%tpszybe>?2c<@ru=l~=}=al#& z)C6ariEoC{k%GSkE)@)jc~V#A1znp(%K>tqgl~6t@m|p}$AwecQ8!}wBK+ILYY=p!wgP!Zjz$h0Ekj_k)FG7GbgAk$* zN5)zkFCT(iz|hlzrvbk%m@m%e!wkq51OFy;ey+>uFWUJSbZ=tpD->ps&?f?q7R*%# zlYmE4fqq++Ft|SXO2Pbu_GS-*ez#y=qMd?!fxb`h#fbSvM4O9sFb2Cz&el4{&Pd)2 zsT`!@xg=*1L+Td&PK=#v{}itMb+IMoN!_k4Hu`%_pU>!CJ)cpu-c;Aljd=(v=O9C_ z+DCQm>L8aYWWzE^%vNuNBOfCgzql^FhB)a1ilh zf7-`(7H^tdY zf?V(3iC$fOTqK0P#9&j+czaNr$|4H=3KV@ph|GM!+%5VlV)pY0=IcqZvzc_*Sr0pR zi2RMfk3?Gf)_8k#{uekgUpiu#hoNKCsZ8`DhBEvAzCyoIw@$FTst@$!1YD;d^fd{% zmLq+7XM#P}-cd(4s&6mGj!K*Aqx@eZV?j8Wey*iKSW1kHt=D_NE@+JMnC(14dI{Nh zMc7@D++o4b!vv>+sCH?%*?BXkfxR>~IF$_4DZ^dFP+~BBk~qF*f*<*2NV6dWqYUkc z*^eNYu0)*L^`y%s?TyTNQ%L4>EV)QGk>0@F2+TUd6REf)-ULamQNG=IKKz9>7iQW= z{2XX%jcqRn>1SJWFU@Ycej6KmX?D{S7(docKg{?*H|=kW4LBR>5p5y1@hrUsr}pl% z^!JSSou!L&z%JHK4z|)9s`oRh((Q5~yuG_#nhR#OekT_x73qO_h!*H)^NdVFKB7WB zKHqMww(8aSxP0|>b-wM+BOR3bV*yZ|=2(vUAof-j&2Q(9b83p(*@cY*oRzaNU+{l;M6Cvke=_|slPk(+VJG=2{9~m#(=@gvs$X;O zxpsG@pB{&0>a*})Z~nx0yZC&&v)+AaD0V(0e7C{#zi%iW9;35WG){zj$Si(6Sx_SqX*nW z51eRcU1NLUu^t5B*5_>XTRW0z~2bwH%&hXX8WxN z9&3x8gE#EPFUC@Z9*%_51=CMxPs|~>Uje*fx7VQ1Q-mT>E5?U|Oc3}&p`QcgGQr0X zPZ9h%5}q%(E$|hBX?vOAeAwS0_z7gFGd)`7`h76A3Z4O6E%;{$nb|DJ@EK@kvw$1u z+b7!@=k7pWeiqShPzeF#ljeQ|JwY%X-KK(R!c1U6=6;wq6Ij4pz+on^fSc&lDR#zL zG(AVuXqwAHX`0K)%+wX|JYcpw(m#Q@?SkLa^I^h%&#yO6u@i&KNQMcD`G8{u7by6c zf_KA|86|@ZlPeTDAAVd_Mcv)N!v(j1?s&lskhFOKKxQtm7J6gY-0I^${h?7PKQD~d zz+B)#Ix+7O;%2&}+)hg26}3@(4lpq^BL;h#U#}^*6IFq}s~p9v^y}BkvD@+2csmB! z{w7=prrKRpYu$S)p1-YiaH@R-n|95bW+#!kY?_^pX`Fkfp<8dQ_qfq_(~M~DbQ4XR zZqLodC@QqVK7b2#6V%zhYnG&3lA zHpYtHI|EK#Yweq9((Ex4?!i}nT`|)w!?Kwu(_7*CzL`eqBd{SHHOtNo^3CB`h|iECQAFuF&6t{9&QD0{wfTb1?ZNu{_o5(sL0z$;7bJ7xHa{PCMAg0lg&{ z{C;?tFqYwJk0BP9V4BcbRhJM${yNCd7dj2C75YP8%r z5=x21x7tJ;Yla%IqLC+ZNz5nbQmb6V>eY<#q3Y|r*8)vyT3v2`>E{l7i{Z8m)g_F^ z=xvOq=#z}9^pGWRdv3_no0q^hdMQsIWqc@47cGSw6suP+g;XQ`DWhzidnKYm9b{Cb z_b?iyn_h)zl%B7CFR>4trT1M8JB@VKG9>eHA3bjwHYV?(Z(oM{ zb8kQW(lTr^zOET6Mt?`rx@_Yf#px5v;TG!wdKG~6*1yhgV^J$Uxa zs>tSxa(PC6bVby>Y&_^Z+^81E>g!Tfi{RA^NIo~w^{m9u#xJ9Hn5|SN{$(Hv?D~89TJ-e{69c!UNvK#yBw*ro?u$nBI!;-ef88xCSqAQqS z?8mr4En0VNZs=Y2w%pr3d31CgYCYcxJrL|4Vzo<)Ei+OEZ;Nh!pYpNDSwwMC~L|%#3Y0Dzl>BrMl%OL%; zJvbsYev+*yzpvm&Ah&BUzsmgD=GSL_{S+&rOJYvl-#-vD+%WSSZhpC16V8YEjWEB# zNE1bwUv6N71vuunuKBHJeq+sVocWD6zX|5IzWHrne!2J)13Bimq4`ZVzbTgK*<$@? z+34m>&*tyGoU#T7{XN(M-7e2xL>zF1dJY1Pu2)_;+w%7!j&bR|iR-$!n7E$#uob;> zW;3J^4T}6qjd<)l&Q6A~{bazg^@nvDaXLr2dG?3(T?$6M=@VyKVg2wXCV39)&t$pM z6aB~lVxOxJ2pjq^6kNuz5QX8ycD?cAtgsPpL&9&xu?hf#Ry03 zwd+&hvyz)|4;3>Sp>9xPA=H4^THU}*nXw3UgDI#Pi%`5(tuatl8^iI~Z^%=%-3R~~ zzabIyFjS(uLx!~)ZjUSWq-pY?r}FjN3+iC78$=on7g{4aQw(s2u!sSE!x0dgsNt^7 zCTf`K(nJka?cDz#5eeNUYJ|OrBK8&d9j5YK-6Z%KO$>V|*jAcn?@U|4p(;<3qZ7wb zets|{dDYS|0u7FeNE65jT?D! z2~?AyNz5p7-!3Ra#I@*};Hb$x@CMy#{K>0EW4;$G4hAn{I`^ahnF%nM6gI&c*D!>X z=3$Vy3@e1Ri7Vi_w}?w?PIXDW_7co8?2r+e8{L08DF-BO58_It(QI#!B;yfmr)-F(^+#I) zHr7lEsj2)eAlk^?KeEhaQd3!}YN*KY@n+V3tBu?*BU^u%iJbsUZKUFyUtEObjwJ5q zL6Xz~Nn3?f;1-pn9YT_}`-+@OOXIcqn@h4U$A@jq1ou9JUHrFzp}6{P2^Ntt4~N>w z%>}dd$}Hs0+oz3RdK$N>sB!5`flrll0PcVD?F%`oo{GWVUiRLr-zR54aypRUMO$?8?TB3q>d z`KVxM%O6Zkz8XP*mrLAEBh?@v+$^T-{0t$a?Cd!ffb?&2B_-ocx6VkOy75f)t&YZg zB>&k3Y_NF4`Ey$C)0mZQr`lb(pWUu060>89)W8b71>B0FYL;5g7=e#@wQ+f*UfNQ1 z33|mcC0UNFpHY%aLHf5PNu$l(r*ab5g>&PyD-P(meYx7W8+P*W3weXP%+W(uRstz)TkkJsig@D^Y@n(@ux?ajM8o)^abc^IhB z<=1(FPjdFL@#Yz1l>9?cBSC8*sjQoLK1||yY6W<1gzDTug{hQby~Pvp*#&d1k+Brz zg)}yVIFLkkg0A7aa3-9K$l?9winqrj5YO-7?7Bh?458SU9x=WaA?a^G4X~7M`WI4? zJy)pu1thZX3MGyvk-b+aF?b%S?7%{)BS>Tq;1W6NZTZjTO%zJ#M}No=;*Dn%Sc>y- z{|fvDcIx-)Ml757r?y=RXus03X9W>)_M z?pz5kDJrTir5k7%Yphwg=iVD?WaOt#T^r9`Q7 zlc~`C_Hwz@=D|6^rqf+3>nv&~556NKO+Oy{@h5 z5QP)`d((W-Go34XBqO}{ZT$0&ih*{0T@;9n(wnah^w4?pR9&ZHL1ay% zJbXc;*JMMk;)bwDz4?ASO3#mi6{8#bU_=w0c4JtC^HWiM3^hEHud?tx&o}vM4*usy z!@z^+C~VB+{^=CNf4}Od2j&*S;&n+owYWb936-zcVnp^1{!E;5<&vG7eO(vdXgKHO z1bPyLPYRzpWJ&ps9f8qLPxjSY?5hqA@V##3Fb;HIa&zA8I=v8G#|mG}E&9SjwXXh^ z8zz+px=-$3Sl)l&z}zbaPsz0-b)dca<@a+MoUPzj&y8xrI;cKM*L1}HR%?v~pcyFG;-JG%!0|kx+y~naL5Jxxoitd9YyI4B#8zStxuvO0r7jEKLu2I--S> zwm(fDy-t-Tv@naO$Yjal@n$q%59y_v>V+*MV-@}*F>u^_qRc23$H#gb$v>Y#j?t^{ zh|E)8N^r=XXV@H2REl4jy}f`~`AO_bA>9H+##I&yvMsgNOUhkl!umbVlN> zNb9-N`9bRqA*?V0(PqUhpb1Ryhtn0zoB){>IO6ecm5vJW0_6+NI6wQ0^U-q7%8$47 zq24O9YkRAtq|dp7dynitWN=AIPJZWtGbx=qwc}{U8FHOEx6>_)RV$URkFLjN7SoE= z{G{o#CYMxHVv22=w{~jWq{@jC^{>UMHw1g0t?o@NDVbAMQkmbbq-qwT4wgG;QaKy{ znM7A?!mc;lc7$R4J2N7|IT;b)bd434;g*tROqG$$CW6}_PR9{vJaardh>aiZZc`Cw zyh3+*Ra_RB2*YvI%Qny&g~2E^)_nU%;Bt2_;YGl4Ocmii93K4{V)UyF zFX2cvZ;kH!U!3vJ+!>cR<`4k`gGL^{omtuB$#4aeXR{Ue$A~k2iu;`w6KDj5ZV>Vm z_zjk)P)d3p&120Wv2kFqR5OIm!DmnY&Jetv81_3smve{4KFj}^2(iY#2~qfkLcAFe zxZ21w%4X@|Rw1|#uvZwa1c+_t>cT-X@~jD7T~D_za4=5#HrA$xxt`a$U4lNqsl2NY2H=Qe9U@T2%g{K% zY&uMsbY{kD$bhEmnbuNZ+E3-a%?!P8c!`+=S{kx77Ex-tiziT~i;JfccXcsp+KM~N z#a>nOjA~45BVto9<_dus^wK(y>3bz$VsXqC!gL(5Jq$!ocuI!Yn>dLkZi*)Yh@6Wc z{t|Hm!A~M*^yoRY!wPFg+yp-enK&pPWT(t_!jtDwUo0STG>rBX%vbh)f_wR~ib?Qp zIKUSOE`)B0;D<4;zyi> z=H;Ps6%3UKPJ^L|g82o*G{Ik>WU~cNfy`XN{21|a!7C9j5!@;Q^}k$B_)oG|3myui z>jZQ1*UZBs6UUID+l2lg=nn`^gCX^d6={s@^%6+9nw_JEYw`% zNV9wr3@)lO+uZ@bip1uL5KDfg;9HQn>jb|H`pts3!06|KxxmgWy@Z`2`Rp7@7y#kp?68tiBn+oPm30$>I-G3pmPJ*W+ z?k;!+OE36G6mkS`kP7@q52J;Vi0f1;cs^npFfyza!TbW&d@}*vtw?x{(8Hi>b|nD) zPGlM@3XCpq;fDpk16}qTwAq#ax8XH8q4&AZV<7%g@KMA(sPH99V0Kvnz8*&NkvQoS zp=-W?1s;G}86k9bP9=iRL&6h@5i^+eqD_NX8v(*XGPv#;fnOHWXcn;9P@9h~gzHH+ zBQv-oM5Yk*J4BcG0bD^a8$Ha6jOk zf=hwl7R)~UQ)1N6OUUro#HhI$kU8whLxydE^lLzmBnEF1=#2$e0q2<=oWZyXC%uKi zPwj>XW~Wk0j8b-jp$ehD2|CAZY1GEGzg}=J;9CWAiS@mLv!VNxU|uFJZlG=v{m(Du zWD*$WU)+E(6Sxr?5=(gyrA#M=97Dd)UqYkoA$TQp&L+mezy}da|1dCDKNBAWHVb)i z&MUHnbl(3dI9bmV9BIIJ3(f@IDVW~XtAe`$e=2w|uvxH+j6R7>oFJVI2*GTgjD-1M zGXJp#n3s>w8?S)}+wdgTRmdmCK}V%nFmrmoU}kix;56XN1s4O~MJ#r<6XTMN1bw&2 z3wc z!Omb}x=09S>tUSt1D+5AMz7zM~cV{9b2CvY>7xd7=~ZG=$*M!qmuGV{S4lrIOpr_fol zfyBH<2t$c+6abfn(5DikV01n&6PXFXtBH}hdyu*Hz(GHT)EFKTMhY6y>%{Rc{R^R& zqI%5&CAoe-3H@q_nSV3`qwILv!X6XgW7e!a0-k_7XtvN3fUgpaH>jYyun0m=!q6ka zh=B47f?J|R9~Imfbk3$Tfx0L`iePrlxq>ZRiekZCVRN)#{$Ho5f?MELUSn|3vQ~nz zT^QHF=+lC4!6jf|NAV>}k2DPa2QczuFdwz`1S@2Yl|%VVVBRgn=6Va}D_RS|1Az;X zz7e_#F?%>FY(d;t@K1;b3g*4bhX!Rx9~(kv_e(l6YTRvLW^|s=`7XIYFf)3M;2;NK zR?7*Op|20YHwm5x`mKUFoPUSl6~OG_yk74y!EBqmLh!SK`yqZM1iyjyXO!vlzbg#l z4+IZJd{8hV_um}ia+mAUKo#e`@Us9;qU3M#WZ!dVfN-gy@--b1{>-*Lf zfwhpVY>a0vH7q?=HO0!~K(!nCb?Ff##Qn-68s@pzx25h_0^Tz0 z|MWBlif58H54>ojcQtvNh4&PA_G5mt6L(y1)B^r2Tuu}An*EpK*#y|23815 z4(i=MjtKB3H{nZHbkP|WzBd~{2iFUk?UtcF8q^jSnqNPDneaU#=drjW${WnJ1j;bn z0YIH1o**nm;YOitwFEXJWr(TvsB2GJ9SB#&>)oX)aU9?KJ!2qS(=gVy!7B^SJ)Y@3 zabARj;bV9r#YmY6HNwG6Fd_D!o(@i}@P_)$X4rabFNmQTO)HWs1eqo{JpFIa&_A!C z+q*)<1kd(?GwdxYvdsSjW`5Fec=mV+A=(8bH@pFZkAUa}&*0Bz7~I^`YmeIoMI;rV zUQJj6_qEvEMD97&c_g{%)P9WhVsoxGKD&jGU13yF3-nQxm%wZ?q(LsUdcH^MTR~@W zGTraCHugeyC*N&lqIZROD{#}@t(&I+V>|mTm9-z>xBug>xAODaweMgy1Z-ZNFlS

2T8Gr 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�M(DIuj!C8I{p7%NqKIt?#-OOKl|@rJQ8ub(#7gPfR|S29xu_jHC` z_8#BEPnB7t#rfPBB{en#DiyJ-F&{4M9OEprJI7R7l=BMeACN0?ouuR!}B~j(c5VwP5U^BHP}Kp52dM(|9DhD z8Z+dmEYtcpHS&AnI$lSmynls<#=HYzsO#Z_2(yR;T#9CHh9|%;S#qEch9_@=qYPzR z0h^%;@JL`YQ~^!|?x-@Afcq$JjG8whD=@XIR0oPAIEzXO|5NEsBp zfQv6xC=2)(Vl8$*BE?8_1RqC?h;p*jTya&%wpYx-SP#V|zypbOX2q!>BYgsX=PK?E znX-i{#M_oxwSv$MaM)0Re@1aSWZqHS5coUAY!iPFBXV4ebJ3lL6Hu8LJa2`qD&RpO z>_uR*KD|<^oc_{DY}Xr_lJL(Y6Q?R>2+Z$rdE_YOeZ5r}i}Z6cdBnG;mr}&IIU8?H zU=OK)hbg9N78zBi!=A2~ey^{9O~r)_^(<7Lc(GzON)$K7+p6gP3J!l)+!prZ75Env zGd-^;ZVvnP3ivI>4B!N@7U@@tc^5x@38O;)pchnRYC1d+dr&m=%t)BQtKul-3&AH= zz^q%8F9Kh)0?t&-rr)Fj&Lu8`2WE4gIuLXGO+^!cS?R(l=%*OfBh>OBLwvRJV}LmV zqdc+M!l8OtN6E9^au!3(M9R(qPO7Bm|qd7zUS-5D@ER$J z9@oMb{M$fhgXK$%ERbXHs(rV1IPV%cLuy-8exe55dEniHKe1);P(0{)wvw3>{D^Ro%zv8b(DTA? zhGI^TUPUg@Uorc9%ghOUGovw!ls;D?W<0y8z09!ub^gn(_FG&wdyCJf^(E- zY7=freLcN{KUsb}R=V5uZ;2_LS~7Li#k`TGxT8 zHhPrDdB>s}bxC*Lx7Op5{g=o2z#2}clxnlbPWLz;T2v6BBY)=$>sh}P#QRh1Km0N@ z-d~7u<-PI#dR8xak=A_qCf?uNdQ562`0JIug=!vBTGj4P<)Modn+~?e$4=sNK$D!B znw)|5X;4lCzX|BZpeO?7=Q-4z0F?xmQIivq2&dQ-{Fu}^&PcmAE~<^4j#!Vf>p(_c zN$}Unolll&zS3q`8i%WFs?q!%W`DwRLdiwOKkBgLY9*rLpyNrVZ&L#jB5Sx(T-je- zmWG<0JbA1qvWQ1@hFl}ugchK2J3^!@6Qb_1AH4$5Bri1M@mPaQSk`Fp)hPvn7zvsP zD|Qf)nP&K!CJok7ur-({tF|FLQZo_R|D`MWiT?bC<5;0;84oioCfds2h25UmTL+Wr zOud%-6a8t`8-a;s#OfOQPVewbB6kj#_Y(c}E3Lf5lns}tD*l}6KZ0pMd26OkOw$mR z-zZ|?n`z2)ZP5>x76&Q#&~M*}o9<5XrubAzR8(ogIhHD)B>8KY!x{fIz6HAw#{YGE8zT}PzNKq! zq#kPuuj}LATFVasa?lLYGz_42O+(kqXu*V>r?Pd>uoOMkG`2H)$xIXPUs7dDvcFmx zPcaNhUr$KWK3trppaKtEKQmY{HcQVJQS+cv|=V1mu%y5sW6;~ zt<{#-}SC$Iiy;%PoX7o%mxd7z`PjOIgbRt!+&q^&eSlo6@7e|6UbL z)^p;Yd?l*(`=O@y2EPCBHTvXX`?JgaG_ z!c(9H>nQRkk7qH`uvfEAjD)ZJhm^{{S3zr!53aYR%CQuGY8hXWz5#OK;<7QIGf7+v z2c9K}%pe!eAL5G^2btOPEc_lGouD`z*4g2$y&!jcx^xJqXuE7ojg` zXbRkm<;q)A{f&LBBl)s3)nBhcZ@9+xLZ2V<$q8V1_3g+dkBzM(apJG$Z&S#kG%@1a z*!v)GnaJ>ryjVUp3aJdOan&}FiIZEZ`BN&zasEo)%-OYm4InL#_D5*ODJ?* zY=xz}(EY_~a;BQUO6ooE&~W;UX&!5`6>dzS`Y$!o{I#pUbxGkmi}i!12Y;;fR-BZi z`KwN>l@OYecya+kxowih+(`z70t>PKtgWUxoBrAiGw9LWxB{M7YU^nNC<@(!fH^L= z)=rn>Y5wE@-#a%+m?~Cve`@s`P_IHsyghVxgRr?_Ea43!ue!fF?ZMUkS!I0K?p?CI ztdG&Z!sc^wdr4t2Z=7Lct+eY@Mjajm&br#uH{74IjasYi>2S+uhfb5v-kj$DTPrT} z^bL1YC|Zy8hW#?N{YO7#v{B+xUuC+#a9`E@pZTbj=p?e_1XQ)c5^N#cM-D`o&`=$- zaFe%@OR}JbKY1AMS*Cn=?`;GczR+&q&Au7G#mJD5dpf+eH}3!B_>TR65NGt8f@FVU zVz^R%uHkPGJqE+-2Fw=gs69p+*YsB%7VacigRBp1FC@aZvj59FnMnc@UQ5>3^rv)T z_mB^@m%4`~*N2k9PO-l_Fkjf_O>Eib^%@O`=WxZ&my6I<)(JE1MrIuJ`K3B|sUje; z9_yt2CFK8y?wk_;dw1S9oFAqhoU&UZz<=h)h<~}Yn~HGxTl?>b{-x1^DGDQ6DKnt!p5rXeS@(a z5ax1MqaxANz`s?;n}1lZ^Ir&qr^4V-1Z&;v5#Hq@5dVHboj8=p134E)@uevU_+hQ zcrRyD_L4GFrSD3U$(jDNGAAXRJ5x-gkUOE9$h8%e_J&X>L$39PW@urhGrXLZ*-LIT zO8G@((=Xf@z7^e2A^i_ROT%d+F=x2JOH5NWlmRyyH6$9bTHWMrDK%^RQ_>!VSS+W9 zp*Srx+WJAvTH8?u)b_XXvB!#+6%h7uu%9biYh&E^MmQql!o$O+vAEBMT0m&L*o>9B zBGH^h86x^KCRR&MsY1j65`pAp_~mfkO!fl1FggjCsy2|$kvG-BuaPXX>+S;Pz&gOm zPKdQ@kUU0`)0+UVJcIb-SQZDvp*iGW!~79)D6s?RdjkAHt>u|Du^i``Q0xq9;Jveg zz9IBoW@T{L5Lga8G$b5(6>BnkI5S-}>r0--lzW8riCNqC{PYwlW1{qg^@>pH$t7tQOS)QTnrF7sn z@}b;Mp)>C^6`W1(n6_;)&@mF21HC)Ue!N6#@Lx9Bge>R+kKU#VIThpZb~+fzk(0<@z?grRm0s_ zx#@wx(BPE={Z;K)a|1zv@P1-puF!WtWUW5Ea6uCy+lDUm*Nd6$FvTJFcK_yHU>4;` zVr>CR3#593K(?$O5IMr?5xjGV|4pyVALj2RpAGY8#&#LlyVHPfeR`LaWHrlel^3+G z_MeDAOGexF#J8rF(2OZQKZMb>dg9uIF#EW8jUGBOPc_pp@jQrFqg59;R&Pn{Yr=db z;Q03Vfl0!Dd?8Wj?pLi$JyTJ2P4|x56o#0L)LV(9FbtoEeg6 z?rJfc)b}`5_t#DBPg{3mnz!&DH_E_J5;V9BQ1Mg^WAa+7esf%ht3GRVxBwZWaINDVC0wJ*99)E> z%Df9#K{m{I7e6ozEG)yk2nS#jYh&+8tdSc*JPtoFlkp#}2sZ$SI{{NpjkU@$zHkR(u{+ z_+!OCgZ~NGoTSj!k1Vl5N?(NMYKk9#T~9GRG*f&xBG+E=D##C3%%vo=mk^PoJhyc* zqTHo9S8*EnMT)<%k^koX5eV%<>R(ldN{G-qimB)`#SG{>#cYqiD)vF%Jh+FR+fj}Q z2#^6Yuo{XHn5Us)K3r;PFv`C^Jea5W@GuD;xbK0U6HyPizmu3B*}6t?cSLlyVn%Mh z;(L%24=dgTey?KgG%&9fL(k)od0+XjfWI*Bv%rC`fPSwIK}5(r-3LDe0arn#VkGLq zy{Y0H$P_4MPM8;2Aj6cIC;h;;!0%|4;g+>h#dRP*OK}-D|IgKnBZ$#0it7UZO)=M8 z?^4Vx-JqCR`gg@X@J}ng0`}{Q`3bWR6mLi5PAYDTNc?Wzh(Syn$6yM8=EYQggzQaK z%;#g~trqaCXzi815lQZ$n8i0(F(WrxF(YSQYJp!ClXR|uQ!zis^`7EikecIRj1+#M_(_y~G;}dx(-EN*#Y2!t=5RzA z9H!x-y*k{10D3Ae1V2=9H)P#-#S;<0OvNnwTNJYu+@Uxbe%C1e2ZS~&&P61)EA~L9 z>^Z%tj&gcUaW)jauXr~C_(Jh4r0NI7TtL34_mESeByrJQCAN@lnW^C_aZ= zxjux;Jp2gNZR&uqJR1~qxqPeQ^H6F|D?k8GBciV=&zyT#@c{&ILh%a7oK<`e0{c_( zRupj*5Di2GCpa1*GgM#kV4I6BN&ZJxlRp2zY^F zzMW&<^45UwQ@(8(gf{C%Kg7gaN*Xe@j;P`n(9x+q=;JXmof$doFshlnmy z%vTWZQOq|1A5+X0u~RW4_oBfle>R)f)qyvd4;4ek^OfRWz~*a{h};X1i9mTWqAdGd z#pe-Ny9#`N#XS((SjAr=vDa3RSyWa*s9bSnWcB^TC@-47EB`X^F5;$!_Uu<&1DA(X zW(>0WJ>}bj|3Wdx73O1)(Agc>g&cSAApN1+B$eI>TuU9Ol!FQi5qBfjLFT{;JOiXm z7G!2skolYP9G(d=a+2mT#mIDic@ev}ROxFK9Ns5JgdT^`C(0)Pe?zSD?9-_<4ZQhE zBIKDv2`Cc!{TMizSY>J}e-?ZLJaH#tD7p=b%!dde zgLrz1$#cd5!>@2tW-nwWD1INr3drPK*$u~@^j^j!GEjrz3By!4~JDL2IWshbV#8C6fK7b z^F2WDoGLY{;Gr2Y_^ROBD&}~<7cnB54%}b)&cNdo_XfT}@l@dD#E9s_9RKgqi+!jI z=DUAvGcfOwM}+>42z{(FOxc&p{{a3BG2}DAGh*SC#StrCNAWd~$s>l&w&>Ks~*s)c^*=p2jiHJ)FA-o%9G@QS+UKB>3|u) z$LhB(Qhb6KPfuxnP@XcJTr$$Ufkl<^0x{xS&}r)M9X!_~PNXzuD$3Jy2bI4F`7X*+ zQD5a-qdyo#48N=sBUEM-u=!LRXR|Q3sC?O2c({!haA2)2Cq`B;1-@53^WE)-h?D3Q zXU8bdyWbvSB!DMZy{tSV^tR$8`2CO=4^U|Sq5OPceEJRjf0p4rzo?Mu&yk&BK7kk! z+5|E4xi=_k2;7Q1vhgn9j>L>T%vHoqJ)S2Jp%KcnkC~{L^=Sq%{4V5CS~v586ML9t z>cAANCT<#vu=0(;o3EmQ-v$0T@~ZR|VoktF#f-EUm6H5$ z;7TD}W)_GVtHaH3C{+9~@Ib|n0S{Mv0C+Aja&87vHlLUU40EUQJZx+&F%rY!`v&D1 zi7m-#w19iXhNU&!Iu?Duc0)JTL`KI_=$|pen zSOxiSl}`tM5!m$q+&bqZg#zPYCeg%*1cl7kun>tDWNA(1nOjYXH4^#CGsT^WTjG#= z8^x!Xt!av44tTC3W;^Hjb5RAMmBa`v1A%Q&+zEK6dO&~Ud4{-oXw~yz1^J^Y!|Xk& z{9^e1PBEL}&x)S_zGyJ={}o)YcVdj_xr*Wez~*~U;3ok0B;OK!KZ z49~;hxu^0BY_Rf+!C$RBb7Bl}E&4<=RlXJK#LX)IFyt5TC`Af^3w=Qek>Gp74^b$? z_zitQ2|U*T%vX~TiDU5mvMQz2+bW*~d{X&n==qj7GoRjq3&XW*i5rIa*~)Y3KA%|ohDFNv zMT8z!9EXTL9>Qgwod{r`IvhhJjw@~o{s+aa(BytqycGc?p>txStNYOpDCW9fN5$pf z2P?iA5uKvA0b1G3ih116ef*L(6>(|p33d1r4lgO5k9zQlVjlhXh2lqnZS)KDOI%BF zE+UtwcnylFkK(H!Gg zDWoH(Ic=xuNQ@RmGm#j%L}M1VQEW6q9?hL*FENJjH0F#L{pq~Wy4N=B0KSko9ROxT z1%3>1tq{Mk0>79zBgC7NSv6tX$!CW60~O?X^j6bw#pN4Z;h3Hy)=HenO00^wODUqR z?7b45$5Z$Ew_vl!{r;-5<9>g0dG9K#as;<|$Y-U#opO2@j=*WS-e222ay54LBk(>l zc)h=?dk&X5=JF9<)*c=j9QTp)yc~hckZ^WPrJ6EsgTKDJ5FENmD3A>hS%J%tf|@@^ z))fB(5ZMcEvJq+j5l;6QM6PiOmeUQl=mB`!jmwbc_jq|6muz!=q~3%6Qn&tya2V?z zglltLvg-7aGj#2V%eLln{6@Gg#$`xVke6FGC`;(?^8^>iRGO%DZ&AhnK~;45hr)BM=^T>2lE{aGh}J@**x9&cvn37eplw{}K47 z=g(0!9oVppSsGcMJ}oK#M_gMVFg{Q^GS$ z*7v}_>6?wVdmy~Wm>w$7{=c6<)w)^vh*1r#+QF{XoGGr;3&G&b+RJY`{EJ6( z;84;u!O^DfU*5N9PWVWW5qCKO*}FX=C0MPl)9f-Qm6{EN^A}r{Wn?|)T=0eZ&ZG8! zZZpKGBR%BN#!i%6)5zH+*Y2@9%AW;zi{|vzZY3!g;E$7nCQe!qKg}ajIlV&NH)F64 zC?(#FIUeilk)^pfp5h{M5Y^Oh?*X?_%%c=dU4raOTDRyARn2UuaE zDkE)X?+Vn>=9^Pg{Tsy0!CAmOv!RRf*|7U7egu9;C}v(wRs0-yvyTOSzXrcp`KgH3 zU5e{M<{^voS_rL%10MsMfZ-6V;NfM(9I+i$TnqLGin)@@tt0gN0PG(X^H3fO=_Su| zB4QL%rkdgu?rY$YL=<`o4ra3pu!~4^Qoa%tnY}LHpGIoTUKe00nxrzHLFPKeOeMD* zQDtPDcjV`BES0B=M9tXlNk3PM%kM$Bxiqqr6#*GzFN0%%PP zn`WT$tKrvdSApMez?tnTm|W3tvlexJ4(vXv7kRR+xsx1C?meZB%iGPJHr7#zZGm&I zwo1JgPIk)uaJ&H#;FDdNzY#-Oo|MAbsv_%JIBinKBlt)}i7p>Os*&P@!5>>VC#-Jf z1T>BE$B+%5aAr0w=@j5k@{S3_6VEKsDFC-4sdRFM_wIFI~?w$5Vv>nd_%J7=~vPkw9%(IL{QfTGo8Re{sc>MP%lurjR? za-zVg*YFr-6`bjW4Ps(Cd=C7HFWng8%^8E;+v9kId2$tx1>cb-d70k2{$UvX?hl=9 z;Gda*JF_iIGrbwUg~*YgWX_fU{mir6<{0Za4eSGzgR9?j?(sRy5Zklz@}0IE*&Y%2 zOIy7E-yLf0O7FXz1i9%`XKTHZ5HEr0Hi)BU#f0hgx0ri?33!%ID52cIq7{7}I0pPMr*FdTihXTh5(CkIf33 zkAF@9Pa`WkD83E#$&4Pr)AYwbWp0Jcm5OITW~5^DTA>~rGB<%QRlXejY{gfBze(}$ z;1}}IDV6f^@e+058o~<2?7__37BW-8Z&H2^_#KMp0UuI)P)>Z|bhUO#qc5HQ)(2Ad zB~Ivzmj`Lh3GV;W8SG722l;Tnz*kJdjqIaft8bjawtb|MtT=^ca;}mbhi!Qz`#Wb2 zZitV3hn8_XUVixwY@BpG?KHA&TV|eiGSF`1G)@6|4vlmrP6EX#AbkR+5&U@?jbN3; zo`LI)sPIK{k!OUwjQjT)97*rUBDmmW`fX>N2LI(;kEYFYo3)hex>5D5LBZN*ode!~ zKRt}K-I3>NxUsKgn||&<4dDyMDbx{vKjP1 zhMgG?Kc)N)NOdp84?t&s#lv9_^&-I(;+{8d-XP{#(PI_=E{|lpnPneC_f1OOiX2*? zcnox#y}j^O4|uuq+}CIJ`GS8Ae%C19AHh7JxFvGO?D>U^A342W`7Ma!Va427_;&cT z`>{T308oeHu)kBh4fcBg}|i` z@?4NjR{RolX8JhEfI}oaFw=;6!1w^gX;6BNV(w|=#%IcK|ME)3JCT5`ipvn#KE?b* z#k-1ch2PH)fxr*84EU3WWulR3> zKu~dXAh@HcJKaKOU$42_6`g%)bGJ>MO{fq2f&f)Mg^Z0O#y?F><^LgXK-5n5Nu?HU zhINNzw{SD<2@x^?mnm!E>k~?b7tht#hqAndn`|TQn_9SCtOIfezD6Y@f9^)K7#{nq z2NZM5^`pcX%Fvjtn2@;!+Fv4H3wIsh11irve_Q$X;NMps!Od*xes29LgIl?oF;^nT zE+QBP*BYOOY32Tw>qj=vLS(svi)JA)+~0xha$*Fxqi!9jov++E#BZJA8xRk(YYVb}kCOHIZjwE~Cvk%!vVvdc zyAzx;j&s99$tQ@B@iYgB5h~4x#Lz@@ni$od#zKn2HL30sqcd31x_Js&>dcuA=Uvg-lsv<*RV}@MT)2(N>?JmXX_KEv&JugvI$aF6tp)uc9LU>qwik$NyIG_LN2sC=CzalH{lk+kX! zJ@_0LEPGXsEGC;M+j_gn^297B;lGZo^KzP-uO*EM#?E%0h${QpvQR6MnAz6Pccapn zc@vHTuyVmCPak_NFkwjKzMD}`tC}`oZHzRvz=>cl$g!ix3R+_?`3nBiSCGC!o}Q)v z-pvl8l;KAT=R3Li7wDPQ#ag=?&Zg;E7Z@9yoM4v7Ef4B>_(UJ}X4TF4ARgH~-|5-u zdb(jP6#jYU1zJ7e^?1r+j9zl115JiW(_F@mCX+wbykPzUC&nt<&#M3=t+jt3LUpFh z81J$6b5(^Vq-lRxQv-6=s~eFGR!Gf3CX^<0`P$jgm8W6pESyyJeWOVlXexN?4fZMZ z<||ci4uI@m=ya(1PB?CjST;08_{S_S{Z{8;>toq}tFz2HE(33Kwzj&H&TZ+u_CEA- zp%~stNRwfX7Ag)XZMVo@GlycN{!V{T9vi7wa%#(Z#xx&-w^Dx z+L_}IHeKh`xy;hGSpe_Q+DZ&g+Ta|$jDY#BM0j2XAzWrSn7hamQdu{QhxH zjI4Ud87F(oqH37c@L=7CoyCFFM{uJslSEXlr%++dBoTO-BL}uS{jA%i+T%_e`(mUF zc^vBl&&%A$ojK7b&VStA&1(k>s-^GZO&rBNul#ZBmCIVx!dgGem5v;scQ~ z;3+3RdMs4!Q*OAdc?!oCa~~0>^3=Ek&SoqQ+yyyh#^S(r!AJ2L_&3BLK`~z$F}q5@ zzXF`D{542PJ;gsE8d-`{!M9ZWD(sFvoP0{9%m%)1M*LkRDc3Z%3q0s@%80-zeruV`mkAia7kN_Wr8$p@Q)WYUFe7#v;_Hq~=T6)5x0@^6b-CVVp0`o^c+r66B+2oF}m)xcpftZ5@2%S;uQzjb!t4Sj223 zZ#?JpPuYMTXB%!w;cYd~6PqyFJnwY1w?@js=bbJod|j^y_YFR8pczaIu?ceGc_^za zk$Vt4A_mKT58p!A<0RP+N6P9wPFBilBI<$ z#w=Ip==eJFIy%0S7)z}*<~fe;G1^C-pVXrHFxcpjvpm8++DdjG!Fq0eiFy;yKgP&a zv=+-|SoW4Yx$vel+g{sRX1)b`M{7Ct7K9U}&D)SlmnCmI`!R~`e-u}p<-wyU#Wbn* z4z8NZBv|&5{<8ZWr=fkuD__5ZPP15|-o;Ar@C@`v(&}Aw*(KQ=&n65)f-MuX(9gzf zS@KLl^tO4j=k z$Gu6xudj4|^*eqjwLK^Q|9YZwX;i`IA+;rau-h!yV6^kuWmb0o>D2M*V9KXdulb+L#~c6z2aa<-w%tn)Tx8lFyR!`c11tFaK!p5! zn_D^f&_pNKP8o@Gn|nHHJ_obro({ZI&P;OJ4B~U>cF1hb;5hEL=hHQ*J)+ONzOv5^Wfi<6<3HT()Fp-&e06QA zlWjlkmBdnKxV1Vsqtsb|1JcT$ZGL62@3qeLmOTI;N&X;!?=Kg*j(juS$w7BrZ3Y_p zFe#sb7QU*AoScEjMO&&!^O<;L8jzBie85vh7S43)HmrjVjZ5@lgM2dWe7D*;>R`o~ zY3(;NaS(jxnDQf~N%G8=s5m^VE&n*LuK#P)#rHg+GvODa7cVZ)ZjB@1(G%VzkyG6N zb|idmv)sHK*>j~+KR9QK8yn}aaBNTK%Qo?XV6~gw$jc9x3uMuJH$Ip#&xPOkbmYtJ zx3~+^a6E6xw9@G_OD2x_&u5ryg%M*$&KxPJ_c@J&N9Vge?C2S2!(*qHOc*(J%;d4b z+i!Kh_NP`sshIYMA5L3M`vYE&hr~;<2*t{p4W1o6zwXykF*hQbh6(;064qIHK9@EP z6a2N{O~VAv2X7iC@XL@j4HKA8A*ZTezWX;v@kHof$fq_8n96Tghe$+th2jP9e7EA| z@US*414R!g{sJ$aQhWkR zFDmA0vJY;IbUJX9;?H5nE9Pc(Gr54w2FTP_9x6h|wSe~_td8Vc;DW8Vp9*aTo}qXZ zV$H({7+4-Wn|=cLh)+h`;U<;60=IqY&L!`+6?1pVZ;FHP$9W}nFhOQ_12AV7)s@eI zZFZ)BzZw2oD1Q}lyrW{CCt)Ttkf{NTZaq|V$00C9g?O;U4T|~gwq=Slpp=PWBsPOz zqc{Qbe2_w(9ie&r13Uoo+m(M3d{8kTZ$GcNCbvPoq8H!7;T^>}Q2JRI!~RY&Q~s;s z`(@j5H@S?WW~U3nWG3VyQy7;|psqkM=L=@}kTQ_JT6xNInVIr@zA;NNJNjD{-vd47 z!3<<-LY@mz;ocGhCp@Ny1G{xI?b6X0?>6Kaz%gR*Ed7(pv!u;#eDH(7dvHzp22#Gl zos&BmVe-}3@Duo;Iy0CTh~aHJe3_{iyd9P{ce*9r_+-IsxYu#xR9uH+cO5ajajohm z!N%@AZ%}GZO^5--nNaxk=Vxk(GF=&{d8> zx;x)#{X(VrB=Q?#NHZJHDc>6WugWtgyofKg4+ifLhqU)BcjxEw5EgSt2RKe8PpkV4 z;2q8u!2b=pt1B)EUMzRF`Py;cTez5RBu02N_Y-UW>?MXjn$L)JjO_-$y~q90whHCg zTCDr7sxCSAVR86Kb(seni&<~nhgIPmsd+!F>!cKxy|9Mtz2BXU;c}~WxIEiX7O#VB zXL)a(+Xx>>^sdJe?a@Y3e?3-hi!cc9BK=9-QPteu?pTji+apzFJ)He%joEvak64po zZ)q%NLD?r$r0NEF%21+k;Y&B?}p$(XUDW{t-xQ+jLc2DXb|3Jjc0mn6ftyn2uG{d9@PuVcq#h;&U=(%Gk-;KSK!wa7eL*cinGDLuXqLU zNyVI>{iwJvDzME;hxD-VnE@&Ay^wFF{Clw5<59z{qultqt>D4zB!JKWC@~)n0?vdG zpBPcmGhW#}3Il?>alKo)C9n@D-UokXQzUgDou<|Uw}b9;D#OWt_|8)o@~AaNw;%F; z#YvzmDdyo~X3qiia6e=%+f&ncSObT8D#Qn=O%(s)l^aK6LjbpiUZLDn**x0K>^2-? zS1ZL0*rOE>g($Z_F^%kxN)>agFiY_y$jnvTRg%ZJnImrkWwukoTV06Wqt1UI`s)<) zquLKC<_>f-35R?O;K!BELzaZ^U*o}t?_b5T2EkYl$euCS>(Wi$8{=l%d%WU|b%$Fu zg5$?xPfg0N2rS$a#UMiAo~TxE+c^M98Fx=ugt**@^BP%(t6e!@eh8 z7GC4dw$I`dNY|hbTAd_4C&50DBx@(R4eb_I`QrW=n6iC4$*t$xgwAY=Bu_@aQpF6L z&DTK@<|Q*EM$)_blH`WTZk(?*nT z?f>$Ws9CEPEkjRhNSH4=k=qy%n>ynKHqZ>I#3 zf;ZO+OskyoDQ+^TMxF=^%qL=1%5+u0lO?i6pv}}Kpo-L)OKN=-KLN}U0lhUuE?%X0 zJ*wRV#a#BBqS%GJ*+B*QF3@RqPyru8sV`NT4X$i&5lFJ9x^kdJAfs#m?sL1;jps@| zuedV8cv&$Azy}p`Qg}r1cd*}8%+>f$72AmGw~EIhPCqN=_kJwadc-LR8_NeWv1K40 zYhQC)1~SWL!F`ar*9SIx!Xf(x!knx8FyLDhk4HF*6dyy}mnnV;O_e(;84d^g4=COP zznc^{Mtrv_=9_3cEKeun;Ym0=qYiH)05dRvq91|H;2gLM1`y0t27quwyRd-Y#tw~E zfuvNfW1m!azV!8t;%{780r&13k+=)WT|{JU_AT%qfImm^EAVIbor8Y|iQr_EGF-;x z!UFL$znq1>vMH6Y5ly*0aBr%(06Ov%b9`8!_ybgqUW&g4?ys09BMntt8+?i4M$kV_ zF$-wA;^yFQvANEU$Rxn?3U%n`lht{FjHao`*)NpiJ=LsOLN)`L{D<=Fd;^f9jzzAV zf9Z6<>hNbBeD=zP;@A$V06I@g@=+M$p6CPoeMQPs=Rn1skhv2gJdKP}=EO{OU?k=#ZUcLn;sL0A`KS#U zxZbNABfw!bBX$frjw`0VQ;MnYSH(9VM|iWPJpGy{Fu;Xy=L-qs={HX?{a&H?Zs20` z)CEF^klJDDFv=CDO(3hR4N_#DsKA{bA6NI+q0pR>2%bld@$n4^OgGTcF0MKSefD8^99lc$(DkC{p<k#2LsA^6QcS=cUcjs1cvXWIpmQW)=7RPH;-9aR6%NX>%4oK!mZLsn1? zH=dhg1EFR%?lA>{F7{BL{8A9eqFujzAPd8p!R-Sj_Peg^YL8fg{lk=~_NGpV5$w*L1D`WS^{;@sn-HQo z?Q^Y7^LQE)ewW&hKpEK3C|Ok$n9WF4>V`ft zO|rWMlIF6>ot48x@a4n^=~ljjuTzo>Yfel+i)O zSHbf!#T?JaVGPQ^@_gmRQ&NKucMmMAQfBU02%W|Z`VlLdCGZfg;b!#-DWWkWa|A;3 z7G)3!%_-vO5H=gkuvAQwg4Bl}o3$iHDbtu2+j)!@%?vtV%1#r9q=nzjuS2ZWgUd(Z zSMd4fZuqr(&L+cq{Y(d5HKfO!h?^3^Ur~lzd|=p(gx{P`^aSgV4D9n``QXg>Kp$&> zw4VTLvMi^yP>#W}_ZCae#6WNRWUDL5$Tj^4I4WyUK9$Kp& zkz`M4V2*w68p)dmdW0;R2H^?v-n2j?JV^Ck8{q2&^{x%n^Ch>$XhHgeu^;IeTK2d$ z5NDt2DC@5c)I}8cLke*^Lu;cXPd8Tk>49V!w88yvd)0DVw8#t1LW+Y=Zg9t2^_?#D zuShR!Jgum!?WB9wbZWBr#qC$rviwapHJYt z!J`kl?ISRwO5Eg5Ozesl{TYfUiC^goZ8RG%cW!bUT8m`&CO23w4gyK|O4ZFMJF^e% zVG!I70Mh#-wxgjr;VF>Z`zWS(N8}%mx>KxgWbk9|^|0Q2%&i$&0C#KcdO81?n^QT% ze6j9YE?ip0W=oZkN-Dd#5B0#X;#zAzL_4U-OY3IJ(wKm_gcS~1Hu0_5Z;U@*9#IYgKS@XEP~G zcZCp*qNuCshiNl$_0+?a$G?RYNWanT5njNUH$Lvp!HQ`@(oh=AnvD1Ckl4Df^H6`w)s0g`l7haYA zAE(o@v2#>y`DvHCOG>YZij>yRxO4u8iFLha+yvSFjJrakZoO~VuQnW zyDPoPY(pJmBGy@5V&axXbdE_}<13D?HcU43^;eP8)hacX=NGx3%7}ybuuqp4++CJc z5WM)JyTrEgWyvdUtCTHh<7T#wo6zGZZZ;-%+h^pXSFi#8Aoil|bF<1~(d^7RA)M}j zmxU^}8hDA~2zXzim~U^~r{51l&|*MZD)ic`>L4l3r{0W)xUWuIHU z>@Bp4&(+~B;I9==MqsBEb1D6tV$NEBQ#=f|2iHs+-!X6$e+l_2ia7-5iX>&OL*%j) zbFWx)Gi8PYx60Xh4l;zyA1UlosqeNplI@N1^fknad=rqIAILEcQEfw?y%-NtE` zRLbXFbwLnw-<_FL1K$nYRQY%*J>VvH=VYq0QhbhQHVHy>3^J@(dCqpZWrgZ^7W**8 zeBd}r@j<^FIpAhY>5m}Hqc+I$P49W?-V*w5Q(O;tsbbUs&q~E_B5zkK?hKjx6(>OF zM#ayP_R8vmc$;K{96RV{W1JEDn%h5RBz$x6I{ZkdCoz0Yl)10DZBmyZ zumz+rwq`Y6tj0g%F#Bkl995tnp#IM|s>Q#fRM_BWJeVL5}-4M*LH5 z9^A`VI@gnf`)S}M#2D0aJg`A!7b2|9#5%y`8;%Ss6EZIo!*3Z6wiRnV#OVyVUT)8n_j&oTT%^zi$!_aQmV%G*Tp$jStS9|Hb~Amu zGjV^BxqwzHS@$uXG4w`O^p@AaL~m@28wCk6D|{35VF@IGh+!K-Kx7jFZpaJ-gb>gGVFv{-yNseDQA#BW zs58S;1l$NNBe;!>3*d$eh#40K6$L~ESN`8ueL6Jad;Wg^d9UE6zB+Zzsp{(L+WJ)W zkxVs${)*0OA^fN@#dsj>*A$ox?KucVm$X8H>g4mm)~$ZWn6I#F?bs{ZnxXot7lLD= znk1r5dyM1fybq0^Z}nA)<8U4U?C*-z##e%q>leKm9BtM%RKWoJl;5Z3?hf`ocbo!i zo+7_C^{E}!G40%VDDI<1?+(VP@`7Nh`gV6P*2kBm_1--nJe+P zm}>o-SaSNtR0rM+4o~ljxKi*k(Kq39=tMl*Q@g%6xqkFp!TDa#FGl@=cY<%2o`w*u z-iDyoo_-J0-i5Jh?)#W4HzcTC^iL(I)cwJBW{T>w9~0j=Rkc5u7ezbiBc9tgMWw6F z`-1_`Qu_#m-TN{7-4#>~`-8cj%tYF=!92F$0H(^RC~9rWhM9Ea04Bi8=UEBAQ^PxV z?yP#pTWvjeMAv^Z&)R279Y6G2b^L<=bk35e?wM~*t=(A_R!37qDQd?8E9Jkb^eY!w z!E>W{I6jKU2S4X9d;D71$Mr)0g_FgQKD|>kme( zxn_Dl+)U?bnTs3fJS}e<>e$tRtjVk#&f~)2p7XeHjO#ou5Nmu~+5tO{OCEU25oRTz zY3L}yCGf8lJQn^`!2{LlMHURwRmx&3+q_D3Uu;cHIKfEi7&!LxMb+*sZc+dKVyl@4 zX3|HOT6174Qnkz)Y7SQQ%dGaOG9NF4{Cw49xs~USMJG_EIxn}fJ;VHZtz^n_SUuhb zqnqVcuIILZ+DQL}fZ7W`noH*V)L4?LI1mh*Q7U{rx+rxZ82>x(Zi_m1l6SX8{fPs? zpF)9R6qg4Vu5R7nPjernfd=e9t~I6gM?McOFr#q0ODbln4JTnJdHa!Ir*o?Zo6D$z z>ZvpfIlV5Jjx$Gs!`1dLgH>@LR#aEcuB@)8o-}#VOhfe!SV?OASHb4zs?nCte}@^0 z&py0s%FpNlzWX{jzeyU0-ntGohshjnm##y=N7Ri!;LRj$5jycMf^-6d(enkf3hAl> z`t!I;x~c#dB7T(c(0FXTV7|eI1^D%R5qLN<8G|_Iuri z8o)h~7)@%JKo2PGE12`Rfr4)ZE*Cru{Q5mI_z!}n=bgYyXdQZ`FfD~YQE(E3rwFE{ z=uE+Uvv!T(<0yrNf@$}&T<{+FGSu zXRy|M9`F>Ae%r4ae#VQ4?&{>v!R)c`B8sojoiXbh!qx>DJU|Tb&EU~Snb1zPUkH5< z@G;?k8~i^AopsAVhFMD8)wRE%Kb)r4{UVv7;Xk#Gz~xO7u7`YT?=N@<#Z+mT&iHwV zzf0oJSEnJK#>P%hEfIYeqWKguksMWU8rnI{J|xmC^pg_3)2GIq##<4_r=!%U{Q%-y z5<{G7I|$}v@qA(q{Nc)kJ{dfEc|XRc|54~Hk4j?b{28+P01W7S5_lHru{QrwAE#G{ zpa55pD9{2Kx?dCx23;RMi}@SflR~Gt7+>c*?-=$A{Wva8zdwkx`T1&+`p1FJ6LW|k zhb*1lAJuLu4nBUJ^@%Ui9+`9csA0rXg;?ZHMYx$*Mp*X~%jAU@=1gArI5?A+BgE)9 zb$YA_Qh-UsQfO_6V~zTM`K|MOo`!Ul8nk$DR8;GSE`y=@J6W1>D8#e(VMSM z(p#Y}3|X*^%20QNtRbHD8R{_oO&O}BDSVvKwxX#u+jBT$O^h|$468XY=xp=V(HO|C zRJ~&1WvW|Z(dU+^r()6P=BN*2(dS-@o8(tk94I4IL7bK6sl!(NICQfc+o*-`{af2& zbxUtHf*H#ma76LI_&8JbogU1|`kyBt#f2q>wfA&wW7gC^Iz9NRH;{><@?f=IceGu` zj-NiMa!SGfhY3`#b9+5KiMe)8@B*cexg0S!*hAejHyBBpHS53Ih)|mqZdQNqyx^bA z+Rf|FGJnz&mtJ#&arM7k6D&2=)?rPCtDENsH>O{LE6_bCX0cq?(LE^eM}{h2fG2GL zXnCx7pz#6dDEMXgY>Y@>1HYT#h43#F%n{xY!Cch3Snz!493!{_^eVxZg3h*>I+p>% zDC&-53H9+fw^?bsUP`yfqDx8FyDDL7dqD<+6wjppC|Yqz`BP4 zKL;oJoh-3YA*MqJWFawaE(FZw!Wkmm1sPi?m{Z3sg8vSz_a}j$qab}U2rw^TpYUYW zKN<Jq8jBCvI8{>`XjZ>4-a70J zOh%d>>=*gJ89uAZWb3qhP|oqKdSqVkq}r8gjZlxIS?$%;$Aj&anQkThX2Z}&txK~K zqb5w9K6&g+*QUSG1FcZ~b?H{Qr|GjOGW}fRp`EPuXIcfVN8_I8(J)B;fc0n?n6$q5 zv4h*L3YuHp2R@2mjgVOX76@(vvBiQpxVc3zCmd`AsNx0CS#ODtnCh|SR#qCjiz7l~ zt~e@Ve7ZW?+)D9`@Zg9At797H?s_C?p}Pjl`A|hRj~d#->TW)uZfs!{#O!AR64Sw+ z;U8>a^*3A749n>Z`w~lM$TrUD49C|G3ZvukoC>PCEbAX0pQQ@F48WQ;+ZuyO-TrJu zzSUIa=jiV2+8p#S)70N`(4W+(S8~u>aR1Z$9(5|m%JNKN9ZXJQ^MNE99S@sZsjL}R zwtqeP%Q5;`#3(kOAzArCr_~yL)5f5^toHfh;P1W~?{c0T;}WjFa7l1k6Y zRh9L-*9HfA)s4%d)70JTf=Xduz?JpbuX=xUI@>8-Yjf>7l7uq#0e|UH2OhvY=x5x~ z&O*BaMXq}t#BnJ!ce1V7bK-4NvEWx?B! zm%W1dYHFWg&ixJwz6m-%6Fe3?-|3}2D7puV^fDZ9XI!jah6Cn<$JUjJJpvv*W(4NP zJoFO=jJj!`_YBOh@#s}K;O5||6xjv9din%9ClRZKzLJO0aEikOXd}lKfOtg|-Wv!W z2Vp%m0v-SL-Y)VqiBb)lg2`7t2Zg7EmwUK=5v*UFA&$I!bsH4SBO18KO*$7olLfDb z?o7el){rH*hhL3&2sNk#I=Tss$3Bz__P}SONPSzOW3b?t!NZv{=`G=p5PS@}*`Sin zdFOb+8-cG9Tm-*H@FmbQTW{8bz$!3t5Jchbh*&K6A>b8)X9C|W_+?Zbt_G8zcY`as z#M~TuuVAiMY!}=M_(j2-Iqo3_FZ)H~phP4ify2aEjF1g_P`bdc>NW>E4xm+JKPYlq z&P$1zC}cQlqg*e@(X@i}UZC?JY|^KKu1&5$uUCm%f(1CrsLz(*g+;~4#zEmqz@>aF z(l3F|VF0CDgZ{P9cdMti1iPnme#G?zXYQgWa8O$l&FQ3xfl^I2*WVC5@g0H;X%YQog7`i<%!^aFSg0t_;(nk-Ik*c{2jf`;T)C! z3|8m+t81UpR%oxl^K8vknL7~sbgrtVzbm)?=^epsCT4*%o(;~yT=R=(!B$tSdOQc= zfV%NH*sje`_0Qpc?>bK%crKXh{~F!Ng{sN(Aa!ldo;#jnWn{b2%~uyc4~w-n>RNdI z!x*4;vCpMA&_ccXd@wg^Qx;0ZREJwzzxVltPKC8Q|Et|)ZTe%>jv~ctFSs`N%2;>f z_|Bg)`#<_q=B{ligE+FDoy z*cO$ZU?eZs{DFRfq$cs63sr>N!dPl@K% z>pvsS7uVGE9DAr{L+Q1B&%Ta^xhkujHO5mPRN)^zMQYTyf#>Syw6~Hy<_GHL4punw zA^YWM7Pr~Jn%rtYj z&LM&@YfLlCVcVEaHhxzRK~FSBr9s~YTr%!{_l3eEXj`(Uc7gt>}73QFwY~ z1c&4O*}3voTJ8ppw#-pkf0XPZKZbe3C-AZv&tMLh9Br4i#LaexiBwwO;>4a^65nGS zLm_hu|DwG^t|D_9v$e&^ubs;+r+J6rB z<@CsO!`#Lwy0A9r-0sNf+s#vv-$Oj^FN{!!p`5iXY99G{e>o)!`aZy4``UH_$*F)e z*Ur@{cRDJ=xOLAt#oY#+5q*SlTM9XyPMj@rf=vxX?2A9?)SK1JW8JL*N9v?=dt2zEF~4VR@Z31R zJ(Dnha0(-z!9x6awq6GYOs^SnVp$nY~0UXUni* zTB>D!)g;lPI*%Hh`pU&a!{pmH)W2(p>%wSm3v^vgGM%fj^O$1(b{3i3&pNqFkbrIt z@%L9ro1N`NBYpxR0ZBM@ys^SZXxOa{=M1yNbShClohKeB%0&^zd`=&(^JK&9W_}Cg zxp8i1UEJMGDQ`Q!JsR_0j0IY=XQNVGu>6wggE}J@>w=67XRAF7%ABm}LS^|Ic1u&t zDq+{d^Vp20Q|zxllxX|23z6}tS2JT8WkvjG_{0Ruk{$_C#M=EA|>E8noQj(RAZtV{TkBZef^}nbU>VCg_H$BMOm>t` zCGaE_umoZ$tyQ-)0yU2{3TVXK+fqT_x60h-ZK)#XSx=~!Bi@);qgR?YrkmB>%1axN z7`D{E^UYRj$F07!zA1jgjv0{X!NZjL=fx%r!;2D4birviU~cG1y|oPYAl->TkJ!;D zN{lxXqDoU$85V^z)y;>3DQ5H9MZMDES`-`J08@fKh_zI&-+~d!17BJh5d^cELp4Q5 zSXWp~BHKc0JV7O=7)qIg)_wuG$eE)F^gKHQ7zG00n3^I;x3y`vGZz^6H(m%*YL9&A zE4HsI-!ie~HX({>b|_Xm{^ChjKbm-7-l)O8V-Wg_ zT|wmTJkuPY%dAT)V?bg7R^ZM*lYw4olqgX}Us=t(T~iIU=%zrDsxPtf)y}Sb17y?$ z>WjKsGdws_+uL3>d}+2*TkrN~tM#A3^rNtcb#LvM1xae*mS}6u4hut&yZyzgaIP7n zGFIVJUo}0g-dB{dZoui`68r=mvil9f@e&J@q> zI4&32UsMoSeRA8xr?-22uE^~koW|SvsX+#hJ zMtyIkDcCnD#nLgyp?9Q8vlGZf2e?MSWll&PE2?Ifes#e=P?+VlpicG8DU(>$}A zX7=45A(&G8 z{)a(qRvUJaHOnkhr$>8E);}}EnrNCiDzDsXY38a6%dMeBypRO^)5APqK7k2%jt@$r zb~xG4qb1N%)l218g}GYg4TF+v)P?l=)mII(9yMEMf*~W_mJGv-*BzrhZjWzk(+gFv zi}6A@OVwX&?TB0kVun2JV<^$t=DLcwr;X>s%jjz3o8iS3*qDt>JUd-DowDfRc!{~- zyqyh=lM6OdPFf>?<9g{Ytk`B{P6BMf;(Ox{TpQK$QmbY9Lq3oY^r4sIh6uf~4Ls1J zMqFxjo6JSm{u0mQQHBbpQ30=ya(&QL(G-c83$POf^E2C%1z!h#ws7R(ncf`d5^saQ z(xI!Mmo;i|{T28b;d%}!ydYRFcM0ZRl)Zw7z~3i07rEdKXKEaM zd?9!>a`c^G13AKTta%;=4NKJOiOa0`q8W(L>(UUu9T9q68kk#H`5Ku@Hy{DME)6_a z{fNAKo-$$LYb8gxKCf4r5yjPAz0wT49QoiWVie!*7lRyNni+j2^eFJ?m1oeo9Ye1? z1MdSrIyNnP1AM*m4Eg|ML$5poKL=T?JY%yz0yHnuOV7aE`j92UxE!O6;Nw{CMuyX- zB3>^;gSQBBdKntHM#YZ63$cGhsWv05tlY;CrB{{_bqXr<$};e9i0hSQ;0|b)MxYF? zK~d?2WYESSUN0mAUk)95AsP5H=+Fzvzyrar7m|VbwRpXd3|tBPgk<+q$i67JEz0Y4 z!B>Ij1HpXhjWSgJE3Ej)kA4iDA;j|74<@~_5BMc8>1`*(;L&H=0lI zIk=lo<@iD(IQfZ!GG`S#o*0QOM#34y0b5b7 z(Cb0ZC&oybjwdycp9x<;jD(r?K%uiFcnlNiWqjJkGXP<>0oMoFYBhFwCRl769ieF>2JA_zZL&SVcNB)LZC5$o3QZW|T6{j?nzvZx*>!B3Oy8Bt`;k zQT5q%tx*|4UrahL1&;QAOrQaZ))M3D`Mf_MxC)pr>quV#{0uQCU!!%5XS<*IiLEW(v6~Y4rKLt-{4^#`lra!eC?By`Fi5!w495dK~QF981{0?3Ea42--o8ZV|3%ln14Lgy8*#~{xdlwfaS8H1O* zct#TABHw|2CQg1HhQcF*BHXh?H_Igu5dk(}ONGt~wOTMuFZD6xyj(c_@&ihlp@&75 zoBX#4zaRWB2wn&N-Nfuq+1u$?BM`=a!f zT+zIfm{l5%pNV4T)`7lVc$n$uh5iHRyM&)lmfn~J*&e`OMHnHYmS2QvG$MHN2@~MR zpcOF^ZUUt|PKi6V=y=)=>1>qx5hD}Xs4G=MN0c#L=zT%w5%=V|5_mZ=^h7v>+9UxE zp|%l2a)OCgj97+O?+85(w}>B(pp7HlVWHoFtNeyI!=^{00iz!FFG<8Ql;b&Cq*sEi zTad?9ATSKVgCrsXTq&59d7)q)<8!-UuaDbA^lmk_hwO96%7>J=Os(4)NQtm!=aYnN z5%cnhbKsW=hNs(Ehvy=plYf}tFno?2$wT}n!PHYtj9k&x;79xi_;XmboxrscF&;jj zBS+v?!K?{)y71kCspx(e-YB>~{H-qhm|*IEUT_?{wwEL@2L3+56nw2xtp*Ve0G>(4$A;*P}=2Z3dDD zn2p6S7uKUn(7S-n9>?jOu6FT6rn>^O1yg|ClG7!v6r2q@?e0k@R)RYL-{Zpf3Z@?R zP)@J)xM1e{X&Xm$@4r(b+985tDhd$4BDe$a8!o(0FuS$}7yd+W3Fu$A@KM36L+mE0 zAMFViCfKzh&;o&wL=fwdFz8|66rnRQ{RS9xDq=tB^rIaG^NG;biRhY$y9%98fqqNO zE(>m`!;kdsUD+ZqQX;~jk9FZnmm>D1PG5ViU?!%A$#}epmkXVGZgSyU1v5kU0Y{vU zm)m`badGys?SdZ>I$JpQz2xCV>=KNi?lYY}bwBB7XGs6Zg%1g)9(I}1rACaOBtQW# znl(lc^Q3NK3h2Xdq#tcgIxu;1Tv&g(26XbXYm~mz9;@TgApZqICmx_N%6~Ni>@z74 z2G#?5;PZgT37xn~a5rFW5dt3K=|X3nnCHR^1XDlzTc@vGC3qz0>)4PxedK+DS-iAe zAf1@QQR3midd495389YyeWwfS*#me+gT7nn#BT_$0e(+#gn7uMh9fud_b zX9vwpWC81)@xUE{{XUyctar(SehIKXjtH0u^HTt*Xf({^qvE>h3pPHN(6By z!To^QDN`T}tWTx`E&v`X^iIIs(oddZV0PS20uuzc0ey-KPZwN3b2&Yo5v5#bpfHs# zbKyS=W&*dm@a=-jK<73Y$`Y>^+)wSPvVJtnYL8x&qCT2v6=I1gagtSlC8j>`2C&YQ z;^(v@eFf*hXCLG|=7R;3UQWy!49AitPQ}@*)||*{1Q(Ko!lqk_A2Dy>3SwlBZY6%i zEZ-$;8!;ZByOS7W7rFv}q84yK!g+>aEX3Gw}gjfo&+(o~H zSPGM-^A5jWevyK$bJ5omORZ|K>Gpm9gaH)zNV8{s>&aH7S0%jYZKiG>WyX1qwO3nf zut{aEfp;Z1+xM#)Y!nUWubBqgN}>H>8ff|Xs+`32*q(8^2^bf9;yfk!IDHGm7Yx<# z9f$|X*%!x-li0>o<=vpN<8%-g=Bv`_RxUo(GxV4zMO{bQ9r4II9WM%mX(L&5RQJ^ zBt;FG1-`frYRoL~HS^l%Ax}Aoa}QQ@z@x4norSf*RXB9ZiLQk8gI?n~`_A)hsq!DPmZ&XW2 z`Z!DP4Sr^>73Xi0jnS2QljK!>)G2U!N-t31dDtFYjQSf!_Ij#YhD5jYT+&X}%!6H5 zm+OCbQ+ZKgCpC7Q+16Z9Uwgf^)(Ws~@fexszVhOM?gMAdoK*e4dARe=uPuDV@HVT) zR55F;)cS%oR?=_SiZgBXs*#Cg9+-ZQ8=(FcyBONKl)BhyLe5 zPsGpZzp{uy*8_B}1~Tv-1a!L`#Ryz+BNN7bg1-h&rQp|0)qfqfZQPEqp0P-`q3yq^ zT-~zHN{OLJ!x@psAaW^N$wK^`9)-rkPN57SMw8wO8ioj+Iz|Y+4D?Y#e+_zP3C4FD z)?=4Qt(vjkD)78%s{7YlS>`zPk_1m7=)q2c%nepIoZfl)20UfBg!vn+jyRt2K?HHw z;hqf`b7ZU2jQqE!w)9kTeYbn9nf|d{{&q^wT}Y!zmkZ}%_0APNN-X{2Zeke;u(~-T zf%eR~bdII#rs(V7{_fBr< zd|nr|Au7-|@c>H1kC8{Q&QlxAEmKtdr&cTTBUSO8|7uS14AXBeg6E$L)S84SK1y^P z7npT_!`~)z7>3g$k^@A#tWDk5+I*x29Z~X`O)ZMeJ|m;&)P~7{h(1hEm3(9-)9NHP z($PF)aT3;+9{w}t9LQ5gu>;X2Bs9us;+TB|Z1WD-bkM>BXG=MX8ZA!99&{U$U^qtU zOynbE^9N~x(jyH|aB6MQORd!h!9X+hLozn1;XK34CJNW#F{Yk#EC^fC3#QJ%>e1LK zldwI<(3Qb9D@gg(S$~!}(Tk6O@dbTF#n>76_Q%YM31iXtYuVac_kWI~L1NVA%ROhc zwz{ESm=?0s@SbJ@Tjl7A@lzo=t9qt7{=Ah~TfQOTx7z5U+Mg%lIL3ZfwEE*Ie@G>L z6tE8jX*}NV9yKODaFv?SJk&;QZXU`@G}scw$#B=kswN?jp3c^VWZpRo*QgtHV0;TS2d)yl9=KXC zYuj>SXKui^x6Y$~k3^7ZBQccHZN|^ppZ2U^Uf>&o$^QW{`2TEjeNz1)9LiMvk^&{_ zx`+ehH_*x^CRKIM4qX^A&{XLl zNK6kS3LX9O^wJ zY8<0z=id`A}X63e({;?xb@Auv|j!3Z39SPnM zw?wzv(_^r$LO4p{(=BwR$7zQn{V=dwdN?Z4S_+7B;x-{nnNp!|g+At;KV5O*^FI6? zKgfu=A_8ARVGe$dqeA|6iO)iyi}$nTPS^+Nq)GB6em&|J;+v zv0;B~uMV}RP;E)aC;eX4$IT?Gdp|idoIF1qE-EZ6bp7p8T#V3pMXL9Q(QPN3>U?%v z-VyJ?kMBH%tqr~ZVec3STu_j=!>g_GXZjDXUtf^B!%Lg|zyW62ZPj0p7XcsED{>>6 z!?o2R{HfI}`OFT4{F+cr;|_#p5<*$3_)}|J=7v)L*Hm!wc)pId6_`fYz;21CLsp6B zC=LvKAvh_*QZc-5jmKY)-EsSU9uuN`QS2*t6+Y^;^OrBLun$oGU7-KY*MG0ofBDYc zKIjjxI5n=r)Sfh7kLCQUVebWvuxtUlIGfyuWSy?hpC6AB(LI>I10nhc-zkUF-;q#r z2K9+)Lnh>ySz~){n3tK{kYkHn#@IM*8LT0tFQ-P`)*EJ9EsbD4CF@>+U@{OQ zM$v;y(N?-`8AF#^GZQxV?IM&j%k3<*XP(p8SH9fI~Gq zKMAHxo=l=$bN=Q1Z$ZO+s0sR5jargycIDq(qPkHS`9xB5KDs*Dw{zLm$)%mIZoG!G z^VN-J6^;1{Ghe^XRp}tGw9>iKIl9Ttk={2TrPfE+KZ>%0G3y?Jgk5ZX`jZN0XQ+>p zp)AsPTUyu~5paoag%|Qd>DaKDb$k(QU*Lt#1?sH4ZYwYgTSOfBY#y>gXXm6diY+r5 zY;>8;1mv_7kVW!6`hxU+h{kMWvj)=HYG_!K`HqlsJ`xb-I}WYlNj}hx!uB1iqPTsB zFo=`#sQQRhIJ{XK~2S2m!?#Ap@pPjvGveVi5scy`VQ^-0$lTog0f8ZL6`b^H8qSpEOt)g}w zy=hL`93+@^@z8A-(zLVd&@?9(xswcYx|CSN-V)FlHgfS`&(IGxwCS|>*k!?a0-aq7 ziwKMHGwbeXOmLBt6Z^6kISJ;G*<8GU9vqWcB1uHB2@j6t%`LY2iN#w*(i%YHmKULA zk=9soor}NW9g^Yf`t~gN?Zj_z5|<3$LZ%RYX5DcxwPnz}8^!O}9{_ahu2O z&^*?_$DABSP z*me}?$&(&%L|w)TQJ@P{6Z~b5k)Z#u>p_iWzoIqxr;)5cxGdOJ8{%XSJsg6JjdK#jhSOM zgSAzo^*=Jjy#dZv0tDMT#`^h=v3>_fdO)IWtlv=^>!%d>Va<USE@>HC?^YXCl|EvbwJRX9~UpvBBp}Ov}d|r^~fH{p1j)@__yV z92a%nacfC!dGB!GFrEjGQTN*`ld8>5EA*e8itilMpZ=ez!Pn+mc&5%!Q-^s{z2hq< zsF&mL5ri2t$5&6fvg1TOuhKJ3jGNk+`+S~GK65}-fzMOyGcP*JYQBVa{xfOjpD++; z)ot5e_)%%2%!@UU@B%AN?&pQ6;fKLhB?TIo#w-*(>H<;$&{KB(H_@v0h zvk15Uny~ha(736yrpz=ZkFBnrI?k9e>6%ImKQE{ldU3_jL56zegw-Zt9KJ3(VR~hy zL7@56*C(v=ePmV-d}D1o*REe5sgrScpuJl3t#w-6S>}ybPp9Jh1FOHY63=Dll16yy zjDo2%uwpW8R^_b93S%Z$LYTzNc{P;=(pT~6IK*|&ch)HXmCTN6f6^*wJjNuXJ~;_H z{F3I;vFhiOR(eo>{nTE){^Abnk9gXS{u52x6MKS910!f_55s)*%s{J9UG+VVzN~o6 zpZMFR{rcMnn@?GHsIu0<2g5r)r?`rE0F9Rm0;!uQtaYc7kz*JHA9i3e9i z$Eahsn&H~=k2|TazQq^3+SpYRj~pLGoMNKi@$(U+i^C7?PorbFy@dG4CP2ENG1lg1 zM@+}ACjpa;?mA?T0uzj&(Obego~A2YXmK19;Lw52RuH34W3efA0uv=*XCO|$m?eqh zAZ|6dN#d!8TMh1lkD;Hj9=~|w>(d+HJ1)WmACwStj;-eUSQdwY@h?cg&QPlU5G+aT zkQ0ow#=Q)7I9;2!Gv4OUz?m1UJdIVDS(iohJSe_7_1a^SxE3~M4&%c%#$dvT%d~L-Uc4S4*~>X`EFU<{!?J_ZvVu9L zadh%Z;)S0$qn(TmJL-U8tb*T(Oi~;E;G*-g<3%a+65j!T;3ymC%8;|D&jff-B43c} z^$%cLkn4pz;GUqTF@5LFdn;m2D&ROeqwq}ltck>b!|1S!VA^{06igGWzJgc7XT>8w zjwY~I_<$#Xc7@QH`6|JYp8%>Q&=meW!M%{cV!8 zQ+>#+;O)5LhXnIw!J~pNg3c%OyC4Wt$qtG50RD@DJHvlX@F@tqEBG7u9}8X$`d5Ol z1pZ#|7-Yha1ev+ZK}!{U7kIJ-=lW3o`e-{aeT4{pv>h;wgoX$cH#uA(xHE8#VCXdF z3tj{|je40_5fbK`F=ARy?GoG%bp7rg^rs+ug!BjkpF#K+VR{x3dfSCCX+tvLi$K@! z?qyEa2a1`%O`u;ytOv+2P!i0CXo~QRM8XS*ApL+8IHC$3?gou(qfGos*?E zL{VShcZs6_;EoIba0YM@%)|^p4y~CvCd@4kEK*3i<^nv2D)Q1^*HeD!p}!lTSh_84$!uf&V~c-b&=f%yw8jK&j<<}XM|oD zf{(ZBF zamW>MR4{AnH^dO;Jd0n{W3FGuedj?;&WpTkp)=DRh*@%w)%z;I&rJ6aI$uo;5)2E- z2$pYcMXVq^GK<1wsuoN|(_B2)x#&xTo&Z^%1;)(rGc6m5n}P^eFZ5x!kY|aZzY_ZQ zYK-!yIphZt!RPfO5h#TKH>Xp8Ps8_u_XGbzj0D&mTd3VWSfsM=;I3$9Vj*xUF>f}U z-fsaqOQ?u+=nNpEr4b2SiogXTT#1B-3Y{PK;Rh2bunu@EF_iL2nI$+3toK`hUJT6h zl9&k#dhR2R1rd%%vr*5#pyxT^iEKs$52)blXS#PJVgs1I7Q7L+;1sc5>cnC(9!VxZ zo-DyH0_PL+UchyC@mxTRggwX*_h3<$6&9O>aM~#X*FbnCBT$TV`a3e9BTeIG5$F$| zb%HY>{FGq6hTkjrPvHNT;N@Ob{5 z$A->n{4NuWsiu8`IxosB6ZoTG@_SImq!UL8X3a*Eh{bBq(sPFwm3Q?^SW&n2ME&V|3Hi>I#( z_ZJ+7PHm13e&UN=df0}d2}JqBO|k=ec%jV%aZ}l#&>)BH|V)oZf?NE-@>k9#08gCUj3lclN2qs98~n zI3f|m#{@%{?sA+iDar?7bWh~bgF3?;_S8;7YZAye1M^K}5uI!(P`S^_? zmJW5iVA3ZNBWHBg_z_djf3Yz@Uq$y4F|!KyIWhV?x}S(;oExKUuBB}bvl^qVrRzkD zR*24d(1|NZm*;9JF;j)p&!G$<_!7{$82yOJ1sotr-kxW~qL6C)bR3{~=6 z)5v1aaGm_GbkSE4%e6e_qHiab%i8OrzeOyUc*I4I9AiMPmt%N4Z>X=tEO9v9=SW5e zlP={j*Tp}dSjt3q3BtdLbSa+~T>QI;<76GF!4>fdu@s!XW1;_>*WLWh)WND?d;rfDo6~&Nt7)hu zJ~cSBn?JAJLQr{)`S|I&4WHqW_#qpb2G3YwsJ#_ta@R|N@i-Wz_|dI&$@69~j`G#` zx#jOrN1yekbgL4UQuuUtxWsvH9C5x?af@%YlUILz&KuY6ewPr_a)eg@N@%Ao)chvl zDTPnB)g>RJ){Zta)JM<2-f{P6Gp^m!h}6Xd#yI>ObNJl+jpQdg;MT zOL-0>oYc8_&@F#IEE#bR6I%U>{+Owb{o+qfDE<|Fr_e9_75!CPPh^zPk)(D95I!L- z4G9QpHv+(&-{P=gECe6(%TB?`FUM()3Hxp4;ubo<M*b+W>t>*jhWziQEj}zP?n{qK#mp!q)gX8QdWX zS;^>bLM@xjNO9E1-^;(^`vDI^nZsn%ZW^wv)SO;M z!(5!+6%}VTxeYNMW5PLhrNi2WE=vn89yDms;3eMZflJ{H9)zz+^W>$+4fSf#W9g~vK1@i7&e7~_~AU2Ks06A=cUg6vK|LQz$oTFmm>Up;mMXWqC>iY7=OtU%d%_Gpv4u*R%#b1lv~L oj*wjm_@Py7=UJF;W;HEE1qdT$UWcVCLFyRf^9lqV=j7}E0k%1jkN^Mx diff --git a/variants/arduino_101/linker_scripts/flash.ld b/variants/arduino_101/linker_scripts/flash.ld index e75a7059..03344a54 100644 --- a/variants/arduino_101/linker_scripts/flash.ld +++ b/variants/arduino_101/linker_scripts/flash.ld @@ -48,7 +48,7 @@ MEMORY * See below stack section for __stack_start and __firq_stack_start */ __stack_size = 2048; -__firq_stack_size = 512; +__firq_stack_size = 1024; /* Minimum heap size to allocate * Actual heap size might be bigger due to page size alignment */ From 4c89a183f8e509c3f7c08a589a291ebb91ee171e Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 19 Dec 2016 14:59:55 +0800 Subject: [PATCH 2/2] Add test sketches --- .../examples/test/CallbackLED/CallbackLED.ino | 90 ++ .../AccelerometerBLE/AccelerometerBLE.ino | 94 ++ .../Genuino101CurieBLEHeartRateMonitor.ino | 100 +++ .../StandardFirmataBLE/BLEStream.h | 243 +++++ .../StandardFirmataBLE/StandardFirmataBLE.ino | 833 ++++++++++++++++++ .../StandardFirmataBLE/bleConfig.h | 112 +++ .../StarterKit101_BLE/StarterKit101_BLE.ino | 37 + .../examples/test/central/central.ino | 86 ++ .../test/notification/notification.ino | 78 ++ .../test/notifycentral/notifycentral.ino | 89 ++ .../CurieBLE/examples/test/test/test.ino | 71 ++ 11 files changed, 1833 insertions(+) create mode 100644 libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h create mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino create mode 100644 libraries/CurieBLE/examples/test/central/central.ino create mode 100644 libraries/CurieBLE/examples/test/notification/notification.ino create mode 100644 libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino create mode 100644 libraries/CurieBLE/examples/test/test/test.ino diff --git a/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino new file mode 100644 index 00000000..7cbcd21b --- /dev/null +++ b/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +#include "CurieBLE.h" + +const int ledPin = 13; // set ledPin to use on-board LED +BLEPeripheral blePeripheral; // create peripheral instance + +BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service + +// create switch characteristic and allow remote device to read and write +BLECharCharacteristic switchChar("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); + +void setup() { + Serial.begin(9600); + pinMode(ledPin, OUTPUT); // use the LED on pin 13 as an output + + // set the local name peripheral advertises + blePeripheral.setLocalName("LEDCB"); + // set the UUID for the service this peripheral advertises + blePeripheral.setAdvertisedServiceUuid(ledService.uuid()); + + // add service and characteristic + blePeripheral.addAttribute(ledService); + blePeripheral.addAttribute(switchChar); + + // assign event handlers for connected, disconnected to peripheral + blePeripheral.setEventHandler(BLEConnected, blePeripheralConnectHandler); + blePeripheral.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); + + // assign event handlers for characteristic + switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten); +// set an initial value for the characteristic + switchChar.setValue(0); + + // advertise the service + blePeripheral.begin(); + Serial.println(("Bluetooth device active, waiting for connections...")); +} + +void loop() { + // poll peripheral + blePeripheral.poll(); +} + +void blePeripheralConnectHandler(BLECentral& central) { + // central connected event handler + Serial.print("Connected event, central: "); + Serial.println(central.address()); +} + +void blePeripheralDisconnectHandler(BLECentral& central) { + // central disconnected event handler + Serial.print("Disconnected event, central: "); + Serial.println(central.address()); +} + +void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + // central wrote new value to characteristic, update LED + Serial.print("Characteristic event, written: "); + + if (switchChar.value()) { + Serial.println("LED on"); + digitalWrite(ledPin, HIGH); + } else { + Serial.println("LED off"); + digitalWrite(ledPin, LOW); + } +} + +/* + Copyright (c) 2016 Intel Corporation. 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/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino new file mode 100644 index 00000000..0e660cb6 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino @@ -0,0 +1,94 @@ +#include +#include "CurieIMU.h" + + +BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) +BLEService accelService("19B10010-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service + +// BLE accelerometer Characteristic - custom 128-bit UUID, read by central +BLEFloatCharacteristic accelX("19B10011-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify); +BLEFloatCharacteristic accelY("19B10012-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify); +BLEFloatCharacteristic accelZ("19B10013-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify); + +long lastUpdate = 0; + +void setup() { + Serial.begin(9600); + + // set advertised local name and service UUID: + blePeripheral.setLocalName("tigoeAcc"); + blePeripheral.setAdvertisedServiceUuid(accelService.uuid()); + + // add service and characteristic: + blePeripheral.addAttribute(accelService); + blePeripheral.addAttribute(accelX); + blePeripheral.addAttribute(accelY); + blePeripheral.addAttribute(accelZ); + + CurieIMU.begin(); + + // Set the accelerometer range to 2G + CurieIMU.setAccelerometerRange(2); + // set the initial value for the characeristic: + accelX.setValue(0); + accelY.setValue(0); + accelZ.setValue(0); + + // begin advertising BLE service: + blePeripheral.begin(); + pinMode(13, OUTPUT); + Serial.println("Starting"); +} + +void loop() { + // listen for BLE peripherals to connect: + BLECentral central = blePeripheral.central(); + + // if a central is connected to peripheral: + if (central) { + digitalWrite(13, HIGH); + Serial.print("Connected to central: "); + // print the central's MAC address: + Serial.println(central.address()); + + // while the central is still connected to peripheral: + while (central.connected()) { + long now = millis(); + if (now - lastUpdate > 1000) { + updateAccelerometer(); + lastUpdate = now; + } + } + // when the central disconnects, print it out: + Serial.print("Disconnected from central: "); + Serial.println(central.address()); + digitalWrite(13, LOW); + + } +} + +void updateAccelerometer() { + int axRaw, ayRaw, azRaw; // raw accelerometer values + float ax, ay, az; + + // read raw accelerometer measurements from device + CurieIMU.readAccelerometer(axRaw, ayRaw, azRaw); + + // convert the raw accelerometer data to G's + ax = convertRawAcceleration(axRaw); + ay = convertRawAcceleration(ayRaw); + az = convertRawAcceleration(azRaw); + + accelX.setValue(ax); + accelY.setValue(ay); + accelZ.setValue(az); +} + +float convertRawAcceleration(int aRaw) { + // since we are using 2G range + // -2g maps to a raw value of -32768 + // +2g maps to a raw value of 32767 + + float a = (aRaw * 2.0) / 32768.0; + return a; +} diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino new file mode 100644 index 00000000..b7062f34 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino @@ -0,0 +1,100 @@ +/* + Copyright (c) 2015 Intel Corporation. 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 +*/ + +/* + This sketch example partially implements the standard Bluetooth Low-Energy Heart Rate service. + For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +*/ + +#include + +BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) +BLEService heartRateService("180D"); // BLE Heart Rate Service + +// BLE Heart Rate Measurement Characteristic" +BLECharacteristic heartRateChar("2A37", // standard 16-bit characteristic UUID + BLERead | BLENotify, 2); // remote clients will be able to get notifications if this characteristic changes + // the characteristic is 2 bytes long as the first field needs to be "Flags" as per BLE specifications + // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml + +int oldHeartRate = 0; // last heart rate reading from analog input +long previousMillis = 0; // last time the heart rate was checked, in ms + +void setup() { + Serial.begin(9600); // initialize serial communication + pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected + + /* Set a local name for the BLE device + This name will appear in advertising packets + and can be used by remote devices to identify this BLE device + The name can be changed but maybe be truncated based on space left in advertisement packet */ + blePeripheral.setLocalName("HeartRateSketch"); + blePeripheral.setAdvertisedServiceUuid(heartRateService.uuid()); // add the service UUID + blePeripheral.addAttribute(heartRateService); // Add the BLE Heart Rate service + blePeripheral.addAttribute(heartRateChar); // add the Heart Rate Measurement characteristic + + /* Now activate the BLE device. It will start continuously transmitting BLE + advertising packets and will be visible to remote BLE central devices + until it receives a new connection */ + blePeripheral.begin(); + Serial.println("Bluetooth device active, waiting for connections..."); +} + +void loop() { + // listen for BLE peripherals to connect: + BLECentral central = blePeripheral.central(); + + // if a central is connected to peripheral: + if (central) { + Serial.print("Connected to central: "); + // print the central's MAC address: + Serial.println(central.address()); + // turn on the LED to indicate the connection: + digitalWrite(13, HIGH); + + // check the heart rate measurement every 200ms + // as long as the central is still connected: + while (central.connected()) { + long currentMillis = millis(); + // if 200ms have passed, check the heart rate measurement: + if (currentMillis - previousMillis >= 200) { + previousMillis = currentMillis; + updateHeartRate(); + } + } + // when the central disconnects, turn off the LED: + digitalWrite(13, LOW); + Serial.print("Disconnected from central: "); + Serial.println(central.address()); + } +} + +void updateHeartRate() { + /* Read the current voltage level on the A0 analog input pin. + This is used here to simulate the heart rate's measurement. + */ + int heartRateMeasurement = analogRead(A0); + int heartRate = map(heartRateMeasurement, 0, 1023, 0, 100); + if (heartRate != oldHeartRate) { // if the heart rate has changed + Serial.print("Heart Rate is now: "); // print it + Serial.println(heartRate); + const unsigned char heartRateCharArray[2] = { 0, (char)heartRate }; + heartRateChar.setValue(heartRateCharArray, 2); // and update the heart rate measurement characteristic + oldHeartRate = heartRate; // save the level for next comparison + } +} diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h new file mode 100644 index 00000000..87256762 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h @@ -0,0 +1,243 @@ +/* + BLEStream.h + + Based on BLESerial.cpp by Voita Molda + https://github.com/sandeepmistry/arduino-BLEPeripheral/blob/master/examples/serial/BLESerial.h + + Last updated April 4th, 2016 + */ + +#ifndef _BLE_STREAM_H_ +#define _BLE_STREAM_H_ + +#include +#if defined(_VARIANT_ARDUINO_101_X_) +#include +#define _MAX_ATTR_DATA_LEN_ BLE_MAX_ATTR_DATA_LEN +#else +//#include +#define _MAX_ATTR_DATA_LEN_ BLE_ATTRIBUTE_MAX_VALUE_LENGTH +#endif + +#define BLESTREAM_TXBUFFER_FLUSH_INTERVAL 80 +#define BLESTREAM_MIN_FLUSH_INTERVAL 8 // minimum interval for flushing the TX buffer + +// #define BLE_SERIAL_DEBUG + +class BLEStream : public BLEPeripheral, public Stream +{ + public: + BLEStream(unsigned char req = 0, unsigned char rdy = 0, unsigned char rst = 0); + + void begin(...); + bool poll(); + void end(); + void setFlushInterval(int); + + virtual int available(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); + virtual size_t write(uint8_t byte); + using Print::write; + virtual operator bool(); + + private: + bool _connected; + unsigned long _flushed; + int _flushInterval; + static BLEStream* _instance; + + size_t _rxHead; + size_t _rxTail; + size_t _rxCount() const; + unsigned char _rxBuffer[256]; + size_t _txCount; + unsigned char _txBuffer[_MAX_ATTR_DATA_LEN_]; + + BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); + BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART"); + BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, _MAX_ATTR_DATA_LEN_); + BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)"); + BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, _MAX_ATTR_DATA_LEN_); + BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)"); + + void _received(const unsigned char* data, size_t size); + static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic); +}; + + +/* + * BLEStream.cpp + * Copied here as a hack to avoid having to install the BLEPeripheral libarary even if it's + * not needed. + */ + +BLEStream* BLEStream::_instance = NULL; + +BLEStream::BLEStream(unsigned char req, unsigned char rdy, unsigned char rst) : +#if defined(_VARIANT_ARDUINO_101_X_) + BLEPeripheral() +#else + BLEPeripheral(req, rdy, rst) +#endif +{ + this->_txCount = 0; + this->_rxHead = this->_rxTail = 0; + this->_flushed = 0; + this->_flushInterval = BLESTREAM_TXBUFFER_FLUSH_INTERVAL; + BLEStream::_instance = this; + + addAttribute(this->_uartService); + addAttribute(this->_uartNameDescriptor); + setAdvertisedServiceUuid(this->_uartService.uuid()); + addAttribute(this->_rxCharacteristic); + addAttribute(this->_rxNameDescriptor); + this->_rxCharacteristic.setEventHandler(BLEWritten, BLEStream::_received); + addAttribute(this->_txCharacteristic); + addAttribute(this->_txNameDescriptor); +} + +void BLEStream::begin(...) +{ + BLEPeripheral::begin(); +#ifdef BLE_SERIAL_DEBUG + Serial.println(F("BLEStream::begin()")); +#endif +} + +bool BLEStream::poll() +{ + // BLEPeripheral::poll is called each time connected() is called + this->_connected = BLEPeripheral::connected(); + if (millis() > this->_flushed + this->_flushInterval) { + flush(); + } + return this->_connected; +} + +void BLEStream::end() +{ + this->_rxCharacteristic.setEventHandler(BLEWritten, (void(*)(BLECentral&, BLECharacteristic&))NULL); + this->_rxHead = this->_rxTail = 0; + flush(); + BLEPeripheral::disconnect(); +} + +int BLEStream::available(void) +{ +// BLEPeripheral::poll only calls delay(1) in CurieBLE so skipping it here to avoid the delay +#ifndef _VARIANT_ARDUINO_101_X_ + // TODO Need to do more testing to determine if all of these calls to BLEPeripheral::poll are + // actually necessary. Seems to run fine without them, but only minimal testing so far. + BLEPeripheral::poll(); +#endif + int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer); +#ifdef BLE_SERIAL_DEBUG + if (retval > 0) { + Serial.print(F("BLEStream::available() = ")); + Serial.println(retval); + } +#endif + return retval; +} + +int BLEStream::peek(void) +{ +#ifndef _VARIANT_ARDUINO_101_X_ + BLEPeripheral::poll(); +#endif + if (this->_rxTail == this->_rxHead) return -1; + uint8_t byte = this->_rxBuffer[this->_rxTail]; +#ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLEStream::peek() = 0x")); + Serial.println(byte, HEX); +#endif + return byte; +} + +int BLEStream::read(void) +{ +#ifndef _VARIANT_ARDUINO_101_X_ + BLEPeripheral::poll(); +#endif + if (this->_rxTail == this->_rxHead) return -1; + this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer); + uint8_t byte = this->_rxBuffer[this->_rxTail]; +#ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLEStream::read() = 0x")); + Serial.println(byte, HEX); +#endif + return byte; +} + +void BLEStream::flush(void) +{ + if (this->_txCount == 0) return; +#ifndef _VARIANT_ARDUINO_101_X_ + // ensure there are available packets before sending + while(!this->_txCharacteristic.canNotify()) { + BLEPeripheral::poll(); + } +#endif + this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount); + this->_flushed = millis(); + this->_txCount = 0; +#ifdef BLE_SERIAL_DEBUG + Serial.println(F("BLEStream::flush()")); +#endif +} + +size_t BLEStream::write(uint8_t byte) +{ +#ifndef _VARIANT_ARDUINO_101_X_ + BLEPeripheral::poll(); +#endif + if (this->_txCharacteristic.subscribed() == false) return 0; + this->_txBuffer[this->_txCount++] = byte; + if (this->_txCount == sizeof(this->_txBuffer)) flush(); +#ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLEStream::write( 0x")); + Serial.print(byte, HEX); + Serial.println(F(") = 1")); +#endif + return 1; +} + +BLEStream::operator bool() +{ + bool retval = this->_connected = BLEPeripheral::connected(); +#ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLEStream::operator bool() = ")); + Serial.println(retval); +#endif + return retval; +} + +void BLEStream::setFlushInterval(int interval) +{ + if (interval > BLESTREAM_MIN_FLUSH_INTERVAL) { + this->_flushInterval = interval; + } +} + +void BLEStream::_received(const unsigned char* data, size_t size) +{ + for (size_t i = 0; i < size; i++) { + this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer); + this->_rxBuffer[this->_rxHead] = data[i]; + } +#ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLEStream::received(")); + for (int i = 0; i < size; i++) Serial.print(data[i], HEX); + Serial.println(F(")")); +#endif +} + +void BLEStream::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic) +{ + BLEStream::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength()); +} + + +#endif // _BLE_STREAM_H_ diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino new file mode 100644 index 00000000..cf1e3a74 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino @@ -0,0 +1,833 @@ +/* + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + To download a host software package, please click on the following link + to open the list of Firmata client libraries in your default browser. + https://github.com/firmata/arduino#firmata-client-libraries + Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2009-2016 Jeff Hoefs. 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. + See file LICENSE.txt for further informations on licensing terms. + Last updated October 16th, 2016 +*/ + +#include +#include +#include + +//#define SERIAL_DEBUG +#include "utility/firmataDebug.h" + +/* + * Uncomment the following include to enable interfacing + * with Serial devices via hardware or software serial. + */ +// In order to use software serial, you will need to compile this sketch with +// Arduino IDE v1.6.6 or higher. Hardware serial should work back to Arduino 1.0. +//#include "utility/SerialFirmata.h" + +// follow the instructions in bleConfig.h to configure your BLE hardware +#include "bleConfig.h" + +#define I2C_WRITE 0x00 //B00000000 +#define I2C_READ 0x08 //B00001000 +#define I2C_READ_CONTINUOUSLY 0x10 //B00010000 +#define I2C_STOP_READING 0x18 //B00011000 +#define I2C_READ_WRITE_MODE_MASK 0x18 //B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK 0x20 //B00100000 +#define I2C_END_TX_MASK 0x40 //B01000000 +#define I2C_STOP_TX 1 +#define I2C_RESTART_TX 0 +#define I2C_MAX_QUERIES 8 +#define I2C_REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 1 + +// min cannot be < 0x0006. Adjust max if necessary +#define FIRMATA_BLE_MIN_INTERVAL 0x0006 // 7.5ms (7.5 / 1.25) +#define FIRMATA_BLE_MAX_INTERVAL 0x0018 // 30ms (30 / 1.25) + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +#ifdef FIRMATA_SERIAL_FEATURE +SerialFirmata serialFeature; +#endif + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; + byte stopTX; +}; + +/* for i2c read continuous more */ +i2c_device_info query[I2C_MAX_QUERIES]; + +byte i2cRxData[64]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +// Forward declare a few functions to avoid compiler errors with older versions +// of the Arduino IDE. +void setPinModeCallback(byte, int); +void reportAnalogCallback(byte analogPin, int value); +void sysexCallback(byte, byte, byte*); + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, PIN_MODE_I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(stopTX); // default = true + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Serial output queue using Serial.print() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE) + return; + + if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT || mode == PIN_MODE_PULLUP) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + Firmata.setPinState(pin, 0); + switch (mode) { + case PIN_MODE_ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + } + Firmata.setPinMode(pin, PIN_MODE_ANALOG); + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + Firmata.setPinMode(pin, INPUT); + } + break; + case PIN_MODE_PULLUP: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP); + Firmata.setPinMode(pin, PIN_MODE_PULLUP); + Firmata.setPinState(pin, 1); + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == PIN_MODE_PWM) { + // Disable PWM if pin mode was previously set to PWM. + digitalWrite(PIN_TO_DIGITAL(pin), LOW); + } + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + Firmata.setPinMode(pin, OUTPUT); + } + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + Firmata.setPinMode(pin, PIN_MODE_PWM); + } + break; + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) { + Firmata.setPinMode(pin, PIN_MODE_SERVO); + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case PIN_MODE_I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + Firmata.setPinMode(pin, PIN_MODE_I2C); + } + break; + case PIN_MODE_SERIAL: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handlePinMode(pin, PIN_MODE_SERIAL); +#endif + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +/* + * Sets the value of an individual pin. Useful if you want to set a pin value but + * are not tracking the digital port state. + * Can only be used on pins configured as OUTPUT. + * Cannot be used to enable pull-ups on Digital INPUT pins. + */ +void setPinValueCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == OUTPUT) { + Firmata.setPinState(pin, value); + digitalWrite(PIN_TO_DIGITAL(pin), value); + } + } +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (Firmata.getPinMode(pin)) { + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + Firmata.setPinState(pin, value); + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + Firmata.setPinState(pin, value); + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) { + pinValue = ((byte)value & mask) ? 1 : 0; + if (Firmata.getPinMode(pin) == OUTPUT) { + pinWriteMask |= mask; + } else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) { + // only handle INPUT here for backwards compatibility +#if ARDUINO > 100 + pinMode(pin, INPUT_PULLUP); +#else + // only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier + pinWriteMask |= mask; +#endif + } + Firmata.setPinState(pin, pinValue); + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte stopTX; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + // need to invert the logic here since 0 will be default for client + // libraries that have not updated to add support for restart tx + if (argv[1] & I2C_END_TX_MASK) { + stopTX = I2C_RESTART_TX; + } + else { + stopTX = I2C_STOP_TX; // default + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= I2C_MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + query[queryIndex].stopTX = stopTX; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + queryIndexToSkip = 0; + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < I2C_MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + query[i].stopTX = query[i + 1].stopTX; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, PIN_MODE_SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)PIN_MODE_PULLUP); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(PIN_MODE_ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PIN_MODE_PWM); + Firmata.write(8); // 8 = 8-bit resolution + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(PIN_MODE_SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(PIN_MODE_I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleCapability(pin); +#endif + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write(Firmata.getPinMode(pin)); + Firmata.write((byte)Firmata.getPinState(pin) & 0x7F); + if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) >> 7) & 0x7F); + if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + + case SERIAL_MESSAGE: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleSysex(command, argc, argv); +#endif + break; + } +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.reset(); +#endif + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, PIN_MODE_ANALOG); + } else if (IS_PIN_DIGITAL(i)) { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + isResetting = false; +} + +void setup() +{ + DEBUG_BEGIN(9600); + + Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + stream.setLocalName(FIRMATA_BLE_LOCAL_NAME); + + // set the BLE connection interval - this is the fastest interval you can read inputs + stream.setConnectionInterval(FIRMATA_BLE_MIN_INTERVAL, FIRMATA_BLE_MAX_INTERVAL); + // set how often the BLE TX buffer is flushed (if not full) + stream.setFlushInterval(FIRMATA_BLE_MAX_INTERVAL); + +#ifdef BLE_REQ + for (byte i = 0; i < TOTAL_PINS; i++) { + if (IS_IGNORE_BLE_PINS(i)) { + Firmata.setPinMode(i, PIN_MODE_IGNORE); + } + } +#endif + + stream.begin(); + Firmata.begin(stream); + + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + // do not process data if no BLE connection is established + // poll will send the TX buffer at the specified flush interval or when the buffer is full + if (!stream.poll()) return; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * Stream buffer using Stream.write() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis = currentMillis; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX); + } + } + } + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.update(); +#endif +} diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h new file mode 100644 index 00000000..aeb96a81 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h @@ -0,0 +1,112 @@ +/*================================================================================================== + * BLE CONFIGURATION + * + * If you are using an Arduino 101, you do not need to make any changes to this file (unless you + * need a unique ble local name (see below). If you are using another supported BLE board or shield, + * follow the instructions for the specific board or shield below. + * + * Make sure you have the Intel Curie Boards package v1.0.6 or higher installed via the Arduino + * Boards Manager. + * + * Supported boards and shields: + * - Arduino 101 (recommended) + * - RedBearLab BLE Shield (v2) ** to be verified ** + * - RedBearLab BLE Nano ** works with modifications ** + * + *================================================================================================*/ + +// change this to a unique name per board if running StandardFirmataBLE on multiple boards +// within the same physical space +#define FIRMATA_BLE_LOCAL_NAME "FIRMATA" + +/* + * RedBearLab BLE Shield + * + * If you are using a RedBearLab BLE shield, uncomment the define below. + * Also, change the define for BLE_RST if you have the jumper set to pin 7 rather than pin 4. + * + * You will need to use the shield with an Arduino Zero, Due, Mega, or other board with sufficient + * Flash and RAM. Arduino Uno, Leonardo and other ATmega328p and Atmega32u4 boards to not have + * enough memory to run StandardFirmataBLE. + * + * TODO: verify if this works and with which boards it works. + * + * Test script: https://gist.github.com/soundanalogous/927360b797574ed50e27 + */ +//#define REDBEAR_BLE_SHIELD + +#ifdef REDBEAR_BLE_SHIELD +#include +#include +#include "utility/BLEStream.h" + +#define BLE_REQ 9 +#define BLE_RDY 8 +#define BLE_RST 4 // 4 or 7 via jumper on shield + +BLEStream stream(BLE_REQ, BLE_RDY, BLE_RST); +#endif + + +/*================================================================================================== + * END BLE CONFIGURATION - you should not need to change anything below this line + *================================================================================================*/ + +/* + * Arduino 101 + * + * Make sure you have the Intel Curie Boards package v1.0.6 or higher installed via the Arduino + * Boards Manager. + * + * Test script: https://gist.github.com/soundanalogous/927360b797574ed50e27 + */ +#ifdef _VARIANT_ARDUINO_101_X_ +#include +#include "BLEStream.h" +BLEStream stream; +#endif + + +/* + * RedBearLab BLE Nano (with default switch settings) + * + * Blocked on this issue: https://github.com/RedBearLab/nRF51822-Arduino/issues/46 + * Works with modifications. See comments at top of the test script referenced below. + * When the RBL nRF51822-Arduino library issue is resolved, this should work witout + * any modifications. + * + * Test script: https://gist.github.com/soundanalogous/d39bb3eb36333a0906df + * + * Note: If you have changed the solder jumpers on the Nano you may encounter issues since + * the pins are currently mapped in Firmata only for the default (factory) jumper settings. + */ +// #ifdef BLE_NANO +// #include +// #include "utility/BLEStream.h" +// BLEStream stream; +// #endif + + +/* + * RedBearLab Blend and Blend Micro + * + * StandardFirmataBLE requires too much Flash and RAM to run on the ATmega32u4-based Blend + * and Blend Micro boards. It may work with ConfigurableFirmata selecting only analog and/or + * digital I/O. + */ +// #if defined(BLEND_MICRO) || defined(BLEND) +// #include +// #include +// #include "utility/BLEStream.h" + +// #define BLE_REQ 6 +// #define BLE_RDY 7 +// #define BLE_RST 4 + +// BLEStream stream(BLE_REQ, BLE_RDY, BLE_RST); +// #endif + + +#if defined(BLE_REQ) && defined(BLE_RDY) && defined(BLE_RST) +#define IS_IGNORE_BLE_PINS(p) ((p) == BLE_REQ || (p) == BLE_RDY || (p) == BLE_RST) +#endif diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino new file mode 100644 index 00000000..641af931 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino @@ -0,0 +1,37 @@ +#include +// change the next line based on what your smartphone is configured to advertise +const String deviceName = "Nexus 5X"; +const int rssiTrigger = -50; +const int ledPin = 13; + +void setup() { + Serial.begin(9600); + BLE.begin(); + pinMode(ledPin, OUTPUT); + BLE.setEventHandler(BLEDiscovered, bleCentralDiscoverHandler); + while (!Serial) ; + Serial.println("Bluetooth device active, start scanning..."); + BLE.scan(true); +} + +void loop() { + BLE.poll(); +} + +void bleCentralDiscoverHandler(BLEDevice peripheral) { + if (peripheral.hasLocalName()) { + Serial.println(peripheral.localName()); + if (peripheral.localName().indexOf(deviceName) != -1) { + Serial.println(" found"); + Serial.print("Rssi: "); + Serial.println(peripheral.rssi()); + if (peripheral.rssi() > rssiTrigger) { + Serial.println("LED ON"); + digitalWrite(ledPin, HIGH); + } else { + Serial.println("LED OFF"); + digitalWrite(ledPin, LOW); + } + } + } +} diff --git a/libraries/CurieBLE/examples/test/central/central.ino b/libraries/CurieBLE/examples/test/central/central.ino new file mode 100644 index 00000000..5bc13524 --- /dev/null +++ b/libraries/CurieBLE/examples/test/central/central.ino @@ -0,0 +1,86 @@ + +#include "CurieBLE.h" + +// LED pin +#define LED_PIN 13 + +void setup() { + Serial.begin(9600); + Serial.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + BLE.scanForName("LED"); +} + +void controlLed(BLEDevice &peripheral) +{ + static bool discovered = false; + // connect to the peripheral + Serial.print("Connecting ... "); + Serial.println(peripheral.address()); + + if (peripheral.connect()) + { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + } + else + { + Serial.println("Failed to connect!"); + return; + } + + peripheral.discoverAttributes(); + + BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) + { + peripheral.disconnect(); + Serial.println("Peripheral does not have LED characteristic!"); + delay(5000); + return; + } + + + unsigned char ledstate = 0; + + discovered = false; + while (peripheral.connected()) + { + if (ledstate == 1) + { + ledstate = 0; + } + else + { + ledstate = 1; + } + ledCharacteristic.write(&ledstate, sizeof(ledstate)); + delay(5000); + } + Serial.print("Disconnected"); + Serial.println(peripheral.address()); +} + +void loop() { + BLEDevice peripheral = BLE.available(); + if (peripheral) + { + Serial.println(peripheral.address()); + BLE.stopScan(); + delay (1000); + // central connected to peripheral + controlLed(peripheral); + delay (4000); + BLE.scanForName("LED"); + } +} + + diff --git a/libraries/CurieBLE/examples/test/notification/notification.ino b/libraries/CurieBLE/examples/test/notification/notification.ino new file mode 100644 index 00000000..542102bb --- /dev/null +++ b/libraries/CurieBLE/examples/test/notification/notification.ino @@ -0,0 +1,78 @@ + +#include "CurieBLE.h" + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); + +BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial.begin(9600); + Serial.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + // set advertised local name and service UUID + BLE.setLocalName("LED"); + BLE.setAdvertisedServiceUuid(ledService.uuid()); + + // add service and characteristic + BLE.addService(ledService); + ledService.addCharacteristic(switchCharacteristic); + switchCharacteristic.addDescriptor(switchDescriptor); + unsigned char test = 1; + switchCharacteristic.writeValue(&test,1); + BLE.advertise(); +} + +void loop() { + static int i = 0; + BLEDevice central = BLE.central(); + bool temp = central; +i++; + if (temp) { + // central connected to peripheral + Serial.print(i); + Serial.print(F("Connected to central: ")); + Serial.println(central.address()); + + Serial.print(temp); + + unsigned char ledstate = 0; + + while (central.connected()) { + // central still connected to peripheral + if (switchCharacteristic.canNotify()) + { + + if (ledstate == 1) + { + ledstate = 0; + digitalWrite(LED_PIN, LOW); + } + else + { + ledstate = 1; + digitalWrite(LED_PIN, HIGH); + } + switchCharacteristic.writeValue(&ledstate, sizeof(ledstate)); + delay(5000); + } + } + + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } + //delay (1000); +} diff --git a/libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino b/libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino new file mode 100644 index 00000000..47086b18 --- /dev/null +++ b/libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino @@ -0,0 +1,89 @@ + +#include "CurieBLE.h" + +// LED pin +#define LED_PIN 13 + +void setup() { + Serial.begin(9600); + Serial.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + BLE.scanForName("LED"); +} + +void controlLed(BLEDevice &peripheral) +{ + static bool discovered = false; + // connect to the peripheral + Serial.print("Connecting ... "); + Serial.println(peripheral.address()); + + if (peripheral.connect()) + { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + } + else + { + Serial.println("Failed to connect!"); + return; + } + + peripheral.discoverAttributes(); + + BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) + { + peripheral.disconnect(); + Serial.println("Peripheral does not have LED characteristic!"); + delay(5000); + return; + } + ledCharacteristic.subscribe(); + + + discovered = false; + while (peripheral.connected()) + { + if (ledCharacteristic.valueUpdated()) + { + char ledValue = *ledCharacteristic.value(); + + if (ledValue) { + Serial.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + } else { + Serial.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + } + } + } + Serial.print("Disconnected"); + Serial.println(peripheral.address()); +} + +void loop() { + //pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + BLEDevice peripheral = BLE.available(); + //pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (peripheral) + { + Serial.println(peripheral.address()); + BLE.stopScan(); + delay (1000); + // central connected to peripheral + controlLed(peripheral); + delay (4000); + BLE.scanForName("LED"); + } +} + + diff --git a/libraries/CurieBLE/examples/test/test/test.ino b/libraries/CurieBLE/examples/test/test/test.ino new file mode 100644 index 00000000..bd4da60c --- /dev/null +++ b/libraries/CurieBLE/examples/test/test/test.ino @@ -0,0 +1,71 @@ + +#include "CurieBLE.h" + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); + +BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial.begin(9600); + Serial.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + // set advertised local name and service UUID + BLE.setLocalName("LED"); + BLE.setAdvertisedServiceUuid(ledService.uuid()); + + // add service and characteristic + BLE.addService(ledService); + ledService.addCharacteristic(switchCharacteristic); + switchCharacteristic.addDescriptor(switchDescriptor); + unsigned char test = 1; + switchCharacteristic.writeValue(&test,1); + BLE.advertise(); +} + +void loop() { + static int i = 0; + BLEDevice central = BLE.central(); + bool temp = central; +i++; + if (temp) { + // central connected to peripheral + Serial.print(i); + Serial.print(F("Connected to central: ")); + Serial.println(central.address()); + + Serial.print(temp); + + while (central.connected()) { + // central still connected to peripheral + if (switchCharacteristic.written()) { + char ledValue = *switchCharacteristic.value(); + // central wrote new value to characteristic, update LED + if (ledValue) { + Serial.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + } else { + Serial.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + } + } + } + + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } + //delay (1000); +}